diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-09 18:39:20 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-11-09 18:39:20 +0100 |
commit | 2094c666c6175e6e8cfb5a5a7a49da6004d042d8 (patch) | |
tree | 4a5ef1b1d9f3b653b4a522dddc65e0401d3e32c2 /lib | |
parent | 48d30ac32352545800bbc70130796a85ab7a62c3 (diff) | |
download | abrt-2094c666c6175e6e8cfb5a5a7a49da6004d042d8.tar.gz abrt-2094c666c6175e6e8cfb5a5a7a49da6004d042d8.tar.xz abrt-2094c666c6175e6e8cfb5a5a7a49da6004d042d8.zip |
applied gavin's patch to Catcut. Refactored common xmlrpc code into lib/Utils.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Plugins/Bugzilla.cpp | 110 | ||||
-rw-r--r-- | lib/Plugins/Catcut.cpp | 609 | ||||
-rw-r--r-- | lib/Plugins/Catcut.h | 3 | ||||
-rw-r--r-- | lib/Utils/Makefile.am | 4 | ||||
-rw-r--r-- | lib/Utils/abrt_xmlrpc.cpp | 76 | ||||
-rw-r--r-- | lib/Utils/abrt_xmlrpc.h | 27 |
6 files changed, 554 insertions, 275 deletions
diff --git a/lib/Plugins/Bugzilla.cpp b/lib/Plugins/Bugzilla.cpp index ecd4dd67..b3cef929 100644 --- a/lib/Plugins/Bugzilla.cpp +++ b/lib/Plugins/Bugzilla.cpp @@ -1,6 +1,7 @@ #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> #include "abrtlib.h" +#include "abrt_xmlrpc.h" #include "Bugzilla.h" #include "CrashTypes.h" #include "DebugDump.h" @@ -50,20 +51,6 @@ static void create_new_bug_description(const map_crash_report_t& pCrashReport, s pDescription += make_description_bz(pCrashReport); } -// FIXME: we still leak memory if this function detects a fault: -// many instances when we leave non-freed or non-xmlrpc_DECREF'ed data behind. -static void throw_if_xml_fault_occurred(xmlrpc_env *env) -{ - if (env->fault_occurred) - { - std::string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env->fault_string, env->fault_code); - xmlrpc_env_clean(env); // this is needed ONLY if fault_occurred - xmlrpc_env_init(env); // just in case user catches ex and _continues_ to use env - error_msg("%s", errmsg.c_str()); // show error in daemon log - throw CABRTException(EXCEP_PLUGIN, errmsg); - } -} - /* * Static namespace for xmlrpc stuff. @@ -72,81 +59,18 @@ static void throw_if_xml_fault_occurred(xmlrpc_env *env) namespace { -struct ctx { - xmlrpc_client* client; - xmlrpc_server_info* server_info; - - ctx(const char* url, bool no_ssl_verify) { new_xmlrpc_client(url, no_ssl_verify); } - ~ctx() { destroy_xmlrpc_client(); } +struct ctx: public abrt_xmlrpc_conn { + ctx(const char* url, bool no_ssl_verify): abrt_xmlrpc_conn(url, no_ssl_verify) {} - void new_xmlrpc_client(const char* url, bool no_ssl_verify); - void destroy_xmlrpc_client(); - - void login(const char* login, const char* passwd); - void logout(); - int32_t check_uuid_in_bugzilla(const char* component, const char* UUID); - bool check_cc_and_reporter(uint32_t bug_id, const char* login); - void add_plus_one_cc(uint32_t bug_id, const char* login); - uint32_t new_bug(const map_crash_report_t& pCrashReport); - void add_attachments(const char* bug_id_str, const map_crash_report_t& pCrashReport); + void login(const char* login, const char* passwd); + void logout(); + int32_t check_uuid_in_bugzilla(const char* component, const char* UUID); + bool check_cc_and_reporter(uint32_t bug_id, const char* login); + void add_plus_one_cc(uint32_t bug_id, const char* login); + uint32_t new_bug(const map_crash_report_t& pCrashReport); + void add_attachments(const char* bug_id_str, const map_crash_report_t& pCrashReport); }; -void ctx::new_xmlrpc_client(const char* url, bool no_ssl_verify) -{ - xmlrpc_env env; - xmlrpc_env_init(&env); - - /* This should be done at program startup, once. - * We do it in abrtd's main */ - /* xmlrpc_client_setup_global_const(&env); */ - - struct xmlrpc_curl_xportparms curlParms; - memset(&curlParms, 0, sizeof(curlParms)); - /* curlParms.network_interface = NULL; - done by memset */ - curlParms.no_ssl_verifypeer = no_ssl_verify; - curlParms.no_ssl_verifyhost = no_ssl_verify; -#ifdef VERSION - curlParms.user_agent = PACKAGE_NAME"/"VERSION; -#else - curlParms.user_agent = "abrt"; -#endif - - struct xmlrpc_clientparms clientParms; - memset(&clientParms, 0, sizeof(clientParms)); - clientParms.transport = "curl"; - clientParms.transportparmsP = &curlParms; - clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); - - client = NULL; - xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, - PACKAGE_NAME, VERSION, - &clientParms, XMLRPC_CPSIZE(transportparm_size), - &client); - throw_if_xml_fault_occurred(&env); - - server_info = xmlrpc_server_info_new(&env, url); - if (env.fault_occurred) - { - xmlrpc_client_destroy(client); - client = NULL; - } - throw_if_xml_fault_occurred(&env); -} - -void ctx::destroy_xmlrpc_client() -{ - if (server_info) - { - xmlrpc_server_info_free(server_info); - server_info = NULL; - } - if (client) - { - xmlrpc_client_destroy(client); - client = NULL; - } -} - void ctx::login(const char* login, const char* passwd) { xmlrpc_env env; @@ -156,7 +80,7 @@ void ctx::login(const char* login, const char* passwd) throw_if_xml_fault_occurred(&env); xmlrpc_value* result = NULL; - xmlrpc_client_call2(&env, client, server_info, "User.login", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "User.login", param, &result); xmlrpc_DECREF(param); if (result) xmlrpc_DECREF(result); @@ -179,7 +103,7 @@ void ctx::logout() throw_if_xml_fault_occurred(&env); xmlrpc_value* result = NULL; - xmlrpc_client_call2(&env, client, server_info, "User.logout", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "User.logout", param, &result); xmlrpc_DECREF(param); if (result) xmlrpc_DECREF(result); @@ -195,7 +119,7 @@ bool ctx::check_cc_and_reporter(uint32_t bug_id, const char* login) throw_if_xml_fault_occurred(&env); xmlrpc_value* result = NULL; - xmlrpc_client_call2(&env, client, server_info, "bugzilla.getBug", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "bugzilla.getBug", param, &result); throw_if_xml_fault_occurred(&env); xmlrpc_DECREF(param); @@ -263,7 +187,7 @@ void ctx::add_plus_one_cc(uint32_t bug_id, const char* login) throw_if_xml_fault_occurred(&env); xmlrpc_value* result = NULL; - xmlrpc_client_call2(&env, client, server_info, "Bug.update", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Bug.update", param, &result); throw_if_xml_fault_occurred(&env); xmlrpc_DECREF(result); @@ -281,7 +205,7 @@ int32_t ctx::check_uuid_in_bugzilla(const char* component, const char* UUID) throw_if_xml_fault_occurred(&env); xmlrpc_value* result = NULL; - xmlrpc_client_call2(&env, client, server_info, "Bug.search", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Bug.search", param, &result); throw_if_xml_fault_occurred(&env); xmlrpc_DECREF(param); @@ -362,7 +286,7 @@ uint32_t ctx::new_bug(const map_crash_report_t& pCrashReport) throw_if_xml_fault_occurred(&env); xmlrpc_value* result; - xmlrpc_client_call2(&env, client, server_info, "Bug.create", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Bug.create", param, &result); throw_if_xml_fault_occurred(&env); xmlrpc_value* id; @@ -409,7 +333,7 @@ void ctx::add_attachments(const char* bug_id_str, const map_crash_report_t& pCra free(encoded64); throw_if_xml_fault_occurred(&env); - xmlrpc_client_call2(&env, client, server_info, "bugzilla.addAttachment", param, &result); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "bugzilla.addAttachment", param, &result); throw_if_xml_fault_occurred(&env); xmlrpc_DECREF(result); xmlrpc_DECREF(param); diff --git a/lib/Plugins/Catcut.cpp b/lib/Plugins/Catcut.cpp index 13fa8a41..f594bb48 100644 --- a/lib/Plugins/Catcut.cpp +++ b/lib/Plugins/Catcut.cpp @@ -1,7 +1,8 @@ #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> - +#include <curl/curl.h> #include "abrtlib.h" +#include "abrt_xmlrpc.h" #include "Catcut.h" #include "CrashTypes.h" #include "DebugDump.h" @@ -13,108 +14,8 @@ using namespace std; -static xmlrpc_env env; -static xmlrpc_client* client = NULL; -static struct xmlrpc_clientparms clientParms; -static struct xmlrpc_curl_xportparms curlParms; -static xmlrpc_server_info* server_info = NULL; - - -static string login(const char* login, const char* passwd); -//static void logout(); -static void new_xmlrpc_client(const char* url, bool no_ssl_verify); -static void destroy_xmlrpc_client(); -static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription); -static void get_product_and_version(const string& pRelease, - string& pProduct, - string& pVersion); - - -static void throw_if_xml_fault_occurred() -{ - if (env.fault_occurred) - { - string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code); - error_msg("%s", errmsg.c_str()); // show error in daemon log - throw CABRTException(EXCEP_PLUGIN, errmsg); - } -} - -static void new_xmlrpc_client(const char* url, bool no_ssl_verify) -{ - xmlrpc_env_init(&env); - - /* This should be done at program startup, once. - * We do it in abrtd's main */ - /* xmlrpc_client_setup_global_const(&env); */ - - curlParms.network_interface = NULL; - curlParms.no_ssl_verifypeer = no_ssl_verify; - curlParms.no_ssl_verifyhost = no_ssl_verify; -#ifdef VERSION - curlParms.user_agent = PACKAGE_NAME"/"VERSION; -#else - curlParms.user_agent = "abrt"; -#endif - - clientParms.transport = "curl"; - clientParms.transportparmsP = &curlParms; - clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); - - xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE_NAME, VERSION, &clientParms, XMLRPC_CPSIZE(transportparm_size), - &client); - throw_if_xml_fault_occurred(); - - server_info = xmlrpc_server_info_new(&env, url); - throw_if_xml_fault_occurred(); -} - -static void destroy_xmlrpc_client() -{ - xmlrpc_server_info_free(server_info); - xmlrpc_env_clean(&env); - xmlrpc_client_destroy(client); -} - -static string login(const char* login, const char* passwd) -{ - xmlrpc_value* param = xmlrpc_build_value(&env, "(ss)", login, passwd); - throw_if_xml_fault_occurred(); - - xmlrpc_value* result; - xmlrpc_client_call2(&env, client, server_info, "Catcut.auth", param, &result); - throw_if_xml_fault_occurred(); - xmlrpc_DECREF(param); - - xmlrpc_value *cookie_xml; - const char *cookie; - string cookie_str; - xmlrpc_struct_find_value(&env, result, "cookie", &cookie_xml); - throw_if_xml_fault_occurred(); - xmlrpc_read_string(&env, cookie_xml, &cookie); - throw_if_xml_fault_occurred(); - cookie_str = cookie; - /* xmlrpc_read_string returns *malloc'ed ptr*. - * doc is not very clear on it, but I looked in xmlrpc sources. */ - free((void*)cookie); - xmlrpc_DECREF(cookie_xml); - - xmlrpc_DECREF(result); - - return cookie_str; -} - -// catcut does not have it (yet?) -//static void logout() -//{ -// xmlrpc_value* param = xmlrpc_build_value(&env, "(s)", ""); -// throw_if_xml_fault_occurred(); -// -// xmlrpc_value* result = NULL; /* paranoia */ -// xmlrpc_client_call2(&env, client, server_info, "User.logout", param, &result); -// throw_if_xml_fault_occurred(); -//} +//TODO: move to make_descr.cpp static void create_new_bug_description(const map_crash_report_t& pCrashReport, string& pDescription) { string howToReproduce; @@ -167,39 +68,288 @@ static void create_new_bug_description(const map_crash_report_t& pCrashReport, s } } -static void get_product_and_version(const string& pRelease, - string& pProduct, - string& pVersion) +static void +get_product_and_version(const char *pRelease, + string& pProduct, + string& pVersion) { - if (pRelease.find("Rawhide") != string::npos) + if (strstr(pRelease, "Rawhide")) { pProduct = "Fedora"; pVersion = "rawhide"; return; } - if (pRelease.find("Fedora") != string::npos) + if (strstr(pRelease, "Fedora")) { pProduct = "Fedora"; } - else if (pRelease.find("Red Hat Enterprise Linux") != string::npos) + else if (strstr(pRelease, "Red Hat Enterprise Linux")) { pProduct = "Red Hat Enterprise Linux "; } - string::size_type pos = pRelease.find("release"); - pos = pRelease.find(" ", pos) + 1; - while (pRelease[pos] != ' ') + + const char *release = strstr(pRelease, "release"); + const char *space = release ? strchr(release, ' ') : NULL; + + if (space++) while (*space != '\0' && *space != ' ') { - pVersion += pRelease[pos]; + /* Eat string like "5.2" */ + pVersion += *space; if (pProduct == "Red Hat Enterprise Linux ") { - pProduct += pRelease[pos]; + pProduct += *space; } - pos++; + space++; + } +} + +static int +put_stream(const char *pURL, FILE* f, size_t content_length) +{ + CURL* curl = curl_easy_init(); + if (!curl) + { + throw CABRTException(EXCEP_PLUGIN, "put_stream: Curl library error."); + } + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + /* specify target */ + curl_easy_setopt(curl, CURLOPT_URL, pURL); + /* file handle: passed to the default callback, it will fread() it */ + curl_easy_setopt(curl, CURLOPT_READDATA, f); + /* get file size */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE, content_length); + /*everything is done here; result 0 means success*/ + int result = curl_easy_perform(curl); + /* goodbye */ + curl_easy_cleanup(curl); + return result; +} + +static void +send_string(const char *pURL, + const char *pContent, + int retryCount, + int retryDelaySeconds) +{ + if (pURL[0] == '\0') + { + error_msg(_("send_string: URL not specified")); + return; + } + + do + { + int content_length = strlen(pContent); + FILE* f = fmemopen((void*)pContent, content_length, "r"); + if (!f) + { + throw CABRTException(EXCEP_PLUGIN, "send_string: could not open string stream"); + } + int result = put_stream(pURL, f, content_length); + fclose(f); + if (!result) + return; + update_client(_("Sending failed, try it again: %s"), curl_easy_strerror((CURLcode)result)); + } + /*retry the upload if not succesful, wait a bit before next try*/ + while (--retryCount != 0 && (sleep(retryDelaySeconds), 1)); + + throw CABRTException(EXCEP_PLUGIN, "send_string: could not send string"); +} + +static void +send_file(const char *pURL, + const char *pFilename, + int retryCount, + int retryDelaySeconds) +{ + if (pURL[0] == '\0') + { + error_msg(_("send_file: URL not specified")); + return; + } + + update_client(_("Sending file %s to %s"), pFilename, pURL); + + do + { + FILE* f = fopen(pFilename, "r"); + if (!f) + { + throw CABRTException(EXCEP_PLUGIN, "send_file: could not open string stream"); + } + struct stat buf; + fstat(fileno(f), &buf); /* can't fail */ + int content_length = buf.st_size; + int result = put_stream(pURL, f, content_length); + fclose(f); + if (!result) + return; + update_client(_("Sending failed, try it again: %s"), curl_easy_strerror((CURLcode)result)); + } + /*retry the upload if not succesful, wait a bit before next try*/ + while (--retryCount != 0 && (sleep(retryDelaySeconds), 1)); + + throw CABRTException(EXCEP_PLUGIN, "send_file: could not send file"); +} + +static string +resolve_relative_url(const char *url, const char *base) +{ + // if 'url' is relative (not absolute) combine it with 'base' + // (which must be absolute) + // Only works in limited cases: + // 0) url is already absolute + // 1) url starts with two slashes + // 2) url starts with one slash + + const char *colon = strchr(url, ':'); + const char *slash = strchr(url, '/'); + + if (colon && (!slash || colon < slash)) + { + return url; + } + + const char *end_of_protocol = strchr(base, ':'); + string protocol(base, end_of_protocol - base); + + end_of_protocol += 3; /* skip "://" */ + const char *end_of_host = strchr(end_of_protocol, '/'); + string host(end_of_protocol, end_of_host - end_of_protocol); + + if (url[0] == '/') + { + if (url[1] == '/') + { + protocol += ':'; + protocol += url; + return protocol; + } + protocol += "://"; + protocol += host; + protocol += url; + return protocol; + } + throw CABRTException(EXCEP_PLUGIN, "resolve_relative_url: unhandled relative url"); +} + +// +// struct_find_XXXX +// abstract all the busy work of getting a field's value from +// a struct. XXXX is a type. +// Return true/false = the field is in the struct +// If true, return the field's value in 'value'. +// +// This function currently just assumes that the value in the +// field can be read into the type of 'value'. This should probably +// be fixed to either convert the fields value to the type of 'value' +// or error specifically/usefully. +// +// This function probably should be converted to an overloaded function +// (overloaded on the type of 'value'). It could also be a function +// template. +// + +static bool +struct_find_int(xmlrpc_env* env, xmlrpc_value* result, + const char* fieldName, int& value) +{ + xmlrpc_value* an_xmlrpc_value; + xmlrpc_struct_find_value(env, result, fieldName, &an_xmlrpc_value); + throw_if_xml_fault_occurred(env); + if (an_xmlrpc_value) + { + xmlrpc_read_int(env, an_xmlrpc_value, &value); + throw_if_xml_fault_occurred(env); + xmlrpc_DECREF(an_xmlrpc_value); + return true; } + return false; } -static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport) +static bool +struct_find_string(xmlrpc_env* env, xmlrpc_value* result, + const char* fieldName, string& value) { + xmlrpc_value* an_xmlrpc_value; + xmlrpc_struct_find_value(env, result, fieldName, &an_xmlrpc_value); + throw_if_xml_fault_occurred(env); + if (an_xmlrpc_value) + { + const char* value_s; + xmlrpc_read_string(env, an_xmlrpc_value, &value_s); + throw_if_xml_fault_occurred(env); + value = value_s; + xmlrpc_DECREF(an_xmlrpc_value); + free((void*)value_s); + return true; + } + return false; +} + + +/* + * Static namespace for xmlrpc stuff. + * Used mainly to ensure we always destroy xmlrpc client and server_info. + */ + +namespace { + +struct ctx: public abrt_xmlrpc_conn { + ctx(const char* url, bool no_ssl_verify): abrt_xmlrpc_conn(url, no_ssl_verify) {} + + string login(const char* login, const char* passwd); + string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport); + string request_upload(const char* auth_cookie, const char* pTicketName, + const char* fileName, const char* description); + void add_attachments(const char* xmlrpc_URL, + const char* auth_cookie, + const char* pTicketName, + const map_crash_report_t& pCrashReport, + int retryCount, + int retryDelaySeconds); +}; + +string +ctx::login(const char* login, const char* passwd) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* param = xmlrpc_build_value(&env, "(ss)", login, passwd); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result; + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Catcut.auth", param, &result); + xmlrpc_DECREF(param); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value *cookie_xml; + const char *cookie; + string cookie_str; + xmlrpc_struct_find_value(&env, result, "cookie", &cookie_xml); + throw_if_xml_fault_occurred(&env); + xmlrpc_read_string(&env, cookie_xml, &cookie); + throw_if_xml_fault_occurred(&env); + cookie_str = cookie; + /* xmlrpc_read_string returns *malloc'ed ptr*. + * doc is not very clear on it, but I looked in xmlrpc sources. */ + free((void*)cookie); + xmlrpc_DECREF(cookie_xml); + + xmlrpc_DECREF(result); + + return cookie_str; +} + +string +ctx::new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + string package = pCrashReport.find(FILENAME_PACKAGE)->second[CD_CONTENT]; string component = pCrashReport.find(FILENAME_COMPONENT)->second[CD_CONTENT]; string release = pCrashReport.find(FILENAME_RELEASE)->second[CD_CONTENT]; @@ -214,7 +364,7 @@ static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashR string product; string version; - get_product_and_version(release, product, version); + get_product_and_version(release.c_str(), product, version); xmlrpc_value *param = xmlrpc_build_value(&env, "(s{s:s,s:s,s:s,s:s,s:s,s:s,s:s})", auth_cookie, @@ -226,20 +376,20 @@ static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashR "status_whiteboard", status_whiteboard.c_str(), "platform", arch.c_str() ); - throw_if_xml_fault_occurred(); + throw_if_xml_fault_occurred(&env); xmlrpc_value *result; - xmlrpc_client_call2(&env, client, server_info, "Catcut.createTicket", param, &result); - throw_if_xml_fault_occurred(); + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Catcut.createTicket", param, &result); xmlrpc_DECREF(param); + throw_if_xml_fault_occurred(&env); xmlrpc_value *bug_id_xml; const char *bug_id; string bug_id_str; xmlrpc_struct_find_value(&env, result, "ticket", &bug_id_xml); - throw_if_xml_fault_occurred(); + throw_if_xml_fault_occurred(&env); xmlrpc_read_string(&env, bug_id_xml, &bug_id); - throw_if_xml_fault_occurred(); + throw_if_xml_fault_occurred(&env); bug_id_str = bug_id; log("New bug id: %s", bug_id); update_client(_("New bug id: %s"), bug_id); @@ -251,78 +401,175 @@ static string new_bug(const char *auth_cookie, const map_crash_report_t& pCrashR return bug_id_str; } -//static -//void add_attachments(const string& pBugId, const map_crash_report_t& pCrashReport) -//{ -// xmlrpc_value* result = NULL; -// -// map_crash_report_t::const_iterator it = pCrashReport.begin(); -// for (; it != pCrashReport.end(); it++) -// { -// if (it->second[CD_TYPE] == CD_ATT) -// { -// string description = "File: " + it->first; -// const string& to_encode = it->second[CD_CONTENT]; -// char *encoded64 = encode_base64(to_encode.c_str(), to_encode.length()); -// xmlrpc_value* param = xmlrpc_build_value(&env,"(s{s:s,s:s,s:s,s:s})", -// pBugId.c_str(), -// "description", description.c_str(), -// "filename", it->first.c_str(), -// "contenttype", "text/plain", -// "data", encoded64 -// ); -// free(encoded64); -// throw_if_xml_fault_occurred(); -// -//// catcut has this API: -//// struct response requestUpload(string cookie, string ticket, string filename, string description) -////response MUST include "errno", "errmsg" members; if an upload is approved, -////a "URL" MUST be returned in the response. The description string -////should include a brief description of the file. -//// -////The client should upload the file via HTTP PUT to the provided -////URL. The provided URL may be absolute or relative, if relative it must -////be combined with the base URL of the XML-RPC server using the usual -////rules for relative URL's (RFC 3986). -// xmlrpc_client_call2(&env, client, server_info, "catcut.addAttachment", param, &result); -// throw_if_xml_fault_occurred(); -// } -// } -//} +string +ctx::request_upload(const char* auth_cookie, const char* pTicketName, + const char* fileName, const char* description) +{ + xmlrpc_env env; + xmlrpc_env_init(&env); + + xmlrpc_value* param = xmlrpc_build_value(&env, "(ssss)", + auth_cookie, + pTicketName, + fileName, + description); + throw_if_xml_fault_occurred(&env); + + xmlrpc_value* result = NULL; + xmlrpc_client_call2(&env, m_pClient, m_pServer_info, "Catcut.requestUpload", param, &result); + xmlrpc_DECREF(param); + throw_if_xml_fault_occurred(&env); + + string URL; + bool has_URL = struct_find_string(&env, result, "uri", URL); + if (!has_URL || URL == "") + { + int err; + bool has_errno = struct_find_int(&env, result, "errno", err); + if (has_errno && err) + { + string errmsg; + bool has_errmsg = struct_find_string(&env, result, "errmsg", errmsg); + if (has_errmsg) + { + log("error returned by requestUpload: %s", errmsg.c_str()); + update_client(_("error returned by requestUpload: %s"), errmsg.c_str()); + } + else + { + log("error returned by requestUpload: %d", err); + update_client(_("error returned by requestUpload: %d"), err); + } + } + else + { + log("no URL returned by requestUpload, and no err"); + update_client(_("no URL returned by requestUpload, and no errno")); + } + } + + log("requestUpload returned URL: %s", URL.c_str()); + update_client(_("requestUpload returned URL: %s"), URL.c_str()); + + xmlrpc_DECREF(result); + return URL; +} + +void +ctx::add_attachments(const char* xmlrpc_URL, + const char* auth_cookie, + const char* pTicketName, + const map_crash_report_t& pCrashReport, + int retryCount, + int retryDelaySeconds) +{ + + map_crash_report_t::const_iterator it = pCrashReport.begin(); + for (; it != pCrashReport.end(); it++) + { + if (it->second[CD_TYPE] == CD_ATT) + { + update_client(_("Attaching (CD_ATT): %s"), it->first.c_str()); + + string description = "File: " + it->first; + string URL = request_upload(auth_cookie, + pTicketName, + it->first.c_str(), + description.c_str()); + + URL = resolve_relative_url(URL.c_str(), xmlrpc_URL); + + log("rebased URL: %s", URL.c_str()); + update_client(_("rebased URL: %s"), URL.c_str()); + + send_string(URL.c_str(), it->second[CD_CONTENT].c_str(), + retryCount, retryDelaySeconds); + } + else if (it->second[CD_TYPE] == CD_BIN) + { + update_client(_("Attaching (CD_ATT): %s"), it->first.c_str()); + + string description = "File: " + it->first; + string URL = request_upload(auth_cookie, + pTicketName, + it->first.c_str(), + description.c_str()); + + URL = resolve_relative_url(URL.c_str(), xmlrpc_URL); + + log("rebased URL: %s", URL.c_str()); + update_client(_("rebased URL: %s"), URL.c_str()); + + send_file(URL.c_str(), it->second[CD_CONTENT].c_str(), + retryCount, retryDelaySeconds); + } + } +} + +} /* namespace */ + + +/* + * CReporterCatcut + */ CReporterCatcut::CReporterCatcut() : m_sCatcutURL("http://127.0.0.1:8080/catcut/xmlrpc"), - m_bNoSSLVerify(false) + m_bNoSSLVerify(false), + m_nRetryCount(3), + m_nRetryDelay(20) {} CReporterCatcut::~CReporterCatcut() {} string CReporterCatcut::Report(const map_crash_report_t& pCrashReport, - const map_plugin_settings_t& pSettings, const string& pArgs) + const map_plugin_settings_t& pSettings, + const string& pArgs) { update_client(_("Creating new bug...")); try { - new_xmlrpc_client(m_sCatcutURL.c_str(), m_bNoSSLVerify); - string auth_cookie = login(m_sLogin.c_str(), m_sPassword.c_str()); - string bug_id = (auth_cookie != "") ? new_bug(auth_cookie.c_str(), pCrashReport) : ""; -// add_attachments(to_string(bug_id), pCrashReport); -// update_client(_("Logging out...")); -// logout(); - destroy_xmlrpc_client(); - return "New catcut bug ID: " + bug_id; + ctx catcut_server(m_sCatcutURL.c_str(), m_bNoSSLVerify); + string auth_cookie = catcut_server.login(m_sLogin.c_str(), m_sPassword.c_str()); + string message; + if (auth_cookie != "") + { + string ticket_name = catcut_server.new_bug(auth_cookie.c_str(), pCrashReport); + if (ticket_name != "") + { + catcut_server.add_attachments( + m_sCatcutURL.c_str(), + auth_cookie.c_str(), + ticket_name.c_str(), + pCrashReport, + m_nRetryCount, + m_nRetryDelay + ); + message = "New catcut bug ID: " + ticket_name; + } + else + { + message = "Error could not create ticket"; + } + } + else + { + message = "Error could not create ticket"; + } + return message; } catch (CABRTException& e) { - destroy_xmlrpc_client(); - throw CABRTException(EXCEP_PLUGIN, string("CReporterCatcut::Report(): ") + e.what()); + throw CABRTException(EXCEP_PLUGIN, e.what()); } } void CReporterCatcut::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator it; map_plugin_settings_t::const_iterator end = pSettings.end(); @@ -346,16 +593,16 @@ void CReporterCatcut::SetSettings(const map_plugin_settings_t& pSettings) { m_bNoSSLVerify = it->second == "yes"; } -} - -const map_plugin_settings_t& CReporterCatcut::GetSettings() -{ - m_pSettings["CatcutURL"] = m_sCatcutURL; - m_pSettings["Login"] = m_sLogin; - m_pSettings["Password"] = m_sPassword; - m_pSettings["NoSSLVerify"] = m_bNoSSLVerify ? "yes" : "no"; - - return m_pSettings; + it = pSettings.find("RetryCount"); + if (it != end) + { + m_nRetryCount = atoi(it->second.c_str()); + } + it = pSettings.find("RetryDelay"); + if (it != end) + { + m_nRetryDelay = atoi(it->second.c_str()); + } } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/Catcut.h b/lib/Plugins/Catcut.h index 15efdc1d..4fb89e22 100644 --- a/lib/Plugins/Catcut.h +++ b/lib/Plugins/Catcut.h @@ -11,13 +11,14 @@ class CReporterCatcut : public CReporter std::string m_sLogin; std::string m_sPassword; bool m_bNoSSLVerify; + int m_nRetryCount; + int m_nRetryDelay; public: CReporterCatcut(); virtual ~CReporterCatcut(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, const map_plugin_settings_t& pSettings, diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am index 68c925fd..d2596f39 100644 --- a/lib/Utils/Makefile.am +++ b/lib/Utils/Makefile.am @@ -15,6 +15,7 @@ libABRTUtils_la_SOURCES = \ DebugDump.h DebugDump.cpp \ CommLayerInner.h CommLayerInner.cpp \ abrt_dbus.h abrt_dbus.cpp \ + abrt_xmlrpc.h abrt_xmlrpc.cpp \ Plugin.h Plugin.cpp make_descr.cpp \ Polkit.h Polkit.cpp \ Action.h Database.h Reporter.h Analyzer.h \ @@ -30,6 +31,7 @@ libABRTUtils_la_CPPFLAGS = \ -DVAR_RUN=\"$(VAR_RUN)\" \ $(GLIB_CFLAGS) \ $(DBUS_CFLAGS) \ + $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \ $(POLKIT_CFLAGS) \ -D_GNU_SOURCE libABRTUtils_la_LDFLAGS = \ @@ -37,8 +39,10 @@ libABRTUtils_la_LDFLAGS = \ $(DL_LIBS) \ $(DBUS_LIBS) libABRTUtils_la_LIBADD = \ + $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \ $(POLKIT_LIBS) + install-data-local: $(mkdir_p) '$(DESTDIR)/$(DEBUG_DUMPS_DIR)' chmod 1777 '$(DESTDIR)/$(DEBUG_DUMPS_DIR)' diff --git a/lib/Utils/abrt_xmlrpc.cpp b/lib/Utils/abrt_xmlrpc.cpp new file mode 100644 index 00000000..11c431b8 --- /dev/null +++ b/lib/Utils/abrt_xmlrpc.cpp @@ -0,0 +1,76 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include "abrtlib.h" +#include "abrt_xmlrpc.h" +#include "ABRTException.h" + +void throw_if_xml_fault_occurred(xmlrpc_env *env) +{ + if (env->fault_occurred) + { + std::string errmsg = ssprintf("XML-RPC Fault: %s(%d)", env->fault_string, env->fault_code); + xmlrpc_env_clean(env); // this is needed ONLY if fault_occurred + xmlrpc_env_init(env); // just in case user catches ex and _continues_ to use env + error_msg("%s", errmsg.c_str()); // show error in daemon log + throw CABRTException(EXCEP_PLUGIN, errmsg); + } +} + +void abrt_xmlrpc_conn::new_xmlrpc_client(const char* url, bool no_ssl_verify) +{ + m_pClient = NULL; + m_pServer_info = NULL; + + xmlrpc_env env; + xmlrpc_env_init(&env); + + /* This should be done at program startup, once. + * We do it in abrtd's main */ + /* xmlrpc_client_setup_global_const(&env); */ + + struct xmlrpc_curl_xportparms curlParms; + memset(&curlParms, 0, sizeof(curlParms)); + /* curlParms.network_interface = NULL; - done by memset */ + curlParms.no_ssl_verifypeer = no_ssl_verify; + curlParms.no_ssl_verifyhost = no_ssl_verify; +#ifdef VERSION + curlParms.user_agent = PACKAGE_NAME"/"VERSION; +#else + curlParms.user_agent = "abrt"; +#endif + + struct xmlrpc_clientparms clientParms; + memset(&clientParms, 0, sizeof(clientParms)); + clientParms.transport = "curl"; + clientParms.transportparmsP = &curlParms; + clientParms.transportparm_size = XMLRPC_CXPSIZE(user_agent); + + xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS, + PACKAGE_NAME, VERSION, + &clientParms, XMLRPC_CPSIZE(transportparm_size), + &m_pClient); + throw_if_xml_fault_occurred(&env); + + m_pServer_info = xmlrpc_server_info_new(&env, url); + if (env.fault_occurred) + { + xmlrpc_client_destroy(m_pClient); + m_pClient = NULL; + } + throw_if_xml_fault_occurred(&env); +} + +void abrt_xmlrpc_conn::destroy_xmlrpc_client() +{ + if (m_pServer_info) + { + xmlrpc_server_info_free(m_pServer_info); + m_pServer_info = NULL; + } + if (m_pClient) + { + xmlrpc_client_destroy(m_pClient); + m_pClient = NULL; + } +} diff --git a/lib/Utils/abrt_xmlrpc.h b/lib/Utils/abrt_xmlrpc.h new file mode 100644 index 00000000..e67ab19a --- /dev/null +++ b/lib/Utils/abrt_xmlrpc.h @@ -0,0 +1,27 @@ +#ifndef ABRT_XMLRPC_H_ +#define ABRT_XMLRPC_H_ 1 + +#include <xmlrpc-c/base.h> +#include <xmlrpc-c/client.h> + +/* + * Simple class holding XMLRPC connection data. + * Used mainly to ensure we always destroy xmlrpc client and server_info + * on return or throw. + */ + +struct abrt_xmlrpc_conn { + xmlrpc_client* m_pClient; + xmlrpc_server_info* m_pServer_info; + + abrt_xmlrpc_conn(const char* url, bool no_ssl_verify) { new_xmlrpc_client(url, no_ssl_verify); } + ~abrt_xmlrpc_conn() { destroy_xmlrpc_client(); } + + void new_xmlrpc_client(const char* url, bool no_ssl_verify); + void destroy_xmlrpc_client(); +}; + +/* Utility function */ +void throw_if_xml_fault_occurred(xmlrpc_env *env); + +#endif |