diff options
Diffstat (limited to 'lib')
28 files changed, 859 insertions, 562 deletions
diff --git a/lib/Plugins/Bugzilla.cpp b/lib/Plugins/Bugzilla.cpp index ecd4dd6..06f9334 100644 --- a/lib/Plugins/Bugzilla.cpp +++ b/lib/Plugins/Bugzilla.cpp @@ -1,69 +1,25 @@ #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" #include "ABRTException.h" #include "CommLayerInner.h" #ifdef HAVE_CONFIG_H - #include <config.h> +# include <config.h> #endif #define XML_RPC_SUFFIX "/xmlrpc.cgi" -static void get_product_and_version(const std::string& pRelease, - std::string& pProduct, - std::string& pVersion) -{ - if (pRelease.find("Rawhide") != std::string::npos) - { - pProduct = "Fedora"; - pVersion = "rawhide"; - return; - } - if (pRelease.find("Fedora") != std::string::npos) - { - pProduct = "Fedora"; - } - else if (pRelease.find("Red Hat Enterprise Linux") != std::string::npos) - { - pProduct = "Red Hat Enterprise Linux "; - } - std::string::size_type pos = pRelease.find("release"); - pos = pRelease.find(" ", pos) + 1; - while (pRelease[pos] != ' ') - { - pVersion += pRelease[pos]; - if (pProduct == "Red Hat Enterprise Linux ") - { - pProduct += pRelease[pos]; - } - pos++; - } -} - static void create_new_bug_description(const map_crash_report_t& pCrashReport, std::string& pDescription) { pDescription = "abrt detected a crash.\n\n"; 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 +28,18 @@ static void throw_if_xml_fault_occurred(xmlrpc_env *env) namespace { -struct ctx { - xmlrpc_client* client; - xmlrpc_server_info* server_info; +struct ctx: public abrt_xmlrpc_conn { + ctx(const char* url, bool no_ssl_verify): abrt_xmlrpc_conn(url, no_ssl_verify) {} - ctx(const char* url, bool no_ssl_verify) { new_xmlrpc_client(url, no_ssl_verify); } - ~ctx() { destroy_xmlrpc_client(); } - - 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 +49,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 +72,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 +88,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 +156,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 +174,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); @@ -348,7 +241,7 @@ uint32_t ctx::new_bug(const map_crash_report_t& pCrashReport) std::string product; std::string version; - get_product_and_version(release, product, version); + parse_release(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})", "product", product.c_str(), @@ -362,7 +255,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 +302,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); @@ -444,14 +337,14 @@ std::string CReporterBugzilla::Report(const map_crash_report_t& pCrashReport, std::string BugzillaURL; bool NoSSLVerify; map_plugin_settings_t settings = parse_settings(pSettings); - /* if parse_settings fails it returns an empty map so we need to use defaults*/ - if(!settings.empty()) + /* if parse_settings fails it returns an empty map so we need to use defaults */ + if (!settings.empty()) { Login = settings["Login"]; Password = settings["Password"]; BugzillaXMLRPC = settings["BugzillaXMLRPC"]; BugzillaURL = settings["BugzillaURL"]; - NoSSLVerify = settings["NoSSLVerify"] == "yes"; + NoSSLVerify = string_to_bool(settings["NoSSLVerify"].c_str()); } else { @@ -513,9 +406,9 @@ std::string CReporterBugzilla::Report(const map_crash_report_t& pCrashReport, map_plugin_settings_t CReporterBugzilla::parse_settings(const map_plugin_settings_t& pSettings) { map_plugin_settings_t plugin_settings; - map_plugin_settings_t::const_iterator it; - map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; it = pSettings.find("BugzillaURL"); if (it != end) { @@ -567,6 +460,8 @@ map_plugin_settings_t CReporterBugzilla::parse_settings(const map_plugin_setting void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + //BUG! This gets called when user's keyring contains login data, //then it takes precedence over /etc/abrt/plugins/Bugzilla.conf. //I got a case when keyring had a STALE password, and there was no way @@ -574,9 +469,8 @@ void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) //(by hacking abrt source!), I don't know how to purge it from the keyring. //At the very least, log("SOMETHING") here. - map_plugin_settings_t::const_iterator it; map_plugin_settings_t::const_iterator end = pSettings.end(); - + map_plugin_settings_t::const_iterator it; it = pSettings.find("BugzillaURL"); if (it != end) { @@ -614,10 +508,11 @@ void CReporterBugzilla::SetSettings(const map_plugin_settings_t& pSettings) it = pSettings.find("NoSSLVerify"); if (it != end) { - m_bNoSSLVerify = (it->second == "yes"); + m_bNoSSLVerify = string_to_bool(it->second.c_str()); } } +/* Should not be deleted (why?) */ const map_plugin_settings_t& CReporterBugzilla::GetSettings() { m_pSettings["BugzillaURL"] = m_sBugzillaURL; diff --git a/lib/Plugins/Bugzilla.h b/lib/Plugins/Bugzilla.h index 55a5f5f..9130a69 100644 --- a/lib/Plugins/Bugzilla.h +++ b/lib/Plugins/Bugzilla.h @@ -19,11 +19,13 @@ class CReporterBugzilla : public CReporter public: CReporterBugzilla(); virtual ~CReporterBugzilla(); - 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, const std::string& pArgs); + + virtual void SetSettings(const map_plugin_settings_t& pSettings); + virtual const map_plugin_settings_t& GetSettings(); }; #endif /* BUGZILLA_H_ */ diff --git a/lib/Plugins/CCpp.cpp b/lib/Plugins/CCpp.cpp index 6d6edd5..82c5677 100644 --- a/lib/Plugins/CCpp.cpp +++ b/lib/Plugins/CCpp.cpp @@ -127,6 +127,18 @@ static pid_t ExecVP(char** pArgs, uid_t uid, std::string& pOutput) setreuid(uid, uid); setsid(); + /* Nuke everything which may make setlocale() switch to non-POSIX locale: + * we need to avoid having gdb output in some obscure language. + */ + unsetenv("LANG"); + unsetenv("LC_ALL"); + unsetenv("LC_COLLATE"); + unsetenv("LC_CTYPE"); + unsetenv("LC_MESSAGES"); + unsetenv("LC_MONETARY"); + unsetenv("LC_NUMERIC"); + unsetenv("LC_TIME"); + execvp(pArgs[0], pArgs); /* VERB1 since sometimes we expect errors here */ VERB1 perror_msg("Can't execute '%s'", pArgs[0]); @@ -164,6 +176,8 @@ static LineRating rate_line(const char *line) { #define FOUND(x) (strstr(line, x) != NULL) /* see the "enum LineRating" comments for possible combinations */ + if (FOUND(" at ")) + return Good; const char *function = strstr(line, " in "); if (function) { @@ -171,12 +185,6 @@ static LineRating rate_line(const char *line) { function = NULL; } - else - { - bool source_file = FOUND(" at "); - if (source_file) - return Good; - } } bool library = FOUND(" from "); if (function && library) @@ -193,7 +201,7 @@ static LineRating rate_line(const char *line) /* returns number of "stars" to show */ static int rate_backtrace(const char *backtrace) { - int i, len; + int i, j, len; int multiplier = 0; int rating = 0; int best_possible_rating = 0; @@ -210,6 +218,9 @@ static int rate_backtrace(const char *backtrace) if (backtrace[i] == '#') /* this separates frames from each other */ { std::string s(backtrace + i + 1, len); + for (j=0; j<len; j++) /* replace tabs with spaces */ + if (s[j] == '\t') + s[j] = ' '; multiplier++; rating += rate_line(s.c_str()) * multiplier; best_possible_rating += BestRating * multiplier; @@ -969,7 +980,7 @@ void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) it = pSettings.find("MemoryMap"); if (it != end) { - m_bMemoryMap = it->second == "yes"; + m_bMemoryMap = string_to_bool(it->second.c_str()); } it = pSettings.find("DebugInfo"); if (it != end) @@ -986,19 +997,20 @@ void CAnalyzerCCpp::SetSettings(const map_plugin_settings_t& pSettings) it = pSettings.find("InstallDebuginfo"); if (it != end) { - m_bInstallDebugInfo = it->second == "yes"; + m_bInstallDebugInfo = string_to_bool(it->second.c_str()); } } -const map_plugin_settings_t& CAnalyzerCCpp::GetSettings() -{ - m_pSettings["MemoryMap"] = m_bMemoryMap ? "yes" : "no"; - m_pSettings["DebugInfo"] = m_sDebugInfo; - m_pSettings["DebugInfoCacheMB"] = to_string(m_nDebugInfoCacheMB); - m_pSettings["InstallDebugInfo"] = m_bInstallDebugInfo ? "yes" : "no"; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CAnalyzerCCpp::GetSettings() +//{ +// m_pSettings["MemoryMap"] = m_bMemoryMap ? "yes" : "no"; +// m_pSettings["DebugInfo"] = m_sDebugInfo; +// m_pSettings["DebugInfoCacheMB"] = to_string(m_nDebugInfoCacheMB); +// m_pSettings["InstallDebugInfo"] = m_bInstallDebugInfo ? "yes" : "no"; +// +// return m_pSettings; +//} PLUGIN_INFO(ANALYZER, CAnalyzerCCpp, diff --git a/lib/Plugins/CCpp.h b/lib/Plugins/CCpp.h index e2abdec..3fa0d99 100644 --- a/lib/Plugins/CCpp.h +++ b/lib/Plugins/CCpp.h @@ -44,7 +44,8 @@ class CAnalyzerCCpp : public CAnalyzer virtual void Init(); virtual void DeInit(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); }; #endif /* CCPP */ diff --git a/lib/Plugins/Catcut.cpp b/lib/Plugins/Catcut.cpp index 13fa8a4..e6d16b4 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,253 @@ 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 int +put_stream(const char *pURL, FILE* f, size_t content_length) { - if (pRelease.find("Rawhide") != string::npos) + CURL* curl = curl_easy_init(); + if (!curl) { - pProduct = "Fedora"; - pVersion = "rawhide"; + 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; } - if (pRelease.find("Fedora") != string::npos) + + do { - pProduct = "Fedora"; + 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)); } - else if (pRelease.find("Red Hat Enterprise Linux") != string::npos) + /*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') { - pProduct = "Red Hat Enterprise Linux "; + error_msg(_("send_file: URL not specified")); + return; } - string::size_type pos = pRelease.find("release"); - pos = pRelease.find(" ", pos) + 1; - while (pRelease[pos] != ' ') + + update_client(_("Sending file %s to %s"), pFilename, pURL); + + do { - pVersion += pRelease[pos]; - if (pProduct == "Red Hat Enterprise Linux ") + FILE* f = fopen(pFilename, "r"); + if (!f) { - pProduct += pRelease[pos]; + throw CABRTException(EXCEP_PLUGIN, "send_file: could not open string stream"); } - pos++; + 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 new_bug(const char *auth_cookie, const map_crash_report_t& pCrashReport) +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 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 +329,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); + parse_release(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 +341,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,81 +366,177 @@ 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) { - map_plugin_settings_t::const_iterator it; - map_plugin_settings_t::const_iterator end = pSettings.end(); + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; it = pSettings.find("CatcutURL"); if (it != end) { @@ -344,18 +555,18 @@ void CReporterCatcut::SetSettings(const map_plugin_settings_t& pSettings) it = pSettings.find("NoSSLVerify"); if (it != end) { - m_bNoSSLVerify = it->second == "yes"; + m_bNoSSLVerify = string_to_bool(it->second.c_str()); + } + 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()); } -} - -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; } PLUGIN_INFO(REPORTER, diff --git a/lib/Plugins/Catcut.h b/lib/Plugins/Catcut.h index 15efdc1..4fb89e2 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/Plugins/FileTransfer.cpp b/lib/Plugins/FileTransfer.cpp index 72b3b16..868f54d 100644 --- a/lib/Plugins/FileTransfer.cpp +++ b/lib/Plugins/FileTransfer.cpp @@ -370,8 +370,11 @@ void CFileTransfer::Run(const char *pActionDir, const char *pArgs) void CFileTransfer::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator end = pSettings.end(); - map_plugin_settings_t::const_iterator it = pSettings.find("URL"); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("URL"); if (it != end) { m_sURL = it->second; @@ -405,15 +408,16 @@ void CFileTransfer::SetSettings(const map_plugin_settings_t& pSettings) } } -const map_plugin_settings_t& CFileTransfer::GetSettings() -{ - m_pSettings["URL"] = m_sURL; - m_pSettings["RetryCount"] = to_string(m_nRetryCount); - m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); - m_pSettings["ArchiveType"] = m_sArchiveType; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CFileTransfer::GetSettings() +//{ +// m_pSettings["URL"] = m_sURL; +// m_pSettings["RetryCount"] = to_string(m_nRetryCount); +// m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); +// m_pSettings["ArchiveType"] = m_sArchiveType; +// +// return m_pSettings; +//} PLUGIN_INFO(ACTION, CFileTransfer, diff --git a/lib/Plugins/FileTransfer.h b/lib/Plugins/FileTransfer.h index 9caa256..91207f4 100644 --- a/lib/Plugins/FileTransfer.h +++ b/lib/Plugins/FileTransfer.h @@ -41,7 +41,8 @@ class CFileTransfer : public CAction public: CFileTransfer(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); virtual void Run(const char *pActionDir, const char *pArgs); }; diff --git a/lib/Plugins/KerneloopsReporter.cpp b/lib/Plugins/KerneloopsReporter.cpp index cfb4e05..f7a6cbb 100644 --- a/lib/Plugins/KerneloopsReporter.cpp +++ b/lib/Plugins/KerneloopsReporter.cpp @@ -118,18 +118,23 @@ std::string CKerneloopsReporter::Report(const map_crash_report_t& pCrashReport, void CKerneloopsReporter::SetSettings(const map_plugin_settings_t& pSettings) { - map_plugin_settings_t::const_iterator it = pSettings.find("SubmitURL"); - if (it != pSettings.end()) { + m_pSettings = pSettings; + + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("SubmitURL"); + if (it != end) { m_sSubmitURL = it->second; } } -const map_plugin_settings_t& CKerneloopsReporter::GetSettings() -{ - m_pSettings["SubmitURL"] = m_sSubmitURL; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CKerneloopsReporter::GetSettings() +//{ +// m_pSettings["SubmitURL"] = m_sSubmitURL; +// +// return m_pSettings; +//} PLUGIN_INFO(REPORTER, CKerneloopsReporter, diff --git a/lib/Plugins/KerneloopsReporter.h b/lib/Plugins/KerneloopsReporter.h index 7f6ab8c..af82afb 100644 --- a/lib/Plugins/KerneloopsReporter.h +++ b/lib/Plugins/KerneloopsReporter.h @@ -41,7 +41,8 @@ class CKerneloopsReporter : public CReporter CKerneloopsReporter(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, const map_plugin_settings_t& pSettings, const std::string& pArgs); diff --git a/lib/Plugins/KerneloopsSysLog.cpp b/lib/Plugins/KerneloopsSysLog.cpp index d28ce39..cb15fcc 100644 --- a/lib/Plugins/KerneloopsSysLog.cpp +++ b/lib/Plugins/KerneloopsSysLog.cpp @@ -75,7 +75,6 @@ struct line_info { int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen) { char *c; - enum { maybe, no, yes } syslog_format = maybe; int linecount = 0; int lines_info_alloc = 0; struct line_info *lines_info = NULL; @@ -86,86 +85,73 @@ int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen) buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */ c = buffer; while (c < buffer + buflen) { - char v, linelevel; - int len = 0; + char linelevel; char *c9; char *linepointer; c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */ assert(c9); - len = c9 - c; + *c9 = '\0'; /* turn the \n into a string termination */ + if (c9 == c) + goto next_line; /* in /var/log/messages, we need to strip the first part off, upto the 3rd ':' */ - if (syslog_format == yes - || (syslog_format == maybe - && len > sizeof("Jul 4 11:11:41") - && c[3] == ' ' && c[6] == ' ' && c[9] == ':' && c[12] == ':' - && (v = (c[5] | c[7]|c[8] | c[10]|c[11] | c[13]|c[14])) <= '9' - && v >= '0' - && (v = (c[5] & c[7]&c[8] & c[10]&c[11] & c[13]&c[14])) <= '9' - && v >= '0' - ) + /* 01234567890123456 */ + if ((c9 - c) > sizeof("Jul 4 11:11:11 ") + && c[3] == ' ' + && (c[4] == ' ' || isdigit(c[4])) + && isdigit(c[5]) + && c[6] == ' ' + && isdigit(c[7]) + && isdigit(c[8]) + && c[9] == ':' + && isdigit(c[10]) + && isdigit(c[11]) + && c[12] == ':' + && isdigit(c[13]) + && isdigit(c[14]) + && c[15] == ' ' ) { /* It's syslog file, not a bare dmesg */ - syslog_format = yes; - - char *c2; - int i; - /* skip non-kernel lines */ - c2 = (char*)memmem(c, len, "kernel:", 7); - if (!c2) - c2 = (char*)memmem(c, len, "abrt:", 5); - if (!c2) + /* Skip over timestamp */ + c += 16; + + /* Skip non-kernel lines */ + char *kernel_str = strstr(c, "kernel: "); + if (kernel_str == NULL) { + /* if we see our own marker: + * "hostname abrt: Kerneloops: Reported 1 kernel oopses to Abrt" + * we know we submitted everything upto here already */ + if (strstr(c, "abrt:") && strstr(c, "Abrt")) { + linecount = 0; + lines_info_alloc = 0; + free(lines_info); + lines_info = NULL; + } goto next_line; - - /* skip to message in "Jan 01 01:23:45 hostname kernel: message" */ - for (i = 0; i < 3; i++) { - c = (char*)memchr(c, ':', len); - if (!c) - goto next_line; - c++; - len = c9 - c; } - c++; - len--; - } else if (len) { - syslog_format = no; + c = kernel_str + sizeof("kernel: ")-1; } - linepointer = c; linelevel = 0; /* store and remove kernel log level */ - if (len >= 3 && *c == '<' && *(c+2) == '>') { - linelevel = *(c+1); + if (*c == '<' && c[1] && c[2] == '>') { + linelevel = c[1]; c += 3; - len -= 3; - linepointer = c; } /* remove jiffies time stamp counter if present */ if (*c == '[') { char *c2, *c3; - c2 = (char*)memchr(c, '.', len); - c3 = (char*)memchr(c, ']', len); + c2 = strchr(c, '.'); + c3 = strchr(c, ']'); if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8) { c = c3 + 1; if (*c == ' ') c++; - len = c9 - c; - linepointer = c; } } - - assert(c + len == c9); - *c9 = '\0'; /* turn the \n into a string termination */ - - /* if we see our own marker, we know we submitted everything upto here already */ - if (len >= 4 && memmem(linepointer, len, "Abrt", 4)) { - linecount = 0; - lines_info_alloc = 0; - free(lines_info); - lines_info = NULL; - } + linepointer = c; if (linecount >= lines_info_alloc) { lines_info_alloc += REALLOC_CHUNK; diff --git a/lib/Plugins/Logger.cpp b/lib/Plugins/Logger.cpp index b2ac1ad..6babc73 100644 --- a/lib/Plugins/Logger.cpp +++ b/lib/Plugins/Logger.cpp @@ -19,9 +19,10 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "Logger.h" #include <fstream> #include <sstream> +#include "abrtlib.h" +#include "Logger.h" #include "DebugDump.h" #include "CommLayerInner.h" #include "ABRTException.h" @@ -33,23 +34,30 @@ CLogger::CLogger() : void CLogger::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("LogPath") != pSettings.end()) + m_pSettings = pSettings; + + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("LogPath"); + if (it != end) { - m_sLogPath = pSettings.find("LogPath")->second; + m_sLogPath = it->second; } - if (pSettings.find("AppendLogs") != pSettings.end()) + it = pSettings.find("AppendLogs"); + if (it != end) { - m_bAppendLogs = pSettings.find("AppendLogs")->second == "yes"; + m_bAppendLogs = string_to_bool(it->second.c_str()); } } -const map_plugin_settings_t& CLogger::GetSettings() -{ - m_pSettings["LogPath"] = m_sLogPath; - m_pSettings["AppendLogs"] = m_bAppendLogs ? "yes" : "no"; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CLogger::GetSettings() +//{ +// m_pSettings["LogPath"] = m_sLogPath; +// m_pSettings["AppendLogs"] = m_bAppendLogs ? "yes" : "no"; +// +// return m_pSettings; +//} std::string CLogger::Report(const map_crash_report_t& pCrashReport, const map_plugin_settings_t& pSettings, const std::string& pArgs) diff --git a/lib/Plugins/Logger.h b/lib/Plugins/Logger.h index 0969bea..285e25b 100644 --- a/lib/Plugins/Logger.h +++ b/lib/Plugins/Logger.h @@ -35,7 +35,8 @@ class CLogger : public CReporter CLogger(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, const map_plugin_settings_t& pSettings, const std::string& pArgs); diff --git a/lib/Plugins/Mailx.cpp b/lib/Plugins/Mailx.cpp index f083404..1979f9e 100644 --- a/lib/Plugins/Mailx.cpp +++ b/lib/Plugins/Mailx.cpp @@ -201,33 +201,42 @@ std::string CMailx::Report(const map_crash_report_t& pCrashReport, void CMailx::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("Subject") != pSettings.end()) + m_pSettings = pSettings; + + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("Subject"); + if (it != end) { - m_sSubject = pSettings.find("Subject")->second; + m_sSubject = it->second; } - if (pSettings.find("EmailFrom") != pSettings.end()) + it = pSettings.find("EmailFrom"); + if (it != end) { - m_sEmailFrom = pSettings.find("EmailFrom")->second; + m_sEmailFrom = it->second; } - if (pSettings.find("EmailTo") != pSettings.end()) + it = pSettings.find("EmailTo"); + if (it != end) { - m_sEmailTo = pSettings.find("EmailTo")->second; + m_sEmailTo = it->second; } - if (pSettings.find("SendBinaryData") != pSettings.end()) + it = pSettings.find("SendBinaryData"); + if (it != end) { - m_bSendBinaryData = pSettings.find("SendBinaryData")->second == "yes"; + m_bSendBinaryData = string_to_bool(it->second.c_str()); } } -const map_plugin_settings_t& CMailx::GetSettings() -{ - m_pSettings["Subject"] = m_sSubject; - m_pSettings["EmailFrom"] = m_sEmailFrom; - m_pSettings["EmailTo"] = m_sEmailTo; - m_pSettings["SendBinaryData"] = m_bSendBinaryData ? "yes" : "no"; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CMailx::GetSettings() +//{ +// m_pSettings["Subject"] = m_sSubject; +// m_pSettings["EmailFrom"] = m_sEmailFrom; +// m_pSettings["EmailTo"] = m_sEmailTo; +// m_pSettings["SendBinaryData"] = m_bSendBinaryData ? "yes" : "no"; +// +// return m_pSettings; +//} PLUGIN_INFO(REPORTER, CMailx, diff --git a/lib/Plugins/Mailx.h b/lib/Plugins/Mailx.h index 7af1188..4aa861f 100644 --- a/lib/Plugins/Mailx.h +++ b/lib/Plugins/Mailx.h @@ -47,7 +47,8 @@ class CMailx : public CReporter CMailx(); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); virtual std::string Report(const map_crash_report_t& pCrashReport, const map_plugin_settings_t& pSettings, const std::string& pArgs); diff --git a/lib/Plugins/RunApp.cpp b/lib/Plugins/RunApp.cpp index f816dc2..e525d89 100644 --- a/lib/Plugins/RunApp.cpp +++ b/lib/Plugins/RunApp.cpp @@ -25,40 +25,11 @@ #include "DebugDump.h" #include "ABRTException.h" #include "CommLayerInner.h" +#include "abrtlib.h" #define COMMAND 0 #define FILENAME 1 -/* TODO: do not duplicate: SOSreport.cpp has same function too */ -static void ParseArgs(const char *psArgs, vector_string_t& pArgs) -{ - unsigned ii; - bool is_quote = false; - std::string item; - - for (ii = 0; psArgs[ii]; ii++) - { - if (psArgs[ii] == '"') - { - is_quote = !is_quote; - } - else if (psArgs[ii] == ',' && !is_quote) - { - pArgs.push_back(item); - item.clear(); - } - else - { - item += psArgs[ii]; - } - } - - if (item.size() != 0) - { - pArgs.push_back(item); - } -} - void CActionRunApp::Run(const char *pActionDir, const char *pArgs) { update_client(_("Executing RunApp plugin...")); @@ -66,7 +37,7 @@ void CActionRunApp::Run(const char *pActionDir, const char *pArgs) std::string output; vector_string_t args; - ParseArgs(pArgs, args); + parse_args(pArgs, args, '"'); FILE *fp = popen(args[COMMAND].c_str(), "r"); if (fp == NULL) diff --git a/lib/Plugins/SOSreport.cpp b/lib/Plugins/SOSreport.cpp index fedc51a..287c01e 100644 --- a/lib/Plugins/SOSreport.cpp +++ b/lib/Plugins/SOSreport.cpp @@ -18,7 +18,6 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include <ext/stdio_filebuf.h> #include <fstream> #include <sstream> #include "abrtlib.h" @@ -56,7 +55,7 @@ static std::string ParseFilename(const std::string& pOutput) int filename_start = pOutput.find_first_not_of(" \n\t", p); ErrorCheck(p); - int line_end = pOutput.find_first_of('\n',filename_start); + int line_end = pOutput.find_first_of('\n', filename_start); ErrorCheck(p); int filename_end = pOutput.find_last_not_of(" \n\t", line_end); @@ -65,36 +64,6 @@ static std::string ParseFilename(const std::string& pOutput) return pOutput.substr(filename_start, filename_end - filename_start + 1); } -/* TODO: do not duplicate: RunApp.cpp has same function too */ -static void ParseArgs(const char *psArgs, vector_string_t& pArgs) -{ - unsigned ii; - bool is_quote = false; - std::string item; - - for (ii = 0; psArgs[ii]; ii++) - { - if (psArgs[ii] == '"') - { - is_quote = !is_quote; - } - else if (psArgs[ii] == ',' && !is_quote) - { - pArgs.push_back(item); - item.clear(); - } - else - { - item += psArgs[ii]; - } - } - - if (item.size() != 0) - { - pArgs.push_back(item); - } -} - void CActionSOSreport::Run(const char *pActionDir, const char *pArgs) { update_client(_("Executing SOSreport plugin...")); @@ -108,7 +77,7 @@ void CActionSOSreport::Run(const char *pActionDir, const char *pArgs) std::string command; vector_string_t args; - ParseArgs(pArgs, args); + parse_args(pArgs, args, '"'); if (args.size() == 0 || args[0] == "") { @@ -120,24 +89,11 @@ void CActionSOSreport::Run(const char *pActionDir, const char *pArgs) } update_client(_("running sosreport: %s"), command.c_str()); - FILE *fp = popen(command.c_str(), "r"); - if (fp == NULL) - { - throw CABRTException(EXCEP_PLUGIN, ssprintf("Can't execute '%s'", command.c_str())); - } - -//vda TODO: fix this mess - std::ostringstream output_stream; - __gnu_cxx::stdio_filebuf<char> command_output_buffer(fp, std::ios_base::in); - - output_stream << command << std::endl; - output_stream << &command_output_buffer; - - pclose(fp); + std::string output = command; + output += '\n'; + output += popen_and_save_output(command.c_str()); update_client(_("done running sosreport")); - std::string output = output_stream.str(); - std::string sosreport_filename = ParseFilename(output); std::string sosreport_dd_filename = concat_path_file(pActionDir, "sosreport.tar.bz2"); diff --git a/lib/Plugins/SQLite3.cpp b/lib/Plugins/SQLite3.cpp index ab39d04..3aad054 100644 --- a/lib/Plugins/SQLite3.cpp +++ b/lib/Plugins/SQLite3.cpp @@ -398,18 +398,24 @@ database_row_t CSQLite3::GetUUIDData(const std::string& pUUID, const std::string void CSQLite3::SetSettings(const map_plugin_settings_t& pSettings) { - if (pSettings.find("DBPath") != pSettings.end()) + m_pSettings = pSettings; + + map_plugin_settings_t::const_iterator end = pSettings.end(); + map_plugin_settings_t::const_iterator it; + it = pSettings.find("DBPath"); + if (it != end) { - m_sDBPath = pSettings.find("DBPath")->second; + m_sDBPath = it->second; } } -const map_plugin_settings_t& CSQLite3::GetSettings() -{ - m_pSettings["DBPath"] = m_sDBPath; - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CSQLite3::GetSettings() +//{ +// m_pSettings["DBPath"] = m_sDBPath; +// +// return m_pSettings; +//} PLUGIN_INFO(DATABASE, CSQLite3, diff --git a/lib/Plugins/SQLite3.h b/lib/Plugins/SQLite3.h index 0eb3d08..fc13bd7 100644 --- a/lib/Plugins/SQLite3.h +++ b/lib/Plugins/SQLite3.h @@ -58,7 +58,8 @@ class CSQLite3 : public CDatabase virtual database_row_t GetUUIDData(const std::string& pUUID, const std::string& pUID); virtual void SetSettings(const map_plugin_settings_t& pSettings); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); }; #endif /* SQLITE3_H_ */ diff --git a/lib/Plugins/TicketUploader.cpp b/lib/Plugins/TicketUploader.cpp index e380de0..76bda40 100644 --- a/lib/Plugins/TicketUploader.cpp +++ b/lib/Plugins/TicketUploader.cpp @@ -341,9 +341,10 @@ string CTicketUploader::Report(const map_crash_report_t& pCrashReport, void CTicketUploader::SetSettings(const map_plugin_settings_t& pSettings) { + m_pSettings = pSettings; + map_plugin_settings_t::const_iterator end = pSettings.end(); map_plugin_settings_t::const_iterator it; - it = pSettings.find("Customer"); if (it != end) { @@ -362,12 +363,12 @@ void CTicketUploader::SetSettings(const map_plugin_settings_t& pSettings) it = pSettings.find("Encrypt"); if (it != end) { - m_bEncrypt = it->second == "yes"; + m_bEncrypt = string_to_bool(it->second.c_str()); } it = pSettings.find("Upload"); if (it != end) { - m_bUpload = it->second == "yes"; + m_bUpload = string_to_bool(it->second.c_str()); } it = pSettings.find("RetryCount"); if (it != end) @@ -381,18 +382,19 @@ void CTicketUploader::SetSettings(const map_plugin_settings_t& pSettings) } } -const map_plugin_settings_t& CTicketUploader::GetSettings() -{ - m_pSettings["Customer"] = m_sCustomer; - m_pSettings["Ticket"] = m_sTicket; - m_pSettings["URL"] = m_sURL; - m_pSettings["Encrypt"] = m_bEncrypt ? "yes" : "no"; - m_pSettings["Upload"] = m_bEncrypt ? "yes" : "no"; - m_pSettings["RetryCount"] = to_string(m_nRetryCount); - m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); - - return m_pSettings; -} +//ok to delete? +//const map_plugin_settings_t& CTicketUploader::GetSettings() +//{ +// m_pSettings["Customer"] = m_sCustomer; +// m_pSettings["Ticket"] = m_sTicket; +// m_pSettings["URL"] = m_sURL; +// m_pSettings["Encrypt"] = m_bEncrypt ? "yes" : "no"; +// m_pSettings["Upload"] = m_bEncrypt ? "yes" : "no"; +// m_pSettings["RetryCount"] = to_string(m_nRetryCount); +// m_pSettings["RetryDelay"] = to_string(m_nRetryDelay); +// +// return m_pSettings; +//} PLUGIN_INFO(REPORTER, CTicketUploader, diff --git a/lib/Plugins/TicketUploader.h b/lib/Plugins/TicketUploader.h index 9ae3478..2a7c98a 100644 --- a/lib/Plugins/TicketUploader.h +++ b/lib/Plugins/TicketUploader.h @@ -45,7 +45,8 @@ class CTicketUploader : public CReporter public: CTicketUploader(); virtual ~CTicketUploader(); - virtual const map_plugin_settings_t& GetSettings(); +//ok to delete? +// virtual const map_plugin_settings_t& GetSettings(); virtual void SetSettings(const map_plugin_settings_t& pSettings); virtual std::string Report(const map_crash_report_t& pCrashReport, diff --git a/lib/Utils/DebugDump.cpp b/lib/Utils/DebugDump.cpp index bf793bb..ba11d96 100644 --- a/lib/Utils/DebugDump.cpp +++ b/lib/Utils/DebugDump.cpp @@ -289,6 +289,13 @@ static void DeleteFileDir(const char *pDir) static bool IsTextFile(const char *name) { + /* Some files in our dump directories are known to always be textual */ + if (strcmp(name, "backtrace") == 0 + || strcmp(name, "cmdline") == 0 + ) { + return true; + } + /* This idiotic library thinks that file containing just "0" is not text (!!) magic_t m = magic_open(MAGIC_MIME_TYPE); @@ -328,15 +335,22 @@ static bool IsTextFile(const char *name) int r = full_read(fd, buf, sizeof(buf)); close(fd); + /* Every once in a while, even a text file contains a few garbled + * or unexpected non-ASCII chars. We should not declare it "binary". + */ + const unsigned RATIO = 50; + unsigned total_chars = r + RATIO; + unsigned bad_chars = 1; /* 1 prevents division by 0 later */ while (--r >= 0) { - if (buf[r] >= 0x7f) - return false; - /* Among control chars, only '\t','\n' etc are allowed */ - if (buf[r] < ' ' && !isspace(buf[r])) - return false; + if (buf[r] >= 0x7f + /* among control chars, only '\t','\n' etc are allowed */ + || (buf[r] < ' ' && !isspace(buf[r])) + ) { + bad_chars++; + } } - return true; + return (total_chars / bad_chars) >= RATIO; } void CDebugDump::Delete() @@ -471,7 +485,7 @@ bool CDebugDump::GetNextFile(std::string& pFileName, std::string& pContent, bool { if (is_regular_file(dent, m_sDebugDumpDir.c_str())) { - std::string fullname = m_sDebugDumpDir + '/' + dent->d_name; + std::string fullname = concat_path_file(m_sDebugDumpDir.c_str(), dent->d_name); pFileName = dent->d_name; if (IsTextFile(fullname.c_str())) diff --git a/lib/Utils/Makefile.am b/lib/Utils/Makefile.am index 68c925f..f752992 100644 --- a/lib/Utils/Makefile.am +++ b/lib/Utils/Makefile.am @@ -5,16 +5,20 @@ lib_LTLIBRARIES = libABRTUtils.la # xconnect.cpp libABRTUtils_la_SOURCES = \ + stringops.cpp \ xfuncs.cpp \ encbase64.cpp \ read_write.cpp \ logging.cpp \ copyfd.cpp \ skip_whitespace.cpp \ + popen_and_save_output.cpp \ + parse_release.cpp \ CrashTypesSocket.cpp \ 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 +34,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 +42,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 0000000..11c431b --- /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 0000000..e67ab19 --- /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 diff --git a/lib/Utils/parse_release.cpp b/lib/Utils/parse_release.cpp new file mode 100644 index 0000000..33d3edb --- /dev/null +++ b/lib/Utils/parse_release.cpp @@ -0,0 +1,38 @@ +#include "abrtlib.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +using namespace std; + +void parse_release(const char *pRelease, string& pProduct, string& pVersion) +{ + if (strstr(pRelease, "Rawhide")) + { + pProduct = "Fedora"; + pVersion = "rawhide"; + return; + } + if (strstr(pRelease, "Fedora")) + { + pProduct = "Fedora"; + } + else if (strstr(pRelease, "Red Hat Enterprise Linux")) + { + pProduct = "Red Hat Enterprise Linux "; + } + + const char *release = strstr(pRelease, "release"); + const char *space = release ? strchr(release, ' ') : NULL; + + if (space++) while (*space != '\0' && *space != ' ') + { + /* Eat string like "5.2" */ + pVersion += *space; + if (pProduct == "Red Hat Enterprise Linux ") + { + pProduct += *space; + } + space++; + } +} diff --git a/lib/Utils/popen_and_save_output.cpp b/lib/Utils/popen_and_save_output.cpp new file mode 100644 index 0000000..4bcbcac --- /dev/null +++ b/lib/Utils/popen_and_save_output.cpp @@ -0,0 +1,30 @@ +/* + * Utility routines. + * + * Licensed under GPLv2 or later, see file COPYING in this tarball for details. + */ +#include "abrtlib.h" + +using namespace std; + +string popen_and_save_output(const char *cmd) +{ + string result; + + FILE *fp = popen(cmd, "r"); + if (fp == NULL) /* fork or pipe failed; or out-of-mem */ + { + return result; + } + + size_t sz; + char buf[BUFSIZ + 1]; + while ((sz = fread(buf, 1, sizeof(buf)-1, fp)) > 0) + { + buf[sz] = '\0'; + result += buf; + } + pclose(fp); + + return result; +} diff --git a/lib/Utils/stringops.cpp b/lib/Utils/stringops.cpp new file mode 100644 index 0000000..1b3793f --- /dev/null +++ b/lib/Utils/stringops.cpp @@ -0,0 +1,30 @@ +#include "abrtlib.h" + +void parse_args(const char *psArgs, vector_string_t& pArgs, int quote) +{ + unsigned ii; + bool is_quote = false; + std::string item; + + for (ii = 0; psArgs[ii]; ii++) + { + if (quote != -1 && psArgs[ii] == quote) + { + is_quote = !is_quote; + } + else if (psArgs[ii] == ',' && !is_quote) + { + pArgs.push_back(item); + item.clear(); + } + else + { + item += psArgs[ii]; + } + } + + if (item.size() != 0) + { + pArgs.push_back(item); + } +} |
