#include #include "ev_server.hh" #include "c_string.hh" #include "prefix-config.hh" #include "json-adapter.hh" #include "function_map.hh" #include "pEp-types.hh" #include "json_rpc.hh" #include "pEp-utils.hh" #include "logger.hh" #include "server_version.hh" #include #include #include #include #include // for get_binary_path() #include // libpEpAdapter: #include #include #include #include "json_spirit/json_spirit_reader.h" template<> In::~In() { // do nothing } template<> In::In(const js::Value&, Context* ctx, unsigned) : value( ctx ) { } namespace fs = boost::filesystem; // compile-time default. might be overwritten in main() or before any ev_server function is called. fs::path ev_server::path_to_html = fs::path(html_directory); namespace { std::string version_as_a_string() { std::stringstream ss; ss << server_version(); return ss.str(); } std::string getBinaryPath() { const char* gpg_path = nullptr; const auto status = get_binary_path( PEP_crypt_OpenPGP, &gpg_path); if(status == PEP_STATUS_OK && gpg_path) { return std::string(gpg_path); } throw std::runtime_error("getBinaryPath returns error: " + ::pEp::status_to_string(status) ); } using In_Pep_Session = In; // these are the pEp functions that are callable by the client const FunctionMap functions = { // from message_api.h FP( "Message API", new Separator ), FP( "encrypt_message", new Func, In, Out, In, In>( &encrypt_message ) ), FP( "encrypt_message_and_add_priv_key", new Func, Out, In, In, In>( &encrypt_message_and_add_priv_key) ), FP( "encrypt_message_for_self", new Func, In, In, Out, In, In>( &encrypt_message_for_self ) ), FP( "decrypt_message", new Func, Out, InOutP, Out, InOutP>( &decrypt_message ) ), FP( "outgoing_message_rating", new Func, Out>( &outgoing_message_rating ) ), FP( "identity_rating" , new Func, Out>( &identity_rating) ), FP( "get_key_rating_for_user", new Func, In, Out>( &get_key_rating_for_user) ), FP( "pEp Engine Core API", new Separator), // FP( "log_event", new Func, In, In, In>( &log_event) ), FP( "get_trustwords", new Func, In, In, Out, Out, In>( &get_trustwords) ), FP( "get_languagelist", new Func>( &get_languagelist) ), // FP( "get_phrase" , new Func, In, Out> ( &get_phrase) ), // FP( "get_engine_version", new Func ( &get_engine_version) ), FP( "is_pEp_user" , new Func, Out>( &is_pEp_user) ), FP( "config_passive_mode", new Func>( &config_passive_mode) ), FP( "config_unencrypted_subject", new Func>( &config_unencrypted_subject) ), FP( "Identity Management API", new Separator), FP( "get_identity" , new Func, In, Out>( &get_identity) ), FP( "set_identity" , new Func> ( &set_identity) ), FP( "mark_as_comprimized", new Func> ( &mark_as_compromized) ), FP( "identity_rating" , new Func, Out>( &identity_rating) ), FP( "outgoing_message_rating", new Func, Out>( &outgoing_message_rating) ), FP( "set_identity_flags" , new Func, In>( &set_identity_flags) ), FP( "unset_identity_flags" , new Func, In>( &unset_identity_flags) ), FP( "MIME message handling", new Separator), FP( "mime_decode_message", new Func, In, Out>( &mime_decode_message) ), FP( "Low level Key Management API", new Separator), FP( "generate_keypair", new Func> ( &generate_keypair) ), FP( "delete_keypair", new Func> ( &delete_keypair) ), FP( "import_key" , new Func, In, Out> ( &import_key) ), FP( "export_key" , new Func, Out, Out> ( &export_key) ), FP( "find_keys" , new Func, Out> ( &find_keys) ), FP( "get_trust" , new Func> ( &get_trust) ), FP( "own_key_is_listed", new Func, Out> ( &own_key_is_listed) ), FP( "own_identities_retrieve", new Func>( &own_identities_retrieve ) ), FP( "set_own_key", new Func, In>( &set_own_key ) ), FP( "key_reset_identity", new Func, In>( &key_reset_identity) ), FP( "key_reset_user", new Func , In>( &key_reset_user) ), FP( "key_reset_all_own_keys", new Func( &key_reset_all_own_keys) ), FP( "myself" , new Func> ( &myself) ), FP( "update_identity", new Func> ( &update_identity) ), FP( "trust_personal_key", new Func>( &trust_personal_key) ), FP( "trust_own_key", new Func>( &trust_own_key) ), FP( "key_mistrusted", new Func>( &key_mistrusted) ), FP( "key_reset_trust", new Func>( &key_reset_trust) ), FP( "least_trust" , new Func, Out> ( &least_trust) ), FP( "get_key_rating", new Func, Out> ( &get_key_rating) ), FP( "renew_key" , new Func, In> ( &renew_key) ), FP( "revoke" , new Func, In> ( &revoke_key) ), FP( "key_expired" , new Func, In, Out> ( &key_expired) ), FP( "from blacklist.h & OpenPGP_compat.h", new Separator), FP( "blacklist_add" , new Func> ( &blacklist_add) ), FP( "blacklist_delete", new Func> ( &blacklist_delete) ), FP( "blacklist_is_listed", new Func, Out> ( &blacklist_is_listed) ), FP( "blacklist_retrieve" , new Func> ( &blacklist_retrieve) ), FP( "OpenPGP_list_keyinfo", new Func, Out> ( &OpenPGP_list_keyinfo) ), FP( "Event Listener & Results", new Separator ), FP( "registerEventListener" , new Func, In, In, In> ( &JsonAdapter::registerEventListener) ), FP( "unregisterEventListener", new Func, In, In, In> ( &JsonAdapter::unregisterEventListener) ), FP( "deliverHandshakeResult" , new Func, In > (&deliverHandshakeResult) ), // my own example function that does something useful. :-) FP( "Other", new Separator ), FP( "serverVersion", new Func( &server_version ) ), FP( "version", new Func( &version_as_a_string ) ), FP( "getBinaryPath", new Func( &getBinaryPath ) ), FP( "shutdown", new Func>( &JsonAdapter::shutdown_now ) ), }; bool add_sharks = false; } // end of anonymous namespace void ev_server::sendReplyString(evhttp_request* req, const char* contentType, const std::string& outputText) { auto* outBuf = evhttp_request_get_output_buffer(req); if (!outBuf) return; if(contentType) { evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", contentType); } const int ret = evbuffer_add(outBuf, outputText.data(), outputText.size()); if(ret==0) { evhttp_send_reply(req, HTTP_OK, "", outBuf); }else{ evhttp_send_reply(req, 500, "evbuffer_add() failed.", outBuf); } Log() << Logger::Debug << "sendReplyString(): ret=" << ret << ", contentType=" << (contentType ? "«" + std::string(contentType)+ "»" : "NULL") << ", output=«" << outputText << "»."; } void ev_server::sendFile( evhttp_request* req, const std::string& mimeType, const fs::path& fileName) { auto* outBuf = evhttp_request_get_output_buffer(req); if (!outBuf) return; // not the best for big files, but this server does not send big files. :-) const std::string fileContent = pEp::slurp(fileName.string()); evbuffer_add(outBuf, fileContent.data(), fileContent.size()); evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", mimeType.c_str()); evhttp_send_reply(req, HTTP_OK, "", outBuf); } struct FileRequest { std::string mimeType; fs::path fileName; }; // catch-all callback void ev_server::OnOtherRequest(evhttp_request* req, void*) { static const std::map files = { { "/" , {"text/html" , path_to_html / "index.html" } }, { "/jquery.js" , {"text/javascript", path_to_html / "jquery-2.2.0.min.js" } }, { "/interactive.js" , {"text/javascript", path_to_html / "interactive.js" } }, { "/favicon.ico" , {"image/vnd.microsoft.icon", path_to_html / "json-test.ico"} }, }; const evhttp_uri* uri = evhttp_request_get_evhttp_uri(req); const char* path = evhttp_uri_get_path(uri); const char* uri_string = evhttp_request_get_uri(req); Log() << Logger::Debug << "** Request: [" << uri_string << "] " << (path? " Path: [" + std::string(path) + "]" : "null path") << "\n"; try{ if(path) { const auto q = files.find(path); if(q != files.end()) // found in "files" map { Log() << Logger::Debug << "\t found file \"" << q->second.fileName.string() << "\", type=" << q->second.mimeType << ".\n"; sendFile( req, q->second.mimeType, q->second.fileName); return; } } const std::string reply = std::string("URI \"") + uri_string + "\" not found! " + (!path ? "NULL Path" : "Path: \"" + std::string(path) + "\""); evhttp_send_error(req, HTTP_NOTFOUND, reply.c_str()); } catch(const std::runtime_error& e) { const std::string error_msg = "Internal error caused by URI \"" + std::string(uri_string) + "\""; // Log e.what() to log file, but DON'T send it in HTTP error message // because it might contain sensitive information, e.g. local file paths etc.! Log() << Logger::Error << "OnOtherRequest: " << error_msg << ". what:" << e.what(); evhttp_send_error(req, HTTP_INTERNAL, error_msg.c_str() ); } }; // generate a JavaScript file containing the definition of all registered callable functions, see above. void ev_server::OnGetFunctions(evhttp_request* req, void*) { static const auto& version = server_version(); static const std::string preamble = "var Direction = { In:1, Out:2, InOut:3 };\n" "var Type = {\n" " Blob : 10, // binary strings or 'array of octet'\n" " String : 20, // human-readable, NUL-terminated utf8-encoded strings\n" " StringList: 25,\n" " Message : 30,\n" " PEP_color : 50,\n" " PEP_enc_format: 51,\n" " PEP_STATUS: 59,\n" " Uint16 : 80,\n" " Session : 90 // opaque type. only a special encoded 'handle' is used in JavaScript code\n" " };\n" "\n" "var server_version_name = \"" + version.name + "\";\n" "var server_version = \"" + version.major_minor_patch() + "\";\n" "var add_sharks = " + (add_sharks?"true":"false") + ";\n" "var pEp_functions = "; js::Array jsonfunctions; for(const auto& f : functions) { js::Object o; o.emplace_back( "name", f.first ); f.second->setJavaScriptSignature( o ); jsonfunctions.push_back( o ); } const std::string output = preamble + js::write( jsonfunctions, js::pretty_print | js::raw_utf8 | js::single_line_arrays ) + ";\n" "\n" "// End of generated file.\n"; sendReplyString(req, "text/javascript", output.c_str()); } void ev_server::OnApiRequest(evhttp_request* req, void* obj) { Logger L( Log(), "OnApiReq"); evbuffer* inbuf = evhttp_request_get_input_buffer(req); const size_t length = evbuffer_get_length(inbuf); int request_id = -42; js::Object answer; js::Value p; try { JsonAdapter* ja = static_cast(obj); std::vector data(length); const ev_ssize_t nr = evbuffer_copyout(inbuf, data.data(), data.size()); const std::string data_string(data.data(), data.data() + nr ); if(nr>0) { L << Logger::Debug << "Data: «" << data_string << "»"; bool b = js::read( data_string, p); if(p.type() == js::obj_type) { const js::Object& request = p.get_obj(); answer = call( functions, request, ja ); }else{ const std::string error_msg = "evbuffer_copyout does not return a JSON string. b=" + std::to_string(b); L << Logger::Error << error_msg; answer = make_error( JSON_RPC::PARSE_ERROR, error_msg, js::Value{data_string}, 42 ); } }else{ L << Logger::Error << "Error: " << nr << ".\n"; answer = make_error( JSON_RPC::INTERNAL_ERROR, "evbuffer_copyout returns negative value", p, request_id ); } } catch(const std::exception& e) { L << Logger::Error << "Exception: \"" << e.what() << "\""; answer = make_error( JSON_RPC::INTERNAL_ERROR, "Got a std::exception: \"" + std::string(e.what()) + "\"", p, request_id ); } sendReplyString(req, "text/plain", js::write(answer, js::raw_utf8)); }; Logger& ev_server::Log() { static Logger L("evs"); return L; } void ev_server::addSharks() { add_sharks = true; }