p≡p JSON adapter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

380 lines
18 KiB

2 years ago
2 years ago
2 years ago
2 years ago
4 years ago
4 years ago
4 years ago
  1. #include <pEp/webserver.hh>
  2. #include "ev_server.hh"
  3. #include "c_string.hh"
  4. #include "prefix-config.hh"
  5. #include "json-adapter.hh"
  6. #include "function_map.hh"
  7. #include "pEp-types.hh"
  8. #include "json_rpc.hh"
  9. #include "pEp-utils.hh"
  10. #include "logger.hh"
  11. #include "server_version.hh"
  12. #include <pEp/message_api.h>
  13. #include <pEp/blacklist.h>
  14. #include <pEp/key_reset.h>
  15. #include <pEp/openpgp_compat.h>
  16. #include <pEp/message_api.h> // for get_binary_path()
  17. #include <pEp/mime.h>
  18. // libpEpAdapter:
  19. #include <pEp/Adapter.hh>
  20. #include <pEp/status_to_string.hh>
  21. #include <pEp/slurp.hh>
  22. #include <pEp/message_cache.hh>
  23. #include <boost/filesystem.hpp>
  24. #include "json_spirit/json_spirit_reader.h"
  25. // HACK:
  26. #ifndef JSON_ADAPTER_LIBRARY
  27. #include "mini-adapter-impl.hh"
  28. #else // JSON_ADAPTER_LIBRARY
  29. #include <pEp/callback_dispatcher.hh>
  30. #endif
  31. namespace fs = boost::filesystem;
  32. // compile-time default. might be overwritten in main() or before any ev_server function is called.
  33. fs::path ev_server::path_to_html = fs::path(html_directory);
  34. namespace {
  35. std::string version_as_a_string()
  36. {
  37. std::stringstream ss;
  38. ss << server_version();
  39. return ss.str();
  40. }
  41. std::string getBinaryPath()
  42. {
  43. const char* gpg_path = nullptr;
  44. const auto status = get_binary_path( PEP_crypt_OpenPGP, &gpg_path);
  45. if(status == PEP_STATUS_OK && gpg_path)
  46. {
  47. return std::string(gpg_path);
  48. }
  49. throw std::runtime_error("getBinaryPath returns error: " + ::pEp::status_to_string(status) );
  50. }
  51. // these are the pEp functions that are callable by the client
  52. const FunctionMap functions = {
  53. // from message_api.h
  54. FP( "Message API", new Separator ),
  55. FP( "encrypt_message", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message ) ),
  56. FP( "encrypt_message_and_add_priv_key", new FuncPC<PEP_STATUS, In_Pep_Session,
  57. In<message*>, Out<message*>, In<c_string>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message_and_add_priv_key) ),
  58. FP( "encrypt_message_for_self", new FuncPC<PEP_STATUS, In_Pep_Session,
  59. In<pEp_identity*>, In<message*>, In<stringlist_t*>, Out<message*>, In<PEP_enc_format>, In<PEP_encrypt_flags_t>>( &encrypt_message_for_self ) ),
  60. FP( "decrypt_message", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<message*>, Out<message*>, InOutP<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>>( &decrypt_message ) ),
  61. FP( "cache_decrypt_message", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<message*>, Out<message*>, InOutP<stringlist_t*>, Out<PEP_rating>, InOutP<PEP_decrypt_flags_t>>( &pEp::MessageCache::cache_decrypt_message) ),
  62. FP( "get_key_rating_for_user", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>, Out<PEP_rating>>( &get_key_rating_for_user) ),
  63. // from mime.h
  64. FP( "MIME handling API", new Separator),
  65. FP( "mime_encode_message", new Func<PEP_STATUS, In<const message*>, In<bool>, Out<char*>, In<bool, ParamFlag::NoInput>>( &mime_encode_message )),
  66. FP( "cache_mime_encode_message", new Func<PEP_STATUS, In<int>, In<const message*>, In<bool>, Out<char*>, In<bool, ParamFlag::NoInput>>( &pEp::MessageCache::cache_mime_encode_message)),
  67. FP( "cache_release", new Func<PEP_STATUS, In<c_string>>( &pEp::MessageCache::cache_release )),
  68. FP( "mime_decode_message", new Func<PEP_STATUS, In<c_string>, InLength<>, Out<message*>, In<bool*, ParamFlag::NoInput>>( &mime_decode_message )),
  69. // from pEpEngine.h
  70. FP( "pEp Engine Core API", new Separator),
  71. // FP( "log_event", new Func<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>, In<c_string>, In<c_string>>( &log_event) ),
  72. FP( "get_trustwords", new FuncPC<PEP_STATUS, In_Pep_Session, In<const pEp_identity*>, In<const pEp_identity*>, In<Language>, Out<char*>, Out<size_t>, In<bool>>( &get_trustwords) ),
  73. FP( "re_evaluate_message_rating", new FuncPC<PEP_STATUS, In_Pep_Session, In<message *>, In<stringlist_t *>, In<PEP_rating>, Out<PEP_rating>>( &re_evaluate_message_rating ) ),
  74. FP( "get_languagelist", new FuncPC<PEP_STATUS, In_Pep_Session, Out<char*>>( &get_languagelist) ),
  75. // FP( "get_phrase" , new Func<PEP_STATUS, In_Pep_Session, In<Language>, In<int>, Out<char*>> ( &get_phrase) ),
  76. // FP( "get_engine_version", new Func<const char*> ( &get_engine_version) ),
  77. FP( "is_pEp_user" , new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, Out<bool>>( &is_pEp_user) ),
  78. FP( "config_passive_mode", new FuncCache<void, In_Pep_Session, In<bool>>( "conf_p_m", &config_passive_mode) ),
  79. FP( "config_unencrypted_subject", new FuncCache<void, In_Pep_Session, In<bool>>( "conf_u_s", &config_unencrypted_subject) ),
  80. // not defined in pEpEngine, yet:
  81. // FP( "config_use_only_own_private_keys", new FuncCache<void, In_Pep_Session, In<bool>>( "conf_uoopk", &config_use_only_own_private_keys) ),
  82. FP( "config_service_log" , new FuncCache<void, In_Pep_Session, In<bool>>( "conf_service_log", &config_service_log) ),
  83. FP( "config_cipher_suite", new FuncCache<void, In_Pep_Session, In<PEP_CIPHER_SUITE>>( "config_cipher_suite", &config_cipher_suite) ),
  84. FP( "config_passphrase", new FuncCachePassphrase<void, In_Pep_Session, In<c_string>>( "config_passphrase" ) ),
  85. FP( "config_passphrase_for_new_keys", new FuncCachePassphrase4NewKeys<void, In_Pep_Session, In<bool>, In<c_string>>( "config_passphrase4nk" ) ),
  86. FP( "Identity Management API", new Separator),
  87. FP( "get_identity" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>, Out<pEp_identity*>>( &get_identity) ),
  88. FP( "set_identity" , new FuncPC<PEP_STATUS, In_Pep_Session, In<const pEp_identity*>> ( &set_identity) ),
  89. FP( "mark_as_comprimized", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>> ( &mark_as_compromized) ),
  90. FP( "identity_rating" , new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, Out<PEP_rating>>( &identity_rating) ),
  91. FP( "outgoing_message_rating", new FuncPC<PEP_STATUS, In_Pep_Session, In<message*>, Out<PEP_rating>>( &outgoing_message_rating) ),
  92. FP( "outgoing_message_rating_preview", new FuncPC<PEP_STATUS, In_Pep_Session, In<message*>, Out<PEP_rating>>( &outgoing_message_rating_preview) ),
  93. FP( "set_identity_flags" , new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<identity_flags_t>>( &set_identity_flags) ),
  94. FP( "unset_identity_flags" , new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<identity_flags_t>>( &unset_identity_flags) ),
  95. FP( "Low level Key Management API", new Separator),
  96. FP( "generate_keypair", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &generate_keypair) ),
  97. FP( "delete_keypair", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>> ( &delete_keypair) ),
  98. FP( "import_key" , new FuncPC<PEP_STATUS, In_Pep_Session, In<binary_string>, InLength<>, Out<identity_list*>> ( &import_key) ),
  99. FP( "export_key" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<char*>, Out<std::size_t>> ( &export_key) ),
  100. FP( "find_keys" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<stringlist_t*>> ( &find_keys) ),
  101. FP( "get_trust" , new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &get_trust) ),
  102. FP( "own_key_is_listed", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<bool>> ( &own_key_is_listed) ),
  103. FP( "own_identities_retrieve", new FuncPC<PEP_STATUS, In_Pep_Session, Out<identity_list*>>( &own_identities_retrieve ) ),
  104. FP( "set_own_key", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>, In<c_string>>( &set_own_key ) ),
  105. FP( "key_reset_identity", new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>, In<c_string>>( &key_reset_identity) ),
  106. FP( "key_reset_user", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string> , In<c_string, ParamFlag::NullOkay>>( &key_reset_user) ),
  107. FP( "key_reset_all_own_keys", new FuncPC<PEP_STATUS, In_Pep_Session>( &key_reset_all_own_keys) ),
  108. FP( "myself" , new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &myself) ),
  109. FP( "update_identity", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> ( &update_identity) ),
  110. FP( "trust_personal_key", new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &trust_personal_key) ),
  111. FP( "trust_own_key", new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &trust_own_key) ),
  112. FP( "key_mistrusted", new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &key_mistrusted) ),
  113. FP( "key_reset_trust", new FuncPC<PEP_STATUS, In_Pep_Session, In<pEp_identity*>>( &key_reset_trust) ),
  114. FP( "least_trust" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<PEP_comm_type>> ( &least_trust) ),
  115. FP( "get_key_rating", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<PEP_comm_type>> ( &get_key_rating) ),
  116. FP( "renew_key" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, In<const timestamp*>> ( &renew_key) ),
  117. FP( "revoke" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, In<c_string>> ( &revoke_key) ),
  118. FP( "key_expired" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, In<time_t>, Out<bool>> ( &key_expired) ),
  119. FP( "from blacklist.h & OpenPGP_compat.h", new Separator),
  120. FP( "blacklist_add" , new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>> ( &blacklist_add) ),
  121. FP( "blacklist_delete", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>> ( &blacklist_delete) ),
  122. FP( "blacklist_is_listed", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<bool>> ( &blacklist_is_listed) ),
  123. FP( "blacklist_retrieve" , new FuncPC<PEP_STATUS, In_Pep_Session, Out<stringlist_t*>> ( &blacklist_retrieve) ),
  124. FP( "OpenPGP_list_keyinfo", new FuncPC<PEP_STATUS, In_Pep_Session, In<c_string>, Out<stringpair_list_t*>> ( &OpenPGP_list_keyinfo) ),
  125. FP( "Event Listener & Results", new Separator ),
  126. FP( "deliverHandshakeResult" , new Func<PEP_STATUS, In_Pep_Session, In<sync_handshake_result>, In<const identity_list*> > (&deliverHandshakeResult) ),
  127. // TODO: session_id shall be removed as soon as we find a way to make it automatic again.
  128. // 'std::this_thread::id'' as ID did not work as expected. :-(
  129. // FP( "pollForEvents" , new Func<js::Array, In<JsonAdapter*,ParamFlag::NoInput>, In<unsigned>> (&JsonAdapter::pollForEvents) ),
  130. FP( "pollForEvents" , new Func<js::Array, In<JsonAdapter*,ParamFlag::NoInput>, In<std::string>, In<unsigned>> (&JsonAdapter::pollForEvents2) ),
  131. FP( "create_session" , new Func<std::string>(&JsonAdapter::create_session)),
  132. FP( "close_session" , new Func<void, In<JsonAdapter*,ParamFlag::NoInput>, In<std::string>> (&JsonAdapter::close_session) ),
  133. FP( "Sync", new Separator ),
  134. FP( "leave_device_group" , new FuncPC<PEP_STATUS, In_Pep_Session> (&leave_device_group) ),
  135. FP( "enable_identity_for_sync" , new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> (&enable_identity_for_sync)),
  136. FP( "disable_identity_for_sync", new FuncPC<PEP_STATUS, In_Pep_Session, InOut<pEp_identity*>> (&disable_identity_for_sync)),
  137. #ifndef JSON_ADAPTER_LIBRARY
  138. FP( "startSync", new Func<void> (&pEp::mini::startSync) ),
  139. FP( "stopSync" , new Func<void> (&pEp::mini::stopSync) ),
  140. #else
  141. FP( "startSync", new Func<void> (&pEp::CallbackDispatcher::start_sync) ),
  142. FP( "stopSync" , new Func<void> (&pEp::CallbackDispatcher::stop_sync) ),
  143. #endif
  144. // my own example function that does something useful. :-)
  145. FP( "Other", new Separator ),
  146. FP( "serverVersion", new Func<ServerVersion>( &server_version ) ),
  147. FP( "version", new Func<std::string>( &version_as_a_string ) ),
  148. FP( "getBinaryPath", new Func<std::string>( &getBinaryPath ) ),
  149. FP( "testMessageToSend", new Func<PEP_STATUS, In<message*>> (&JsonAdapter::messageToSend) ),
  150. FP( "shutdown", new Func<void, In<JsonAdapter*,ParamFlag::NoInput>>( &JsonAdapter::shutdown_now ) ),
  151. };
  152. bool add_sharks = false;
  153. } // end of anonymous namespace
  154. ev_server::ev_server(const std::string& address, unsigned short port, bool deliver_html, const std::string& base_url)
  155. : pEp::Webserver(pEp::net::ip::address::from_string(address), port)
  156. {
  157. this->add_url_handler(base_url + "callFunction", ev_server::OnApiRequest);
  158. if(deliver_html)
  159. {
  160. this->add_url_handler("/pEp_functions.js", ev_server::OnGetFunctions);
  161. this->add_generic_url_handler(ev_server::OnOtherRequest);
  162. }
  163. }
  164. pEp::Webserver::response ev_server::sendReplyString(const pEp::Webserver::request& req, const char* contentType, std::string&& outputText)
  165. {
  166. Log() << Logger::Debug << "sendReplyString(): "
  167. << ", contentType=" << (contentType ? "«" + std::string(contentType)+ "»" : "NULL")
  168. << ", output.size()=«" << outputText.size() << "»"
  169. << ", keep_alive=" << req.keep_alive() << ".";
  170. DEBUG_LOG(Log()) << "outputText=«" << outputText << "»";
  171. pEp::Webserver::response res{pEp::http::status::ok, req.version()};
  172. res.set(pEp::http::field::content_type, contentType);
  173. res.keep_alive(req.keep_alive());
  174. res.content_length(outputText.size());
  175. res.body() = std::move(outputText);
  176. return res;
  177. }
  178. pEp::Webserver::response ev_server::sendFile(const pEp::Webserver::request& req, const char* mimeType, const fs::path& fileName)
  179. {
  180. // not the best for big files, but this server does not send big files. :-)
  181. std::string fileContent = pEp::slurp(fileName.string());
  182. return sendReplyString(req, mimeType, std::move(fileContent));
  183. }
  184. struct FileRequest
  185. {
  186. const char* mimeType;
  187. fs::path fileName;
  188. };
  189. // catch-all callback
  190. pEp::Webserver::response ev_server::OnOtherRequest(boost::cmatch match, const pEp::Webserver::request& req)
  191. {
  192. static const std::map<std::string, FileRequest > files =
  193. {
  194. { "/" , {"text/html" , path_to_html / "index.html" } },
  195. { "/jquery.js" , {"text/javascript", path_to_html / "jquery-2.2.0.min.js" } },
  196. { "/interactive.js" , {"text/javascript", path_to_html / "interactive.js" } },
  197. { "/favicon.ico" , {"image/vnd.microsoft.icon", path_to_html / "json-test.ico"} },
  198. };
  199. const std::string path = req.target().to_string(); // NB: is percent-encoded! does not relevant for the supported paths above.
  200. Log() << Logger::Debug << "** Request: [" << req.method_string().to_string() << "] " << "Path: [" + path + "]";
  201. try{
  202. const auto q = files.find(path);
  203. if(q != files.end()) // found in "files" map
  204. {
  205. Log() << Logger::Debug << "\t found file \"" << q->second.fileName.string() << "\", type=" << q->second.mimeType << ".\n";
  206. return sendFile( req, q->second.mimeType, q->second.fileName);
  207. }
  208. return pEp::Webserver::create_status_response(req, pEp::http::status::not_found);
  209. }
  210. catch(const std::runtime_error& e)
  211. {
  212. const std::string error_msg = "Internal error caused by path \"" + path + "\"";
  213. // Log e.what() to log file, but DON'T send it in HTTP error message
  214. // because it might contain sensitive information, e.g. local file paths etc.!
  215. Log() << Logger::Error << "OnOtherRequest: " << error_msg << ". what:" << e.what();
  216. return pEp::Webserver::create_status_response(req, pEp::http::status::internal_server_error );
  217. }
  218. };
  219. // generate a JavaScript file containing the definition of all registered callable functions, see above.
  220. pEp::Webserver::response ev_server::OnGetFunctions(boost::cmatch match, const pEp::Webserver::request& req)
  221. {
  222. static const auto& version = server_version();
  223. static const std::string preamble =
  224. "var Direction = { In:1, Out:2, InOut:3 };\n"
  225. "var Type = {\n"
  226. " Blob : 10, // binary strings or 'array of octet'\n"
  227. " String : 20, // human-readable, NUL-terminated utf8-encoded strings\n"
  228. " StringList: 25,\n"
  229. " Message : 30,\n"
  230. " PEP_color : 50,\n"
  231. " PEP_enc_format: 51,\n"
  232. " PEP_STATUS: 59,\n"
  233. " Uint16 : 80,\n"
  234. " Session : 90 // opaque type. only a special encoded 'handle' is used in JavaScript code\n"
  235. " };\n"
  236. "\n"
  237. "var server_version_name = \"" + version.name + "\";\n"
  238. "var server_version = \"" + version.major_minor_patch() + "\";\n"
  239. "var add_sharks = " + (add_sharks?"true":"false") + ";\n"
  240. "var pEp_functions = ";
  241. js::Array jsonfunctions;
  242. for(const auto& f : functions)
  243. {
  244. js::Object o;
  245. o.emplace_back( "name", f.first );
  246. f.second->setJavaScriptSignature( o );
  247. jsonfunctions.push_back( o );
  248. }
  249. std::string output = preamble + js::write( jsonfunctions, js::pretty_print | js::raw_utf8 | js::single_line_arrays )
  250. + ";\n"
  251. "\n"
  252. "// End of generated file.\n";
  253. return sendReplyString(req, "text/javascript", std::move(output));
  254. }
  255. pEp::Webserver::response ev_server::OnApiRequest(boost::cmatch match, const pEp::Webserver::request& req)
  256. {
  257. Logger L( Log(), "OnApiReq");
  258. int request_id = -42;
  259. js::Object answer;
  260. js::Value p;
  261. try
  262. {
  263. JsonAdapter& ja = JsonAdapter::getInstance();
  264. const std::string data_string = req.body();
  265. if(!data_string.empty())
  266. {
  267. #ifdef DEBUG_ENABLED
  268. L << Logger::Debug << "Data: «" << data_string << "» (" << data_string.size() << " bytes).";
  269. #else
  270. L << Logger::Debug << "Data.size=" << data_string.size() << ".";
  271. #endif
  272. bool b = js::read( data_string, p);
  273. if(p.type() == js::obj_type)
  274. {
  275. const js::Object& request = p.get_obj();
  276. answer = call( functions, request, &ja );
  277. }else{
  278. const std::string error_msg = "request body is not a JSON object. js::read() returned" + std::to_string(b);
  279. L << Logger::Error << error_msg;
  280. answer = make_error( JSON_RPC::PARSE_ERROR, error_msg, js::Value{data_string}, 42 );
  281. }
  282. }else{
  283. answer = make_error( JSON_RPC::INTERNAL_ERROR, "ZERO size request", p, request_id );
  284. }
  285. }
  286. catch(const std::exception& e)
  287. {
  288. L << Logger::Error << "Exception: \"" << e.what() << "\"";
  289. answer = make_error( JSON_RPC::INTERNAL_ERROR, "Got a std::exception: \"" + std::string(e.what()) + "\"", p, request_id );
  290. }
  291. return sendReplyString(req, "text/plain", js::write(answer, js::raw_utf8));
  292. };
  293. Logger& ev_server::Log()
  294. {
  295. static Logger L("evs");
  296. return L;
  297. }
  298. void ev_server::addSharks()
  299. {
  300. add_sharks = true;
  301. }
  302. void ev_server::thread_init()
  303. {
  304. // nothing to do, yet.
  305. }
  306. void ev_server::thread_done()
  307. {
  308. JsonAdapter::getInstance().connection_close_cb();
  309. pEp::Adapter::session(pEp::Adapter::release);
  310. }