BBS2chProxyKeyManager.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. #include "BBS2chProxyKeyManager.h"
  2. #if defined(USE_YYJSON)
  3. #include "yyjson/yyjson.h"
  4. #elif defined(USE_PICOJSON)
  5. #include <fstream>
  6. #include "picojson/picojson.h"
  7. #else
  8. #include "parson/parson.h"
  9. #endif
  10. extern double getCurrentTime(void);
  11. extern void log_printf(int level, const char *format ...);
  12. const std::string BBS2chProxyKeyManager::_emptyKey = "00000000-0000-0000-0000-000000000000";
  13. const std::string& BBS2chProxyKeyManager::getKey()
  14. {
  15. if (_lastKey.empty())
  16. return _emptyKey;
  17. return _lastKey;
  18. }
  19. const std::string& BBS2chProxyKeyManager::getKey(const std::string &userAgent)
  20. {
  21. pthread_mutex_lock(&_mutex);
  22. std::map<std::string, std::string>::iterator it = _keys.find(userAgent);
  23. if (it != _keys.end()) {
  24. const std::string &key = it->second;
  25. pthread_mutex_unlock(&_mutex);
  26. return key;
  27. }
  28. pthread_mutex_unlock(&_mutex);
  29. return _emptyKey;
  30. }
  31. void BBS2chProxyKeyManager::setKey(const std::string &key, const std::string &oldKey, const std::string &userAgent, int reason)
  32. {
  33. /* do nothing when all-zero key is given - is this safe for all cases? */
  34. if (key == _emptyKey) {
  35. return;
  36. }
  37. pthread_mutex_lock(&_mutex);
  38. if (key.empty()) {
  39. if ((reason >= 3320 && reason <= 3324) || (reason >= 3390 && reason <= 3392)) {
  40. _expiredKeys.insert(oldKey);
  41. }
  42. _keys.erase(userAgent);
  43. log_printf(1, "Reset MonaKey for %s\n", userAgent.c_str());
  44. }
  45. else {
  46. _keys[userAgent] = key;
  47. _keyIssueTimes[key] = getCurrentTime();
  48. saveKeys();
  49. log_printf(1, "Updated MonaKey for %s: %s\n", userAgent.c_str(), key.c_str());
  50. }
  51. _lastKey = key;
  52. pthread_mutex_unlock(&_mutex);
  53. }
  54. bool BBS2chProxyKeyManager::isExpired(const std::string &key)
  55. {
  56. return _expiredKeys.count(key) != 0;
  57. }
  58. double BBS2chProxyKeyManager::secondsToWaitBeforePosting(const std::string &key)
  59. {
  60. double seconds = 0.0;
  61. pthread_mutex_lock(&_mutex);
  62. std::map<std::string, double>::iterator it = _keyIssueTimes.find(key);
  63. if (it != _keyIssueTimes.end()) {
  64. seconds = 4.0 - (getCurrentTime() - it->second);
  65. }
  66. pthread_mutex_unlock(&_mutex);
  67. return seconds;
  68. }
  69. void BBS2chProxyKeyManager::setStorage(const char *jsonPath)
  70. {
  71. _storagePath = jsonPath;
  72. }
  73. /* should be called once on main thread */
  74. int BBS2chProxyKeyManager::loadKeys()
  75. {
  76. if (_storagePath.empty()) return 0;
  77. #if defined(USE_YYJSON)
  78. yyjson_doc *doc = yyjson_read_file(_storagePath.c_str(), YYJSON_READ_ALLOW_TRAILING_COMMAS, NULL, NULL);
  79. if (!doc) return 0;
  80. yyjson_val *root = yyjson_doc_get_root(doc);
  81. if (!yyjson_is_obj(root)) {
  82. yyjson_doc_free(doc);
  83. return 0;
  84. }
  85. yyjson_val *keyStore = yyjson_obj_get(root, "MonaKeys");
  86. if (!yyjson_is_obj(keyStore)) {
  87. yyjson_doc_free(doc);
  88. return 0;
  89. }
  90. int numKeys = 0;
  91. double latest = -1;
  92. size_t idx, max;
  93. yyjson_val *key, *val;
  94. yyjson_obj_foreach(keyStore, idx, max, key, val) {
  95. if (!yyjson_is_obj(val)) continue;
  96. const char *userAgent = yyjson_get_str(key);
  97. const char *key_ = yyjson_get_str(yyjson_obj_get(val, "key"));
  98. double issued_on = yyjson_get_real(yyjson_obj_get(val, "issued_on"));
  99. if (key_) {
  100. numKeys++;
  101. _keys[userAgent] = key_;
  102. if (issued_on > 0) {
  103. _keyIssueTimes[key_] = issued_on;
  104. }
  105. if (issued_on > latest) {
  106. _lastKey = key_;
  107. }
  108. }
  109. }
  110. yyjson_doc_free(doc);
  111. return numKeys;
  112. #elif defined(USE_PICOJSON)
  113. std::ifstream ifs(_storagePath, std::ios::binary);
  114. if (!ifs) return 0;
  115. ifs.unsetf(std::ios::skipws);
  116. picojson::value root;
  117. std::string err;
  118. picojson::parse(root, std::istream_iterator<char>(ifs), std::istream_iterator<char>(), &err);
  119. if (!err.empty()) return 0;
  120. if (!root.is<picojson::object>()) return 0;
  121. picojson::value& keyStore = root.get<picojson::object>()["MonaKeys"];
  122. if (!keyStore.is<picojson::object>()) return 0;
  123. int numKeys = 0;
  124. double latest = -1;
  125. picojson::value::object& obj = keyStore.get<picojson::object>();
  126. for (picojson::value::object::iterator it = obj.begin(); it != obj.end(); it++) {
  127. if (!it->second.is<picojson::object>()) continue;
  128. const std::string &userAgent = it->first;
  129. const picojson::value& key = it->second.get<picojson::object>()["key"];
  130. const picojson::value& issued_on = it->second.get<picojson::object>()["issued_on"];
  131. if (key.is<std::string>()) {
  132. const std::string &keyValue = key.get<std::string>();
  133. numKeys++;
  134. _keys[userAgent] = keyValue;
  135. if (issued_on.is<double>()) {
  136. double value = issued_on.get<double>();
  137. if (value > 0) {
  138. _keyIssueTimes[keyValue] = value;
  139. }
  140. if (value > latest) {
  141. _lastKey = keyValue;
  142. }
  143. }
  144. }
  145. }
  146. return numKeys;
  147. #else
  148. JSON_Value *json = json_parse_file(_storagePath.c_str());
  149. if (!json) return 0;
  150. if (json_type(json) != JSONObject) {
  151. json_value_free(json);
  152. return 0;
  153. }
  154. JSON_Object *root = json_object(json);
  155. JSON_Object *keyStore = json_object_get_object(root, "MonaKeys");
  156. if (!keyStore) {
  157. json_value_free(json);
  158. return 0;
  159. }
  160. int numKeys = 0;
  161. double latest = -1;
  162. size_t length = json_object_get_count(keyStore);
  163. for (size_t i=0; i<length; i++) {
  164. const char *userAgent = json_object_get_name(keyStore, i);
  165. JSON_Value *entry = json_object_get_value_at(keyStore, i);
  166. if (json_type(json) != JSONObject) continue;
  167. const char *key = json_object_get_string(json_object(entry), "key");
  168. double issued_on = json_object_get_number(json_object(entry), "issued_on");
  169. if (key) {
  170. numKeys++;
  171. _keys[userAgent] = key;
  172. if (issued_on > 0) {
  173. _keyIssueTimes[key] = issued_on;
  174. }
  175. if (issued_on > latest) {
  176. _lastKey = key;
  177. }
  178. }
  179. }
  180. json_value_free(json);
  181. return numKeys;
  182. #endif
  183. }
  184. /* this private function is called inside setKey(), and should not be called from other places */
  185. bool BBS2chProxyKeyManager::saveKeys()
  186. {
  187. if (_storagePath.empty()) return false;
  188. #if defined(USE_YYJSON)
  189. yyjson_mut_doc *doc;
  190. yyjson_mut_val *root;
  191. yyjson_doc *tmp = yyjson_read_file(_storagePath.c_str(), YYJSON_READ_ALLOW_TRAILING_COMMAS, NULL, NULL);
  192. if (tmp) {
  193. doc = yyjson_doc_mut_copy(tmp, NULL);
  194. yyjson_doc_free(tmp);
  195. root = yyjson_mut_doc_get_root(doc);
  196. if (!yyjson_mut_is_obj(root)) {
  197. log_printf(0, "The file %s looks like JSON but unusable as a MonaKey storage.\n", _storagePath.c_str());
  198. yyjson_mut_doc_free(doc);
  199. return false;
  200. }
  201. } else {
  202. doc = yyjson_mut_doc_new(NULL);
  203. root = yyjson_mut_obj(doc);
  204. yyjson_mut_doc_set_root(doc, root);
  205. }
  206. yyjson_mut_val *keyStore = yyjson_mut_obj(doc);
  207. yyjson_mut_obj_put(root, yyjson_mut_str(doc, "MonaKeys"), keyStore);
  208. for (std::map<std::string, std::string>::iterator it = _keys.begin(); it != _keys.end(); it++) {
  209. const std::string &userAgent = it->first;
  210. const std::string &key = it->second;
  211. double issued_on = 0;
  212. std::map<std::string, double>::iterator it2 = _keyIssueTimes.find(key);
  213. if (it2 != _keyIssueTimes.end()) {
  214. issued_on = it2->second;
  215. }
  216. yyjson_mut_val *entry = yyjson_mut_obj(doc);
  217. yyjson_mut_obj_add_str(doc, entry, "key", key.c_str());
  218. if (issued_on > 0) yyjson_mut_obj_add_real(doc, entry, "issued_on", issued_on);
  219. yyjson_mut_obj_put(keyStore, yyjson_mut_str(doc, userAgent.c_str()), entry);
  220. }
  221. yyjson_write_err err;
  222. yyjson_mut_write_file(_storagePath.c_str(), doc, YYJSON_WRITE_PRETTY|YYJSON_WRITE_ESCAPE_UNICODE, NULL, &err);
  223. if (err.code) {
  224. log_printf(0, "Error while writing MonaKeys to %s: %s\n", _storagePath.c_str(), err.msg);
  225. }
  226. yyjson_mut_doc_free(doc);
  227. return err.code == 0;
  228. #elif defined(USE_PICOJSON)
  229. picojson::value root;
  230. std::ifstream ifs(_storagePath, std::ios::binary);
  231. if (ifs) {
  232. ifs.unsetf(std::ios::skipws);
  233. std::string err;
  234. picojson::parse(root, std::istream_iterator<char>(ifs), std::istream_iterator<char>(), &err);
  235. ifs.close();
  236. if (!err.empty() && !root.is<picojson::object>()) {
  237. log_printf(0, "The file %s looks like JSON but unusable as a MonaKey storage.\n", _storagePath.c_str());
  238. return false;
  239. }
  240. }
  241. if (root.is<picojson::null>()) {
  242. root = picojson::value(picojson::object());
  243. }
  244. picojson::object keyStore;
  245. for (std::map<std::string, std::string>::iterator it = _keys.begin(); it != _keys.end(); it++) {
  246. const std::string &userAgent = it->first;
  247. const std::string &key = it->second;
  248. double issued_on = 0;
  249. std::map<std::string, double>::iterator it2 = _keyIssueTimes.find(key);
  250. if (it2 != _keyIssueTimes.end()) {
  251. issued_on = it2->second;
  252. }
  253. picojson::object entry;
  254. entry.insert(std::make_pair("key", picojson::value(key)));
  255. if (issued_on > 0) entry.insert(std::make_pair("issued_on", picojson::value(issued_on)));
  256. keyStore.insert(std::make_pair(userAgent, entry));
  257. }
  258. root.get<picojson::object>().insert(std::make_pair("MonaKeys", keyStore));
  259. std::ofstream ofs(_storagePath);
  260. if (!ofs) {
  261. log_printf(0, "Error while writing MonaKeys to %s\n", _storagePath.c_str());
  262. return false;
  263. }
  264. root.serialize(std::ostream_iterator<char>(ofs), true);
  265. return true;
  266. #else
  267. JSON_Value *json = json_parse_file(_storagePath.c_str());
  268. if (!json) {
  269. json = json_value_init_object();
  270. }
  271. else if (json_type(json) != JSONObject) {
  272. log_printf(0, "The file %s looks like JSON but unusable as a MonaKey storage.\n", _storagePath.c_str());
  273. json_value_free(json);
  274. return false;
  275. }
  276. JSON_Object *root = json_object(json);
  277. JSON_Value *object = json_value_init_object();
  278. json_object_set_value(root, "MonaKeys", object);
  279. JSON_Object *keyStore = json_object(object);
  280. for (std::map<std::string, std::string>::iterator it = _keys.begin(); it != _keys.end(); it++) {
  281. const std::string &userAgent = it->first;
  282. const std::string &key = it->second;
  283. double issued_on = 0;
  284. std::map<std::string, double>::iterator it2 = _keyIssueTimes.find(key);
  285. if (it2 != _keyIssueTimes.end()) {
  286. issued_on = it2->second;
  287. }
  288. JSON_Value *entry = json_value_init_object();
  289. json_object_set_string(json_object(entry), "key", key.c_str());
  290. if (issued_on > 0) json_object_set_number(json_object(entry), "issued_on", issued_on);
  291. json_object_set_value(keyStore, userAgent.c_str(), entry);
  292. }
  293. bool ret = json_serialize_to_file_pretty(json, _storagePath.c_str()) == JSONSuccess;
  294. if (!ret) {
  295. log_printf(0, "Error while writing MonaKeys to %s\n", _storagePath.c_str());
  296. }
  297. json_value_free(json);
  298. return ret;
  299. #endif
  300. }