summaryrefslogtreecommitdiffstats
path: root/lib/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'lib/plugins')
-rw-r--r--lib/plugins/Bugzilla.conf10
-rw-r--r--lib/plugins/Bugzilla.cpp982
-rw-r--r--lib/plugins/Bugzilla.glade246
-rw-r--r--lib/plugins/Bugzilla.h47
-rw-r--r--lib/plugins/CCpp.conf42
-rw-r--r--lib/plugins/CCpp.cpp1064
-rw-r--r--lib/plugins/CCpp.h53
-rw-r--r--lib/plugins/CCpp_sha1.cpp214
-rw-r--r--lib/plugins/CCpp_sha1.h32
-rw-r--r--lib/plugins/FileTransfer.conf35
-rw-r--r--lib/plugins/FileTransfer.cpp362
-rw-r--r--lib/plugins/FileTransfer.h48
-rw-r--r--lib/plugins/Kerneloops.conf19
-rw-r--r--lib/plugins/Kerneloops.cpp145
-rw-r--r--lib/plugins/Kerneloops.h42
-rw-r--r--lib/plugins/KerneloopsReporter.cpp145
-rw-r--r--lib/plugins/KerneloopsReporter.glade118
-rw-r--r--lib/plugins/KerneloopsReporter.h51
-rw-r--r--lib/plugins/KerneloopsScanner.cpp203
-rw-r--r--lib/plugins/KerneloopsScanner.h42
-rw-r--r--lib/plugins/KerneloopsSysLog.cpp366
-rw-r--r--lib/plugins/KerneloopsSysLog.h34
-rw-r--r--lib/plugins/Logger.conf6
-rw-r--r--lib/plugins/Logger.cpp90
-rw-r--r--lib/plugins/Logger.glade135
-rw-r--r--lib/plugins/Logger.h44
-rw-r--r--lib/plugins/Mailx.conf15
-rw-r--r--lib/plugins/Mailx.cpp195
-rw-r--r--lib/plugins/Mailx.glade184
-rw-r--r--lib/plugins/Mailx.h48
-rw-r--r--lib/plugins/Makefile.am151
-rw-r--r--lib/plugins/Python.conf1
-rw-r--r--lib/plugins/Python.cpp100
-rw-r--r--lib/plugins/Python.h36
-rw-r--r--lib/plugins/Python_hash.cpp445
-rw-r--r--lib/plugins/Python_hash.h29
-rw-r--r--lib/plugins/RHTSupport.conf9
-rw-r--r--lib/plugins/RHTSupport.cpp312
-rw-r--r--lib/plugins/RHTSupport.glade213
-rw-r--r--lib/plugins/RHTSupport.h45
-rw-r--r--lib/plugins/ReportUploader.conf21
-rw-r--r--lib/plugins/ReportUploader.cpp505
-rw-r--r--lib/plugins/ReportUploader.glade249
-rw-r--r--lib/plugins/ReportUploader.h55
-rw-r--r--lib/plugins/RunApp.cpp76
-rw-r--r--lib/plugins/RunApp.h34
-rw-r--r--lib/plugins/SOSreport.conf1
-rw-r--r--lib/plugins/SOSreport.cpp162
-rw-r--r--lib/plugins/SOSreport.h31
-rw-r--r--lib/plugins/SQLite3.conf4
-rw-r--r--lib/plugins/SQLite3.cpp681
-rw-r--r--lib/plugins/SQLite3.h56
-rw-r--r--lib/plugins/abrt-Bugzilla.743
-rw-r--r--lib/plugins/abrt-FileTransfer.772
-rw-r--r--lib/plugins/abrt-KerneloopsReporter.740
-rw-r--r--lib/plugins/abrt-KerneloopsScanner.746
-rw-r--r--lib/plugins/abrt-Logger.744
-rw-r--r--lib/plugins/abrt-Mailx.757
-rw-r--r--lib/plugins/abrt-ReportUploader.755
-rw-r--r--lib/plugins/abrt-RunApp.743
-rw-r--r--lib/plugins/abrt-SQLite3.736
-rw-r--r--lib/plugins/abrt-plugins.744
62 files changed, 8713 insertions, 0 deletions
diff --git a/lib/plugins/Bugzilla.conf b/lib/plugins/Bugzilla.conf
new file mode 100644
index 00000000..4eebd994
--- /dev/null
+++ b/lib/plugins/Bugzilla.conf
@@ -0,0 +1,10 @@
+Enabled = yes
+
+# Bugzilla URL
+BugzillaURL = https://bugzilla.redhat.com/
+# yes means that ssl certificates will be checked
+SSLVerify = yes
+# your login has to exist, if you don have any, please create one
+Login =
+# your password
+Password =
diff --git a/lib/plugins/Bugzilla.cpp b/lib/plugins/Bugzilla.cpp
new file mode 100644
index 00000000..f3967cdf
--- /dev/null
+++ b/lib/plugins/Bugzilla.cpp
@@ -0,0 +1,982 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "abrtlib.h"
+#include "abrt_xmlrpc.h"
+#include "Bugzilla.h"
+#include "crash_types.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define XML_RPC_SUFFIX "/xmlrpc.cgi"
+#define MAX_HOPS 5
+
+/*
+ * TODO: npajkovs: better deallocation of xmlrpc value
+ * npajkovs: better gathering function which collects all information from bugzilla
+ * npajkovs: figure out how to deal with cloning bugs
+ * npajkovs: check if attachment was uploaded successul an if not try it again(max 3 times)
+ * and if it still fails. retrun successful, but mention that attaching failed
+ * npajkovs: add option to set comment privat
+ */
+
+struct bug_info {
+ const char* bug_status;
+ const char* bug_resolution;
+ const char* bug_reporter;
+ const char* bug_product;
+ xmlrpc_int32 bug_dup_id;
+ std::vector<const char*> bug_cc;
+};
+
+static void bug_info_init(struct bug_info* bz)
+{
+ bz->bug_status = NULL;
+ bz->bug_resolution = NULL;
+ bz->bug_reporter = NULL;
+ bz->bug_product = NULL;
+ bz->bug_dup_id = -1;
+}
+
+static void bug_info_destroy(struct bug_info* bz)
+{
+ free((void*)bz->bug_status);
+ free((void*)bz->bug_resolution);
+ free((void*)bz->bug_reporter);
+ free((void*)bz->bug_product);
+
+ if (!bz->bug_cc.empty())
+ {
+ for (int ii = 0; ii < bz->bug_cc.size(); ii++)
+ free((void*)bz->bug_cc[ii]);
+
+ bz->bug_cc.clear();
+ }
+}
+
+static int am_i_in_cc(const struct bug_info* bz, const char* login)
+{
+ if (bz->bug_cc.empty())
+ return -1;
+
+ int size = bz->bug_cc.size();
+ for (int ii = 0; ii < size; ii++)
+ {
+ if (strcmp(login, bz->bug_cc[ii]) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+/*
+ * Static namespace for xmlrpc stuff.
+ * Used mainly to ensure we always destroy xmlrpc client and server_info.
+ */
+
+namespace {
+
+struct ctx: public abrt_xmlrpc_conn {
+ xmlrpc_env env;
+
+ ctx(const char* url, bool ssl_verify): abrt_xmlrpc_conn(url, ssl_verify)
+ { xmlrpc_env_init(&env); }
+ ~ctx() { xmlrpc_env_clean(&env); }
+
+ void login(const char* login, const char* passwd);
+ void logout();
+
+ const char* get_bug_status(xmlrpc_value* result_xml);
+ const char* get_bug_resolution(xmlrpc_value* result_xml);
+ const char* get_bug_reporter(xmlrpc_value* result_xml);
+ const char* get_bug_product(xmlrpc_value* relult_xml);
+
+ xmlrpc_value* call_quicksearch_duphash(const char* component, const char* release, const char* duphash);
+ xmlrpc_value* get_cc_member(xmlrpc_value* result_xml);
+ xmlrpc_value* get_member(const char* member, xmlrpc_value* result_xml);
+
+ int get_array_size(xmlrpc_value* result_xml);
+ xmlrpc_int32 get_bug_id(xmlrpc_value* result_xml);
+ xmlrpc_int32 get_bug_dup_id(xmlrpc_value* result_xml);
+ void get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz);
+ int add_plus_one_cc(xmlrpc_int32 bug_id, const char* login);
+ xmlrpc_int32 new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno);
+ int add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashData);
+ int get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id);
+ int add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private);
+
+ xmlrpc_value* call(const char* method, const char* format, ...);
+};
+
+xmlrpc_value* ctx::call(const char* method, const char* format, ...)
+{
+ va_list args;
+ xmlrpc_value* param = NULL;
+ xmlrpc_value* result = NULL;
+ const char* suffix;
+
+ va_start(args, format);
+ xmlrpc_build_value_va(&env, format, args, &param, &suffix);
+ va_end(args);
+
+ if (!env.fault_occurred)
+ {
+ if (*suffix != '\0')
+ {
+ xmlrpc_env_set_fault_formatted(
+ &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+ "specifier: '%s'. There must be exactly one arument.",
+ suffix);
+
+ xmlrpc_DECREF(param);
+ return NULL;
+ }
+
+ xmlrpc_client_call2(&env, m_pClient, m_pServer_info, method, param, &result);
+ xmlrpc_DECREF(param);
+ if (env.fault_occurred)
+ return NULL;
+ }
+
+
+ return result;
+}
+
+xmlrpc_value* ctx::get_member(const char* member, xmlrpc_value* result_xml)
+{
+ xmlrpc_value* cc_member = NULL;
+ xmlrpc_struct_find_value(&env, result_xml, member, &cc_member);
+ if (env.fault_occurred)
+ return NULL;
+
+ return cc_member;
+}
+
+int ctx::get_array_size(xmlrpc_value* result_xml)
+{
+ int size = xmlrpc_array_size(&env, result_xml);
+ if (env.fault_occurred)
+ return -1;
+
+ return size;
+}
+
+xmlrpc_int32 ctx::get_bug_dup_id(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* dup_id = get_member("dup_id", result_xml);
+ if (!dup_id)
+ return -1;
+
+ xmlrpc_int32 dup_id_int = -1;
+ xmlrpc_read_int(&env, dup_id, &dup_id_int);
+ xmlrpc_DECREF(dup_id);
+ if (env.fault_occurred)
+ return -1;
+
+ VERB3 log("got dup_id: %i", dup_id_int);
+ return dup_id_int;
+}
+
+const char* ctx::get_bug_product(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* product_member = get_member("product", result_xml);
+ if (!product_member) //should never happend. Each bug has to set up product
+ return NULL;
+
+ const char* product = NULL;
+ xmlrpc_read_string(&env, product_member, &product);
+ xmlrpc_DECREF(product_member);
+ if (env.fault_occurred)
+ return NULL;
+
+ if (*product != '\0')
+ {
+ VERB3 log("got bug product: %s", product);
+ return product;
+ }
+
+ free((void*)product);
+ return NULL;
+}
+
+const char* ctx::get_bug_reporter(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* reporter_member = get_member("reporter", result_xml);
+ if (!reporter_member)
+ return NULL;
+
+ const char* reporter = NULL;
+ xmlrpc_read_string(&env, reporter_member, &reporter);
+ xmlrpc_DECREF(reporter_member);
+ if (env.fault_occurred)
+ return NULL;
+
+ if (*reporter != '\0')
+ {
+ VERB3 log("got bug reporter: %s", reporter);
+ return reporter;
+ }
+ free((void*)reporter);
+ return NULL;
+}
+
+const char* ctx::get_bug_resolution(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* bug_resolution = get_member("resolution", result_xml);
+ if (!bug_resolution)
+ return NULL;
+
+ const char* resolution_str = NULL;
+ xmlrpc_read_string(&env, bug_resolution, &resolution_str);
+ xmlrpc_DECREF(bug_resolution);
+ if (env.fault_occurred)
+ return NULL;
+
+ if (*resolution_str != '\0')
+ {
+ VERB3 log("got resolution: %s", resolution_str);
+ return resolution_str;
+ }
+ free((void*)resolution_str);
+ return NULL;
+}
+
+const char* ctx::get_bug_status(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* bug_status = get_member("bug_status", result_xml);
+ if (!bug_status)
+ return NULL;
+
+ const char* status_str = NULL;
+ xmlrpc_read_string(&env, bug_status, &status_str);
+ xmlrpc_DECREF(bug_status);
+ if (env.fault_occurred)
+ return NULL;
+
+ if (*status_str != '\0')
+ {
+ VERB3 log("got bug_status: %s", status_str);
+ return status_str;
+ }
+ free((void*)status_str);
+ return NULL;
+}
+
+void ctx::get_bug_cc(xmlrpc_value* result_xml, struct bug_info* bz)
+{
+ xmlrpc_value* cc_member = get_member("cc", result_xml);
+ if (!cc_member)
+ return;
+
+ int array_size = xmlrpc_array_size(&env, cc_member);
+ if (array_size == -1)
+ return;
+
+ VERB3 log("count members on cc %i", array_size);
+
+ for (int i = 0; i < array_size; i++)
+ {
+ xmlrpc_value* item = NULL;
+ xmlrpc_array_read_item(&env, cc_member, i, &item);
+ if (env.fault_occurred)
+ return;
+
+ if (item)
+ {
+ const char* cc = NULL;
+ xmlrpc_read_string(&env, item, &cc);
+ xmlrpc_DECREF(item);
+ if (env.fault_occurred)
+ {
+ xmlrpc_DECREF(cc_member);
+ return;
+ }
+
+ if (*cc != '\0')
+ {
+ bz->bug_cc.push_back(cc);
+ VERB3 log("member on cc is %s", cc);
+ continue;
+ }
+ free((void*)cc);
+ }
+ }
+ xmlrpc_DECREF(cc_member);
+ return;
+}
+
+xmlrpc_value* ctx::call_quicksearch_duphash(const char* component, const char* release, const char* duphash)
+{
+ char *query = NULL;
+ if (!release)
+ query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash);
+ else
+ {
+ char *product = NULL;
+ char *version = NULL;
+ parse_release(release, &product, &version);
+ query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"",
+ component, duphash, product
+ );
+ free(product);
+ free(version);
+ }
+
+ VERB3 log("quicksearch for `%s'", query);
+ xmlrpc_value *ret = call("Bug.search", "({s:s})", "quicksearch", query);
+ free(query);
+ return ret;
+}
+
+xmlrpc_int32 ctx::get_bug_id(xmlrpc_value* result_xml)
+{
+ xmlrpc_value* item = NULL;
+ xmlrpc_array_read_item(&env, result_xml, 0, &item);
+ if (env.fault_occurred)
+ return -1;
+
+ xmlrpc_value* bug = get_member("bug_id", item);
+ xmlrpc_DECREF(item);
+ if (!bug)
+ return -1;
+
+ xmlrpc_int32 bug_id = -1;
+ xmlrpc_read_int(&env, bug, &bug_id);
+ xmlrpc_DECREF(bug);
+ if (env.fault_occurred)
+ return -1;
+
+ VERB3 log("got bug_id %d", (int)bug_id);
+ return bug_id;
+}
+
+int ctx::add_plus_one_cc(xmlrpc_int32 bug_id, const char* login)
+{
+ xmlrpc_value* result = call("Bug.update", "({s:i,s:{s:(s)}})", "ids", (int)bug_id, "updates", "add_cc", login);
+ if (result)
+ xmlrpc_DECREF(result);
+ return result ? 0 : -1;
+}
+
+int ctx::add_comment(xmlrpc_int32 bug_id, const char* comment, bool is_private)
+{
+ xmlrpc_value* result = call("Bug.add_comment", "({s:i,s:s,s:b})", "id", (int)bug_id,
+ "comment", comment,
+ "private", is_private);
+ if (result)
+ xmlrpc_DECREF(result);
+ return result ? 0 : -1;
+}
+
+xmlrpc_int32 ctx::new_bug(const map_crash_data_t& pCrashData, int depend_on_bugno)
+{
+ const std::string& package = get_crash_data_item_content(pCrashData, FILENAME_PACKAGE);
+ const std::string& component = get_crash_data_item_content(pCrashData, FILENAME_COMPONENT);
+ const std::string& release = get_crash_data_item_content(pCrashData, FILENAME_RELEASE);
+ const std::string& arch = get_crash_data_item_content(pCrashData, FILENAME_ARCHITECTURE);
+ const std::string& duphash = get_crash_data_item_content(pCrashData, CD_DUPHASH);
+ const char *reason = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_REASON);
+ const char *function = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_CRASH_FUNCTION);
+
+ std::string summary = "[abrt] " + package;
+ if (function != NULL && strlen(function) < 30)
+ {
+ summary += ": ";
+ summary += function;
+ }
+
+ if (reason != NULL)
+ {
+ summary += ": ";
+ summary += reason;
+ }
+ std::string status_whiteboard = "abrt_hash:" + duphash;
+
+ std::string description = "abrt version: "VERSION"\n";
+ description += make_description_bz(pCrashData);
+
+ char *product = NULL;
+ char *version = NULL;
+ parse_release(release.c_str(), &product, &version);
+
+ xmlrpc_value* result = NULL;
+ if (depend_on_bugno > -1)
+ {
+ result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})",
+ "product", product,
+ "component", component.c_str(),
+ "version", version,
+ "summary", summary.c_str(),
+ "description", description.c_str(),
+ "status_whiteboard", status_whiteboard.c_str(),
+ "platform", arch.c_str(),
+ "dependson", depend_on_bugno
+ );
+ }
+ else
+ {
+ result = call("Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})",
+ "product", product,
+ "component", component.c_str(),
+ "version", version,
+ "summary", summary.c_str(),
+ "description", description.c_str(),
+ "status_whiteboard", status_whiteboard.c_str(),
+ "platform", arch.c_str()
+ );
+
+ }
+ free(product);
+ free(version);
+
+ if (!result)
+ return -1;
+
+ xmlrpc_value* id = get_member("id", result);
+ xmlrpc_DECREF(result);
+ if (!id)
+ return -1;
+
+ xmlrpc_int32 bug_id = -1;
+ xmlrpc_read_int(&env, id, &bug_id);
+ xmlrpc_DECREF(id);
+ if (env.fault_occurred)
+ return -1;
+
+ log("New bug id: %i", (int)bug_id);
+ update_client(_("New bug id: %i"), (int)bug_id);
+
+ return bug_id;
+}
+
+int ctx::add_attachments(const char* bug_id_str, const map_crash_data_t& pCrashData)
+{
+ map_crash_data_t::const_iterator it = pCrashData.begin();
+ for (; it != pCrashData.end(); it++)
+ {
+ const std::string &itemname = it->first;
+ const std::string &type = it->second[CD_TYPE];
+ const std::string &content = it->second[CD_CONTENT];
+
+ if (type == CD_TXT
+ && (content.length() > CD_TEXT_ATT_SIZE || itemname == FILENAME_BACKTRACE)
+ ) {
+ char *encoded64 = encode_base64(content.c_str(), content.length());
+ xmlrpc_value* result = call("bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s})", bug_id_str,
+ "description", ("File: " + itemname).c_str(),
+ "filename", itemname.c_str(),
+ "contenttype", "text/plain",
+ "data", encoded64
+ );
+ free(encoded64);
+ if (!result)
+ return -1;
+
+ xmlrpc_DECREF(result);
+ }
+ }
+ return 0;
+}
+
+int ctx::get_bug_info(struct bug_info* bz, xmlrpc_int32 bug_id)
+{
+ xmlrpc_value* result = call("bugzilla.getBug", "(s)", to_string(bug_id).c_str());
+ if (!result)
+ return -1;
+
+ if (result)
+ {
+ bz->bug_product = get_bug_product(result);
+ if (bz->bug_product == NULL)
+ return -1;
+
+ bz->bug_status = get_bug_status(result);
+ if (bz->bug_status == NULL)
+ return -1;
+
+ bz->bug_reporter = get_bug_reporter(result);
+ if (bz->bug_reporter == NULL)
+ return -1;
+
+ // mandatory when bug status is CLOSED
+ if (strcmp(bz->bug_status, "CLOSED") == 0)
+ {
+ bz->bug_resolution = get_bug_resolution(result);
+ if ((env.fault_occurred) && (bz->bug_resolution == NULL))
+ return -1;
+ }
+
+ // mandatory when bug status is CLOSED and resolution is DUPLICATE
+ if ((strcmp(bz->bug_status, "CLOSED") == 0)
+ && (strcmp(bz->bug_resolution, "DUPLICATE") == 0)
+ ) {
+ bz->bug_dup_id = get_bug_dup_id(result);
+ if (env.fault_occurred)
+ return -1;
+ }
+
+ get_bug_cc(result, bz);
+ if (env.fault_occurred)
+ return -1;
+
+ xmlrpc_DECREF(result);
+ return 0;
+ }
+ return -1;
+}
+
+//-------------------------------------------------------------------
+// ^
+// | nice
+// -------------------------------------------------------------------
+// | BAD
+// v
+//-------------------------------------------------------------------
+//TODO: need to rewrite
+void ctx::login(const char* login, const char* passwd)
+{
+ xmlrpc_value* result = call("User.login", "({s:s,s:s})", "login", login, "password", passwd);
+
+ if (!result)
+ {
+ std::string errmsg = ssprintf(_("Cannot login. Check Edit->Plugins->Bugzilla and /etc/abrt/plugins/Bugzilla.conf. Server said: %s"), env.fault_string);
+ error_msg("%s", errmsg.c_str()); // show error in daemon log
+ throw CABRTException(EXCEP_PLUGIN, "%s", errmsg.c_str());
+ }
+ xmlrpc_DECREF(result);
+}
+
+void ctx::logout()
+{
+ xmlrpc_value* result = call("User.logout", "(s)", "");
+ if (result)
+ xmlrpc_DECREF(result);
+
+ throw_if_xml_fault_occurred(&env);
+}
+
+} /* namespace */
+
+
+/*
+ * CReporterBugzilla
+ */
+
+static map_plugin_settings_t parse_settings(const map_plugin_settings_t& pSettings)
+{
+ map_plugin_settings_t plugin_settings;
+
+ map_plugin_settings_t::const_iterator end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+ it = pSettings.find("BugzillaURL");
+ if (it != end)
+ {
+ std::string BugzillaURL = it->second;
+ //remove the /xmlrpc.cgi part from old settings
+ //FIXME: can be removed after users are informed about new config format
+ std::string::size_type pos = BugzillaURL.find(XML_RPC_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ BugzillaURL.erase(pos);
+ }
+ //remove the trailing '/'
+ while (BugzillaURL[BugzillaURL.length() - 1] == '/')
+ {
+ BugzillaURL.erase(BugzillaURL.length() - 1);
+ }
+ plugin_settings["BugzillaXMLRPC"] = BugzillaURL + XML_RPC_SUFFIX;
+ plugin_settings["BugzillaURL"] = BugzillaURL;
+ }
+
+ it = pSettings.find("Login");
+ if (it == end)
+ {
+ /* if any of the option is not set we use the defaults for everything */
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Login"] = it->second;
+
+ it = pSettings.find("Password");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Password"] = it->second;
+
+ it = pSettings.find("SSLVerify");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["SSLVerify"] = it->second;
+
+ VERB1 log("User settings ok, using them instead of defaults");
+ return plugin_settings;
+}
+
+CReporterBugzilla::CReporterBugzilla() :
+ m_bSSLVerify(true),
+ m_sBugzillaURL("https://bugzilla.redhat.com"),
+ m_sBugzillaXMLRPC("https://bugzilla.redhat.com"XML_RPC_SUFFIX),
+ m_bRatingRequired(true)
+{}
+
+CReporterBugzilla::~CReporterBugzilla()
+{}
+
+std::string CReporterBugzilla::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ xmlrpc_int32 bug_id = -1;
+ std::string Login;
+ std::string Password;
+ std::string BugzillaXMLRPC;
+ std::string BugzillaURL;
+ bool SSLVerify;
+ 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())
+ {
+ Login = settings["Login"];
+ Password = settings["Password"];
+ BugzillaXMLRPC = settings["BugzillaXMLRPC"];
+ BugzillaURL = settings["BugzillaURL"];
+ SSLVerify = string_to_bool(settings["SSLVerify"].c_str());
+ }
+ else
+ {
+ Login = m_sLogin;
+ Password = m_sPassword;
+ BugzillaXMLRPC = m_sBugzillaXMLRPC;
+ BugzillaURL = m_sBugzillaURL;
+ SSLVerify = m_bSSLVerify;
+ }
+
+ if ((Login == "") || (Password == ""))
+ {
+ VERB3 log("Empty login and password");
+ throw CABRTException(EXCEP_PLUGIN, _("Empty login or password.\nPlease check "PLUGINS_CONF_DIR"/Bugzilla.conf."));
+ }
+
+ const std::string& component = get_crash_data_item_content(pCrashData, FILENAME_COMPONENT);
+ const std::string& duphash = get_crash_data_item_content(pCrashData, CD_DUPHASH);
+ const char *release = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_RELEASE);
+
+ ctx bz_server(BugzillaXMLRPC.c_str(), SSLVerify);
+
+ update_client(_("Logging into bugzilla..."));
+ bz_server.login(Login.c_str(), Password.c_str());
+
+ update_client(_("Checking for duplicates..."));
+
+ char *product = NULL;
+ char *version = NULL;
+ parse_release(release, &product, &version);
+
+ xmlrpc_value *result;
+ if (strcmp(product, "Fedora") == 0)
+ result = bz_server.call_quicksearch_duphash(component.c_str(), product, duphash.c_str());
+ else
+ result = bz_server.call_quicksearch_duphash(component.c_str(), NULL, duphash.c_str());
+
+ if (!result)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ xmlrpc_value *all_bugs = bz_server.get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member 'bugs'"));
+ }
+
+ int all_bugs_size = bz_server.get_array_size(all_bugs);
+ struct bug_info bz;
+ int depend_on_bugno = -1;
+ if (all_bugs_size > 0)
+ {
+ bug_id = bz_server.get_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ if (bug_id == -1)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ bug_info_init(&bz);
+ if (bz_server.get_bug_info(&bz, bug_id) == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ }
+
+ if (strcmp(bz.bug_product, product) != 0)
+ {
+ depend_on_bugno = bug_id;
+ bug_info_destroy(&bz);
+ result = bz_server.call_quicksearch_duphash(component.c_str(), release, duphash.c_str());
+ if (!result)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ all_bugs = bz_server.get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Missing mandatory member 'bugs'"));
+ }
+
+ all_bugs_size = bz_server.get_array_size(all_bugs);
+ if (all_bugs_size > 0)
+ {
+ bug_id = bz_server.get_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ if (bug_id == -1)
+ throw_if_xml_fault_occurred(&bz_server.env);
+
+ bug_info_init(&bz);
+ if (bz_server.get_bug_info(&bz, bug_id) == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ }
+ }
+ else
+ xmlrpc_DECREF(all_bugs);
+ }
+ }
+ free(product);
+ free(version);
+
+ if (all_bugs_size < 0)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+ else if (all_bugs_size == 0) // Create new bug
+ {
+ update_client(_("Creating a new bug..."));
+ bug_id = bz_server.new_bug(pCrashData, depend_on_bugno);
+ if (bug_id < 0)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ throw CABRTException(EXCEP_PLUGIN, _("Bugzilla entry creation failed"));
+ }
+
+ log("Adding attachments to bug %d...", bug_id);
+ int ret = bz_server.add_attachments(to_string(bug_id).c_str(), pCrashData);
+ if (ret == -1)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+
+ update_client(_("Logging out..."));
+ bz_server.logout();
+
+ std::string bug_status = ssprintf(
+ "Status: NEW\n"
+ "%s/show_bug.cgi?id=%u",
+ BugzillaURL.c_str(),
+ (int)bug_id
+ );
+ return bug_status;
+ }
+ else if (all_bugs_size > 1)
+ {
+ // When someone clones bug it has same duphash, so we can find more than 1.
+ // Need to be checked if component is same.
+ VERB3 log("Bugzilla has %u reports with same duphash '%s'", all_bugs_size, duphash.c_str());
+ }
+
+ // decision based on state
+ update_client(_("Bug is already reported: %i"), bug_id);
+
+ xmlrpc_int32 original_bug_id = bug_id;
+ if ((strcmp(bz.bug_status, "CLOSED") == 0) && (strcmp(bz.bug_resolution, "DUPLICATE") == 0))
+ {
+ for (int ii = 0; ii <= MAX_HOPS; ii++)
+ {
+ if (ii == MAX_HOPS)
+ {
+ VERB3 log("Bugzilla could not find a parent of bug %d", (int)original_bug_id);
+ bug_info_destroy(&bz);
+ throw CABRTException(EXCEP_PLUGIN, _("Bugzilla couldn't find parent of bug %d"), (int)original_bug_id);
+ }
+
+ log("Bug %d is a duplicate, using parent bug %d", bug_id, (int)bz.bug_dup_id);
+ bug_id = bz.bug_dup_id;
+ bug_info_destroy(&bz);
+ bug_info_init(&bz);
+
+ if (bz_server.get_bug_info(&bz, bug_id) == -1)
+ {
+ bug_info_destroy(&bz);
+ if (bz_server.env.fault_occurred)
+ {
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+ throw CABRTException(EXCEP_PLUGIN, _("get_bug_info() failed. Could not collect all mandatory information"));
+ }
+
+ // found a bug which is not CLOSED as DUPLICATE
+ if (bz.bug_dup_id == -1)
+ break;
+ }
+ }
+
+ if (strcmp(bz.bug_status, "CLOSED") != 0)
+ {
+ int status = 0;
+ if ((strcmp(bz.bug_reporter, Login.c_str()) != 0) && (am_i_in_cc(&bz, Login.c_str())))
+ {
+ VERB2 log(_("Adding %s to CC list"), Login.c_str());
+ update_client(_("Adding %s to CC list"), Login.c_str());
+ status = bz_server.add_plus_one_cc(bug_id, Login.c_str());
+ }
+
+ if (status == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+
+ std::string description = make_description_reproduce_comment(pCrashData);
+ if (!description.empty())
+ {
+ const char* package = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_PACKAGE);
+ const char* release = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_RELEASE);
+ const char* arch = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_ARCHITECTURE);
+ const char* is_private = get_crash_data_item_content_or_NULL(pCrashData, "is_private");
+
+ description = ssprintf("Package: %s\n"
+ "Architecture: %s\n"
+ "OS Release: %s\n"
+ "%s", package, arch, release, description.c_str()
+ );
+
+ update_client(_("Adding new comment to bug %d"), (int)bug_id);
+
+ bool is_priv = is_private && (is_private[0] == '1');
+ if (bz_server.add_comment(bug_id, description.c_str(), is_priv) == -1)
+ {
+ bug_info_destroy(&bz);
+ throw_if_xml_fault_occurred(&bz_server.env);
+ }
+ }
+ }
+
+ update_client(_("Logging out..."));
+ bz_server.logout();
+
+ std::string bug_status = ssprintf(
+ "Status: %s%s%s\n"
+ "%s/show_bug.cgi?id=%u",
+ bz.bug_status,
+ bz.bug_resolution ? " " : "",
+ bz.bug_resolution ? bz.bug_resolution : "",
+ BugzillaURL.c_str(),
+ (int)bug_id
+ );
+
+ bug_info_destroy(&bz);
+
+ return bug_status;
+}
+
+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
+//for me to know that it is being used. Moreover, when I discovered it
+//(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 end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+ it = pSettings.find("BugzillaURL");
+ if (it != end)
+ {
+ m_sBugzillaURL = it->second;
+ //remove the /xmlrpc.cgi part from old settings
+ //FIXME: can be removed after users are informed about new config format
+ std::string::size_type pos = m_sBugzillaURL.find(XML_RPC_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ m_sBugzillaURL.erase(pos);
+ }
+ //remove the trailing '/'
+ while (m_sBugzillaURL[m_sBugzillaURL.length() - 1] == '/')
+ {
+ m_sBugzillaURL.erase(m_sBugzillaURL.length() - 1);
+ }
+ /*
+ if (*(--m_sBugzillaURL.end()) == '/')
+ {
+ m_sBugzillaURL.erase(--m_sBugzillaURL.end());
+ }
+ */
+ m_sBugzillaXMLRPC = m_sBugzillaURL + XML_RPC_SUFFIX;
+ }
+ it = pSettings.find("Login");
+ if (it != end)
+ {
+ m_sLogin = it->second;
+ }
+ it = pSettings.find("Password");
+ if (it != end)
+ {
+ m_sPassword = it->second;
+ }
+ it = pSettings.find("SSLVerify");
+ if (it != end)
+ {
+ m_bSSLVerify = string_to_bool(it->second.c_str());
+ }
+}
+
+/* Should not be deleted (why?) */
+const map_plugin_settings_t& CReporterBugzilla::GetSettings()
+{
+ m_pSettings["BugzillaURL"] = m_sBugzillaURL;
+ m_pSettings["Login"] = m_sLogin;
+ m_pSettings["Password"] = m_sPassword;
+ m_pSettings["SSLVerify"] = m_bSSLVerify ? "yes" : "no";
+ m_pSettings["RatingRequired"] = m_bRatingRequired ? "yes" : "no";
+
+ return m_pSettings;
+}
+
+PLUGIN_INFO(REPORTER,
+ CReporterBugzilla,
+ "Bugzilla",
+ "0.0.4",
+ _("Reports bugs to bugzilla"),
+ "npajkovs@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/Bugzilla.glade");
diff --git a/lib/plugins/Bugzilla.glade b/lib/plugins/Bugzilla.glade
new file mode 100644
index 00000000..cabdd06a
--- /dev/null
+++ b/lib/plugins/Bugzilla.glade
@@ -0,0 +1,246 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">12</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lBugzillaURL">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Bugzilla URL:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lLogin">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Login(email):</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lPassword">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password:</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_BugzillaURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Login">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="cb_Password">
+ <property name="label" translatable="yes">Show password</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_SSLVerify">
+ <property name="label" translatable="yes">SSL verify</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Bugzilla plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">Don't have an account yet?</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xpad">5</property>
+ <property name="label" translatable="yes">You can create it &lt;a href="https://bugzilla.redhat.com/createaccount.cgi"&gt;here&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="selectable">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="bApply">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">bApply</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAction" id="action1"/>
+</interface>
diff --git a/lib/plugins/Bugzilla.h b/lib/plugins/Bugzilla.h
new file mode 100644
index 00000000..a4c9a01e
--- /dev/null
+++ b/lib/plugins/Bugzilla.h
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef BUGZILLA_H_
+#define BUGZILLA_H_
+
+#include "plugin.h"
+#include "reporter.h"
+
+class CReporterBugzilla : public CReporter
+{
+ private:
+ bool m_bSSLVerify;
+ std::string m_sBugzillaURL;
+ std::string m_sBugzillaXMLRPC;
+ std::string m_sLogin;
+ std::string m_sPassword;
+ bool m_bRatingRequired;
+
+ public:
+ CReporterBugzilla();
+ virtual ~CReporterBugzilla();
+
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *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.conf b/lib/plugins/CCpp.conf
new file mode 100644
index 00000000..86bfb7db
--- /dev/null
+++ b/lib/plugins/CCpp.conf
@@ -0,0 +1,42 @@
+# Configuration file for CCpp hook and plugin
+Enabled = yes
+
+# If you also want to dump file named "core"
+# in crashed process' current dir, set to "yes"
+MakeCompatCore = yes
+
+# Do you want a copy of crashed binary be saved?
+# (useful, for example, when _deleted binary_ segfaults)
+SaveBinaryImage = no
+
+# Generate backtrace
+Backtrace = yes
+
+# Generate backtrace for crashes uploaded from remote machines.
+# Note that for reliable backtrace generation, your local machine
+# needs to have the crashed executable and all libraries it uses,
+# and they need to be the same versions as on remote machines.
+# If you cannot ensure that, it's better to set this option to "no"
+BacktraceRemotes = no
+
+# Generate memory map too (IGNORED FOR NOW)
+MemoryMap = no
+
+# How to get debuginfo: install, mount
+## install - download and install debuginfo packages
+## mount - mount fedora NFS with debug info
+## (IGNORED FOR NOW)
+DebugInfo = install
+
+# If this option is set to "yes",
+# debuginfos will be installed to @@LOCALSTATEDIR@@/cache/abrt-di
+InstallDebugInfo = yes
+
+# Additional directories to search for debuginfos.
+# For example, you can list a network-mounted shared store
+# of all debuginfos here.
+# ReadonlyLocalDebugInfoDirs = /path1:/path2:...
+
+# Keep @@LOCALSTATEDIR@@/cache/abrt-di
+# from growing out-of-bounds.
+DebugInfoCacheMB = 4000
diff --git a/lib/plugins/CCpp.cpp b/lib/plugins/CCpp.cpp
new file mode 100644
index 00000000..629da665
--- /dev/null
+++ b/lib/plugins/CCpp.cpp
@@ -0,0 +1,1064 @@
+/*
+ CCpp.cpp
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <set>
+#include <iomanip>
+//#include <nss.h>
+//#include <sechash.h>
+#include "abrtlib.h"
+#include "strbuf.h"
+#include "CCpp.h"
+#include "abrt_exception.h"
+#include "debug_dump.h"
+#include "comm_layer_inner.h"
+#include "Polkit.h"
+#include "backtrace.h"
+#include "CCpp_sha1.h"
+
+using namespace std;
+
+#define CORE_PATTERN_IFACE "/proc/sys/kernel/core_pattern"
+#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %p %s %u %c"
+#define CORE_PIPE_LIMIT_IFACE "/proc/sys/kernel/core_pipe_limit"
+/* core_pipe_limit specifies how many dump_helpers might run at the same time
+0 - means unlimited, but the it's not guaranteed that /proc/<pid> of crashing
+process might not be available for dump_helper
+4 - means that 4 dump_helpers can run at the same time, which should be enough
+for ABRT, we can miss some crashes, but what are the odds that more processes
+crash at the same time? This value has been recommended by nhorman
+*/
+#define CORE_PIPE_LIMIT "4"
+
+#define DEBUGINFO_CACHE_DIR LOCALSTATEDIR"/cache/abrt-di"
+
+CAnalyzerCCpp::CAnalyzerCCpp() :
+ m_bBacktrace(true),
+ m_bBacktraceRemotes(false),
+ m_bMemoryMap(false),
+ m_bInstallDebugInfo(true),
+ m_nDebugInfoCacheMB(4000)
+{}
+
+static string create_hash(const char *pInput)
+{
+ unsigned int len;
+
+#if 0
+{
+ char hash_str[SHA1_LENGTH*2 + 1];
+ unsigned char hash[SHA1_LENGTH];
+ HASHContext *hc;
+ hc = HASH_Create(HASH_AlgSHA1);
+ if (!hc)
+ {
+ error_msg_and_die("HASH_Create(HASH_AlgSHA1) failed"); /* paranoia */
+ }
+ HASH_Begin(hc);
+ HASH_Update(hc, (const unsigned char*)pInput, strlen(pInput));
+ HASH_End(hc, hash, &len, sizeof(hash));
+ HASH_Destroy(hc);
+
+ char *d = hash_str;
+ unsigned char *s = hash;
+ while (len)
+ {
+ *d++ = "0123456789abcdef"[*s >> 4];
+ *d++ = "0123456789abcdef"[*s & 0xf];
+ s++;
+ len--;
+ }
+ *d = '\0';
+//log("hash1:%s str:'%s'", hash_str, pInput);
+}
+#endif
+
+ char hash_str[SHA1_RESULT_LEN*2 + 1];
+ unsigned char hash2[SHA1_RESULT_LEN];
+ sha1_ctx_t sha1ctx;
+ sha1_begin(&sha1ctx);
+ sha1_hash(pInput, strlen(pInput), &sha1ctx);
+ sha1_end(hash2, &sha1ctx);
+ len = SHA1_RESULT_LEN;
+
+ char *d = hash_str;
+ unsigned char *s = hash2;
+ while (len)
+ {
+ *d++ = "0123456789abcdef"[*s >> 4];
+ *d++ = "0123456789abcdef"[*s & 0xf];
+ s++;
+ len--;
+ }
+ *d = '\0';
+//log("hash2:%s str:'%s'", hash_str, pInput);
+
+ return hash_str;
+}
+
+/* Returns status. See `man 2 wait` for status information. */
+static int ExecVP(char **pArgs, uid_t uid, int redirect_stderr, string& pOutput)
+{
+ /* Nuke everything which may make setlocale() switch to non-POSIX locale:
+ * we need to avoid having gdb output in some obscure language.
+ */
+ static const char *const unsetenv_vec[] = {
+ "LANG",
+ "LC_ALL",
+ "LC_COLLATE",
+ "LC_CTYPE",
+ "LC_MESSAGES",
+ "LC_MONETARY",
+ "LC_NUMERIC",
+ "LC_TIME",
+ NULL
+ };
+
+ int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETGUID | EXECFLG_SETSID | EXECFLG_QUIET;
+ if (redirect_stderr)
+ flags |= EXECFLG_ERR2OUT;
+ VERB1 flags &= ~EXECFLG_QUIET;
+
+ int pipeout[2];
+ pid_t child = fork_execv_on_steroids(flags, pArgs, pipeout, (char**)unsetenv_vec, /*dir:*/ NULL, uid);
+
+ /* We use this function to run gdb and unstrip. Bugs in gdb or corrupted
+ * coredumps were observed to cause gdb to enter infinite loop.
+ * Therefore we have a (largish) timeout, after which we kill the child.
+ */
+ int t = time(NULL); /* int is enough, no need to use time_t */
+ int endtime = t + 60;
+ while (1)
+ {
+ int timeout = endtime - t;
+ if (timeout < 0)
+ {
+ kill(child, SIGKILL);
+ pOutput += "\nTimeout exceeded: 60 second, killing ";
+ pOutput += pArgs[0];
+ pOutput += "\n";
+ break;
+ }
+
+ /* We don't check poll result - checking read result is enough */
+ struct pollfd pfd;
+ pfd.fd = pipeout[0];
+ pfd.events = POLLIN;
+ poll(&pfd, 1, timeout * 1000);
+
+ char buff[1024];
+ int r = read(pipeout[0], buff, sizeof(buff) - 1);
+ if (r <= 0)
+ break;
+ buff[r] = '\0';
+ pOutput += buff;
+ t = time(NULL);
+ }
+ close(pipeout[0]);
+
+ int status;
+ waitpid(child, &status, 0); /* prevent having zombie child process */
+
+ return status;
+}
+
+static void GetBacktrace(const char *pDebugDumpDir,
+ const char *pDebugInfoDirs,
+ string& pBacktrace)
+{
+ update_client(_("Generating backtrace"));
+
+ string UID;
+ string executable;
+ {
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ dd.LoadText(FILENAME_EXECUTABLE, executable);
+ dd.LoadText(CD_UID, UID);
+ }
+
+ // Workaround for
+ // http://sourceware.org/bugzilla/show_bug.cgi?id=9622
+ unsetenv("TERM");
+ // This is not necessary, and was observed to cause
+ // environmant corruption (because we run in a thread?):
+ //putenv((char*)"TERM=dumb");
+
+ char *args[21];
+ args[0] = (char*)"gdb";
+ args[1] = (char*)"-batch";
+
+ // when/if gdb supports it:
+ // (https://bugzilla.redhat.com/show_bug.cgi?id=528668):
+ args[2] = (char*)"-ex";
+ string dfd = "set debug-file-directory /usr/lib/debug";
+ const char *p = pDebugInfoDirs;
+ while (1)
+ {
+ const char *colon_or_nul = strchrnul(p, ':');
+ dfd += ':';
+ dfd.append(p, colon_or_nul - p);
+ dfd += "/usr/lib/debug";
+ if (*colon_or_nul != ':')
+ break;
+ p = colon_or_nul + 1;
+ }
+ args[3] = (char*)dfd.c_str();
+
+ /* "file BINARY_FILE" is needed, without it gdb cannot properly
+ * unwind the stack. Currently the unwind information is located
+ * in .eh_frame which is stored only in binary, not in coredump
+ * or debuginfo.
+ *
+ * Fedora GDB does not strictly need it, it will find the binary
+ * by its build-id. But for binaries either without build-id
+ * (= built on non-Fedora GCC) or which do not have
+ * their debuginfo rpm installed gdb would not find BINARY_FILE
+ * so it is still makes sense to supply "file BINARY_FILE".
+ *
+ * Unfortunately, "file BINARY_FILE" doesn't work well if BINARY_FILE
+ * was deleted (as often happens during system updates):
+ * gdb uses specified BINARY_FILE
+ * even if it is completely unrelated to the coredump.
+ * See https://bugzilla.redhat.com/show_bug.cgi?id=525721
+ *
+ * TODO: check mtimes on COREFILE and BINARY_FILE and not supply
+ * BINARY_FILE if it is newer (to at least avoid gdb complaining).
+ */
+ args[4] = (char*)"-ex";
+ string file = ssprintf("file %s", executable.c_str());
+ args[5] = (char*)file.c_str();
+
+ args[6] = (char*)"-ex";
+ string corefile = ssprintf("core-file %s/"FILENAME_COREDUMP, pDebugDumpDir);
+ args[7] = (char*)corefile.c_str();
+
+ args[8] = (char*)"-ex";
+ /*args[9] = ... see below */
+ args[10] = (char*)"-ex";
+ args[11] = (char*)"info sharedlib";
+ /* glibc's abort() stores its message in __abort_msg variable */
+ args[12] = (char*)"-ex";
+ args[13] = (char*)"print (char*)__abort_msg";
+ args[14] = (char*)"-ex";
+ args[15] = (char*)"print (char*)__glib_assert_msg";
+ args[16] = (char*)"-ex";
+ args[17] = (char*)"info registers";
+ args[18] = (char*)"-ex";
+ args[19] = (char*)"disassemble";
+ args[20] = NULL;
+
+ /* Get the backtrace, but try to cap its size */
+ /* Limit bt depth. With no limit, gdb sometimes OOMs the machine */
+ unsigned bt_depth = 2048;
+ const char *thread_apply_all = "thread apply all ";
+ const char *full = " full";
+ while (1)
+ {
+ string cmd = ssprintf("%sbacktrace %u%s", thread_apply_all, bt_depth, full);
+ args[9] = (char*)cmd.c_str();
+ pBacktrace = "";
+ ExecVP(args, xatoi_u(UID.c_str()), /*redirect_stderr:*/ 1, pBacktrace);
+ if (bt_depth <= 64 || pBacktrace.size() < 256*1024)
+ return;
+ bt_depth /= 2;
+ if (bt_depth <= 64 && thread_apply_all[0] != '\0')
+ {
+ /* This program likely has gazillion threads, dont try to bt them all */
+ bt_depth = 256;
+ thread_apply_all = "";
+ }
+ if (bt_depth <= 64 && full[0] != '\0')
+ {
+ /* Looks like there are gigantic local structures or arrays, disable "full" bt */
+ bt_depth = 256;
+ full = "";
+ }
+ }
+}
+
+static void GetIndependentBuildIdPC(const char *unstrip_n_output,
+ string& pIndependentBuildIdPC)
+{
+ // lines look like this:
+ // 0x400000+0x209000 23c77451cf6adff77fc1f5ee2a01d75de6511dda@0x40024c - - [exe]
+ // 0x400000+0x209000 ab3c8286aac6c043fd1bb1cc2a0b88ec29517d3e@0x40024c /bin/sleep /usr/lib/debug/bin/sleep.debug [exe]
+ // 0x7fff313ff000+0x1000 389c7475e3d5401c55953a425a2042ef62c4c7df@0x7fff313ff2f8 . - linux-vdso.so.1
+ const char *line = unstrip_n_output;
+ while (*line)
+ {
+ const char *eol = strchrnul(line, '\n');
+ const char *plus = (char*)memchr(line, '+', eol - line);
+ if (plus)
+ {
+ while (++plus < eol && *plus != '@')
+ {
+ if (!isspace(*plus))
+ {
+ pIndependentBuildIdPC += *plus;
+ }
+ }
+ }
+ if (*eol != '\n') break;
+ line = eol + 1;
+ }
+}
+
+static string run_unstrip_n(const char *pDebugDumpDir)
+{
+ string UID;
+ {
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ dd.LoadText(CD_UID, UID);
+ }
+
+ char* args[4];
+ args[0] = (char*)"eu-unstrip";
+ args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, pDebugDumpDir);
+ args[2] = (char*)"-n";
+ args[3] = NULL;
+
+ string output;
+ ExecVP(args, xatoi_u(UID.c_str()), /*redirect_stderr:*/ 0, output);
+
+ free(args[1]);
+
+ return output;
+}
+
+/* Needs gdb feature from here: https://bugzilla.redhat.com/show_bug.cgi?id=528668
+ * It is slated to be in F12/RHEL6.
+ */
+static void InstallDebugInfos(const char *pDebugDumpDir,
+ const char *debuginfo_dirs,
+ string& build_ids)
+{
+ update_client(_("Starting the debuginfo installation"));
+
+ int pipeout[2]; //TODO: can we use ExecVP?
+ xpipe(pipeout);
+
+ fflush(NULL);
+ pid_t child = fork();
+ if (child < 0)
+ {
+ /*close(pipeout[0]); - why bother */
+ /*close(pipeout[1]); */
+ perror_msg_and_die("fork");
+ }
+ if (child == 0)
+ {
+ close(pipeout[0]);
+ xmove_fd(pipeout[1], STDOUT_FILENO);
+ xmove_fd(xopen("/dev/null", O_RDONLY), STDIN_FILENO);
+
+ char *coredump = xasprintf("%s/"FILENAME_COREDUMP, pDebugDumpDir);
+ /* SELinux guys are not happy with /tmp, using /var/run/abrt */
+ char *tempdir = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%lu-%lu", (long)getpid(), (long)time(NULL));
+ /* log() goes to stderr/syslog, it's ok to use it here */
+ VERB1 log("Executing: %s %s %s %s", "abrt-debuginfo-install", coredump, tempdir, debuginfo_dirs);
+ /* We want parent to see errors in the same stream */
+ xdup2(STDOUT_FILENO, STDERR_FILENO);
+ execlp("abrt-debuginfo-install", "abrt-debuginfo-install", coredump, tempdir, debuginfo_dirs, NULL);
+ perror_msg("Can't execute '%s'", "abrt-debuginfo-install");
+ /* Serious error (1 means "some debuginfos not found") */
+ exit(2);
+ }
+
+ close(pipeout[1]);
+
+ FILE *pipeout_fp = fdopen(pipeout[0], "r");
+ if (pipeout_fp == NULL) /* never happens */
+ {
+ close(pipeout[0]);
+ waitpid(child, NULL, 0);
+ return;
+ }
+
+ /* With 126 debuginfos I've seen lines 9k+ chars long...
+ * yet, having it truly unlimited is bad too,
+ * therefore we are using LARGE, but still limited buffer.
+ */
+ char *buff = (char*) xmalloc(64*1024);
+ while (fgets(buff, 64*1024, pipeout_fp))
+ {
+ strchrnul(buff, '\n')[0] = '\0';
+
+ if (strncmp(buff, "MISSING:", 8) == 0)
+ {
+ build_ids += "Debuginfo absent: ";
+ build_ids += buff + 8;
+ build_ids += "\n";
+ continue;
+ }
+
+ const char *p = buff;
+ while (*p == ' ' || *p == '\t')
+ {
+ p++;
+ }
+ if (*p)
+ {
+ VERB1 log("%s", buff);
+ update_client("%s", buff);
+ }
+ }
+ free(buff);
+ fclose(pipeout_fp);
+
+ int status = 0;
+ while (waitpid(child, &status, 0) < 0 && errno == EINTR)
+ continue;
+ if (WIFEXITED(status))
+ {
+ if (WEXITSTATUS(status) > 1)
+ error_msg("%s exited with %u", "abrt-debuginfo-install", (int)WEXITSTATUS(status));
+ }
+ else
+ {
+ error_msg("%s killed by signal %u", "abrt-debuginfo-install", (int)WTERMSIG(status));
+ }
+}
+
+static double get_dir_size(const char *dirname,
+ string *worst_file,
+ double *maxsz)
+{
+ DIR *dp = opendir(dirname);
+ if (dp == NULL)
+ return 0;
+
+ struct dirent *ep;
+ struct stat stats;
+ double size = 0;
+ while ((ep = readdir(dp)) != NULL)
+ {
+ if (dot_or_dotdot(ep->d_name))
+ continue;
+ string dname = concat_path_file(dirname, ep->d_name);
+ if (lstat(dname.c_str(), &stats) != 0)
+ continue;
+ if (S_ISDIR(stats.st_mode))
+ {
+ double sz = get_dir_size(dname.c_str(), worst_file, maxsz);
+ size += sz;
+ }
+ else if (S_ISREG(stats.st_mode))
+ {
+ double sz = stats.st_size;
+ size += sz;
+
+ if (worst_file)
+ {
+ /* Calculate "weighted" size and age
+ * w = sz_kbytes * age_mins */
+ sz /= 1024;
+ long age = (time(NULL) - stats.st_mtime) / 60;
+ if (age > 0)
+ sz *= age;
+
+ if (sz > *maxsz)
+ {
+ *maxsz = sz;
+ *worst_file = dname;
+ }
+ }
+ }
+ }
+ closedir(dp);
+ return size;
+}
+
+static void trim_debuginfo_cache(unsigned max_mb)
+{
+ while (1)
+ {
+ string worst_file;
+ double maxsz = 0;
+ double cache_sz = get_dir_size(DEBUGINFO_CACHE_DIR, &worst_file, &maxsz);
+ if (cache_sz / (1024 * 1024) < max_mb)
+ break;
+ VERB1 log("%s is %.0f bytes (over %u MB), deleting '%s'",
+ DEBUGINFO_CACHE_DIR, cache_sz, max_mb, worst_file.c_str());
+ if (unlink(worst_file.c_str()) != 0)
+ perror_msg("Can't unlink '%s'", worst_file.c_str());
+ }
+}
+
+string CAnalyzerCCpp::GetLocalUUID(const char *pDebugDumpDir)
+{
+ string executable;
+ string package;
+ {
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ dd.LoadText(FILENAME_EXECUTABLE, executable);
+ dd.LoadText(FILENAME_PACKAGE, package);
+ }
+
+ string unstrip_n_output = run_unstrip_n(pDebugDumpDir);
+ string independentBuildIdPC;
+ GetIndependentBuildIdPC(unstrip_n_output.c_str(), independentBuildIdPC);
+
+ /* package variable has "firefox-3.5.6-1.fc11[.1]" format */
+ /* Remove distro suffix and maybe least significant version number */
+ char *trimmed_package = xstrdup(package.c_str());
+ char *p = trimmed_package;
+ while (*p)
+ {
+ if (*p == '.' && (p[1] < '0' || p[1] > '9'))
+ {
+ /* We found "XXXX.nondigitXXXX", trim this part */
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
+ char *first_dot = strchr(trimmed_package, '.');
+ if (first_dot)
+ {
+ char *last_dot = strrchr(first_dot, '.');
+ if (last_dot != first_dot)
+ {
+ /* There are more than one dot: "1.2.3"
+ * Strip last part, we don't want to distinquish crashes
+ * in packages which differ only by minor release number.
+ */
+ *last_dot = '\0';
+ }
+ }
+ string hash_str = trimmed_package + executable + independentBuildIdPC;
+ free(trimmed_package);
+ return create_hash(hash_str.c_str());
+}
+
+string CAnalyzerCCpp::GetGlobalUUID(const char *pDebugDumpDir)
+{
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ if (dd.Exist(FILENAME_GLOBAL_UUID))
+ {
+ string uuid;
+ dd.LoadText(FILENAME_GLOBAL_UUID, uuid);
+ return uuid;
+ }
+ else
+ {
+ // Compatibility code.
+ // This whole block should be deleted for Fedora 14.
+ log(_("Getting global universal unique identification..."));
+
+ string backtrace_path = concat_path_file(pDebugDumpDir, FILENAME_BACKTRACE);
+ string executable;
+ string package;
+ string uid_str;
+ dd.LoadText(FILENAME_EXECUTABLE, executable);
+ dd.LoadText(FILENAME_PACKAGE, package);
+ if (m_bBacktrace)
+ dd.LoadText(CD_UID, uid_str);
+
+ string independent_backtrace;
+ if (m_bBacktrace)
+ {
+ /* Run abrt-backtrace to get independent backtrace suitable
+ to UUID calculation. */
+ char *args[7];
+ args[0] = (char*)"abrt-backtrace";
+ args[1] = (char*)"--single-thread";
+ args[2] = (char*)"--remove-exit-handlers";
+ args[3] = (char*)"--frame-depth=5";
+ args[4] = (char*)"--remove-noncrash-frames";
+ args[5] = (char*)backtrace_path.c_str();
+ args[6] = NULL;
+
+ int pipeout[2];
+ xpipe(pipeout); /* stdout of abrt-backtrace */
+
+ fflush(NULL);
+ pid_t child = fork();
+ if (child == -1)
+ perror_msg_and_die("fork");
+ if (child == 0)
+ {
+ VERB1 log("Executing %s", args[0]);
+
+ xmove_fd(pipeout[1], STDOUT_FILENO);
+ close(pipeout[0]); /* read side of the pipe */
+
+ /* abrt-backtrace is executed under the user's uid and gid. */
+ uid_t uid = xatoi_u(uid_str.c_str());
+ struct passwd* pw = getpwuid(uid);
+ gid_t gid = pw ? pw->pw_gid : uid;
+ setgroups(1, &gid);
+ xsetregid(gid, gid);
+ xsetreuid(uid, uid);
+
+ execvp(args[0], args);
+ VERB1 perror_msg("Can't execute '%s'", args[0]);
+ exit(1);
+ }
+
+ close(pipeout[1]); /* write side of the pipe */
+
+ /* Read the result from abrt-backtrace. */
+ int r;
+ char buff[1024];
+ while ((r = safe_read(pipeout[0], buff, sizeof(buff) - 1)) > 0)
+ {
+ buff[r] = '\0';
+ independent_backtrace += buff;
+ }
+ close(pipeout[0]);
+
+ /* Wait until it exits, and check the exit status. */
+ errno = 0;
+ int status;
+ waitpid(child, &status, 0);
+ if (!WIFEXITED(status))
+ {
+ perror_msg("abrt-backtrace not executed properly, "
+ "status: %x signal: %d", status, WIFSIGNALED(status));
+ }
+ else
+ {
+ int exit_status = WEXITSTATUS(status);
+ if (exit_status == 79) /* EX_PARSINGFAILED */
+ {
+ /* abrt-backtrace returns alternative backtrace
+ representation in this case, so everything will work
+ as expected except worse duplication detection */
+ log_msg("abrt-backtrace failed to parse the backtrace");
+ }
+ else if (exit_status == 80) /* EX_THREADDETECTIONFAILED */
+ {
+ /* abrt-backtrace returns backtrace with all threads
+ in this case, so everything will work as expected
+ except worse duplication detection */
+ log_msg("abrt-backtrace failed to determine crash frame");
+ }
+ else if (exit_status != 0)
+ {
+ /* this is unexpected problem and it should be investigated */
+ error_msg("abrt-backtrace run failed, exit value: %d",
+ exit_status);
+ }
+ }
+
+ /*VERB1 log("abrt-backtrace result: %s", independent_backtrace.c_str());*/
+ }
+ /* else: no backtrace, independent_backtrace == ""
+ no backtrace => rating = 0
+ */
+ else
+ {
+ dd.SaveText(FILENAME_RATING, "0");
+ }
+
+ string hash_base = package + executable + independent_backtrace;
+ return create_hash(hash_base.c_str());
+ }
+}
+
+static bool DebuginfoCheckPolkit(uid_t uid)
+{
+ fflush(NULL);
+ int child_pid = fork();
+ if (child_pid < 0)
+ {
+ perror_msg_and_die("fork");
+ }
+ if (child_pid == 0)
+ {
+ //child
+ xsetreuid(uid, uid);
+ PolkitResult result = polkit_check_authorization(getpid(),
+ "org.fedoraproject.abrt.install-debuginfos");
+ exit(result != PolkitYes); //exit 1 (failure) if not allowed
+ }
+
+ //parent
+ int status;
+ if (waitpid(child_pid, &status, 0) > 0
+ && WIFEXITED(status)
+ && WEXITSTATUS(status) == 0
+ ) {
+ return true; //authorization OK
+ }
+ log("UID %d is not authorized to install debuginfos", uid);
+ return false;
+}
+
+void CAnalyzerCCpp::CreateReport(const char *pDebugDumpDir, int force)
+{
+ string package, executable, UID;
+
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+
+ /* Skip remote crashes. */
+ if (dd.Exist(FILENAME_REMOTE))
+ {
+ std::string remote_str;
+ dd.LoadText(FILENAME_REMOTE, remote_str);
+ bool remote = (remote_str.find('1') != std::string::npos);
+ if (remote && !m_bBacktraceRemotes)
+ return;
+ }
+
+ if (!m_bBacktrace)
+ return;
+
+ if (!force)
+ {
+ bool bt_exists = dd.Exist(FILENAME_BACKTRACE);
+ if (bt_exists)
+ return; /* backtrace already exists */
+ }
+
+ dd.LoadText(FILENAME_PACKAGE, package);
+ dd.LoadText(FILENAME_EXECUTABLE, executable);
+ dd.LoadText(CD_UID, UID);
+ dd.Close(); /* do not keep dir locked longer than needed */
+
+ string build_ids;
+ if (m_bInstallDebugInfo && DebuginfoCheckPolkit(xatoi_u(UID.c_str())))
+ {
+ if (m_nDebugInfoCacheMB > 0)
+ trim_debuginfo_cache(m_nDebugInfoCacheMB);
+ InstallDebugInfos(pDebugDumpDir, m_sDebugInfoDirs.c_str(), build_ids);
+ }
+ else
+ VERB1 log(_("Skipping the debuginfo installation"));
+
+ /* Create and store backtrace. */
+ string backtrace_str;
+ GetBacktrace(pDebugDumpDir, m_sDebugInfoDirs.c_str(), backtrace_str);
+ dd.Open(pDebugDumpDir);
+ dd.SaveText(FILENAME_BACKTRACE, (backtrace_str + build_ids).c_str());
+
+ if (m_bMemoryMap)
+ dd.SaveText(FILENAME_MEMORYMAP, "memory map of the crashed C/C++ application, not implemented yet");
+
+ /* Compute and store UUID from the backtrace. */
+ char *backtrace_cpy = xstrdup(backtrace_str.c_str());
+ struct backtrace *backtrace = backtrace_parse(backtrace_cpy, false, false);
+ free(backtrace_cpy);
+ if (backtrace)
+ {
+ /* Get the quality of the full backtrace. */
+ float q1 = backtrace_quality(backtrace);
+
+ /* Remove all the other threads except the crash thread. */
+ struct thread *crash_thread = backtrace_find_crash_thread(backtrace);
+ if (crash_thread)
+ backtrace_remove_threads_except_one(backtrace, crash_thread);
+ else
+ log_msg("Detection of crash thread failed");
+
+ /* Get the quality of the crash thread. */
+ float q2 = backtrace_quality(backtrace);
+
+ backtrace_remove_noncrash_frames(backtrace);
+
+ /* Do the frame removal now. */
+ backtrace_limit_frame_depth(backtrace, 5);
+ /* Frame removal can be done before removing exit handlers. */
+ backtrace_remove_exit_handlers(backtrace);
+
+ /* Get the quality of frames around the crash. */
+ float q3 = backtrace_quality(backtrace);
+
+ /* Compute UUID. */
+ struct strbuf *bt = backtrace_tree_as_str(backtrace, false);
+ strbuf_prepend_str(bt, executable.c_str());
+ strbuf_prepend_str(bt, package.c_str());
+ dd.SaveText(FILENAME_GLOBAL_UUID, create_hash(bt->buf).c_str());
+ strbuf_free(bt);
+
+ /* Compute and store backtrace rating. */
+ /* Compute and store backtrace rating. The crash frame
+ is more important that the others. The frames around
+ the crash are more important than the rest. */
+ float qtot = 0.25f * q1 + 0.35f * q2 + 0.4f * q3;
+
+ /* Turn the quality to rating. */
+ const char *rating;
+ if (qtot < 0.6f) rating = "0";
+ else if (qtot < 0.7f) rating = "1";
+ else if (qtot < 0.8f) rating = "2";
+ else if (qtot < 0.9f) rating = "3";
+ else rating = "4";
+ dd.SaveText(FILENAME_RATING, rating);
+
+ /* Get the function name from the crash frame. */
+ if (crash_thread)
+ {
+ struct frame *crash_frame = crash_thread->frames;
+ struct frame *abort_frame = thread_find_abort_frame(crash_thread);
+ if (abort_frame)
+ crash_frame = abort_frame->next;
+ if (crash_frame && crash_frame->function && 0 != strcmp(crash_frame->function, "??"))
+ dd.SaveText(FILENAME_CRASH_FUNCTION, crash_frame->function);
+ }
+
+ backtrace_free(backtrace);
+ }
+ else
+ {
+ /* If the parser failed fall back to the independent backtrace. */
+ /* If we write and use a hand-written parser instead of the bison one,
+ the parser never fails, and it will be possible to get rid of
+ the independent_backtrace and backtrace_rate_old. */
+ struct strbuf *ibt = independent_backtrace(backtrace_str.c_str());
+ strbuf_prepend_str(ibt, executable.c_str());
+ strbuf_prepend_str(ibt, package.c_str());
+ dd.SaveText(FILENAME_GLOBAL_UUID, create_hash(ibt->buf).c_str());
+ strbuf_free(ibt);
+
+ /* Compute and store backtrace rating. */
+ /* Crash frame is not known so store nothing. */
+ dd.SaveText(FILENAME_RATING, to_string(backtrace_rate_old(backtrace_str.c_str())).c_str());
+ }
+
+ dd.Close();
+}
+
+/*
+ this is just a workaround until kernel changes it's behavior
+ when handling pipes in core_pattern
+*/
+#ifdef HOSTILE_KERNEL
+#define CORE_SIZE_PATTERN "Max core file size=1:unlimited"
+static int isdigit_str(char *str)
+{
+ do {
+ if (*str < '0' || *str > '9')
+ return 0;
+ } while (*++str);
+ return 1;
+}
+
+static int set_limits()
+{
+ DIR *dir = opendir("/proc");
+ if (!dir) {
+ /* this shouldn't fail, but to be safe.. */
+ return 1;
+ }
+
+ struct dirent *ent;
+ while ((ent = readdir(dir)) != NULL) {
+ if (!isdigit_str(ent->d_name))
+ continue;
+
+ char limits_name[sizeof("/proc/%s/limits") + sizeof(long)*3];
+ snprintf(limits_name, sizeof(limits_name), "/proc/%s/limits", ent->d_name);
+ FILE *limits_fp = fopen(limits_name, "r");
+ if (!limits_fp) {
+ break;
+ }
+
+ char line[128];
+ char *ulimit_c = NULL;
+ while (1) {
+ if (fgets(line, sizeof(line)-1, limits_fp) == NULL)
+ break;
+ if (strncmp(line, "Max core file size", sizeof("Max core file size")-1) == 0) {
+ ulimit_c = skip_whitespace(line + sizeof("Max core file size")-1);
+ skip_non_whitespace(ulimit_c)[0] = '\0';
+ break;
+ }
+ }
+ fclose(limits_fp);
+ if (!ulimit_c || ulimit_c[0] != '0' || ulimit_c[1] != '\0') {
+ /*process has nonzero ulimit -c, so need to modify it*/
+ continue;
+ }
+ /* echo -n 'Max core file size=1:unlimited' >/proc/PID/limits */
+ int fd = open(limits_name, O_WRONLY);
+ if (fd >= 0) {
+ errno = 0;
+ /*full_*/
+ ssize_t n = write(fd, CORE_SIZE_PATTERN, sizeof(CORE_SIZE_PATTERN)-1);
+ if (n < sizeof(CORE_SIZE_PATTERN)-1)
+ log("warning: can't write core_size limit to: %s", limits_name);
+ close(fd);
+ }
+ else
+ {
+ log("warning: can't open %s for writing", limits_name);
+ }
+ }
+ closedir(dir);
+ return 0;
+}
+#endif /* HOSTILE_KERNEL */
+
+void CAnalyzerCCpp::Init()
+{
+ FILE *fp = fopen(CORE_PATTERN_IFACE, "r");
+ if (fp)
+ {
+ char line[PATH_MAX];
+ if (fgets(line, sizeof(line), fp))
+ m_sOldCorePattern = line;
+ fclose(fp);
+ }
+ if (m_sOldCorePattern[0] == '|')
+ {
+ if (m_sOldCorePattern == CORE_PATTERN)
+ {
+ log("warning: %s already contains %s, "
+ "did abrt daemon crash recently?",
+ CORE_PATTERN_IFACE, CORE_PATTERN);
+ /* There is no point in "restoring" CORE_PATTERN_IFACE
+ * to CORE_PATTERN on exit. Will restore to a default value:
+ */
+ m_sOldCorePattern = "core";
+ } else {
+ log("warning: %s was already set to run a crash analyser (%s), "
+ "abrt may interfere with it",
+ CORE_PATTERN_IFACE, CORE_PATTERN);
+ }
+ }
+#ifdef HOSTILE_KERNEL
+ if (set_limits() != 0)
+ log("warning: failed to set core_size limit, ABRT won't detect crashes in"
+ "compiled apps");
+#endif
+
+ fp = fopen(CORE_PATTERN_IFACE, "w");
+ if (fp)
+ {
+ fputs(CORE_PATTERN, fp);
+ fclose(fp);
+ }
+
+ /* read the core_pipe_limit and change it if it's == 0
+ otherwise the abrt-hook-ccpp won't be able to read /proc/<pid>
+ of the crashing process
+ */
+ fp = fopen(CORE_PIPE_LIMIT_IFACE, "r");
+ if (fp)
+ {
+ /* we care only about the first char, if it's
+ * not '0' then we don't have to change it,
+ * because it means that it's already != 0
+ */
+ char pipe_limit[2];
+ if (!fgets(pipe_limit, sizeof(pipe_limit), fp))
+ pipe_limit[0] = '1'; /* not 0 */
+ fclose(fp);
+ if (pipe_limit[0] == '0')
+ {
+ fp = fopen(CORE_PIPE_LIMIT_IFACE, "w");
+ if (fp)
+ {
+ fputs(CORE_PIPE_LIMIT, fp);
+ fclose(fp);
+ }
+ else
+ {
+ log("warning: failed to set core_pipe_limit, ABRT won't detect"
+ "crashes in compiled apps if kernel > 2.6.31");
+ }
+ }
+ }
+}
+
+void CAnalyzerCCpp::DeInit()
+{
+ /* no need to restore the core_pipe_limit, because it's only used
+ when there is s pipe in core_pattern
+ */
+ FILE *fp = fopen(CORE_PATTERN_IFACE, "w");
+ if (fp)
+ {
+ fputs(m_sOldCorePattern.c_str(), fp);
+ fclose(fp);
+ }
+}
+
+void CAnalyzerCCpp::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("Backtrace");
+ if (it != end)
+ {
+ m_bBacktrace = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("BacktraceRemotes");
+ if (it != end)
+ {
+ m_bBacktraceRemotes = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("MemoryMap");
+ if (it != end)
+ {
+ m_bMemoryMap = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("DebugInfo");
+ if (it != end)
+ {
+ m_sDebugInfo = it->second;
+ }
+ it = pSettings.find("DebugInfoCacheMB");
+ if (it != end)
+ {
+ m_nDebugInfoCacheMB = xatou(it->second.c_str());
+ }
+ it = pSettings.find("InstallDebugInfo");
+ if (it == end) //compat, remove after 0.0.11
+ it = pSettings.find("InstallDebuginfo");
+ if (it != end)
+ {
+ m_bInstallDebugInfo = string_to_bool(it->second.c_str());
+ }
+ m_sDebugInfoDirs = DEBUGINFO_CACHE_DIR;
+ it = pSettings.find("ReadonlyLocalDebugInfoDirs");
+ if (it != end)
+ {
+ m_sDebugInfoDirs += ':';
+ m_sDebugInfoDirs += it->second;
+ }
+}
+
+//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,
+ "CCpp",
+ "0.0.1",
+ _("Analyzes crashes in C/C++ programs"),
+ "zprikryl@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/CCpp.h b/lib/plugins/CCpp.h
new file mode 100644
index 00000000..f547686f
--- /dev/null
+++ b/lib/plugins/CCpp.h
@@ -0,0 +1,53 @@
+/*
+ CCpp.h - header file for C/C++ analyzer plugin
+ - it can get UUID and memory maps from core files
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef CCPP_H_
+#define CCPP_H_
+
+#include <string>
+#include "plugin.h"
+#include "analyzer.h"
+
+class CAnalyzerCCpp : public CAnalyzer
+{
+ private:
+ bool m_bBacktrace;
+ bool m_bBacktraceRemotes;
+ bool m_bMemoryMap;
+ bool m_bInstallDebugInfo;
+ unsigned m_nDebugInfoCacheMB;
+ std::string m_sOldCorePattern;
+ std::string m_sDebugInfo;
+ std::string m_sDebugInfoDirs;
+
+ public:
+ CAnalyzerCCpp();
+ virtual std::string GetLocalUUID(const char *pDebugDumpDir);
+ virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
+ virtual void CreateReport(const char *pDebugDumpDir, int force);
+ virtual void Init();
+ virtual void DeInit();
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+//ok to delete?
+// virtual const map_plugin_settings_t& GetSettings();
+};
+
+#endif /* CCPP */
diff --git a/lib/plugins/CCpp_sha1.cpp b/lib/plugins/CCpp_sha1.cpp
new file mode 100644
index 00000000..86a9e831
--- /dev/null
+++ b/lib/plugins/CCpp_sha1.cpp
@@ -0,0 +1,214 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Based on shasum from http://www.netsw.org/crypto/hash/
+ * Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * Issue Date: 10/11/2002
+ *
+ * This is a byte oriented version of SHA1 that operates on arrays of bytes
+ * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+ *
+ * ---------------------------------------------------------------------------
+ */
+#include "abrtlib.h"
+#include "CCpp_sha1.h"
+
+#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
+# define SHA1_BIG_ENDIAN 1
+# define SHA1_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define SHA1_BIG_ENDIAN 1
+# define SHA1_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define SHA1_BIG_ENDIAN 0
+# define SHA1_LITTLE_ENDIAN 1
+#else
+# error "Can't determine endianness"
+#endif
+
+
+#define rotl32(x,n) (((x) << (n)) | ((x) >> (32 - (n))))
+#define rotr32(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
+/* for sha512: */
+#define rotr64(x,n) (((x) >> (n)) | ((x) << (64 - (n))))
+#if SHA1_LITTLE_ENDIAN
+static inline uint64_t hton64(uint64_t v)
+{
+ return (((uint64_t)htonl(v)) << 32) | htonl(v >> 32);
+}
+#else
+#define hton64(v) (v)
+#endif
+#define ntoh64(v) hton64(v)
+
+/* To check alignment gcc has an appropriate operator. Other
+ compilers don't. */
+#if defined(__GNUC__) && __GNUC__ >= 2
+# define UNALIGNED_P(p,type) (((uintptr_t) p) % __alignof__(type) != 0)
+#else
+# define UNALIGNED_P(p,type) (((uintptr_t) p) % sizeof(type) != 0)
+#endif
+
+
+/* Some arch headers have conflicting defines */
+#undef ch
+#undef parity
+#undef maj
+#undef rnd
+
+static void sha1_process_block64(sha1_ctx_t *ctx)
+{
+ unsigned t;
+ uint32_t W[80], a, b, c, d, e;
+ const uint32_t *words = (uint32_t*) ctx->wbuffer;
+
+ for (t = 0; t < 16; ++t) {
+ W[t] = ntohl(*words);
+ words++;
+ }
+
+ for (/*t = 16*/; t < 80; ++t) {
+ uint32_t T = W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16];
+ W[t] = rotl32(T, 1);
+ }
+
+ a = ctx->hash[0];
+ b = ctx->hash[1];
+ c = ctx->hash[2];
+ d = ctx->hash[3];
+ e = ctx->hash[4];
+
+/* Reverse byte order in 32-bit words */
+#define ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z))))
+#define parity(x,y,z) ((x) ^ (y) ^ (z))
+#define maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y))))
+/* A normal version as set out in the FIPS. This version uses */
+/* partial loop unrolling and is optimised for the Pentium 4 */
+#define rnd(f,k) \
+ do { \
+ uint32_t T = a; \
+ a = rotl32(a, 5) + f(b, c, d) + e + k + W[t]; \
+ e = d; \
+ d = c; \
+ c = rotl32(b, 30); \
+ b = T; \
+ } while (0)
+
+ for (t = 0; t < 20; ++t)
+ rnd(ch, 0x5a827999);
+
+ for (/*t = 20*/; t < 40; ++t)
+ rnd(parity, 0x6ed9eba1);
+
+ for (/*t = 40*/; t < 60; ++t)
+ rnd(maj, 0x8f1bbcdc);
+
+ for (/*t = 60*/; t < 80; ++t)
+ rnd(parity, 0xca62c1d6);
+#undef ch
+#undef parity
+#undef maj
+#undef rnd
+
+ ctx->hash[0] += a;
+ ctx->hash[1] += b;
+ ctx->hash[2] += c;
+ ctx->hash[3] += d;
+ ctx->hash[4] += e;
+}
+
+void sha1_begin(sha1_ctx_t *ctx)
+{
+ ctx->hash[0] = 0x67452301;
+ ctx->hash[1] = 0xefcdab89;
+ ctx->hash[2] = 0x98badcfe;
+ ctx->hash[3] = 0x10325476;
+ ctx->hash[4] = 0xc3d2e1f0;
+ ctx->total64 = 0;
+ ctx->process_block = sha1_process_block64;
+}
+
+static const uint32_t init256[] = {
+ 0x6a09e667,
+ 0xbb67ae85,
+ 0x3c6ef372,
+ 0xa54ff53a,
+ 0x510e527f,
+ 0x9b05688c,
+ 0x1f83d9ab,
+ 0x5be0cd19
+};
+static const uint32_t init512_lo[] = {
+ 0xf3bcc908,
+ 0x84caa73b,
+ 0xfe94f82b,
+ 0x5f1d36f1,
+ 0xade682d1,
+ 0x2b3e6c1f,
+ 0xfb41bd6b,
+ 0x137e2179
+};
+
+/* Used also for sha256 */
+void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx)
+{
+ unsigned in_buf = ctx->total64 & 63;
+ unsigned add = 64 - in_buf;
+
+ ctx->total64 += len;
+
+ while (len >= add) { /* transfer whole blocks while possible */
+ memcpy(ctx->wbuffer + in_buf, buffer, add);
+ buffer = (const char *)buffer + add;
+ len -= add;
+ add = 64;
+ in_buf = 0;
+ ctx->process_block(ctx);
+ }
+
+ memcpy(ctx->wbuffer + in_buf, buffer, len);
+}
+
+/* Used also for sha256 */
+void sha1_end(void *resbuf, sha1_ctx_t *ctx)
+{
+ unsigned pad, in_buf;
+
+ in_buf = ctx->total64 & 63;
+ /* Pad the buffer to the next 64-byte boundary with 0x80,0,0,0... */
+ ctx->wbuffer[in_buf++] = 0x80;
+
+ /* This loop iterates either once or twice, no more, no less */
+ while (1) {
+ pad = 64 - in_buf;
+ memset(ctx->wbuffer + in_buf, 0, pad);
+ in_buf = 0;
+ /* Do we have enough space for the length count? */
+ if (pad >= 8) {
+ /* Store the 64-bit counter of bits in the buffer in BE format */
+ uint64_t t = ctx->total64 << 3;
+ t = hton64(t);
+ /* wbuffer is suitably aligned for this */
+ *(uint64_t *) (&ctx->wbuffer[64 - 8]) = t;
+ }
+ ctx->process_block(ctx);
+ if (pad >= 8)
+ break;
+ }
+
+ in_buf = (ctx->process_block == sha1_process_block64) ? 5 : 8;
+ /* This way we do not impose alignment constraints on resbuf: */
+ if (SHA1_LITTLE_ENDIAN) {
+ unsigned i;
+ for (i = 0; i < in_buf; ++i)
+ ctx->hash[i] = htonl(ctx->hash[i]);
+ }
+ memcpy(resbuf, ctx->hash, sizeof(ctx->hash[0]) * in_buf);
+}
diff --git a/lib/plugins/CCpp_sha1.h b/lib/plugins/CCpp_sha1.h
new file mode 100644
index 00000000..abadfd92
--- /dev/null
+++ b/lib/plugins/CCpp_sha1.h
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Based on shasum from http://www.netsw.org/crypto/hash/
+ * Majorly hacked up to use Dr Brian Gladman's sha1 code
+ *
+ * Copyright (C) 2002 Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * ---------------------------------------------------------------------------
+ * Issue Date: 10/11/2002
+ *
+ * This is a byte oriented version of SHA1 that operates on arrays of bytes
+ * stored in memory. It runs at 22 cycles per byte on a Pentium P4 processor
+ *
+ * ---------------------------------------------------------------------------
+ */
+
+#define SHA1_RESULT_LEN (5 * 4)
+
+typedef struct sha1_ctx_t {
+ uint32_t hash[8]; /* 5, +3 elements for sha256 */
+ uint64_t total64;
+ uint8_t wbuffer[64]; /* NB: always correctly aligned for uint64_t */
+ void (*process_block)(struct sha1_ctx_t*);
+} sha1_ctx_t;
+
+void sha1_begin(sha1_ctx_t *ctx);
+void sha1_hash(const void *buffer, size_t len, sha1_ctx_t *ctx);
+void sha1_end(void *resbuf, sha1_ctx_t *ctx);
diff --git a/lib/plugins/FileTransfer.conf b/lib/plugins/FileTransfer.conf
new file mode 100644
index 00000000..111c1c4b
--- /dev/null
+++ b/lib/plugins/FileTransfer.conf
@@ -0,0 +1,35 @@
+# Configuration of the FileTransfer reporter plugin.
+Enabled = yes
+
+# The plugin is invoked in the abrt.conf file, usually in the
+# ActionsAndReporters option and/or the [cron] section.
+# There are two modes of invocation:
+#
+# * Specify FileTransfer(one) in ActionsAndReporters directive.
+# Immediately after crash is detected, the plugin transfers
+# crash data to the server specified via URL directive in this file.
+#
+# * Specify FileTransfer(store) in ActionsAndReporters directive
+# and add "HH:MM = FileTransfer" line in [cron] section.
+# At the time of the crash, the plugin stores a record of it
+# in its internal list. When specified time is reached,
+# the plugin iterates through its internal list and sends
+# every recorded crash to the specified URL.
+# After that, the internal list is cleared.
+
+
+# URL to upload the files to
+# supported: ftp, ftps, http, https, scp, sftp, tftp, file
+# for example: ftp://user:password@server.name/directory
+# or: scp://user:password@server.name:port/directory etc.
+# for testing: file:///tmp/test_directory
+URL =
+
+# Archive type, one of .zip, .tar.gz or .tar.bz2
+ArchiveType = .tar.gz
+
+# How many times we try to upload the file
+RetryCount = 3
+
+# How long we wait between we retry the upload (in seconds)
+RetryDelay = 20
diff --git a/lib/plugins/FileTransfer.cpp b/lib/plugins/FileTransfer.cpp
new file mode 100644
index 00000000..cf7cf1f3
--- /dev/null
+++ b/lib/plugins/FileTransfer.cpp
@@ -0,0 +1,362 @@
+/*
+ FileTransfer.cpp
+
+ Copyright (C) 2009 Daniel Novotny (dnovotny@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <libtar.h>
+#include <bzlib.h>
+#include <zlib.h>
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "FileTransfer.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+#define HBLEN 255
+#define FILETRANSFER_DIRLIST DEBUG_DUMPS_DIR "/FileTransferDirlist.txt"
+
+CFileTransfer::CFileTransfer()
+:
+ m_sArchiveType(".tar.gz"),
+ m_nRetryCount(3),
+ m_nRetryDelay(20)
+{
+}
+
+void CFileTransfer::SendFile(const char *pURL, const char *pFilename)
+{
+ int len = strlen(pURL);
+ if (len == 0)
+ {
+ error_msg(_("FileTransfer: URL not specified"));
+ return;
+ }
+
+ update_client(_("Sending archive %s to %s"), pFilename, pURL);
+
+ string wholeURL = concat_path_file(pURL, strrchr(pFilename, '/') ? : pFilename);
+
+ int count = m_nRetryCount;
+ while (1)
+ {
+ FILE *f = fopen(pFilename, "r");
+ if (!f)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't open archive file '%s'", pFilename);
+ }
+
+ struct stat buf;
+ fstat(fileno(f), &buf); /* never fails */
+
+ CURL *curl = xcurl_easy_init();
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ /* specify target */
+ curl_easy_setopt(curl, CURLOPT_URL, wholeURL.c_str());
+ /* FILE handle: passed to the default callback, it will fread() it */
+ curl_easy_setopt(curl, CURLOPT_READDATA, f);
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)buf.st_size);
+
+ /* everything is done here; result 0 means success */
+ int result = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ fclose(f);
+ if (result == 0 || --count <= 0)
+ break;
+ /* retry the upload if not succesful, wait a bit before next try */
+ sleep(m_nRetryDelay);
+ }
+}
+
+static void create_tar(const char *archive_name, const char *directory)
+{
+ TAR *tar;
+
+ if (tar_open(&tar, (char *)archive_name, NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
+ {
+ return;
+ }
+ tar_append_tree(tar, (char *)directory, (char*)".");
+ tar_close(tar);
+}
+
+static void create_targz(const char *archive_name, const char *directory)
+{
+ char *name_without_gz = xstrdup(archive_name);
+ strrchr(name_without_gz, '.')[0] = '\0';
+
+ create_tar(name_without_gz, directory);
+
+ int fd = open(name_without_gz, O_RDONLY);
+ if (fd < 0)
+ {
+ remove(name_without_gz);
+ free(name_without_gz);
+ return;
+ }
+
+ gzFile gz = gzopen(archive_name, "w");
+ if (gz == NULL)
+ {
+ close(fd);
+ remove(name_without_gz);
+ free(name_without_gz);
+ return;
+ }
+
+ char buf[BUFSIZ];
+ ssize_t bytesRead;
+ while ((bytesRead = full_read(fd, buf, BUFSIZ)) > 0)
+ {
+ gzwrite(gz, buf, bytesRead); // TODO: check that return value == bytesRead
+ }
+
+ gzclose(gz);
+ close(fd);
+ remove(name_without_gz);
+ free(name_without_gz);
+}
+
+static void create_tarbz2(const char * archive_name, const char * directory)
+{
+ char *name_without_bz2 = xstrdup(archive_name);
+ strrchr(name_without_bz2, '.')[0] = '\0';
+
+ create_tar(name_without_bz2, directory);
+
+ int tarFD = open(name_without_bz2, O_RDONLY);
+ if (tarFD == -1)
+ {
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+ FILE *f = fopen(archive_name, "w");
+ if (f == NULL)
+ {
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+ int bzError;
+ BZFILE *bz = BZ2_bzWriteOpen(&bzError, f, /*BLOCK_MULTIPLIER:*/ 7, 0, 0);
+ if (bz == NULL)
+ {
+ fclose(f);
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+ return;
+ }
+
+ char buf[BUFSIZ];
+ ssize_t bytesRead;
+ while ((bytesRead = read(tarFD, buf, BUFSIZ)) > 0)
+ {
+ BZ2_bzWrite(&bzError, bz, buf, bytesRead);
+ }
+
+ BZ2_bzWriteClose(&bzError, bz, 0, NULL, NULL);
+ fclose(f);
+ close(tarFD);
+ remove(name_without_bz2);
+ free(name_without_bz2);
+}
+
+void CFileTransfer::CreateArchive(const char *pArchiveName, const char *pDir)
+{
+ if (m_sArchiveType == ".tar")
+ {
+ create_tar(pArchiveName, pDir);
+ }
+ else if (m_sArchiveType == ".tar.gz")
+ {
+ create_targz(pArchiveName, pDir);
+ }
+ else if (m_sArchiveType == ".tar.bz2")
+ {
+ create_tarbz2(pArchiveName, pDir);
+ }
+ else
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Unknown/unsupported archive type %s", m_sArchiveType.c_str());
+ }
+}
+
+/* Returns the last component of the directory path.
+ * Careful to not return "" on "/path/path2/", but "path2".
+ */
+static string DirBase(const char *pStr)
+{
+ int end = strlen(pStr);
+ if (end > 1 && pStr[end-1] == '/')
+ {
+ end--;
+ }
+ int beg = end;
+ while (beg > 0 && pStr[beg-1] != '/')
+ {
+ beg--;
+ }
+ return string(pStr + beg, end - beg);
+}
+
+void CFileTransfer::Run(const char *pActionDir, const char *pArgs, int force)
+{
+ if (strcmp(pArgs, "store") == 0)
+ {
+ /* Remember pActiveDir for later sending */
+ FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "a");
+ fprintf(dirlist, "%s\n", pActionDir);
+ fclose(dirlist);
+ VERB3 log("Remembered '%s' for future file transfer", pActionDir);
+ return;
+ }
+
+ update_client(_("FileTransfer: Creating a report..."));
+
+ char hostname[HBLEN];
+ gethostname(hostname, HBLEN-1);
+ hostname[HBLEN-1] = '\0';
+
+ char tmpdir_name[] = "/tmp/abrtuploadXXXXXX";
+ /* mkdtemp does mkdir(xxx, 0700), should be safe (is it?) */
+ if (mkdtemp(tmpdir_name) == NULL)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp");
+ }
+
+ if (strcmp(pArgs, "one") == 0)
+ {
+ /* Just send one archive */
+ string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(pActionDir).c_str(), m_sArchiveType.c_str());
+ try
+ {
+ CreateArchive(archivename.c_str(), pActionDir);
+ SendFile(m_sURL.c_str(), archivename.c_str());
+ }
+ catch (CABRTException& e)
+ {
+ error_msg(_("Cannot create and send an archive: %s"), e.what());
+ }
+ unlink(archivename.c_str());
+ }
+ else
+ {
+ /* Tar up and send all remebered directories */
+ FILE *dirlist = fopen(FILETRANSFER_DIRLIST, "r");
+ if (!dirlist)
+ {
+ /* not an error */
+ VERB3 log("No saved crashes to transfer");
+ goto del_tmp_dir;
+ }
+
+ char dirname[PATH_MAX];
+ while (fgets(dirname, sizeof(dirname), dirlist) != NULL)
+ {
+ strchrnul(dirname, '\n')[0] = '\0';
+ string archivename = ssprintf("%s/%s-%s%s", tmpdir_name, hostname, DirBase(dirname).c_str(), m_sArchiveType.c_str());
+ try
+ {
+ VERB3 log("Creating archive '%s' of dir '%s'", archivename.c_str(), dirname);
+ CreateArchive(archivename.c_str(), dirname);
+ VERB3 log("Sending archive to '%s'", m_sURL.c_str());
+ SendFile(m_sURL.c_str(), archivename.c_str());
+ }
+ catch (CABRTException& e)
+ {
+ error_msg(_("Cannot create and send an archive: %s"), e.what());
+ }
+ VERB3 log("Deleting archive '%s'", archivename.c_str());
+ unlink(archivename.c_str());
+ }
+
+ fclose(dirlist);
+ /* all the files we're able to send should be sent now,
+ starting over with clean table */
+ unlink(FILETRANSFER_DIRLIST);
+ }
+
+ del_tmp_dir:
+ rmdir(tmpdir_name);
+}
+
+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;
+ it = pSettings.find("URL");
+ if (it != end)
+ {
+ m_sURL = it->second;
+ }
+
+ it = pSettings.find("RetryCount");
+ if (it != end)
+ {
+ m_nRetryCount = xatoi_u(it->second.c_str());
+ }
+
+ it = pSettings.find("RetryDelay");
+ if (it != end)
+ {
+ m_nRetryDelay = xatoi_u(it->second.c_str());
+ }
+
+ it = pSettings.find("ArchiveType");
+ if (it != end)
+ {
+ /* currently supporting .tar, .tar.gz, .tar.bz2 and .zip */
+ m_sArchiveType = it->second;
+ if (m_sArchiveType[0] != '.')
+ {
+ m_sArchiveType = "." + m_sArchiveType;
+ }
+ }
+}
+
+//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,
+ "FileTransfer",
+ "0.0.6",
+ _("Sends a report via FTP or SCTP"),
+ "dnovotny@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/FileTransfer.h b/lib/plugins/FileTransfer.h
new file mode 100644
index 00000000..5e9f4938
--- /dev/null
+++ b/lib/plugins/FileTransfer.h
@@ -0,0 +1,48 @@
+/*
+ FileTransfer.h - header file for the file transfer plugin
+ - it uploads the file via ftp or sctp
+
+ Copyright (C) 2009 Daniel Novotny (dnovotny@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef FILETRANSFER_H_
+#define FILETRANSFER_H_
+
+#include <string>
+#include "plugin.h"
+#include "action.h"
+
+class CFileTransfer : public CAction
+{
+ private:
+ std::string m_sURL;
+ std::string m_sArchiveType;
+ int m_nRetryCount;
+ int m_nRetryDelay;
+
+ void CreateArchive(const char *pArchiveName, const char *pDir);
+ void SendFile(const char *pURL, const char *pFilename);
+
+ public:
+ CFileTransfer();
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+//ok to delete?
+// virtual const map_plugin_settings_t& GetSettings();
+ virtual void Run(const char *pActionDir, const char *pArgs, int force);
+};
+
+#endif /* FILETRANSFER_H_ */
diff --git a/lib/plugins/Kerneloops.conf b/lib/plugins/Kerneloops.conf
new file mode 100644
index 00000000..77fca513
--- /dev/null
+++ b/lib/plugins/Kerneloops.conf
@@ -0,0 +1,19 @@
+Enabled = yes
+
+# Do we want kernel oopses to be visible to any user?
+# Set to "yes" for compatibility with kerneloops.org tool.
+InformAllUsers = yes
+
+# Automatically perform reporting.
+# With default abrt.conf, it invokes KerneloopsReporter
+# and thus reports oops to kerneloops.org.
+# ("root" because all oopses are filed by abrt with user "root")
+# AutoReportUIDs = root
+
+# Kerneloops Scanner configuration
+##################################
+SysLogFile = /var/log/messages
+
+# KerneloopsReporter configuration
+##################################
+SubmitURL = http://submit.kerneloops.org/submitoops.php
diff --git a/lib/plugins/Kerneloops.cpp b/lib/plugins/Kerneloops.cpp
new file mode 100644
index 00000000..1a2532e8
--- /dev/null
+++ b/lib/plugins/Kerneloops.cpp
@@ -0,0 +1,145 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Authors:
+ Anton Arapov <anton@redhat.com>
+ Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#include "abrtlib.h"
+#include "Kerneloops.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+static unsigned hash_oops_str(const char *oops_ptr)
+{
+ unsigned char old_c;
+ unsigned char c = 0;
+ unsigned hash = 0;
+
+ /* Special-case: if the first line is of form:
+ * WARNING: at net/wireless/core.c:614 wdev_cleanup_work+0xe9/0x120 [cfg80211]() (Not tainted)
+ * then hash only "file:line func+ofs/len" part.
+ */
+ if (strncmp(oops_ptr, "WARNING: at ", sizeof("WARNING: at ")-1) == 0) {
+ const char *p = oops_ptr + sizeof("WARNING: at ")-1;
+ p = strchr(p, ' '); /* skip filename:NNN */
+ if (p) {
+ p = strchrnul(p + 1, ' '); /* skip function_name+0xNN/0xNNN */
+ oops_ptr += sizeof("WARNING: at ")-1;
+ while (oops_ptr < p) {
+ c = *oops_ptr++;
+ hash = ((hash << 5) ^ (hash >> 27)) ^ c;
+ }
+ return hash;
+ }
+ }
+
+ while (1) {
+ old_c = c;
+ c = *oops_ptr++;
+ if (!c)
+ break;
+ if (c == '\n') {
+ // Exclude some lines which have process name - in some oops classes
+ // process name is irrelevant and changes with every oops.
+ // Lines we filter out:
+ // Pid: 8003, comm: Xorg Not tainted (2.6.27.9-159.fc10.i686 #1)
+ // Process Xorg (pid: 8003, ti=f0a0c000 task=f2380000 task.ti=f0a0c000)
+ if (strncmp(oops_ptr, "Pid: ", 5) == 0
+ || strncmp(oops_ptr, "Process ", 8) == 0
+ ) {
+ while (*oops_ptr && *oops_ptr != '\n')
+ oops_ptr++;
+ continue;
+ }
+ }
+ if (!isalnum(old_c)) {
+ if (c >= '0' && c <= '9') {
+ // Convert all (possibly hex) numbers to just one '0'
+ if (c == '0' && *oops_ptr == 'x') // "0xSOMETHING"
+ oops_ptr++;
+ while (isxdigit(*oops_ptr))
+ oops_ptr++;
+ c = '0';
+ } else
+ if ((c|0x20) >= 'a' && (c|0x20) <= 'f') {
+ // This *may be* a hex number without 0x prefix: "f0a0c000"
+ // Check that it indeed is, and replace with '0'
+ const char *oops_ptr2 = oops_ptr;
+ while (isxdigit(*oops_ptr2))
+ oops_ptr2++;
+ // Does it end in a letter which is not a hex digit?
+ // (Example: "abcw" is not a hex number, "abc " is)
+ if (!isalpha(*oops_ptr2)) {
+ // It's "abc " case. Skip the "abc" string
+ oops_ptr = oops_ptr2;
+ c = '0';
+ }
+ // else: hash the string as-is
+ }
+ }
+ // TODO: Drop call trace tail - in interrupt-driven oopses,
+ // everything before interrupt is irrelevant.
+ // Example of call trace part of oops:
+ // Call Trace:
+ // [<f88e11c7>] ? radeon_cp_resume+0x7d/0xbc [radeon]
+ // [<f88745f8>] ? drm_ioctl+0x1b0/0x225 [drm]
+ // [<f88e114a>] ? radeon_cp_resume+0x0/0xbc [radeon]
+ // [<c049b1c0>] ? vfs_ioctl+0x50/0x69
+ // [<c049b414>] ? do_vfs_ioctl+0x23b/0x247
+ // [<c0460a56>] ? audit_syscall_entry+0xf9/0x123
+ // [<c049b460>] ? sys_ioctl+0x40/0x5c
+ // [<c0403c76>] ? syscall_call+0x7/0xb
+
+ /* An algorithm proposed by Donald E. Knuth in The Art Of Computer
+ * Programming Volume 3, under the topic of sorting and search
+ * chapter 6.4.
+ */
+ hash = ((hash << 5) ^ (hash >> 27)) ^ c;
+ }
+ return hash;
+}
+
+std::string CAnalyzerKerneloops::GetLocalUUID(const char *pDebugDumpDir)
+{
+ VERB3 log("Getting local universal unique identification");
+
+ std::string oops;
+ {
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ dd.LoadText(FILENAME_BACKTRACE, oops);
+ }
+
+ unsigned hash = hash_oops_str(oops.c_str());
+ hash &= 0x7FFFFFFF;
+
+ return to_string(hash);
+}
+
+std::string CAnalyzerKerneloops::GetGlobalUUID(const char *pDebugDumpDir)
+{
+ return GetLocalUUID(pDebugDumpDir);
+}
+
+PLUGIN_INFO(ANALYZER,
+ CAnalyzerKerneloops,
+ "Kerneloops",
+ "0.0.2",
+ _("Analyzes kernel oopses"),
+ "anton@redhat.com",
+ "https://people.redhat.com/aarapov",
+ "");
diff --git a/lib/plugins/Kerneloops.h b/lib/plugins/Kerneloops.h
new file mode 100644
index 00000000..2160f444
--- /dev/null
+++ b/lib/plugins/Kerneloops.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007, Intel Corporation
+ * Copyright 2009, Red Hat Inc.
+ *
+ * This file is part of Abrt.
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Anton Arapov <anton@redhat.com>
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#ifndef __INCLUDE_GUARD_KERNELOOPS_H_
+#define __INCLUDE_GUARD_KERNELOOPS_H_
+
+#include "plugin.h"
+#include "analyzer.h"
+#include <string>
+
+class CAnalyzerKerneloops : public CAnalyzer
+{
+ public:
+ virtual std::string GetLocalUUID(const char *pDebugDumpDir);
+ virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
+ virtual void CreateReport(const char *pDebugDumpDir, int force) {}
+};
+
+#endif
diff --git a/lib/plugins/KerneloopsReporter.cpp b/lib/plugins/KerneloopsReporter.cpp
new file mode 100644
index 00000000..2b4df3a0
--- /dev/null
+++ b/lib/plugins/KerneloopsReporter.cpp
@@ -0,0 +1,145 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Authors:
+ Anton Arapov <anton@redhat.com>
+ Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "KerneloopsReporter.h"
+#include "comm_layer_inner.h"
+#include "abrt_exception.h"
+
+/* helpers */
+static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ size *= nmemb;
+/*
+ char *c, *c1, *c2;
+
+ log("received: '%*.*s'\n", (int)size, (int)size, (char*)ptr);
+ c = (char*)xzalloc(size + 1);
+ memcpy(c, ptr, size);
+ c1 = strstr(c, "201 ");
+ if (c1) {
+ c1 += 4;
+ c2 = strchr(c1, '\n');
+ if (c2)
+ *c2 = 0;
+ }
+ free(c);
+*/
+
+ return size;
+}
+
+/* Send oops data to kerneloops.org-style site, using HTTP POST */
+/* Returns 0 on success */
+static CURLcode http_post_to_kerneloops_site(const char *url, const char *oopsdata)
+{
+ CURLcode ret;
+ CURL *handle;
+ struct curl_httppost *post = NULL;
+ struct curl_httppost *last = NULL;
+
+ handle = xcurl_easy_init();
+ curl_easy_setopt(handle, CURLOPT_URL, url);
+
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "oopsdata",
+ CURLFORM_COPYCONTENTS, oopsdata,
+ CURLFORM_END);
+ curl_formadd(&post, &last,
+ CURLFORM_COPYNAME, "pass_on_allowed",
+ CURLFORM_COPYCONTENTS, "yes",
+ CURLFORM_END);
+
+
+ curl_easy_setopt(handle, CURLOPT_HTTPPOST, post);
+ curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writefunction);
+
+ ret = curl_easy_perform(handle);
+
+ curl_formfree(post);
+ curl_easy_cleanup(handle);
+
+ return ret;
+}
+
+
+/* class CKerneloopsReporter */
+CKerneloopsReporter::CKerneloopsReporter() :
+ m_sSubmitURL("http://submit.kerneloops.org/submitoops.php")
+{}
+
+std::string CKerneloopsReporter::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ CURLcode ret;
+
+ update_client(_("Creating and submitting a report..."));
+
+ map_crash_data_t::const_iterator it = pCrashData.find(FILENAME_BACKTRACE);
+ if (it != pCrashData.end()) {
+ ret = http_post_to_kerneloops_site(
+ m_sSubmitURL.c_str(),
+ it->second[CD_CONTENT].c_str()
+ );
+ }
+
+ if (ret != CURLE_OK) {
+ char* err_str = xasprintf("Kernel oops has not been sent due to %s", curl_easy_strerror(ret));
+ CABRTException e(EXCEP_PLUGIN, err_str);
+ free(err_str);
+ throw e;
+ }
+ /* Server replies with:
+ * 200 thank you for submitting the kernel oops information
+ * RemoteIP: 34192fd15e34bf60fac6a5f01bba04ddbd3f0558
+ * - no URL or bug ID apparently...
+ */
+ return "Kernel oops report was uploaded";
+}
+
+void CKerneloopsReporter::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("SubmitURL");
+ if (it != end) {
+ m_sSubmitURL = it->second;
+ }
+}
+
+//ok to delete?
+//const map_plugin_settings_t& CKerneloopsReporter::GetSettings()
+//{
+// m_pSettings["SubmitURL"] = m_sSubmitURL;
+//
+// return m_pSettings;
+//}
+
+PLUGIN_INFO(REPORTER,
+ CKerneloopsReporter,
+ "KerneloopsReporter",
+ "0.0.1",
+ _("Sends kernel oops information to kerneloops.org"),
+ "anton@redhat.com",
+ "http://people.redhat.com/aarapov",
+ PLUGINS_LIB_DIR"/KerneloopsReporter.glade");
diff --git a/lib/plugins/KerneloopsReporter.glade b/lib/plugins/KerneloopsReporter.glade
new file mode 100644
index 00000000..1ba287b8
--- /dev/null
+++ b/lib/plugins/KerneloopsReporter.glade
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">12</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lSubmitURL">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Submit URL:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_SubmitURL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Kerneloops Reporter plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">button1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/lib/plugins/KerneloopsReporter.h b/lib/plugins/KerneloopsReporter.h
new file mode 100644
index 00000000..cb939d0d
--- /dev/null
+++ b/lib/plugins/KerneloopsReporter.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2007, Intel Corporation
+ * Copyright 2009, Red Hat Inc.
+ *
+ * This file is part of Abrt.
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Anton Arapov <anton@redhat.com>
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#ifndef __INCLUDE_GUARD_KERNELOOPSREPORTER_H_
+#define __INCLUDE_GUARD_KERNELOOPSREPORTER_H_
+
+#include "plugin.h"
+#include "reporter.h"
+
+#include <string>
+
+class CKerneloopsReporter : public CReporter
+{
+ private:
+ std::string m_sSubmitURL;
+
+ public:
+ CKerneloopsReporter();
+
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+//ok to delete?
+// virtual const map_plugin_settings_t& GetSettings();
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs);
+};
+
+#endif
diff --git a/lib/plugins/KerneloopsScanner.cpp b/lib/plugins/KerneloopsScanner.cpp
new file mode 100644
index 00000000..dc1f6d0a
--- /dev/null
+++ b/lib/plugins/KerneloopsScanner.cpp
@@ -0,0 +1,203 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Authors:
+ Anton Arapov <anton@redhat.com>
+ Arjan van de Ven <arjan@linux.intel.com>
+*/
+
+#include <assert.h>
+#include <syslog.h>
+#include <asm/unistd.h> /* __NR_syslog */
+#include "abrtlib.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+#include "KerneloopsSysLog.h"
+#include "KerneloopsScanner.h"
+
+// TODO: https://fedorahosted.org/abrt/ticket/78
+
+static int scan_dmesg(vector_string_t& oopsList)
+{
+ VERB1 log("Scanning dmesg");
+
+ /* syslog(3) - read the last len bytes from the log buffer
+ * (non-destructively), but dont read more than was written
+ * into the buffer since the last"clear ring buffer" cmd.
+ * Returns the number of bytes read.
+ */
+ char *buffer = (char*)xzalloc(16*1024);
+ syscall(__NR_syslog, 3, buffer, 16*1024 - 1); /* always NUL terminated */
+ int cnt_FoundOopses = extract_oopses(oopsList, buffer, strlen(buffer));
+ free(buffer);
+
+ return cnt_FoundOopses;
+}
+
+
+/* "dumpoops" tool uses these two functions too */
+extern "C" {
+
+int scan_syslog_file(vector_string_t& oopsList, const char *filename, time_t *last_changed_p)
+{
+ VERB1 log("Scanning syslog file '%s'", filename);
+
+ char *buffer;
+ struct stat statb;
+ int fd;
+ int cnt_FoundOopses;
+ ssize_t sz;
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return 0;
+ statb.st_size = 0; /* paranoia */
+ if (fstat(fd, &statb) != 0 || statb.st_size < 1)
+ {
+ close(fd);
+ return 0;
+ }
+
+ if (last_changed_p != NULL)
+ {
+ if (*last_changed_p == statb.st_mtime)
+ {
+ VERB1 log("Syslog file '%s' hasn't changed since last scan, skipping", filename);
+ close(fd);
+ return 0;
+ }
+ *last_changed_p = statb.st_mtime;
+ }
+
+ /*
+ * In theory we have a race here, since someone could spew
+ * to /var/log/messages before we read it in... we try to
+ * deal with it by reading at most 10kbytes extra. If there's
+ * more than that.. any oops will be in dmesg anyway.
+ * Do not try to allocate an absurd amount of memory; ignore
+ * older log messages because they are unlikely to have
+ * sufficiently recent data to be useful. 32MB is more
+ * than enough; it's not worth looping through more log
+ * if the log is larger than that.
+ */
+ sz = statb.st_size + 10*1024;
+ if (statb.st_size > (32*1024*1024 - 10*1024))
+ {
+ xlseek(fd, statb.st_size - (32*1024*1024 - 10*1024), SEEK_SET);
+ sz = 32*1024*1024;
+ }
+ buffer = (char*)xzalloc(sz);
+ sz = full_read(fd, buffer, sz);
+ close(fd);
+
+ cnt_FoundOopses = 0;
+ if (sz > 0)
+ cnt_FoundOopses = extract_oopses(oopsList, buffer, sz);
+ free(buffer);
+
+ return cnt_FoundOopses;
+}
+
+void save_oops_to_debug_dump(const vector_string_t& oopsList)
+{
+ unsigned countdown = 16; /* do not report hundreds of oopses */
+ unsigned idx = oopsList.size();
+ time_t t = time(NULL);
+ pid_t my_pid = getpid();
+
+ VERB1 log("Saving %u oopses as crash dump dirs", idx >= countdown ? countdown-1 : idx);
+
+ while (idx != 0 && --countdown != 0)
+ {
+ char path[sizeof(DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu") + 3 * sizeof(long)*3];
+ sprintf(path, DEBUG_DUMPS_DIR"/kerneloops-%lu-%lu-%lu", (long)t, (long)my_pid, (long)idx);
+ try
+ {
+ std::string oops = oopsList.at(--idx);
+ const char *first_line = oops.c_str();
+ char *second_line = (char*)strchr(first_line, '\n'); /* never NULL */
+ *second_line++ = '\0';
+
+ CDebugDump dd;
+ dd.Create(path, /*uid:*/ 0);
+ dd.SaveText(FILENAME_ANALYZER, "Kerneloops");
+ dd.SaveText(FILENAME_EXECUTABLE, "kernel");
+ dd.SaveText(FILENAME_KERNEL, first_line);
+ dd.SaveText(FILENAME_CMDLINE, "not_applicable");
+ dd.SaveText(FILENAME_BACKTRACE, second_line);
+ /* Optional, makes generated bz more informative */
+ strchrnul(second_line, '\n')[0] = '\0';
+ dd.SaveText(FILENAME_REASON, second_line);
+ }
+ catch (CABRTException& e)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "%s: %s", __func__, e.what());
+ }
+ }
+}
+
+} /* extern "C" */
+
+
+CKerneloopsScanner::CKerneloopsScanner()
+{
+ int cnt_FoundOopses;
+ m_syslog_last_change = 0;
+
+ /* Scan dmesg, on first call only */
+ vector_string_t oopsList;
+ cnt_FoundOopses = scan_dmesg(oopsList);
+ if (cnt_FoundOopses > 0)
+ save_oops_to_debug_dump(oopsList);
+}
+
+void CKerneloopsScanner::Run(const char *pActionDir, const char *pArgs, int force)
+{
+ const char *syslog_file = "/var/log/messages";
+ map_plugin_settings_t::const_iterator it = m_pSettings.find("SysLogFile");
+ if (it != m_pSettings.end())
+ syslog_file = it->second.c_str();
+
+ vector_string_t oopsList;
+ int cnt_FoundOopses = scan_syslog_file(oopsList, syslog_file, &m_syslog_last_change);
+ if (cnt_FoundOopses > 0)
+ {
+ save_oops_to_debug_dump(oopsList);
+ /*
+ * This marker in syslog file prevents us from
+ * re-parsing old oopses (any oops before it is
+ * ignored by scan_syslog_file()). The only problem
+ * is that we can't be sure here that syslog_file
+ * is the file where syslog(xxx) stuff ends up.
+ */
+ openlog("abrt", 0, LOG_KERN);
+ syslog(LOG_WARNING,
+ "Kerneloops: Reported %u kernel oopses to Abrt",
+ cnt_FoundOopses);
+ closelog();
+ }
+}
+
+PLUGIN_INFO(ACTION,
+ CKerneloopsScanner,
+ "KerneloopsScanner",
+ "0.0.1",
+ _("Periodically scans for and saves kernel oopses"),
+ "anton@redhat.com",
+ "http://people.redhat.com/aarapov",
+ "");
diff --git a/lib/plugins/KerneloopsScanner.h b/lib/plugins/KerneloopsScanner.h
new file mode 100644
index 00000000..0904146e
--- /dev/null
+++ b/lib/plugins/KerneloopsScanner.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007, Intel Corporation
+ * Copyright 2009, Red Hat Inc.
+ *
+ * This file is part of Abrt.
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Anton Arapov <anton@redhat.com>
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#ifndef KERNELOOPSSCANNER_H_
+#define KERNELOOPSSCANNER_H_
+
+#include "abrt_types.h"
+#include "plugin.h"
+#include "action.h"
+
+class CKerneloopsScanner : public CAction
+{
+private:
+ time_t m_syslog_last_change;
+public:
+ CKerneloopsScanner();
+ virtual void Run(const char *pActionDir, const char *pArgs, int force);
+};
+
+#endif
diff --git a/lib/plugins/KerneloopsSysLog.cpp b/lib/plugins/KerneloopsSysLog.cpp
new file mode 100644
index 00000000..66cce416
--- /dev/null
+++ b/lib/plugins/KerneloopsSysLog.cpp
@@ -0,0 +1,366 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ Authors:
+ Anton Arapov <anton@redhat.com>
+ Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#include "abrtlib.h"
+#include "KerneloopsSysLog.h"
+#include <assert.h>
+
+static void queue_oops(vector_string_t &vec, const char *data, const char *version)
+{
+ vec.push_back(ssprintf("%s\n%s", version, data));
+}
+
+/*
+ * extract_version tries to find the kernel version in given data
+ */
+static int extract_version(const char *linepointer, char *version)
+{
+ int ret;
+
+ ret = 0;
+ if (strstr(linepointer, "Pid")
+ || strstr(linepointer, "comm")
+ || strstr(linepointer, "CPU")
+ || strstr(linepointer, "REGS")
+ || strstr(linepointer, "EFLAGS")
+ ) {
+ char* start;
+ char* end;
+
+ start = strstr((char*)linepointer, "2.6.");
+ if (start) {
+ end = strchr(start, ')');
+ if (!end)
+ end = strchrnul(start, ' ');
+ strncpy(version, start, end-start);
+ ret = 1;
+ }
+ }
+
+ if (!ret)
+ strncpy(version, "undefined", 9);
+
+ return ret;
+}
+
+/*
+ * extract_oops tries to find oops signatures in a log
+ */
+struct line_info {
+ char *ptr;
+ char level;
+};
+static int record_oops(vector_string_t &oopses, struct line_info* lines_info, int oopsstart, int oopsend)
+{
+ int q;
+ int len;
+ int is_version;
+ char *oops;
+ char *version;
+
+ len = 2;
+ for (q = oopsstart; q <= oopsend; q++)
+ len += strlen(lines_info[q].ptr) + 1;
+
+ oops = (char*)xzalloc(len);
+ version = (char*)xzalloc(len);
+
+ is_version = 0;
+ for (q = oopsstart; q <= oopsend; q++) {
+ if (!is_version)
+ is_version = extract_version(lines_info[q].ptr, version);
+ if (lines_info[q].ptr[0]) {
+ strcat(oops, lines_info[q].ptr);
+ strcat(oops, "\n");
+ }
+ }
+ int rv = 1;
+ /* too short oopses are invalid */
+ if (strlen(oops) > 100) {
+ queue_oops(oopses, oops, version);
+ } else {
+ VERB3 log("Dropped oops: too short");
+ rv = 0;
+ }
+ free(oops);
+ free(version);
+ return rv;
+}
+#define REALLOC_CHUNK 1000
+int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen)
+{
+ char *c;
+ int linecount = 0;
+ int lines_info_alloc = 0;
+ struct line_info *lines_info = NULL;
+
+ /* Split buffer into lines */
+
+ if (buflen != 0)
+ buffer[buflen - 1] = '\n'; /* the buffer usually ends with \n, but let's make sure */
+ c = buffer;
+ while (c < buffer + buflen) {
+ char linelevel;
+ char *c9;
+ char *colon;
+
+ c9 = (char*)memchr(c, '\n', buffer + buflen - c); /* a \n will always be found */
+ assert(c9);
+ *c9 = '\0'; /* turn the \n into a string termination */
+ if (c9 == c)
+ goto next_line;
+
+ /* Is it a syslog file (/var/log/messages or similar)?
+ * Even though _usually_ it looks like "Nov 19 12:34:38 localhost kernel: xxx",
+ * some users run syslog in non-C locale:
+ * "2010-02-22T09:24:08.156534-08:00 gnu-4 gnome-session[2048]: blah blah"
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ !!!
+ * We detect it by checking for N:NN:NN pattern in first 15 chars
+ * (and this still is not good enough... false positive: "pci 0000:15:00.0: PME# disabled")
+ */
+ colon = strchr(c, ':');
+ if (colon && colon > c && colon < c + 15
+ && isdigit(colon[-1]) /* N:... */
+ && isdigit(colon[1]) /* ...N:NN:... */
+ && isdigit(colon[2])
+ && colon[3] == ':'
+ && isdigit(colon[4]) /* ...N:NN:NN... */
+ && isdigit(colon[5])
+ ) {
+ /* It's syslog file, not a bare dmesg */
+
+ /* 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")) {
+ VERB3 log("Found our marker at line %d, restarting line count from 0", linecount);
+ linecount = 0;
+ lines_info_alloc = 0;
+ free(lines_info);
+ lines_info = NULL;
+ }
+ goto next_line;
+ }
+ c = kernel_str + sizeof("kernel: ")-1;
+ }
+
+ linelevel = 0;
+ /* store and remove kernel log level */
+ if (*c == '<' && c[1] && c[2] == '>') {
+ linelevel = c[1];
+ c += 3;
+ }
+ /* remove jiffies time stamp counter if present */
+ if (*c == '[') {
+ char *c2 = strchr(c, '.');
+ char *c3 = strchr(c, ']');
+ if (c2 && c3 && (c2 < c3) && (c3-c) < 14 && (c2-c) < 8) {
+ c = c3 + 1;
+ if (*c == ' ')
+ c++;
+ }
+ }
+ if (linecount >= lines_info_alloc) {
+ lines_info_alloc += REALLOC_CHUNK;
+ lines_info = (line_info*)xrealloc(lines_info,
+ lines_info_alloc * sizeof(struct line_info));
+ }
+ lines_info[linecount].ptr = c;
+ lines_info[linecount].level = linelevel;
+ linecount++;
+next_line:
+ c = c9 + 1;
+ }
+
+ /* Analyze lines */
+
+ int i;
+ char prevlevel = 0;
+ int oopsstart = -1;
+ int inbacktrace = 0;
+ int oopsesfound = 0;
+
+ i = 0;
+ while (i < linecount) {
+ char *curline = lines_info[i].ptr;
+
+ if (curline == NULL) {
+ i++;
+ continue;
+ }
+ while (*curline == ' ')
+ curline++;
+
+ if (oopsstart < 0) {
+ /* find start-of-oops markers */
+ if (strstr(curline, "general protection fault:"))
+ oopsstart = i;
+ else if (strstr(curline, "BUG:"))
+ oopsstart = i;
+ else if (strstr(curline, "kernel BUG at"))
+ oopsstart = i;
+ else if (strstr(curline, "do_IRQ: stack overflow:"))
+ oopsstart = i;
+ else if (strstr(curline, "RTNL: assertion failed"))
+ oopsstart = i;
+ else if (strstr(curline, "Eeek! page_mapcount(page) went negative!"))
+ oopsstart = i;
+ else if (strstr(curline, "near stack overflow (cur:"))
+ oopsstart = i;
+ else if (strstr(curline, "double fault:"))
+ oopsstart = i;
+ else if (strstr(curline, "Badness at"))
+ oopsstart = i;
+ else if (strstr(curline, "NETDEV WATCHDOG"))
+ oopsstart = i;
+ else if (strstr(curline, "WARNING: at ")) /* WARN_ON() generated message */
+ oopsstart = i;
+ else if (strstr(curline, "Unable to handle kernel"))
+ oopsstart = i;
+ else if (strstr(curline, "sysctl table check failed"))
+ oopsstart = i;
+ else if (strstr(curline, "INFO: possible recursive locking detected"))
+ oopsstart = i;
+ // Not needed: "--[ cut here ]--" is always followed
+ // by "Badness at", "kernel BUG at", or "WARNING: at" string
+ //else if (strstr(curline, "------------[ cut here ]------------"))
+ // oopsstart = i;
+ else if (strstr(curline, "list_del corruption."))
+ oopsstart = i;
+ else if (strstr(curline, "list_add corruption."))
+ oopsstart = i;
+ if (strstr(curline, "Oops:") && i >= 3)
+ oopsstart = i-3;
+
+ if (oopsstart >= 0) {
+ /* debug information */
+ VERB3 {
+ log("Found oops at line %d: '%s'", oopsstart, lines_info[oopsstart].ptr);
+ if (oopsstart != i)
+ log("Trigger line is %d: '%s'", i, c);
+ }
+ /* try to find the end marker */
+ int i2 = i + 1;
+ while (i2 < linecount && i2 < (i+50)) {
+ if (strstr(lines_info[i2].ptr, "---[ end trace")) {
+ inbacktrace = 1;
+ i = i2;
+ break;
+ }
+ i2++;
+ }
+ }
+ }
+
+ /* Are we entering a call trace part? */
+ /* a call trace starts with "Call Trace:" or with the " [<.......>] function+0xFF/0xAA" pattern */
+ if (oopsstart >= 0 && !inbacktrace) {
+ if (strstr(curline, "Call Trace:"))
+ inbacktrace = 1;
+ else
+ if (strnlen(curline, 9) > 8
+ && curline[0] == '[' && curline[1] == '<'
+ && strstr(curline, ">]")
+ && strstr(curline, "+0x")
+ && strstr(curline, "/0x")
+ ) {
+ inbacktrace = 1;
+ }
+ }
+
+ /* Are we at the end of an oops? */
+ else if (oopsstart >= 0 && inbacktrace) {
+ int oopsend = INT_MAX;
+
+ /* line needs to start with " [" or have "] [" if it is still a call trace */
+ /* example: "[<ffffffffa006c156>] radeon_get_ring_head+0x16/0x41 [radeon]" */
+ if (curline[0] != '['
+ && !strstr(curline, "] [")
+ && !strstr(curline, "--- Exception")
+ && !strstr(curline, "LR =")
+ && !strstr(curline, "<#DF>")
+ && !strstr(curline, "<IRQ>")
+ && !strstr(curline, "<EOI>")
+ && !strstr(curline, "<<EOE>>")
+ && strncmp(curline, "Code: ", 6) != 0
+ && strncmp(curline, "RIP ", 4) != 0
+ && strncmp(curline, "RSP ", 4) != 0
+ ) {
+ oopsend = i-1; /* not a call trace line */
+ }
+ /* oops lines are always more than 8 chars long */
+ else if (strnlen(curline, 8) < 8)
+ oopsend = i-1;
+ /* single oopses are of the same loglevel */
+ else if (lines_info[i].level != prevlevel)
+ oopsend = i-1;
+ else if (strstr(curline, "Instruction dump:"))
+ oopsend = i;
+ /* if a new oops starts, this one has ended */
+ else if (strstr(curline, "WARNING: at ") && oopsstart != i) /* WARN_ON() generated message */
+ oopsend = i-1;
+ else if (strstr(curline, "Unable to handle") && oopsstart != i)
+ oopsend = i-1;
+ /* kernel end-of-oops marker (not including marker itself) */
+ else if (strstr(curline, "---[ end trace"))
+ oopsend = i-1;
+
+ if (oopsend <= i) {
+ VERB3 log("End of oops at line %d (%d): '%s'", oopsend, i, lines_info[oopsend].ptr);
+ if (record_oops(oopses, lines_info, oopsstart, oopsend))
+ oopsesfound++;
+ oopsstart = -1;
+ inbacktrace = 0;
+ }
+ }
+
+ prevlevel = lines_info[i].level;
+ i++;
+
+ if (oopsstart >= 0) {
+ /* Do we have a suspiciously long oops? Cancel it */
+ if (i-oopsstart > 60) {
+ inbacktrace = 0;
+ oopsstart = -1;
+ VERB3 log("Dropped oops, too long");
+ continue;
+ }
+ if (!inbacktrace && i-oopsstart > 40) {
+ /*inbacktrace = 0; - already is */
+ oopsstart = -1;
+ VERB3 log("Dropped oops, too long");
+ continue;
+ }
+ }
+ } /* while (i < linecount) */
+
+ /* process last oops if we have one */
+ if (oopsstart >= 0 && inbacktrace) {
+ int oopsend = i-1;
+ VERB3 log("End of oops at line %d (end of file): '%s'", oopsend, lines_info[oopsend].ptr);
+ if (record_oops(oopses, lines_info, oopsstart, oopsend))
+ oopsesfound++;
+ }
+
+ free(lines_info);
+ return oopsesfound;
+}
diff --git a/lib/plugins/KerneloopsSysLog.h b/lib/plugins/KerneloopsSysLog.h
new file mode 100644
index 00000000..a67b33d4
--- /dev/null
+++ b/lib/plugins/KerneloopsSysLog.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2007, Intel Corporation
+ * Copyright 2009, Red Hat Inc.
+ *
+ * This file is part of Abrt.
+ *
+ * This program file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Anton Arapov <anton@redhat.com>
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+
+#ifndef __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_
+#define __INCLUDE_GUARD_KERNELOOPSSYSLOG_H_
+
+#include "abrt_types.h"
+
+int extract_oopses(vector_string_t &oopses, char *buffer, size_t buflen);
+
+#endif
diff --git a/lib/plugins/Logger.conf b/lib/plugins/Logger.conf
new file mode 100644
index 00000000..f96c5b80
--- /dev/null
+++ b/lib/plugins/Logger.conf
@@ -0,0 +1,6 @@
+# Configuration for Logger plugin
+Enabled = yes
+
+LogPath = /var/log/abrt.log
+
+AppendLogs = yes
diff --git a/lib/plugins/Logger.cpp b/lib/plugins/Logger.cpp
new file mode 100644
index 00000000..ece450d4
--- /dev/null
+++ b/lib/plugins/Logger.cpp
@@ -0,0 +1,90 @@
+/*
+ Logger.cpp - it simple writes report to specific file
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "Logger.h"
+#include "debug_dump.h"
+#include "comm_layer_inner.h"
+#include "abrt_exception.h"
+
+CLogger::CLogger() :
+ m_sLogPath("/var/log/abrt.log"),
+ m_bAppendLogs(true)
+{}
+
+void CLogger::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("LogPath");
+ if (it != end)
+ {
+ m_sLogPath = it->second;
+ }
+ it = pSettings.find("AppendLogs");
+ if (it != end)
+ {
+ m_bAppendLogs = string_to_bool(it->second.c_str());
+ }
+}
+
+//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_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ std::string description = make_description_logger(pCrashData);
+
+ /* open, not fopen - want to set mode if we create the file, not just open */
+ const char *fname = m_sLogPath.c_str();
+ int fd = open(fname,
+ m_bAppendLogs ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC,
+ 0600);
+ if (fd < 0)
+ throw CABRTException(EXCEP_PLUGIN, "Can't open '%s'", fname);
+
+ update_client(_("Writing report to '%s'"), fname);
+ description += "\n\n\n";
+ const char *desc = description.c_str();
+ full_write(fd, desc, strlen(desc));
+ close(fd);
+
+ const char *format = m_bAppendLogs ? _("The report was appended to %s") : _("The report was stored to %s");
+ return ssprintf(format, m_sLogPath.c_str());
+}
+
+PLUGIN_INFO(REPORTER,
+ CLogger,
+ "Logger",
+ "0.0.1",
+ _("Writes report to a file"),
+ "zprikryl@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/Logger.glade");
diff --git a/lib/plugins/Logger.glade b/lib/plugins/Logger.glade
new file mode 100644
index 00000000..a0a909a4
--- /dev/null
+++ b/lib/plugins/Logger.glade
@@ -0,0 +1,135 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">12</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">2</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lLogPath">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Logger file:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_LogPath">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_AppendLogs">
+ <property name="label" translatable="yes">Append new logs</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Logger plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">button1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/lib/plugins/Logger.h b/lib/plugins/Logger.h
new file mode 100644
index 00000000..aa7def32
--- /dev/null
+++ b/lib/plugins/Logger.h
@@ -0,0 +1,44 @@
+/*
+ Logger.h - header file for Logger reporter plugin
+ - it simply writes report to specific file
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef LOGGER_H_
+#define LOGGER_H_
+
+#include "plugin.h"
+#include "reporter.h"
+
+class CLogger : public CReporter
+{
+ private:
+ std::string m_sLogPath;
+ bool m_bAppendLogs;
+ public:
+ CLogger();
+
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+//ok to delete?
+// virtual const map_plugin_settings_t& GetSettings();
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs);
+};
+
+#endif /* LOGGER_H_ */
diff --git a/lib/plugins/Mailx.conf b/lib/plugins/Mailx.conf
new file mode 100644
index 00000000..ccd14292
--- /dev/null
+++ b/lib/plugins/Mailx.conf
@@ -0,0 +1,15 @@
+# Configuration to Email reporter plugin
+Enabled = yes
+
+# In abrt.conf, plugin takes one parameter: subject (in "" if you need to embed spaces).
+# If it isn't specified, then a default subject is taken from this file
+Subject = "[abrt] crash report"
+
+# Your Email
+EmailFrom = user@localhost
+
+# Email To
+EmailTo = root@localhost
+
+# Warning! enabling this may cause sending a lot of MB via email
+SendBinaryData = no
diff --git a/lib/plugins/Mailx.cpp b/lib/plugins/Mailx.cpp
new file mode 100644
index 00000000..a94a2c5c
--- /dev/null
+++ b/lib/plugins/Mailx.cpp
@@ -0,0 +1,195 @@
+/*
+ Mailx.cpp
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "Mailx.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+#define MAILX_COMMAND "/bin/mailx"
+
+CMailx::CMailx() :
+ m_sEmailFrom("user@localhost"),
+ m_sEmailTo("root@localhost"),
+ m_sSubject("[abrt] full crash report"),
+ m_bSendBinaryData(false)
+{}
+
+static void exec_and_feed_input(uid_t uid, const char* pText, char **pArgs)
+{
+ int pipein[2];
+
+ pid_t child = fork_execv_on_steroids(
+ EXECFLG_INPUT | EXECFLG_QUIET | EXECFLG_SETGUID,
+ pArgs,
+ pipein,
+ /*unsetenv_vec:*/ NULL,
+ /*dir:*/ NULL,
+ uid);
+
+ full_write(pipein[1], pText, strlen(pText));
+ close(pipein[1]);
+
+ waitpid(child, NULL, 0); /* wait for command completion */
+}
+
+static char** append_str_to_vector(char **vec, unsigned &size, const char *str)
+{
+ //log("old vec: %p", vec);
+ vec = (char**) xrealloc(vec, (size+2) * sizeof(vec[0]));
+ vec[size] = xstrdup(str);
+ //log("new vec: %p, added [%d] %p", vec, size, vec[size]);
+ size++;
+ vec[size] = NULL;
+ return vec;
+}
+
+std::string CMailx::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ SetSettings(pSettings);
+ char **args = NULL;
+ unsigned arg_size = 0;
+ args = append_str_to_vector(args, arg_size, MAILX_COMMAND);
+
+//TODO: move email body generation to make_descr.cpp
+ std::string binaryFiles, commonFiles, additionalFiles, DUPHASHFile;
+ map_crash_data_t::const_iterator it;
+ for (it = pCrashData.begin(); it != pCrashData.end(); it++)
+ {
+ if (it->second[CD_TYPE] == CD_TXT)
+ {
+ if (it->first != CD_DUPHASH
+ && it->first != FILENAME_ARCHITECTURE
+ && it->first != FILENAME_KERNEL
+ && it->first != FILENAME_PACKAGE
+ ) {
+ additionalFiles += it->first;
+ additionalFiles += "\n-----\n";
+ additionalFiles += it->second[CD_CONTENT];
+ additionalFiles += "\n\n";
+ }
+ else if (it->first == CD_DUPHASH)
+ {
+ DUPHASHFile += it->first;
+ DUPHASHFile += "\n-----\n";
+ DUPHASHFile += it->second[CD_CONTENT];
+ DUPHASHFile += "\n\n";
+ }
+ else
+ {
+ commonFiles += it->first;
+ commonFiles += "\n-----\n";
+ commonFiles += it->second[CD_CONTENT];
+ commonFiles += "\n\n";
+ }
+ }
+ if (it->second[CD_TYPE] == CD_BIN)
+ {
+ binaryFiles += " -a ";
+ binaryFiles += it->second[CD_CONTENT];
+ if (m_bSendBinaryData)
+ {
+ args = append_str_to_vector(args, arg_size, "-a");
+ args = append_str_to_vector(args, arg_size, it->second[CD_CONTENT].c_str());
+ }
+ }
+ }
+
+ std::string emailBody = "Duplicate check\n";
+ emailBody += "=====\n\n";
+ emailBody += DUPHASHFile;
+ emailBody += "\nCommon information\n";
+ emailBody += "=====\n\n";
+ emailBody += commonFiles;
+ emailBody += "\nAdditional information\n";
+ emailBody += "=====\n\n";
+ emailBody += additionalFiles;
+ emailBody += '\n';
+
+ args = append_str_to_vector(args, arg_size, "-s");
+ args = append_str_to_vector(args, arg_size, (pArgs[0] != '\0' ? pArgs : m_sSubject.c_str()));
+ args = append_str_to_vector(args, arg_size, "-r");
+ args = append_str_to_vector(args, arg_size, m_sEmailFrom.c_str());
+ args = append_str_to_vector(args, arg_size, m_sEmailTo.c_str());
+
+ update_client(_("Sending an email..."));
+ const char *uid_str = get_crash_data_item_content(pCrashData, CD_UID).c_str();
+ exec_and_feed_input(xatoi_u(uid_str), emailBody.c_str(), args);
+
+ while (*args)
+ {
+ free(*args++);
+ }
+ args -= arg_size;
+ free(args);
+
+ return "Email was sent to: " + m_sEmailTo;
+}
+
+void CMailx::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("Subject");
+ if (it != end)
+ {
+ m_sSubject = it->second;
+ }
+ it = pSettings.find("EmailFrom");
+ if (it != end)
+ {
+ m_sEmailFrom = it->second;
+ }
+ it = pSettings.find("EmailTo");
+ if (it != end)
+ {
+ m_sEmailTo = it->second;
+ }
+ it = pSettings.find("SendBinaryData");
+ if (it != end)
+ {
+ m_bSendBinaryData = string_to_bool(it->second.c_str());
+ }
+}
+
+//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,
+ "Mailx",
+ "0.0.2",
+ _("Sends an email with a report (via mailx command)"),
+ "zprikryl@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/Mailx.glade");
diff --git a/lib/plugins/Mailx.glade b/lib/plugins/Mailx.glade
new file mode 100644
index 00000000..656204b5
--- /dev/null
+++ b/lib/plugins/Mailx.glade
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">12</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">4</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lSubject">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Subject:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Subject">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_EmailFrom">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lEmailFrom">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Your Email:</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lEmailTo">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Recipient's Email:</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_EmailTo">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_SendBinaryData">
+ <property name="label" translatable="yes">Send Binary Data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Mailx plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">button1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/lib/plugins/Mailx.h b/lib/plugins/Mailx.h
new file mode 100644
index 00000000..aa870ec6
--- /dev/null
+++ b/lib/plugins/Mailx.h
@@ -0,0 +1,48 @@
+/*
+ Mailx.h - header file for Mailx reporter plugin
+ - it simple sends an email to specific address via mailx command
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef MAILX_H_
+#define MAILX_H_
+
+#include <string>
+#include "plugin.h"
+#include "reporter.h"
+
+class CMailx : public CReporter
+{
+ private:
+ std::string m_sEmailFrom;
+ std::string m_sEmailTo;
+ std::string m_sSubject;
+ bool m_bSendBinaryData;
+
+ public:
+ CMailx();
+
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+//ok to delete?
+// virtual const map_plugin_settings_t& GetSettings();
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs);
+};
+
+#endif
diff --git a/lib/plugins/Makefile.am b/lib/plugins/Makefile.am
new file mode 100644
index 00000000..147a778e
--- /dev/null
+++ b/lib/plugins/Makefile.am
@@ -0,0 +1,151 @@
+AM_CPPFLAGS = -I$(srcdir)/../../inc -I$(srcdir)/../utils
+pluginslibdir=$(PLUGINS_LIB_DIR)
+pluginslib_LTLIBRARIES = \
+ libCCpp.la \
+ libMailx.la \
+ libSQLite3.la \
+ libLogger.la \
+ libKerneloopsScanner.la\
+ libKerneloops.la \
+ libKerneloopsReporter.la \
+ libRunApp.la \
+ libSOSreport.la \
+ libBugzilla.la \
+ libRHTSupport.la \
+ libReportUploader.la \
+ libPython.la \
+ libFileTransfer.la
+
+dist_pluginslib_DATA = \
+ Logger.glade \
+ Mailx.glade \
+ Bugzilla.glade \
+ RHTSupport.glade \
+ ReportUploader.glade \
+ KerneloopsReporter.glade
+
+pluginsconfdir = $(PLUGINS_CONF_DIR)
+dist_pluginsconf_DATA = \
+ CCpp.conf \
+ Mailx.conf \
+ SQLite3.conf \
+ Logger.conf \
+ Kerneloops.conf \
+ Bugzilla.conf \
+ RHTSupport.conf \
+ ReportUploader.conf \
+ FileTransfer.conf \
+ Python.conf \
+ SOSreport.conf
+
+man_MANS = \
+ abrt-FileTransfer.7 \
+ abrt-Bugzilla.7 \
+ abrt-KerneloopsScanner.7 \
+ abrt-KerneloopsReporter.7 \
+ abrt-Logger.7 \
+ abrt-Mailx.7 \
+ abrt-plugins.7 \
+ abrt-SQLite3.7 \
+ abrt-RunApp.7 \
+ abrt-ReportUploader.7
+
+EXTRA_DIST = $(man_MANS)
+
+$(DESTDIR)/$(DEBUG_INFO_DIR):
+ $(mkdir_p) '$@'
+
+install-data-hook: $(DESTDIR)/$(DEBUG_INFO_DIR)
+ sed 's: = /var/: = $(localstatedir)/:g' -i \
+ $(DESTDIR)$(sysconfdir)/abrt/plugins/SQLite3.conf \
+ $(DESTDIR)$(sysconfdir)/abrt/plugins/Logger.conf
+
+INC_PATH=$(srcdir)/../../inc
+UTILS_PATH=$(srcdir)/../utils
+
+# CCpp
+libCCpp_la_SOURCES = CCpp.cpp CCpp.h CCpp_sha1.cpp CCpp_sha1.h
+libCCpp_la_LDFLAGS = -avoid-version
+#libCCpp_la_LIBADD =
+libCCpp_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) \
+ -DCCPP_HOOK_PATH=\"${libexecdir}/abrt-hook-ccpp\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"'
+# -DHOSTILE_KERNEL
+
+# Kerneloops
+libKerneloops_la_SOURCES = Kerneloops.cpp Kerneloops.h
+libKerneloops_la_LDFLAGS = -avoid-version
+libKerneloops_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH)
+
+# KerneloopsReporter
+libKerneloopsReporter_la_SOURCES = KerneloopsReporter.cpp KerneloopsReporter.h
+libKerneloopsReporter_la_LDFLAGS = -avoid-version
+libKerneloopsReporter_la_LIBADD = $(CURL_LIBS)
+libKerneloopsReporter_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(CURL_CFLAGS) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\"
+
+# KerneloopsScanner
+libKerneloopsScanner_la_SOURCES = KerneloopsScanner.cpp KerneloopsScanner.h KerneloopsSysLog.cpp KerneloopsSysLog.h
+libKerneloopsScanner_la_LDFLAGS = -avoid-version
+libKerneloopsScanner_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\"
+
+# Mailx
+libMailx_la_SOURCES = Mailx.cpp Mailx.h
+libMailx_la_LDFLAGS = -avoid-version
+libMailx_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" -DLOCALSTATEDIR='"$(localstatedir)"'
+
+# SQLite3
+libSQLite3_la_SOURCES = SQLite3.cpp SQLite3.h
+libSQLite3_la_LDFLAGS = -avoid-version
+libSQLite3_la_LIBADD = $(SQLITE3_LIBS)
+libSQLite3_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(SQLITE3_CFLAGS) -DLOCALSTATEDIR='"$(localstatedir)"'
+
+# Logger
+libLogger_la_SOURCES = Logger.cpp Logger.h
+libLogger_la_LDFLAGS = -avoid-version
+libLogger_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\"
+
+# RunApp
+libRunApp_la_SOURCES = RunApp.h RunApp.cpp
+libRunApp_la_LDFLAGS = -avoid-version
+
+# SOSreport
+libSOSreport_la_SOURCES = SOSreport.cpp SOSreport.h
+libSOSreport_la_LDFLAGS = -avoid-version
+
+# Bugzilla
+libBugzilla_la_SOURCES = Bugzilla.h Bugzilla.cpp
+libBugzilla_la_LIBADD = $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS)
+libBugzilla_la_LDFLAGS = -avoid-version
+libBugzilla_la_CPPFLAGS = $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+ -I$(INC_PATH) -I$(UTILS_PATH) \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\"
+
+# RHTSupport
+libRHTSupport_la_SOURCES = RHTSupport.h RHTSupport.cpp
+libRHTSupport_la_LIBADD =
+libRHTSupport_la_LDFLAGS = -avoid-version -ltar
+libRHTSupport_la_CPPFLAGS = \
+ -I$(INC_PATH) -I$(UTILS_PATH) \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"'
+
+# ReportUploader
+libReportUploader_la_SOURCES = ReportUploader.h ReportUploader.cpp
+libReportUploader_la_LDFLAGS = -avoid-version
+libReportUploader_la_LIBADD = $(CURL_LIBS)
+libReportUploader_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(CURL_CFLAGS) -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\"
+
+# Python
+libPython_la_SOURCES = Python.h Python.cpp Python_hash.h Python_hash.cpp
+#libPython_la_LIBADD = $(NSS_LIBS)
+libPython_la_LDFLAGS = -avoid-version
+libPython_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH)
+
+# FileTrasfer
+libFileTransfer_la_SOURCES = FileTransfer.cpp FileTransfer.h
+libFileTransfer_la_LDFLAGS = -avoid-version -ltar -lbz2 -lz
+libFileTransfer_la_LIBADD = $(CURL_LIBS)
+libFileTransfer_la_CPPFLAGS = -I$(INC_PATH) -I$(UTILS_PATH) $(CURL_CFLAGS) -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\"
diff --git a/lib/plugins/Python.conf b/lib/plugins/Python.conf
new file mode 100644
index 00000000..3201c6da
--- /dev/null
+++ b/lib/plugins/Python.conf
@@ -0,0 +1 @@
+Enabled = yes
diff --git a/lib/plugins/Python.cpp b/lib/plugins/Python.cpp
new file mode 100644
index 00000000..5f2f6e17
--- /dev/null
+++ b/lib/plugins/Python.cpp
@@ -0,0 +1,100 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "Python.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "Python_hash.h"
+
+using namespace std;
+
+string CAnalyzerPython::GetLocalUUID(const char *pDebugDumpDir)
+{
+ CDebugDump dd;
+ dd.Open(pDebugDumpDir);
+ string bt;
+ dd.LoadText(FILENAME_BACKTRACE, bt);
+
+ const char *bt_str = bt.c_str();
+ const char *bt_end = strchrnul(bt_str, '\n');
+
+ char hash_str[MD5_RESULT_LEN*2 + 1];
+ unsigned char hash2[MD5_RESULT_LEN];
+ md5_ctx_t md5ctx;
+ md5_begin(&md5ctx);
+ // Better:
+ // "example.py:1:<module>:ZeroDivisionError: integer division or modulo by zero"
+ //md5_hash(bt_str, bt_end - bt_str, &md5ctx);
+ // For now using compat version:
+ {
+ char *copy = xstrndup(bt_str, bt_end - bt_str);
+ char *s = copy;
+ char *d = copy;
+ unsigned colon_cnt = 0;
+ while (*s && colon_cnt < 3) {
+ if (*s != ':')
+ *d++ = *s;
+ else
+ colon_cnt++;
+ s++;
+ }
+ // "example.py1<module>"
+ md5_hash(copy, d - copy, &md5ctx);
+//*d = '\0'; log("str:'%s'", copy);
+ free(copy);
+ }
+ md5_end(hash2, &md5ctx);
+
+ // Hash is MD5_RESULT_LEN bytes long, but we use only first 4
+ // (I don't know why old Python code was using only 4, I mimic that)
+ unsigned len = 4;
+ char *d = hash_str;
+ unsigned char *s = hash2;
+ while (len) {
+ *d++ = "0123456789abcdef"[*s >> 4];
+ *d++ = "0123456789abcdef"[*s & 0xf];
+ s++;
+ len--;
+ }
+ *d = '\0';
+//log("hash2:%s str:'%.*s'", hash_str, (int)(bt_end - bt_str), bt_str);
+
+ return hash_str;
+}
+string CAnalyzerPython::GetGlobalUUID(const char *pDebugDumpDir)
+{
+ return GetLocalUUID(pDebugDumpDir);
+}
+
+void CAnalyzerPython::Init()
+{
+}
+
+void CAnalyzerPython::DeInit()
+{
+}
+
+PLUGIN_INFO(ANALYZER,
+ CAnalyzerPython,
+ "Python",
+ "0.0.1",
+ _("Analyzes crashes in Python programs"),
+ "zprikryl@redhat.com, jmoskovc@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/Python.h b/lib/plugins/Python.h
new file mode 100644
index 00000000..9f9395c5
--- /dev/null
+++ b/lib/plugins/Python.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef PYTHON_H_
+#define PYTHON_H_
+
+#include <string>
+#include "plugin.h"
+#include "analyzer.h"
+
+class CAnalyzerPython : public CAnalyzer
+{
+ public:
+ virtual std::string GetLocalUUID(const char *pDebugDumpDir);
+ virtual std::string GetGlobalUUID(const char *pDebugDumpDir);
+ virtual void CreateReport(const char *pDebugDumpDir, int force) {}
+ virtual void Init();
+ virtual void DeInit();
+};
+
+#endif /* PYTHON_H_ */
diff --git a/lib/plugins/Python_hash.cpp b/lib/plugins/Python_hash.cpp
new file mode 100644
index 00000000..36ddacdc
--- /dev/null
+++ b/lib/plugins/Python_hash.cpp
@@ -0,0 +1,445 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * md5.c - Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+#include "abrtlib.h"
+#include "Python_hash.h"
+#include <byteswap.h>
+
+#if defined(__BIG_ENDIAN__) && __BIG_ENDIAN__
+# define MD5_BIG_ENDIAN 1
+# define MD5_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __BIG_ENDIAN
+# define MD5_BIG_ENDIAN 1
+# define MD5_LITTLE_ENDIAN 0
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+# define MD5_BIG_ENDIAN 0
+# define MD5_LITTLE_ENDIAN 1
+#else
+# error "Can't determine endianness"
+#endif
+
+/* SWAP_LEnn means "convert CPU<->little_endian if needed (by swapping bytes)" */
+#if MD5_BIG_ENDIAN
+# define SWAP_BE32(x) (x)
+# define SWAP_LE32(x) bswap_32(x)
+#else
+# define SWAP_BE32(x) bswap_32(x)
+# define SWAP_LE32(x) (x)
+#endif
+
+
+/* 0: fastest, 3: smallest */
+#define MD5_SIZE_VS_SPEED 3
+
+/* Initialize structure containing state of computation.
+ * (RFC 1321, 3.3: Step 3)
+ */
+void md5_begin(md5_ctx_t *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+ ctx->total = 0;
+ ctx->buflen = 0;
+}
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ * and defined in the RFC 1321. The first function is a little bit optimized
+ * (as found in Colin Plumbs public domain implementation).
+ * #define FF(b, c, d) ((b & c) | (~b & d))
+ */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF(d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+#define rotl32(w, s) (((w) << (s)) | ((w) >> (32 - (s))))
+
+/* Hash a single block, 64 bytes long and 4-byte aligned. */
+static void md5_hash_block(const void *buffer, md5_ctx_t *ctx)
+{
+ uint32_t correct_words[16];
+ const uint32_t *words = (const uint32_t *)buffer;
+
+#if MD5_SIZE_VS_SPEED > 0
+ static const uint32_t C_array[] = {
+ /* round 1 */
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
+ /* round 2 */
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
+ 0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8,
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
+ /* round 3 */
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05,
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
+ /* round 4 */
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
+ };
+ static const char P_array[] = {
+# if MD5_SIZE_VS_SPEED > 1
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
+# endif
+ 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, /* 2 */
+ 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, /* 3 */
+ 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 /* 4 */
+ };
+# if MD5_SIZE_VS_SPEED > 1
+ static const char S_array[] = {
+ 7, 12, 17, 22,
+ 5, 9, 14, 20,
+ 4, 11, 16, 23,
+ 6, 10, 15, 21
+ };
+# endif /* MD5_SIZE_VS_SPEED > 1 */
+#endif
+ uint32_t A = ctx->A;
+ uint32_t B = ctx->B;
+ uint32_t C = ctx->C;
+ uint32_t D = ctx->D;
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ uint32_t *cwp = correct_words;
+ uint32_t A_save = A;
+ uint32_t B_save = B;
+ uint32_t C_save = C;
+ uint32_t D_save = D;
+
+#if MD5_SIZE_VS_SPEED > 1
+ const uint32_t *pc;
+ const char *pp;
+ const char *ps;
+ int i;
+ uint32_t temp;
+
+ for (i = 0; i < 16; i++) {
+ cwp[i] = SWAP_LE32(words[i]);
+ }
+ words += 16;
+
+# if MD5_SIZE_VS_SPEED > 2
+ pc = C_array;
+ pp = P_array;
+ ps = S_array - 4;
+
+ for (i = 0; i < 64; i++) {
+ if ((i & 0x0f) == 0)
+ ps += 4;
+ temp = A;
+ switch (i >> 4) {
+ case 0:
+ temp += FF(B, C, D);
+ break;
+ case 1:
+ temp += FG(B, C, D);
+ break;
+ case 2:
+ temp += FH(B, C, D);
+ break;
+ case 3:
+ temp += FI(B, C, D);
+ }
+ temp += cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+# else
+ pc = C_array;
+ pp = P_array;
+ ps = S_array;
+
+ for (i = 0; i < 16; i++) {
+ temp = A + FF(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FG(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FH(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+ ps += 4;
+ for (i = 0; i < 16; i++) {
+ temp = A + FI(B, C, D) + cwp[(int) (*pp++)] + *pc++;
+ temp = rotl32(temp, ps[i & 3]);
+ temp += B;
+ A = D;
+ D = C;
+ C = B;
+ B = temp;
+ }
+
+# endif /* MD5_SIZE_VS_SPEED > 2 */
+#else
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+# define OP(a, b, c, d, s, T) \
+ do { \
+ a += FF(b, c, d) + (*cwp++ = SWAP_LE32(*words)) + T; \
+ ++words; \
+ a = rotl32(a, s); \
+ a += b; \
+ } while (0)
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+ T[i] = (int)(4294967296.0 * fabs(sin(i))), i=1..64
+ */
+
+# if MD5_SIZE_VS_SPEED == 1
+ const uint32_t *pc;
+ const char *pp;
+ int i;
+# endif /* MD5_SIZE_VS_SPEED */
+
+ /* Round 1. */
+# if MD5_SIZE_VS_SPEED == 1
+ pc = C_array;
+ for (i = 0; i < 4; i++) {
+ OP(A, B, C, D, 7, *pc++);
+ OP(D, A, B, C, 12, *pc++);
+ OP(C, D, A, B, 17, *pc++);
+ OP(B, C, D, A, 22, *pc++);
+ }
+# else
+ OP(A, B, C, D, 7, 0xd76aa478);
+ OP(D, A, B, C, 12, 0xe8c7b756);
+ OP(C, D, A, B, 17, 0x242070db);
+ OP(B, C, D, A, 22, 0xc1bdceee);
+ OP(A, B, C, D, 7, 0xf57c0faf);
+ OP(D, A, B, C, 12, 0x4787c62a);
+ OP(C, D, A, B, 17, 0xa8304613);
+ OP(B, C, D, A, 22, 0xfd469501);
+ OP(A, B, C, D, 7, 0x698098d8);
+ OP(D, A, B, C, 12, 0x8b44f7af);
+ OP(C, D, A, B, 17, 0xffff5bb1);
+ OP(B, C, D, A, 22, 0x895cd7be);
+ OP(A, B, C, D, 7, 0x6b901122);
+ OP(D, A, B, C, 12, 0xfd987193);
+ OP(C, D, A, B, 17, 0xa679438e);
+ OP(B, C, D, A, 22, 0x49b40821);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+# undef OP
+# define OP(f, a, b, c, d, k, s, T) \
+ do { \
+ a += f(b, c, d) + correct_words[k] + T; \
+ a = rotl32(a, s); \
+ a += b; \
+ } while (0)
+
+ /* Round 2. */
+# if MD5_SIZE_VS_SPEED == 1
+ pp = P_array;
+ for (i = 0; i < 4; i++) {
+ OP(FG, A, B, C, D, (int) (*pp++), 5, *pc++);
+ OP(FG, D, A, B, C, (int) (*pp++), 9, *pc++);
+ OP(FG, C, D, A, B, (int) (*pp++), 14, *pc++);
+ OP(FG, B, C, D, A, (int) (*pp++), 20, *pc++);
+ }
+# else
+ OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP(FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP(FG, D, A, B, C, 10, 9, 0x02441453);
+ OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 3. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FH, A, B, C, D, (int) (*pp++), 4, *pc++);
+ OP(FH, D, A, B, C, (int) (*pp++), 11, *pc++);
+ OP(FH, C, D, A, B, (int) (*pp++), 16, *pc++);
+ OP(FH, B, C, D, A, (int) (*pp++), 23, *pc++);
+ }
+# else
+ OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP(FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP(FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+
+ /* Round 4. */
+# if MD5_SIZE_VS_SPEED == 1
+ for (i = 0; i < 4; i++) {
+ OP(FI, A, B, C, D, (int) (*pp++), 6, *pc++);
+ OP(FI, D, A, B, C, (int) (*pp++), 10, *pc++);
+ OP(FI, C, D, A, B, (int) (*pp++), 15, *pc++);
+ OP(FI, B, C, D, A, (int) (*pp++), 21, *pc++);
+ }
+# else
+ OP(FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP(FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP(FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
+# endif /* MD5_SIZE_VS_SPEED == 1 */
+#endif /* MD5_SIZE_VS_SPEED > 1 */
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
+
+/* Feed data through a temporary buffer to call md5_hash_aligned_block()
+ * with chunks of data that are 4-byte aligned and a multiple of 64 bytes.
+ * This function's internal buffer remembers previous data until it has 64
+ * bytes worth to pass on. Call md5_end() to flush this buffer. */
+void md5_hash(const void *buffer, size_t len, md5_ctx_t *ctx)
+{
+ char *buf = (char *)buffer;
+
+ /* RFC 1321 specifies the possible length of the file up to 2^64 bits,
+ * Here we only track the number of bytes. */
+ ctx->total += len;
+
+ /* Process all input. */
+ while (len) {
+ unsigned i = 64 - ctx->buflen;
+
+ /* Copy data into aligned buffer. */
+ if (i > len) i = len;
+ memcpy(ctx->buffer + ctx->buflen, buf, i);
+ len -= i;
+ ctx->buflen += i;
+ buf += i;
+
+ /* When buffer fills up, process it. */
+ if (ctx->buflen == 64) {
+ md5_hash_block(ctx->buffer, ctx);
+ ctx->buflen = 0;
+ }
+ }
+}
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ * in first 16 bytes following RESBUF. The result is always in little
+ * endian byte order, so that a byte-wise output yields to the wanted
+ * ASCII representation of the message digest.
+ */
+void md5_end(void *resbuf, md5_ctx_t *ctx)
+{
+ char *buf = ctx->buffer;
+ int i;
+
+ /* Pad data to block size. */
+ buf[ctx->buflen++] = 0x80;
+ memset(buf + ctx->buflen, 0, 128 - ctx->buflen);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ ctx->total <<= 3;
+ if (ctx->buflen > 56)
+ buf += 64;
+ for (i = 0; i < 8; i++)
+ buf[56 + i] = ctx->total >> (i*8);
+
+ /* Process last bytes. */
+ if (buf != ctx->buffer)
+ md5_hash_block(ctx->buffer, ctx);
+ md5_hash_block(buf, ctx);
+
+ /* The MD5 result is in little endian byte order.
+ * We (ab)use the fact that A-D are consecutive in memory.
+ */
+#if MD5_BIG_ENDIAN
+ ctx->A = SWAP_LE32(ctx->A);
+ ctx->B = SWAP_LE32(ctx->B);
+ ctx->C = SWAP_LE32(ctx->C);
+ ctx->D = SWAP_LE32(ctx->D);
+#endif
+ memcpy(resbuf, &ctx->A, sizeof(ctx->A) * 4);
+}
diff --git a/lib/plugins/Python_hash.h b/lib/plugins/Python_hash.h
new file mode 100644
index 00000000..cc1d2c43
--- /dev/null
+++ b/lib/plugins/Python_hash.h
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * md5.c - Compute MD5 checksum of strings according to the
+ * definition of MD5 in RFC 1321 from April 1992.
+ *
+ * Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
+ *
+ * Copyright (C) 1995-1999 Free Software Foundation, Inc.
+ * Copyright (C) 2001 Manuel Novoa III
+ * Copyright (C) 2003 Glenn L. McGrath
+ * Copyright (C) 2003 Erik Andersen
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#define MD5_RESULT_LEN 16
+
+typedef struct md5_ctx_t {
+ uint32_t A;
+ uint32_t B;
+ uint32_t C;
+ uint32_t D;
+ uint64_t total;
+ uint32_t buflen;
+ char buffer[128];
+} md5_ctx_t;
+void md5_begin(md5_ctx_t *ctx);
+void md5_hash(const void *data, size_t length, md5_ctx_t *ctx);
+void md5_end(void *resbuf, md5_ctx_t *ctx);
diff --git a/lib/plugins/RHTSupport.conf b/lib/plugins/RHTSupport.conf
new file mode 100644
index 00000000..ed2c3a85
--- /dev/null
+++ b/lib/plugins/RHTSupport.conf
@@ -0,0 +1,9 @@
+Enabled = yes
+
+URL = https://api.access.redhat.com/rs
+# No means that ssl certificates will not be checked
+SSLVerify = yes
+# Your login has to exist
+Login =
+# Your password
+Password =
diff --git a/lib/plugins/RHTSupport.cpp b/lib/plugins/RHTSupport.cpp
new file mode 100644
index 00000000..c0f03c17
--- /dev/null
+++ b/lib/plugins/RHTSupport.cpp
@@ -0,0 +1,312 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#define _GNU_SOURCE 1 /* for stpcpy */
+#include <libtar.h>
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "abrt_rh_support.h"
+#include "crash_types.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+#include "RHTSupport.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+using namespace std;
+
+
+#if 0 //unused
+static char *xml_escape(const char *str)
+{
+ const char *s = str;
+ unsigned count = 1; /* for NUL */
+ while (*s)
+ {
+ if (*s == '&')
+ count += sizeof("&amp;")-2;
+ if (*s == '<')
+ count += sizeof("&lt;")-2;
+ if (*s == '>')
+ count += sizeof("&gt;")-2;
+ if ((unsigned char)*s > 126 || (unsigned char)*s < ' ')
+ count += sizeof("\\x00")-2;
+ count++;
+ s++;
+ }
+ char *result = (char*)xmalloc(count);
+ char *d = result;
+ s = str;
+ while (*s)
+ {
+ if (*s == '&')
+ d = stpcpy(d, "&amp;");
+ else if (*s == '<')
+ d = stpcpy(d, "&lt;");
+ else if (*s == '>')
+ d = stpcpy(d, "&gt;");
+ else
+ if ((unsigned char)*s > 126
+ || ( (unsigned char)*s < ' '
+ && *s != '\t'
+ && *s != '\n'
+ && *s != '\r'
+ )
+ ) {
+ *d++ = '\\';
+ *d++ = 'x';
+ *d++ = "0123456789abcdef"[(unsigned char)*s >> 4];
+ *d++ = "0123456789abcdef"[(unsigned char)*s & 0xf];
+ }
+ else
+ *d++ = *s;
+ s++;
+ }
+ *d = '\0';
+ return result;
+}
+#endif
+
+
+/*
+ * CReporterRHticket
+ */
+
+CReporterRHticket::CReporterRHticket() :
+ m_bSSLVerify(true),
+ m_sStrataURL("https://api.access.redhat.com/rs")
+{}
+
+CReporterRHticket::~CReporterRHticket()
+{}
+
+string CReporterRHticket::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ string retval;
+
+ map_plugin_settings_t::const_iterator end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+ it = pSettings.find("URL");
+ string URL = (it == end ? m_sStrataURL : it->second);
+ it = pSettings.find("Login");
+ string login = (it == end ? m_sLogin : it->second);
+ it = pSettings.find("Password");
+ string password = (it == end ? m_sPassword : it->second);
+ it = pSettings.find("SSLVerify");
+ bool ssl_verify = (it == end ? m_bSSLVerify : string_to_bool(it->second.c_str()));
+
+ const string& package = get_crash_data_item_content(pCrashData, FILENAME_PACKAGE);
+// const string& component = get_crash_data_item_content(pCrashData, FILENAME_COMPONENT);
+// const string& release = get_crash_data_item_content(pCrashData, FILENAME_RELEASE);
+// const string& arch = get_crash_data_item_content(pCrashData, FILENAME_ARCHITECTURE);
+// const string& duphash = get_crash_data_item_content(pCrashData, CD_DUPHASH);
+ const char *reason = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_REASON);
+ const char *function = get_crash_data_item_content_or_NULL(pCrashData, FILENAME_CRASH_FUNCTION);
+
+ string summary = "[abrt] " + package;
+ if (function && strlen(function) < 30)
+ {
+ summary += ": ";
+ summary += function;
+ }
+ if (reason)
+ {
+ summary += ": ";
+ summary += reason;
+ }
+
+ string description = "abrt version: "VERSION"\n";
+ description += make_description_bz(pCrashData);
+
+ reportfile_t* file = new_reportfile();
+
+ /* SELinux guys are not happy with /tmp, using /var/run/abrt */
+ char *tempfile = xasprintf(LOCALSTATEDIR"/run/abrt/tmp-%lu-%lu.tar.gz", (long)getpid(), (long)time(NULL));
+
+ int pipe_from_parent_to_child[2];
+ xpipe(pipe_from_parent_to_child);
+ pid_t child = fork();
+ if (child == 0)
+ {
+ /* child */
+ close(pipe_from_parent_to_child[1]);
+ xmove_fd(xopen3(tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600), 1);
+ xmove_fd(pipe_from_parent_to_child[0], 0);
+ execlp("gzip", "gzip", NULL);
+ perror_msg_and_die("can't execute '%s'", "gzip");
+ }
+ close(pipe_from_parent_to_child[0]);
+
+ TAR *tar = NULL;
+ if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile,
+ /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
+ {
+ retval = "can't create temporary file in "LOCALSTATEDIR"/run/abrt";
+ goto ret;
+ }
+
+ {
+ map_crash_data_t::const_iterator it = pCrashData.begin();
+ for (; it != pCrashData.end(); it++)
+ {
+ if (it->first == CD_COUNT) continue;
+ if (it->first == CD_DUMPDIR) continue;
+ if (it->first == CD_INFORMALL) continue;
+ if (it->first == CD_REPORTED) continue;
+ if (it->first == CD_MESSAGE) continue; // plugin's status message (if we already reported it yesterday)
+ if (it->first == FILENAME_DESCRIPTION) continue; // package description
+
+ const char *content = it->second[CD_CONTENT].c_str();
+ if (it->second[CD_TYPE] == CD_TXT)
+ {
+ reportfile_add_binding_from_string(file, it->first.c_str(), content);
+ }
+ else if (it->second[CD_TYPE] == CD_BIN)
+ {
+ const char *basename = strrchr(content, '/');
+ if (basename)
+ basename++;
+ else
+ basename = content;
+ string xml_name = concat_path_file("content", basename);
+ reportfile_add_binding_from_namedfile(file,
+ /*on_disk_filename */ content,
+ /*binding_name */ it->first.c_str(),
+ /*recorded_filename*/ xml_name.c_str(),
+ /*binary */ 1);
+ if (tar_append_file(tar, (char*)content, (char*)(xml_name.c_str())) != 0)
+ {
+ retval = "can't create temporary file in "LOCALSTATEDIR"/run/abrt";
+ goto ret;
+ }
+ }
+ }
+ }
+
+ /* Write out content.xml in the tarball's root */
+ {
+ const char *signature = reportfile_as_string(file);
+ unsigned len = strlen(signature);
+ unsigned len512 = (len + 511) & ~511;
+ char *block = (char*)memcpy(xzalloc(len512), signature, len);
+ th_set_type(tar, S_IFREG | 0644);
+ th_set_mode(tar, S_IFREG | 0644);
+ //th_set_link(tar, char *linkname);
+ //th_set_device(tar, dev_t device);
+ //th_set_user(tar, uid_t uid);
+ //th_set_group(tar, gid_t gid);
+ //th_set_mtime(tar, time_t fmtime);
+ th_set_path(tar, (char*)"content.xml");
+ th_set_size(tar, len);
+ th_finish(tar); /* caclulate and store th xsum etc */
+ if (th_write(tar) != 0
+ || full_write(tar_fd(tar), block, len512) != len512
+ || tar_close(tar) != 0
+ ) {
+ retval = "can't create temporary file in "LOCALSTATEDIR"/run/abrt";
+ goto ret;
+ }
+ tar = NULL;
+ }
+
+ {
+ update_client(_("Creating a new case..."));
+ char* result = send_report_to_new_case(URL.c_str(),
+ login.c_str(),
+ password.c_str(),
+ ssl_verify,
+ summary.c_str(),
+ description.c_str(),
+ package.c_str(),
+ tempfile
+ );
+ VERB3 log("post result:'%s'", result);
+ retval = result;
+ free(result);
+ }
+
+ ret:
+ // Damn, selinux does not allow SIGKILLing our own child! wtf??
+ //kill(child, SIGKILL); /* just in case */
+ waitpid(child, NULL, 0);
+ if (tar)
+ tar_close(tar);
+ //close(pipe_from_parent_to_child[1]); - tar_close() does it itself
+ unlink(tempfile);
+ free(tempfile);
+ reportfile_free(file);
+
+ if (strncasecmp(retval.c_str(), "error", 5) == 0)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "%s", retval.c_str());
+ }
+ return retval;
+}
+
+void CReporterRHticket::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("URL");
+ if (it != end)
+ {
+ m_sStrataURL = it->second;
+ }
+ it = pSettings.find("Login");
+ if (it != end)
+ {
+ m_sLogin = it->second;
+ }
+ it = pSettings.find("Password");
+ if (it != end)
+ {
+ m_sPassword = it->second;
+ }
+ it = pSettings.find("SSLVerify");
+ if (it != end)
+ {
+ m_bSSLVerify = string_to_bool(it->second.c_str());
+ }
+}
+
+/* Should not be deleted (why?) */
+const map_plugin_settings_t& CReporterRHticket::GetSettings()
+{
+ m_pSettings["URL"] = m_sStrataURL;
+ m_pSettings["Login"] = m_sLogin;
+ m_pSettings["Password"] = m_sPassword;
+ m_pSettings["SSLVerify"] = m_bSSLVerify ? "yes" : "no";
+
+ return m_pSettings;
+}
+
+PLUGIN_INFO(REPORTER,
+ CReporterRHticket,
+ "RHticket",
+ "0.0.4",
+ "Reports bugs to Red Hat support",
+ "Denys Vlasenko <dvlasenk@redhat.com>",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/RHTSupport.glade");
diff --git a/lib/plugins/RHTSupport.glade b/lib/plugins/RHTSupport.glade
new file mode 100644
index 00000000..64fd6c26
--- /dev/null
+++ b/lib/plugins/RHTSupport.glade
@@ -0,0 +1,213 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">12</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">5</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="lURL">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">RHTSupport URL:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lLogin">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Login:</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="lPassword">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Password:</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_URL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Login">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="cb_Password">
+ <property name="label" translatable="yes">Show password</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_SSLVerify">
+ <property name="label" translatable="yes">SSL verify</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;RHTSupport plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="bApply">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">bApply</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAction" id="action1"/>
+</interface>
diff --git a/lib/plugins/RHTSupport.h b/lib/plugins/RHTSupport.h
new file mode 100644
index 00000000..a96a3b3d
--- /dev/null
+++ b/lib/plugins/RHTSupport.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2010 ABRT team
+ Copyright (C) 2010 RedHat Inc
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef RHTICKET_H_
+#define RHTICKET_H_
+
+#include "plugin.h"
+#include "reporter.h"
+
+class CReporterRHticket: public CReporter
+{
+ private:
+ bool m_bSSLVerify;
+ std::string m_sStrataURL;
+ std::string m_sLogin;
+ std::string m_sPassword;
+
+ public:
+ CReporterRHticket();
+ virtual ~CReporterRHticket();
+
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs);
+
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+ virtual const map_plugin_settings_t& GetSettings();
+};
+
+#endif
diff --git a/lib/plugins/ReportUploader.conf b/lib/plugins/ReportUploader.conf
new file mode 100644
index 00000000..57692531
--- /dev/null
+++ b/lib/plugins/ReportUploader.conf
@@ -0,0 +1,21 @@
+Enabled = yes
+
+# Customer = "Example Inc."
+# Ticket = IT12345
+# Encrypt = yes
+# If set to "no" or commented out,
+# compressed ticket data will be copied to /tmp:
+# Upload = yes
+
+# If "Upload = yes", URL to upload the files to.
+# supported: ftp, ftps, http, https, scp, sftp, tftp, file
+# for example: ftp://user:password@server.name/directory
+# or: scp://user:password@server.name:port/directory etc.
+# for testing: file:///tmp/test_directory
+# URL =
+
+# How many times we try to upload the file
+# RetryCount = 3
+
+# How long we wait between we retry the upload (in seconds)
+# RetryDelay = 20
diff --git a/lib/plugins/ReportUploader.cpp b/lib/plugins/ReportUploader.cpp
new file mode 100644
index 00000000..eb01691c
--- /dev/null
+++ b/lib/plugins/ReportUploader.cpp
@@ -0,0 +1,505 @@
+/*
+ ReportUploader.cpp
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "abrt_curl.h"
+#include "ReportUploader.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+
+CReportUploader::CReportUploader() :
+ m_bEncrypt(false),
+ m_bUpload(false),
+ m_nRetryCount(3),
+ m_nRetryDelay(20)
+{}
+
+CReportUploader::~CReportUploader()
+{}
+
+
+static void RunCommand(const char *cmd)
+{
+ int retcode = system(cmd);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+}
+
+static string ReadCommand(const char *cmd)
+{
+ FILE* fp = popen(cmd, "r");
+ if (!fp)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Error running '%s'", cmd);
+ }
+
+ string result;
+ char buff[1024];
+ while (fgets(buff, sizeof(buff), fp) != NULL)
+ {
+ strchrnul(buff, '\n')[0] = '\0';
+ result += buff;
+ }
+
+ int retcode = pclose(fp);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+
+ return result;
+}
+
+static void WriteCommand(const char *cmd, const char *input)
+{
+ FILE* fp = popen(cmd, "w");
+ if (!fp)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "error running '%s'", cmd);
+ }
+
+ /* Hoping it's not too big to get us forever blocked... */
+ fputs(input, fp);
+
+ int retcode = pclose(fp);
+ if (retcode)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "'%s' exited with %d", cmd, retcode);
+ }
+}
+
+void CReportUploader::SendFile(const char *pURL, const char *pFilename, int retry_count, int retry_delay)
+{
+ if (pURL[0] == '\0')
+ {
+ error_msg(_("FileTransfer: URL not specified"));
+ return;
+ }
+
+ update_client(_("Sending archive %s to %s"), pFilename, pURL);
+
+ const char *base = (strrchr(pFilename, '/') ? : pFilename-1) + 1;
+ string wholeURL = concat_path_file(pURL, base);
+ int count = retry_count;
+ int result;
+ while (1)
+ {
+ FILE* f = fopen(pFilename, "r");
+ if (!f)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't open archive file '%s'", pFilename);
+ }
+ struct stat buf;
+ fstat(fileno(f), &buf); /* never fails */
+ CURL* curl = xcurl_easy_init();
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ /* specify target */
+ curl_easy_setopt(curl, CURLOPT_URL, wholeURL.c_str());
+ curl_easy_setopt(curl, CURLOPT_READDATA, f);
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)buf.st_size);
+ /* everything is done here; result 0 means success */
+ result = curl_easy_perform(curl);
+ /* goodbye */
+ curl_easy_cleanup(curl);
+ fclose(f);
+ if (result != 0)
+ {
+ update_client(_("Sending failed, trying again. %s"), curl_easy_strerror((CURLcode)result));
+ }
+ if (result == 0 || --count <= 0)
+ break;
+ /* retry the upload if not succesful, wait a bit before next try */
+ sleep(retry_delay);
+ }
+
+ if (count <= 0 && result != 0)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Curl can not send a ticket");
+ }
+}
+
+
+static void write_str_to_file(const char *str, const char *path, const char *fname)
+{
+ string ofile_name = concat_path_file(path, fname);
+ FILE *ofile = fopen(ofile_name.c_str(), "w");
+ if (!ofile)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't open '%s'", ofile_name.c_str());
+ }
+ fputs(str, ofile);
+ fclose(ofile);
+}
+
+string CReportUploader::Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs)
+{
+ string customer_name;
+ string ticket_name;
+ string upload_url;
+ bool do_encrypt;
+ bool do_upload;
+ int retry_count;
+ int retry_delay;
+
+ /* if parse_settings fails it returns an empty map so we need to use defaults */
+ map_plugin_settings_t settings = parse_settings(pSettings);
+ // Get ticket name, customer name, and do_encrypt from config settings
+ if (!settings.empty())
+ {
+ customer_name = settings["Customer"];
+ ticket_name = settings["Ticket"];
+ upload_url = settings["URL"];
+ do_encrypt = string_to_bool(settings["Encrypt"].c_str());
+ do_upload = string_to_bool(settings["Upload"].c_str());
+ retry_count = xatoi_u(settings["RetryCount"].c_str());
+ retry_delay = xatoi_u(settings["RetryDelay"].c_str());
+ }
+ else
+ {
+ customer_name = m_sCustomer;
+ ticket_name = m_sTicket;
+ upload_url = m_sURL;
+ do_encrypt = m_bEncrypt;
+ do_upload = m_bUpload;
+ retry_count = m_nRetryCount;
+ retry_delay = m_nRetryDelay;
+ }
+ update_client(_("Creating a ReportUploader report..."));
+
+ bool have_ticket_name = (ticket_name != "");
+ if (!have_ticket_name)
+ {
+ ticket_name = "ReportUploader-newticket";
+ }
+
+ // Format the time to add to the file name
+ char timebuf[256];
+ time_t curtime = time(NULL);
+ strftime(timebuf, sizeof(timebuf), "-%Y%m%d%H%M%S", gmtime(&curtime));
+
+ // Create a tmp work directory, and within that
+ // create the "<ticketname>-yyyymmddhhmmss" directory
+ // which will be the root of the tarball
+ string file_name = ticket_name + timebuf;
+
+ char tmpdir_name[] = "/tmp/abrtuploadXXXXXX";
+ if (mkdtemp(tmpdir_name) == NULL)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't mkdir a temporary directory in /tmp");
+ }
+ string tmptar_name = concat_path_file(tmpdir_name, file_name.c_str());
+
+ if (mkdir(tmptar_name.c_str(), 0700))
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't mkdir '%s'", tmptar_name.c_str());
+ }
+
+ // Copy each entry into the tarball root.
+ // Files are simply copied, strings are written to a file
+ // TODO: some files are totally useless:
+ // "Reported", "Message" (plugin's output), "DumpDir",
+ // "Description" (package description) - maybe skip those?
+ map_crash_data_t::const_iterator it;
+ for (it = pCrashData.begin(); it != pCrashData.end(); it++)
+ {
+ const char *content = it->second[CD_CONTENT].c_str();
+ if (it->second[CD_TYPE] == CD_TXT)
+ {
+ write_str_to_file(content, tmptar_name.c_str(), it->first.c_str());
+ }
+ else if (it->second[CD_TYPE] == CD_BIN)
+ {
+ string ofile_name = concat_path_file(tmptar_name.c_str(), it->first.c_str());
+ if (copy_file(content, ofile_name.c_str(), 0644) < 0)
+ {
+ throw CABRTException(EXCEP_PLUGIN,
+ "Can't copy '%s' to '%s'",
+ content,
+ ofile_name.c_str()
+ );
+ }
+ }
+ }
+
+ // add ticket_name and customer name to tarball
+ if (have_ticket_name)
+ {
+ write_str_to_file(ticket_name.c_str(), tmptar_name.c_str(), "TICKET");
+ }
+ if (customer_name != "")
+ {
+ write_str_to_file(customer_name.c_str(), tmptar_name.c_str(), "CUSTOMER");
+ }
+
+ // Create the compressed tarball
+ string outfile_basename = file_name + ".tar.gz";
+ string outfile_name = concat_path_file(tmpdir_name, outfile_basename.c_str());
+ string cmd = ssprintf("tar -C %s --create --gzip --file=%s %s", tmpdir_name, outfile_name.c_str(), file_name.c_str());
+ RunCommand(cmd.c_str());
+
+ // encrypt if requested
+ string key;
+ if (do_encrypt)
+ {
+ key = ReadCommand("openssl rand -base64 48");
+
+ string infile_name = outfile_name;
+ outfile_basename += ".aes";
+ outfile_name += ".aes";
+
+ cmd = ssprintf("openssl aes-128-cbc -in %s -out %s -pass stdin", infile_name.c_str(), outfile_name.c_str());
+ WriteCommand(cmd.c_str(), key.c_str());
+ }
+
+ // generate md5sum
+ cmd = ssprintf("cd %s; md5sum <%s", tmpdir_name, outfile_basename.c_str());
+ string md5sum = ReadCommand(cmd.c_str());
+
+ // upload or cp to /tmp
+ if (do_upload)
+ {
+ // FIXME: SendFile isn't working sometime (scp)
+ SendFile(upload_url.c_str(), outfile_name.c_str(), retry_count, retry_delay);
+ }
+ else
+ {
+ cmd = ssprintf("cp %s /tmp/", outfile_name.c_str());
+ RunCommand(cmd.c_str());
+ }
+
+ // generate a reciept telling md5sum and encryption key
+ // note: do not internationalize these strings!
+ string msg;
+ if (have_ticket_name)
+ {
+ msg += "Please copy this into ticket: ";
+ msg += ticket_name;
+ msg += '\n';
+ msg += "========cut here========\n";
+ }
+ else
+ {
+ msg += "Please send this to your technical support:\n";
+ msg += "========cut here========\n";
+ }
+ if (do_upload)
+ {
+ msg += "RHUPLOAD: This report was sent to ";
+ msg += upload_url;
+ msg += '\n';
+ }
+ else
+ {
+ msg += "RHUPLOAD: This report was copied into /tmp/:\n";
+ }
+ if (have_ticket_name)
+ {
+ msg += "TICKET: ";
+ msg += ticket_name;
+ msg += '\n';
+ }
+ msg += "FILE: ";
+ msg += outfile_basename;
+ msg += "\nMD5SUM: ";
+ msg += md5sum;
+ msg += '\n';
+ if (do_encrypt)
+ {
+ msg += "KEY: aes-128-cbc\n";
+ msg += key;
+ msg += '\n';
+ }
+ msg += "==========end===========\n";
+
+ // warn the client (why _warn_? it's not an error, maybe update_client?):
+ //error_msg("%s", msg.c_str());
+
+ // delete the temporary directory
+ cmd = ssprintf("rm -rf %s", tmpdir_name);
+ RunCommand(cmd.c_str());
+
+ return msg;
+}
+
+static bool is_string_safe(const char *str)
+{
+ const char *p = str;
+ while (*p)
+ {
+ unsigned char c = *p;
+ if ((c < '0' || c > '9')
+ && c != '_'
+ && c != '-'
+ ) {
+ c |= 0x20; // tolower
+ if (c < 'a' || c > 'z')
+ {
+ return false;
+ }
+ }
+ // only 0-9, -, _, A-Z, a-z reach this point
+ p++;
+ }
+ return true;
+}
+
+void CReportUploader::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)
+ {
+ m_sCustomer = it->second;
+ }
+ // We use m_sTicket as part of filename,
+ // and we use resulting filename in system("cd %s; ...", filename) etc,
+ // so we are very paraniod about allowed chars
+ it = pSettings.find("Ticket");
+ if (it != end && is_string_safe(it->second.c_str()))
+ {
+ m_sTicket = it->second;
+ }
+ it = pSettings.find("URL");
+ if (it != end)
+ {
+ m_sURL = it->second;
+ }
+ it = pSettings.find("Encrypt");
+ if (it != end)
+ {
+ m_bEncrypt = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("Upload");
+ if (it != end)
+ {
+ m_bUpload = string_to_bool(it->second.c_str());
+ }
+ it = pSettings.find("RetryCount");
+ if (it != end)
+ {
+ m_nRetryCount = xatoi_u(it->second.c_str());
+ }
+ it = pSettings.find("RetryDelay");
+ if (it != end)
+ {
+ m_nRetryDelay = xatoi_u(it->second.c_str());
+ }
+}
+
+const map_plugin_settings_t& CReportUploader::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_bUpload ? "yes" : "no";
+ m_pSettings["RetryCount"] = to_string(m_nRetryCount);
+ m_pSettings["RetryDelay"] = to_string(m_nRetryDelay);
+
+ return m_pSettings;
+}
+
+//todo: make static
+map_plugin_settings_t CReportUploader::parse_settings(const map_plugin_settings_t& pSettings)
+{
+ map_plugin_settings_t plugin_settings;
+
+ map_plugin_settings_t::const_iterator end = pSettings.end();
+ map_plugin_settings_t::const_iterator it;
+
+ it = pSettings.find("Customer");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Customer"] = it->second;
+
+ it = pSettings.find("Ticket");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Ticket"] = it->second;
+
+ it = pSettings.find("URL");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["URL"] = it->second;
+
+ it = pSettings.find("Encrypt");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Encrypt"] = it->second;
+
+ it = pSettings.find("Upload");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["Upload"] = it->second;
+
+ it = pSettings.find("RetryCount");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["RetryCount"] = it->second;
+
+ it = pSettings.find("RetryDelay");
+ if (it == end)
+ {
+ plugin_settings.clear();
+ return plugin_settings;
+ }
+ plugin_settings["RetryDelay"] = it->second;
+
+ VERB1 log("User settings ok, using them instead of defaults");
+ return plugin_settings;
+}
+
+PLUGIN_INFO(REPORTER,
+ CReportUploader,
+ "ReportUploader",
+ "0.0.1",
+ _("Packs crash data into .tar.gz file, optionally uploads it via FTP/SCP/etc"),
+ "gavin@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ PLUGINS_LIB_DIR"/ReportUploader.glade");
diff --git a/lib/plugins/ReportUploader.glade b/lib/plugins/ReportUploader.glade
new file mode 100644
index 00000000..c2bbd470
--- /dev/null
+++ b/lib/plugins/ReportUploader.glade
@@ -0,0 +1,249 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="PluginDialog">
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="icon_name">abrt</property>
+ <property name="type_hint">normal</property>
+ <property name="has_separator">False</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkFrame" id="frame1">
+ <property name="visible">True</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">none</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="visible">True</property>
+ <property name="top_padding">6</property>
+ <property name="left_padding">12</property>
+ <child>
+ <object class="GtkTable" id="table1">
+ <property name="visible">True</property>
+ <property name="n_rows">7</property>
+ <property name="n_columns">2</property>
+ <property name="column_spacing">12</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Customer:</property>
+ </object>
+ <packing>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Ticket:</property>
+ </object>
+ <packing>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">URL:</property>
+ </object>
+ <packing>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Customer">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_Ticket">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">1</property>
+ <property name="bottom_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_URL">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">2</property>
+ <property name="bottom_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Retry count:</property>
+ </object>
+ <packing>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_RetryCount">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">3</property>
+ <property name="bottom_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Retry delay:</property>
+ </object>
+ <packing>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ <property name="x_options">GTK_FILL</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="conf_RetryDelay">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="right_attach">2</property>
+ <property name="top_attach">4</property>
+ <property name="bottom_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_Encrypt">
+ <property name="label" translatable="yes">Use encryption</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">5</property>
+ <property name="bottom_attach">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="conf_Upload">
+ <property name="label" translatable="yes">Upload</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="right_attach">2</property>
+ <property name="top_attach">6</property>
+ <property name="bottom_attach">7</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">&lt;b&gt;Report Uploader plugin configuration&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="bApply">
+ <property name="label">gtk-apply</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button2</action-widget>
+ <action-widget response="-10">bApply</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkAction" id="action1"/>
+</interface>
diff --git a/lib/plugins/ReportUploader.h b/lib/plugins/ReportUploader.h
new file mode 100644
index 00000000..4ff780b8
--- /dev/null
+++ b/lib/plugins/ReportUploader.h
@@ -0,0 +1,55 @@
+/*
+ ReportUploader.h
+
+ Attach a configureable Ticket Number and Customer name to a report.
+ Create a compressed, optionally encrypted, tarball.
+ Upload tarball to configureable URL.
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef REPORTUPLOADER_H_
+#define REPORTUPLAODER_H_
+
+#include "plugin.h"
+#include "reporter.h"
+
+class CReportUploader : public CReporter
+{
+ private:
+ std::string m_sCustomer;
+ std::string m_sTicket;
+ std::string m_sURL;
+ bool m_bEncrypt;
+ bool m_bUpload;
+ int m_nRetryCount;
+ int m_nRetryDelay;
+
+ void SendFile(const char *pURL, const char *pFilename, int retry_count, int retry_delay);
+ map_plugin_settings_t parse_settings(const map_plugin_settings_t& pSettings);
+
+ public:
+ CReportUploader();
+ virtual ~CReportUploader();
+ virtual const map_plugin_settings_t& GetSettings();
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+
+ virtual std::string Report(const map_crash_data_t& pCrashData,
+ const map_plugin_settings_t& pSettings,
+ const char *pArgs);
+};
+
+#endif
diff --git a/lib/plugins/RunApp.cpp b/lib/plugins/RunApp.cpp
new file mode 100644
index 00000000..69c9384c
--- /dev/null
+++ b/lib/plugins/RunApp.cpp
@@ -0,0 +1,76 @@
+/*
+ RunApp.cpp
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "RunApp.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+#include "abrtlib.h"
+
+#define COMMAND 0
+#define FILENAME 1
+
+using namespace std;
+
+void CActionRunApp::Run(const char *pActionDir, const char *pArgs, int force)
+{
+ /* Don't update_client() - actions run at crash time, there is no client
+ * to talk to at that point */
+ log("RunApp('%s','%s')", pActionDir, pArgs);
+
+ vector_string_t args;
+ parse_args(pArgs, args, '"');
+
+ if (args.size() <= COMMAND)
+ {
+ return;
+ }
+ const char *cmd = args[COMMAND].c_str();
+ if (!cmd[0])
+ {
+ return;
+ }
+
+ /* NB: we chdir to the dump dir. Command can analyze component and such.
+ * Example:
+ * test x"`cat component`" = x"xorg-x11-apps" && cp /var/log/Xorg.0.log .
+ */
+ size_t cmd_out_size;
+ char *cmd_out = run_in_shell_and_save_output(/*flags:*/ 0, cmd, pActionDir, &cmd_out_size);
+
+ if (args.size() > FILENAME)
+ {
+ CDebugDump dd;
+ dd.Open(pActionDir);
+ dd.SaveBinary(args[FILENAME].c_str(), cmd_out, cmd_out_size);
+ }
+
+ free(cmd_out);
+}
+
+PLUGIN_INFO(ACTION,
+ CActionRunApp,
+ "RunApp",
+ "0.0.1",
+ _("Runs a command, saves its output"),
+ "zprikryl@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/RunApp.h b/lib/plugins/RunApp.h
new file mode 100644
index 00000000..58f572d0
--- /dev/null
+++ b/lib/plugins/RunApp.h
@@ -0,0 +1,34 @@
+/*
+ RunApp.h - Simple action plugin which execute command
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef RUNAPP_H_
+#define RUNAPP_H_
+
+#include "action.h"
+#include <string>
+#include <vector>
+
+class CActionRunApp : public CAction
+{
+ public:
+ virtual void Run(const char *pActionDir, const char *pArgs, int force);
+};
+
+#endif
diff --git a/lib/plugins/SOSreport.conf b/lib/plugins/SOSreport.conf
new file mode 100644
index 00000000..3201c6da
--- /dev/null
+++ b/lib/plugins/SOSreport.conf
@@ -0,0 +1 @@
+Enabled = yes
diff --git a/lib/plugins/SOSreport.cpp b/lib/plugins/SOSreport.cpp
new file mode 100644
index 00000000..6c8ab1f8
--- /dev/null
+++ b/lib/plugins/SOSreport.cpp
@@ -0,0 +1,162 @@
+/*
+ SOSreport.cpp
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include "abrtlib.h"
+#include "abrt_types.h"
+#include "abrt_exception.h"
+#include "SOSreport.h"
+#include "debug_dump.h"
+#include "abrt_exception.h"
+#include "comm_layer_inner.h"
+
+using namespace std;
+
+static char *ParseFilename(char *p)
+{
+ /*
+ the sosreport's filename is embedded in sosreport's output.
+ It appears on the line after the string in 'sosreport_filename_marker',
+ it has leading spaces, and a trailing newline. This function trims
+ any leading and trailing whitespace from the filename.
+ */
+ static const char sosreport_filename_marker[] =
+ "Your sosreport has been generated and saved in:";
+
+ p = strstr(p, sosreport_filename_marker);
+ if (!p)
+ return p;
+ p = skip_whitespace(p + sizeof(sosreport_filename_marker)-1);
+ char *end = strchrnul(p, '\n');
+ while (end > p && isspace(*end))
+ *end-- = '\0';
+ return p[0] == '/' ? p : NULL;
+}
+
+void CActionSOSreport::Run(const char *pActionDir, const char *pArgs, int force)
+{
+ if (!force)
+ {
+ CDebugDump dd;
+ dd.Open(pActionDir);
+ bool bt_exists = dd.Exist("sosreport.tar.bz2") || dd.Exist("sosreport.tar.xz");
+ if (bt_exists)
+ {
+ VERB3 log("%s already exists, not regenerating", "sosreport.tar.bz2");
+ return;
+ }
+ }
+
+ static const char command_default[] =
+ "cd -- '%s' || exit 1;"
+ "nice sosreport --tmp-dir . --batch"
+ " --only=anaconda --only=bootloader"
+ " --only=devicemapper --only=filesys --only=hardware --only=kernel"
+ " --only=libraries --only=memory --only=networking --only=nfsserver"
+ " --only=pam --only=process --only=rpm -k rpm.rpmva=off --only=ssh"
+ " --only=startup --only=yum 2>&1;"
+ "rm sosreport*.md5 2>/dev/null;"
+ "mv sosreport*.tar.bz2 sosreport.tar.bz2 2>/dev/null;"
+ "mv sosreport*.tar.xz sosreport.tar.xz 2>/dev/null;"
+ ;
+ static const char command_prefix[] =
+ "cd -- '%s' || exit 1;"
+ "nice sosreport --tmp-dir . --batch %s 2>&1;"
+ "rm sosreport*.md5 2>/dev/null;"
+ "mv sosreport*.tar.bz2 sosreport.tar.bz2 2>/dev/null;"
+ "mv sosreport*.tar.xz sosreport.tar.xz 2>/dev/null;"
+ ;
+ string command;
+
+ vector_string_t args;
+ parse_args(pArgs, args, '"');
+
+ if (args.size() == 0 || args[0] == "")
+ {
+ command = ssprintf(command_default, pActionDir);
+ }
+ else
+ {
+ command = ssprintf(command_prefix, pActionDir, args[0].c_str());
+ }
+
+ update_client(_("Running sosreport: %s"), command.c_str());
+ string output = command;
+ output += '\n';
+ char *command_out = run_in_shell_and_save_output(/*flags:*/ 0, command.c_str(), /*dir:*/ NULL, /*size_p:*/ NULL);
+ output += command_out;
+ update_client(_("Finished running sosreport"));
+ VERB3 log("sosreport output:'%s'", output.c_str());
+
+// Not needed: now we use "sosreport --tmp-dir DUMPDIR"
+#if 0
+ // Parse:
+ // "Your sosreport has been generated and saved in:
+ // /tmp/sosreport-XXXX.tar.bz2"
+ // Note: ParseFilename modifies its parameter and returns pointer
+ // which points somewhere inside it.
+ char *sosreport_filename = xstrdup(ParseFilename(command_out));
+ free(command_out);
+ if (!sosreport_filename)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't find filename in sosreport output");
+ }
+
+ string sosreport_dd_filename = concat_path_file(pActionDir, "sosreport.tar");
+ char *ext = strrchr(sosreport_filename, '.');
+ if (ext && strcmp(ext, ".tar") != 0)
+ {
+ // Assuming it's .bz2, .gz or some such
+ sosreport_dd_filename += ext;
+ }
+ CDebugDump dd;
+ dd.Open(pActionDir);
+ //Not useful: dd.SaveText("sosreportoutput", output);
+ off_t sz = copy_file(sosreport_filename, sosreport_dd_filename.c_str(), 0644);
+
+ // don't want to leave sosreport-XXXX.tar.bz2 in /tmp
+ unlink(sosreport_filename);
+ // sosreport-XXXX.tar.bz2.md5 too
+ unsigned len = strlen(sosreport_filename);
+ sosreport_filename = (char*)xrealloc(sosreport_filename, len + sizeof(".md5")-1 + 1);
+ strcpy(sosreport_filename + len, ".md5");
+ unlink(sosreport_filename);
+
+ if (sz < 0)
+ {
+ dd.Close();
+ CABRTException e(EXCEP_PLUGIN,
+ "Can't copy '%s' to '%s'",
+ sosreport_filename,
+ sosreport_dd_filename.c_str()
+ );
+ free(sosreport_filename);
+ throw e;
+ }
+ free(sosreport_filename);
+#endif
+}
+
+PLUGIN_INFO(ACTION,
+ CActionSOSreport,
+ "SOSreport",
+ "0.0.2",
+ _("Runs sosreport, saves the output"),
+ "gavin@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/SOSreport.h b/lib/plugins/SOSreport.h
new file mode 100644
index 00000000..4b32940f
--- /dev/null
+++ b/lib/plugins/SOSreport.h
@@ -0,0 +1,31 @@
+/*
+ SOSreport.h - Attach an sosreport to a crash dump
+
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef SOSREPORT_H_
+#define SOSREPORT_H_
+
+#include "action.h"
+
+class CActionSOSreport : public CAction
+{
+ public:
+ virtual void Run(const char *pActionDir, const char *pArgs, int force);
+};
+
+#endif
diff --git a/lib/plugins/SQLite3.conf b/lib/plugins/SQLite3.conf
new file mode 100644
index 00000000..a7617a90
--- /dev/null
+++ b/lib/plugins/SQLite3.conf
@@ -0,0 +1,4 @@
+# Configuration file for database plugin SQLite3
+
+# DB path
+DBPath = /var/spool/abrt/abrt-db
diff --git a/lib/plugins/SQLite3.cpp b/lib/plugins/SQLite3.cpp
new file mode 100644
index 00000000..322f15a1
--- /dev/null
+++ b/lib/plugins/SQLite3.cpp
@@ -0,0 +1,681 @@
+/*
+ SQLite3.cpp
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#include <sqlite3.h>
+#include "abrtlib.h"
+#include "SQLite3.h"
+#include "abrt_exception.h"
+
+using namespace std;
+
+#define ABRT_TABLE_VERSION 4
+#define ABRT_TABLE_VERSION_STR "4"
+#define ABRT_TABLE "abrt_v"ABRT_TABLE_VERSION_STR
+#define ABRT_REPRESULT_TABLE "abrt_v"ABRT_TABLE_VERSION_STR"_reportresult"
+#define SQLITE3_MASTER_TABLE "sqlite_master"
+
+#define COL_UID "UID"
+#define COL_UUID "UUID"
+#define COL_INFORMALL "InformAll"
+#define COL_DEBUG_DUMP_PATH "DebugDumpPath"
+#define COL_COUNT "Count"
+#define COL_REPORTED "Reported"
+#define COL_TIME "Time"
+#define COL_MESSAGE "Message"
+
+#define COL_REPORTER "Reporter"
+
+/* Is this string safe wrt SQL injection?
+ * PHP's mysql_real_escape_string() treats \, ', ", \x00, \n, \r, and \x1a as special.
+ * We are a bit more paranoid and disallow any control chars.
+ */
+static bool is_string_safe(const char *str)
+{
+// Apparently SQLite allows unescaped newlines. More surprisingly,
+// it does not unescape escaped ones - I see lines ending with \ when I do it.
+// I wonder whether this is a bug in SQLite, and whether using unescaped
+// newlines is a danger with other SQL servers.
+// For now, I disabled newline escaping, and also allowed double quote.
+ const char *p = str;
+ while (*p)
+ {
+ unsigned char c = *p;
+// if (c == '\\' && p[1] != '\0')
+// {
+// p += 2;
+// continue;
+// }
+ if ((c < ' ' && c != '\n')
+ || strchr("\\\'", c) //was: "\\\"\'"
+ ) {
+ error_msg("Probable SQL injection: '%s'", str);
+ return false;
+ }
+ p++;
+ }
+ return true;
+}
+
+#ifdef UNUSED_FOR_NOW
+/* Escape \n */
+static string sql_escape(const char *str)
+{
+ const char *s = str;
+ unsigned len = 0;
+ do
+ {
+ if (*s == '\n')
+ len++;
+ len++;
+ } while (*s++);
+
+ char buf[len];
+ s = str;
+ char *d = buf;
+ do
+ {
+ if (*s == '\n')
+ *d++ = '\\';
+ *d++ = *s;
+ } while (*s++);
+
+ return buf;
+}
+#endif
+
+/* Note:
+ * expects "SELECT * FROM ...", not "SELECT <only some fields> FROM ..."
+ */
+static void get_table(vector_database_rows_t& pTable,
+ sqlite3 *db, const char *fmt, ...)
+{
+ va_list p;
+ va_start(p, fmt);
+ char *sql = xvasprintf(fmt, p);
+ va_end(p);
+
+ char **table;
+ int ncol, nrow;
+ char *err = NULL;
+ int ret = sqlite3_get_table(db, sql, &table, &nrow, &ncol, &err);
+ if (ret != SQLITE_OK)
+ {
+ string errstr = ssprintf("Error in SQL:'%s' error: %s", sql, err);
+ free(sql);
+ sqlite3_free(err);
+ throw CABRTException(EXCEP_PLUGIN, errstr.c_str());
+ }
+ VERB2 log("%d rows returned by SQL:%s", nrow, sql);
+ free(sql);
+
+ pTable.clear();
+ int ii;
+ for (ii = 0; ii < nrow; ii++)
+ {
+ int jj;
+ database_row_t row;
+ for (jj = 0; jj < ncol; jj++)
+ {
+ char *val = table[jj + (ncol*ii) + ncol];
+ switch (jj)
+ {
+ case 0: row.m_sUUID = val; break;
+ case 1: row.m_sUID = val; break;
+ case 2: row.m_sInformAll = val; break;
+ case 3: row.m_sDebugDumpDir = val; break;
+ case 4: row.m_sCount = val; break;
+ case 5: row.m_sReported = val; break;
+ case 6: row.m_sTime = val; break;
+ case 7: row.m_sMessage = val; break;
+ }
+ }
+ pTable.push_back(row);
+
+ }
+ sqlite3_free_table(table);
+}
+
+static int execute_sql(sqlite3 *db, const char *fmt, ...)
+{
+ va_list p;
+ va_start(p, fmt);
+ char *sql = xvasprintf(fmt, p);
+ va_end(p);
+
+ char *err = NULL;
+ int ret = sqlite3_exec(db, sql, /*callback:*/ NULL, /*callback param:*/ NULL, &err);
+ if (ret != SQLITE_OK)
+ {
+ string errstr = ssprintf("Error in SQL:'%s' error: %s", sql, err);
+ free(sql);
+ sqlite3_free(err);
+ throw CABRTException(EXCEP_PLUGIN, errstr.c_str());
+ }
+ int affected = sqlite3_changes(db);
+ VERB2 log("%d rows affected by SQL:%s", affected, sql);
+ free(sql);
+
+ return affected;
+}
+
+static bool exists_uuid_uid(sqlite3 *db, const char *UUID, const char *UID)
+{
+ vector_database_rows_t table;
+ get_table(table, db,
+ "SELECT * FROM "ABRT_TABLE
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ UUID, UID
+ );
+ return !table.empty();
+}
+
+static void update_from_old_ver(sqlite3 *db, int old_version)
+{
+ static const char *const update_sql_commands[] = {
+ // v0 -> v1
+ NULL,
+ // v1 -> v2
+ "BEGIN TRANSACTION;"
+ "CREATE TABLE abrt_v2 ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL,"
+ COL_COUNT" INT NOT NULL DEFAULT 1,"
+ COL_REPORTED" INT NOT NULL DEFAULT 0,"
+ COL_TIME" VARCHAR NOT NULL DEFAULT 0,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID"));"
+ "INSERT INTO abrt_v2 "
+ "SELECT "COL_UUID","
+ COL_UID","
+ COL_DEBUG_DUMP_PATH","
+ COL_COUNT","
+ COL_REPORTED","
+ COL_TIME","
+ COL_MESSAGE
+ " FROM abrt;"
+ "DROP TABLE abrt;"
+ "COMMIT;",
+ // v2 -> v3
+ "BEGIN TRANSACTION;"
+ "CREATE TABLE abrt_v3 ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL,"
+ COL_COUNT" INT NOT NULL DEFAULT 1,"
+ COL_REPORTED" INT NOT NULL DEFAULT 0,"
+ COL_TIME" VARCHAR NOT NULL DEFAULT 0,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID"));"
+ "INSERT INTO abrt_v3 "
+ "SELECT "COL_UUID","
+ COL_UID","
+ COL_DEBUG_DUMP_PATH","
+ COL_COUNT","
+ COL_REPORTED","
+ COL_TIME","
+ COL_MESSAGE
+ " FROM abrt_v2;"
+ "DROP TABLE abrt_v2;"
+ "CREATE TABLE abrt_v3_reportresult ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_REPORTER" VARCHAR NOT NULL,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));"
+ "COMMIT;",
+ // v3-> v4
+ "BEGIN TRANSACTION;"
+ "CREATE TABLE abrt_v4("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_INFORMALL" INT NOT NULL DEFAULT 0,"
+ COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL,"
+ COL_COUNT" INT NOT NULL DEFAULT 1,"
+ COL_REPORTED" INT NOT NULL DEFAULT 0,"
+ COL_TIME" VARCHAR NOT NULL DEFAULT 0,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID"));"
+ "INSERT INTO abrt_v4 "
+ "SELECT "COL_UUID","
+ COL_UID","
+ "0," /* COL_INFORMALL */
+ COL_DEBUG_DUMP_PATH","
+ COL_COUNT","
+ COL_REPORTED","
+ COL_TIME","
+ COL_MESSAGE
+ " FROM abrt_v3;"
+ "DROP TABLE abrt_v3;"
+ "UPDATE abrt_v4"
+ " SET "COL_UID"='0', "COL_INFORMALL"=1"
+ " WHERE "COL_UID"='-1';"
+ "CREATE TABLE abrt_v4_reportresult ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_REPORTER" VARCHAR NOT NULL,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));"
+ "INSERT INTO abrt_v4_reportresult "
+ "SELECT * FROM abrt_v3_reportresult;"
+ "DROP TABLE abrt_v3_reportresult;"
+ "COMMIT;",
+ };
+
+ while (old_version < ABRT_TABLE_VERSION)
+ {
+ execute_sql(db, update_sql_commands[old_version]);
+ old_version++;
+ }
+}
+
+static bool check_table(sqlite3 *db)
+{
+ const char *command = "SELECT NAME FROM "SQLITE3_MASTER_TABLE" "
+ "WHERE TYPE='table' AND NAME like 'abrt_v%';";
+ char **table;
+ int ncol, nrow;
+ char *err;
+ int ret = sqlite3_get_table(db, command, &table, &nrow, &ncol, &err);
+ if (ret != SQLITE_OK)
+ {
+ /* Should never happen */
+ error_msg_and_die("SQLite3 database is corrupted");
+ }
+ if (!nrow)
+ {
+ sqlite3_free_table(table);
+ return false;
+ }
+
+ // table format:
+ // table[0]:"NAME" // table[1]:"SQL" <== field names from SELECT
+ // table[2]:"abrt_vNN" // table[3]:"sql"
+ char *tableName = table[0 + ncol];
+ char *underscore = strchr(tableName, '_');
+ if (underscore)
+ {
+ // It can be "abrt_vNN_something", thus using atoi(), not xatoi()
+ int tableVersion = atoi(underscore + 2);
+ sqlite3_free_table(table);
+ if (tableVersion < ABRT_TABLE_VERSION)
+ {
+ update_from_old_ver(db, tableVersion);
+ }
+ return true;
+ }
+ sqlite3_free_table(table);
+ update_from_old_ver(db, 1);
+ return true;
+}
+
+
+CSQLite3::CSQLite3() :
+ m_sDBPath(LOCALSTATEDIR "/spool/abrt/abrt-db"),
+ m_pDB(NULL)
+{}
+
+CSQLite3::~CSQLite3()
+{
+ /* Paranoia. In C++, destructor will abort() if it was called while unwinding
+ * the stack and it throws an exception.
+ */
+ try
+ {
+ DisConnect();
+ m_sDBPath.clear();
+ }
+ catch (...)
+ {
+ error_msg_and_die("Internal error");
+ }
+}
+
+void CSQLite3::DisConnect()
+{
+ if (m_pDB)
+ {
+ sqlite3_close(m_pDB);
+ m_pDB = NULL;
+ }
+}
+
+void CSQLite3::Connect()
+{
+ int ret = sqlite3_open_v2(m_sDBPath.c_str(),
+ &m_pDB,
+ SQLITE_OPEN_READWRITE,
+ NULL
+ );
+
+ if (ret != SQLITE_OK)
+ {
+ if (ret != SQLITE_CANTOPEN)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't open database '%s': %s", m_sDBPath.c_str(), sqlite3_errmsg(m_pDB));
+ }
+
+ ret = sqlite3_open_v2(m_sDBPath.c_str(),
+ &m_pDB,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+ NULL
+ );
+ if (ret != SQLITE_OK)
+ {
+ throw CABRTException(EXCEP_PLUGIN, "Can't create database '%s': %s", m_sDBPath.c_str(), sqlite3_errmsg(m_pDB));
+ }
+ }
+
+ if (!check_table(m_pDB))
+ {
+ execute_sql(m_pDB,
+ "CREATE TABLE "ABRT_TABLE" ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_INFORMALL" INT NOT NULL DEFAULT 0,"
+ COL_DEBUG_DUMP_PATH" VARCHAR NOT NULL,"
+ COL_COUNT" INT NOT NULL DEFAULT 1,"
+ COL_REPORTED" INT NOT NULL DEFAULT 0,"
+ COL_TIME" VARCHAR NOT NULL DEFAULT 0,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID"));"
+ );
+ execute_sql(m_pDB,
+ "CREATE TABLE "ABRT_REPRESULT_TABLE" ("
+ COL_UUID" VARCHAR NOT NULL,"
+ COL_UID" VARCHAR NOT NULL,"
+ COL_REPORTER" VARCHAR NOT NULL,"
+ COL_MESSAGE" VARCHAR NOT NULL DEFAULT '',"
+ "PRIMARY KEY ("COL_UUID","COL_UID","COL_REPORTER"));"
+ );
+ }
+}
+
+void CSQLite3::Insert_or_Update(const char *crash_id,
+ bool inform_all_users,
+ const char *pDebugDumpPath,
+ const char *pTime)
+{
+ const char *UUID = strchr(crash_id, ':');
+ if (!UUID
+ || !is_string_safe(crash_id)
+ || !is_string_safe(pDebugDumpPath)
+ || !is_string_safe(pTime)
+ ) {
+ return;
+ }
+
+ /* Split crash_id into UID:UUID */
+ unsigned uid_len = UUID - crash_id;
+ UUID++;
+ char UID[uid_len + 1];
+ strncpy(UID, crash_id, uid_len);
+ UID[uid_len] = '\0';
+
+ if (!exists_uuid_uid(m_pDB, UUID, UID))
+ {
+ execute_sql(m_pDB,
+ "INSERT INTO "ABRT_TABLE" ("
+ COL_UUID","
+ COL_UID","
+ COL_INFORMALL","
+ COL_DEBUG_DUMP_PATH","
+ COL_TIME
+ ")"
+ " VALUES ('%s','%s',%u,'%s','%s');",
+ UUID, UID, (unsigned)inform_all_users, pDebugDumpPath, pTime
+ );
+ }
+ else
+ {
+ execute_sql(m_pDB,
+ "UPDATE "ABRT_TABLE
+ " SET "COL_COUNT"="COL_COUNT"+1,"COL_TIME"='%s'"
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ pTime,
+ UUID, UID
+ );
+ }
+}
+
+void CSQLite3::DeleteRow(const char *crash_id)
+{
+ const char *UUID = strchr(crash_id, ':');
+ if (!UUID
+ || !is_string_safe(crash_id)
+ ) {
+ return;
+ }
+
+ /* Split crash_id into UID:UUID */
+ unsigned uid_len = UUID - crash_id;
+ UUID++;
+ char UID[uid_len + 1];
+ strncpy(UID, crash_id, uid_len);
+ UID[uid_len] = '\0';
+
+ if (exists_uuid_uid(m_pDB, UUID, UID))
+ {
+ execute_sql(m_pDB, "DELETE FROM "ABRT_TABLE
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ UUID, UID
+ );
+ execute_sql(m_pDB, "DELETE FROM "ABRT_REPRESULT_TABLE
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ UUID, UID
+ );
+ }
+ else
+ {
+ error_msg("crash_id %s is not found in DB", crash_id);
+ }
+}
+
+void CSQLite3::DeleteRows_by_dir(const char *dump_dir)
+{
+ if (!is_string_safe(dump_dir))
+ {
+ return;
+ }
+
+ /* Get UID:UUID pair(s) to delete */
+ vector_database_rows_t table;
+ get_table(table, m_pDB,
+ "SELECT * FROM "ABRT_TABLE
+ " WHERE "COL_DEBUG_DUMP_PATH"='%s';",
+ dump_dir
+ );
+ if (table.empty())
+ {
+ return;
+ }
+
+ /* Delete from both tables */
+ vector_database_rows_t::iterator it = table.begin();
+ while (it != table.end())
+ {
+ execute_sql(m_pDB,
+ "DELETE FROM "ABRT_REPRESULT_TABLE
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ it->m_sUUID.c_str(), it->m_sUID.c_str()
+ );
+ it++;
+ }
+ execute_sql(m_pDB,
+ "DELETE FROM "ABRT_TABLE
+ " WHERE "COL_DEBUG_DUMP_PATH"='%s'",
+ dump_dir
+ );
+}
+
+void CSQLite3::SetReported(const char *crash_id, const char *pMessage)
+{
+ const char *UUID = strchr(crash_id, ':');
+ if (!UUID
+ || !is_string_safe(crash_id)
+ || !is_string_safe(pMessage)
+ ) {
+ return;
+ }
+
+ /* Split crash_id into UID:UUID */
+ unsigned uid_len = UUID - crash_id;
+ UUID++;
+ char UID[uid_len + 1];
+ strncpy(UID, crash_id, uid_len);
+ UID[uid_len] = '\0';
+
+ if (exists_uuid_uid(m_pDB, UUID, UID))
+ {
+ execute_sql(m_pDB,
+ "UPDATE "ABRT_TABLE
+ " SET "COL_REPORTED"=1"
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ UUID, UID
+ );
+ execute_sql(m_pDB,
+ "UPDATE "ABRT_TABLE
+ " SET "COL_MESSAGE"='%s'"
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ pMessage, UUID, UID
+ );
+ }
+ else
+ {
+ error_msg("crash_id %s is not found in DB", crash_id);
+ }
+}
+
+void CSQLite3::SetReportedPerReporter(const char *crash_id,
+ const char *reporter,
+ const char *pMessage)
+{
+ const char *UUID = strchr(crash_id, ':');
+ if (!UUID
+ || !is_string_safe(crash_id)
+ || !is_string_safe(reporter)
+ || !is_string_safe(pMessage)
+ ) {
+ return;
+ }
+
+ /* Split crash_id into UID:UUID */
+ unsigned uid_len = UUID - crash_id;
+ UUID++;
+ char UID[uid_len + 1];
+ strncpy(UID, crash_id, uid_len);
+ UID[uid_len] = '\0';
+
+ int affected_rows = execute_sql(m_pDB,
+ "UPDATE "ABRT_REPRESULT_TABLE
+ " SET "COL_MESSAGE"='%s'"
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s' AND "COL_REPORTER"='%s'",
+ pMessage,
+ UUID, UID, reporter
+ );
+ if (!affected_rows)
+ {
+ execute_sql(m_pDB,
+ "INSERT INTO "ABRT_REPRESULT_TABLE
+ " ("COL_UUID","COL_UID","COL_REPORTER","COL_MESSAGE")"
+ " VALUES ('%s','%s','%s','%s');",
+ UUID, UID, reporter, pMessage
+ );
+ }
+}
+
+vector_database_rows_t CSQLite3::GetUIDData(long caller_uid)
+{
+ vector_database_rows_t table;
+
+ if (caller_uid == 0)
+ {
+ get_table(table, m_pDB, "SELECT * FROM "ABRT_TABLE";");
+ }
+ else
+ {
+ get_table(table, m_pDB,
+ "SELECT * FROM "ABRT_TABLE
+ " WHERE "COL_UID"='%ld' OR "COL_INFORMALL"=1;",
+ caller_uid
+ );
+ }
+ return table;
+}
+
+database_row_t CSQLite3::GetRow(const char *crash_id)
+{
+ const char *UUID = strchr(crash_id, ':');
+ if (!UUID
+ || !is_string_safe(crash_id)
+ ) {
+ return database_row_t();
+ }
+
+ /* Split crash_id into UID:UUID */
+ unsigned uid_len = UUID - crash_id;
+ UUID++;
+ char UID[uid_len + 1];
+ strncpy(UID, crash_id, uid_len);
+ UID[uid_len] = '\0';
+
+ vector_database_rows_t table;
+ get_table(table, m_pDB,
+ "SELECT * FROM "ABRT_TABLE
+ " WHERE "COL_UUID"='%s' AND "COL_UID"='%s';",
+ UUID, UID
+ );
+
+ if (table.size() == 0)
+ {
+ return database_row_t();
+ }
+ return table[0];
+}
+
+void CSQLite3::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("DBPath");
+ if (it != end)
+ {
+ m_sDBPath = it->second;
+ }
+}
+
+//ok to delete?
+//const map_plugin_settings_t& CSQLite3::GetSettings()
+//{
+// m_pSettings["DBPath"] = m_sDBPath;
+//
+// return m_pSettings;
+//}
+
+PLUGIN_INFO(DATABASE,
+ CSQLite3,
+ "SQLite3",
+ "0.0.2",
+ _("Keeps SQLite3 database about all crashes"),
+ "zprikryl@redhat.com,jmoskovc@redhat.com",
+ "https://fedorahosted.org/abrt/wiki",
+ "");
diff --git a/lib/plugins/SQLite3.h b/lib/plugins/SQLite3.h
new file mode 100644
index 00000000..d2af864c
--- /dev/null
+++ b/lib/plugins/SQLite3.h
@@ -0,0 +1,56 @@
+/*
+ SQLite3.h
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#ifndef SQLITE3_H_
+#define SQLITE3_H_
+
+#include "plugin.h"
+#include "database.h"
+
+class CSQLite3 : public CDatabase
+{
+ private:
+ std::string m_sDBPath;
+ sqlite3* m_pDB;
+
+ public:
+ CSQLite3();
+ ~CSQLite3();
+
+ virtual void Connect();
+ virtual void DisConnect();
+
+ virtual void Insert_or_Update(const char *crash_id,
+ bool inform_all_users,
+ const char *pDebugDumpPath,
+ const char *pTime);
+ virtual void DeleteRow(const char *crash_id);
+ virtual void DeleteRows_by_dir(const char *dump_dir);
+ virtual void SetReported(const char *crash_id, const char *pMessage);
+ virtual void SetReportedPerReporter(const char *crash_id,
+ const char *reporter,
+ const char *pMessage);
+ virtual vector_database_rows_t GetUIDData(long caller_uid);
+ virtual database_row_t GetRow(const char *crash_id);
+
+ virtual void SetSettings(const map_plugin_settings_t& pSettings);
+};
+
+#endif
diff --git a/lib/plugins/abrt-Bugzilla.7 b/lib/plugins/abrt-Bugzilla.7
new file mode 100644
index 00000000..99bb60d1
--- /dev/null
+++ b/lib/plugins/abrt-Bugzilla.7
@@ -0,0 +1,43 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+Bugzilla plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIBugzilla\fP plugin
+for \fIabrt\fP.
+.P
+This plugin is used to report the crash to a Bugzilla instance. The
+plugin will determine the package name and distribution version. The
+crash data is attached to the bug report.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+No parameters are necessary.
+.SH CONFIGURATION
+The \fIBugzilla.conf\fP configuration file contains several
+entries in the format "Option = Value". The options are:
+.SS BugzillaURL
+The URL of the Bugzilla instance that you want to use, including the
+path to the xmlrpc. The default is https://bugzilla.redhat.com/xmlrpc.cgi
+.SS Login
+Your Bugzilla login. If you have no Bugzilla account, you cannot
+use the plugin.
+.SS Password
+Your Bugzilla password.
+.SH EXAMPLES
+.P
+This is a snippet from the \fIabrt.conf\fP configuration file.
+When something crashes, use the Bugzilla plugin:
+.P
+[common]
+.br
+ActionsAndReporters = Bugzilla
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Zdenek Prikryl <zprikryl@redhat.com>.
+Manual page written by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-FileTransfer.7 b/lib/plugins/abrt-FileTransfer.7
new file mode 100644
index 00000000..a721dd81
--- /dev/null
+++ b/lib/plugins/abrt-FileTransfer.7
@@ -0,0 +1,72 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+FileTransfer plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIFileTransfer\fP plugin
+for \fIabrt\fP.
+.P
+This plugin is used to transfer the crash report to another
+machine using a file transfer protocol. The protocols supported
+are FTP, FTPS, HTTP, HTTPS, SCP, SFTP, and TFTP.
+.SH INVOCATION
+.P
+The plugin is invoked in the \fIabrt.conf\fP file, usually in the
+\fIActionsAndReporters\fP option and/or the \fI[cron]\fP section.
+There are two modes of invocation:
+.P
+* Specify \fIFileTransfer(one)\fP in ActionsAndReporters directive.
+Immediately after crash is detected, the plugin transfers crash data
+to the server specified in the \fIFileTransfer.conf\fP configuration file.
+.P
+* Specify \fIFileTransfer(store)\fP in ActionsAndReporters directive
+and add \fIHH:MM = FileTransfer\fP line in [cron] section.
+At the time of the crash,
+the plugin stores a record of it in its internal list.
+When specified time is reached, the plugin iterates through
+its internal list and sends every recorded crash to the specified URL.
+After that, the internal list is cleared.
+.SH CONFIGURATION
+The \fIFileTransfer.conf\fP configuration file contains
+several entries in the format "Option = Value". The options are:
+.SS URL
+The URL of the server, where the crash should
+be transfered, specifying the protocol, the path,
+the user name and the password, for example:
+.br
+URL = ftp://user:passwd@server.com/path
+.SS ArchiveType
+The type of the archive in which to pack the crash data.
+Currently, \fI.tar\fP, \fI.tar.gz\fP, \fI.tar.bz2\fP and \fI.zip\fP
+are supported. The default is \fI.tar.gz\fP
+.SS RetryCount
+This specifies how many times the plugin will try to resend
+the file if the transfer was not succesful. The plugin
+waits a while before it retries the transfer: see \fIRetryDelay\fP.
+The default is 3
+.SS RetryDelay
+If the transfer was not succesful, the plugin will
+wait some time before sending the file again. This configuration
+option specifies the time in seconds. The default is 20.
+.SH EXAMPLES
+.P
+Typical configuration in \fIabrt.conf\fP. The crash is stored
+each time it happens and at midnight, all the crash data
+is transferred to a central server.
+.P
+[common]
+.br
+ActionsAndReporters = FileTransfer(store)
+.br
+[cron]
+.br
+00:00 = FileTransfer
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-KerneloopsReporter.7 b/lib/plugins/abrt-KerneloopsReporter.7
new file mode 100644
index 00000000..98bd3874
--- /dev/null
+++ b/lib/plugins/abrt-KerneloopsReporter.7
@@ -0,0 +1,40 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+KerneloopsReporter plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIKerneloopsReporter\fP
+plugin for \fIabrt\fP.
+.P
+This plugin is used to report the crash to the Kerneloops tracker.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+No parameters are necessary.
+.SH CONFIGURATION
+The \fIKerneloopsReporter.conf\fP configuration file contains one entry:
+.SS SubmitURL
+The URL of the kerneloops tracker, the default is
+.br
+SubmitURL = http://submit.kerneloops.org/submitoops.php
+.SH EXAMPLES
+.P
+This is a snippet from the \fIabrt.conf\fP configuration file.
+Each time a kernel oops is detected, run KerneloopsReporter:
+.P
+[common]
+.br
+ActionsAndReporters = Kerneloops, KerneloopsReporter
+.br
+[AnalyzerActionsAndReporters]
+.br
+Kerneloops = KerneloopsReporter
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Anton Arapov <anton@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-KerneloopsScanner.7 b/lib/plugins/abrt-KerneloopsScanner.7
new file mode 100644
index 00000000..ff094847
--- /dev/null
+++ b/lib/plugins/abrt-KerneloopsScanner.7
@@ -0,0 +1,46 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+KerneloopsScanner plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIKerneloopsScanner\fP
+plugin for \fIabrt\fP.
+.P
+This plugin reads the system log file (default /var/log/messages)
+and stores the kernel oops crashes, which were not already
+reported, to abrt's debug dump directory.
+.P
+To distinguish between new crashes and crashes
+that were already reported, the plugin makes its own entry
+in the log file, which acts as a separator.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+No parameters are necessary.
+.SH CONFIGURATION
+The \fIKerneloopsScanner.conf\fP configuration file contains one entry:
+.SS SysLogFile
+The file to scan. The default is
+.br
+SysLogFile = /var/log/messages
+.SH EXAMPLES
+.P
+This is a snippet from the \fIabrt.conf\fP configuration file.
+Every 10 seconds look if there were any kernel crashes:
+.P
+[common]
+.br
+ActionsAndReporters = Kerneloops, KerneloopsScanner
+.br
+[cron]
+.br
+10 = KerneloopsScanner
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Anton Arapov <anton@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-Logger.7 b/lib/plugins/abrt-Logger.7
new file mode 100644
index 00000000..8ae679f8
--- /dev/null
+++ b/lib/plugins/abrt-Logger.7
@@ -0,0 +1,44 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+Logger plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fILogger\fP plugin
+for \fIabrt\fP.
+.P
+This plugin is used to log the crash to a file.
+.P
+The log will contain all the file names as well as their
+content. It also contains "duplicity check": the ID
+of the crash, which is used to tell whether the same
+crash has happened previously.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+No parameters are necessary.
+.SH CONFIGURATION
+The \fILogger.conf\fP configuration file contains
+several entries in a format "Option = Value". The options are:
+.SS LogPath
+The path to the log file.
+.SS AppendLogs
+If set to "yes" (the default) \fILogger\fP will append
+the report to the file, otherwise it will overwrite the file (so
+only the last crash will be stored).
+.SH EXAMPLES
+.P
+This is a snippet from the \fIabrt.conf\fP configuration file.
+Log all the C/C++ application crashes:
+.P
+[AnalyzerActionsAndReporters]
+.br
+CCpp = Logger
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-Mailx.7 b/lib/plugins/abrt-Mailx.7
new file mode 100644
index 00000000..90a8bbce
--- /dev/null
+++ b/lib/plugins/abrt-Mailx.7
@@ -0,0 +1,57 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+Mailx plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIMailx\fP plugin
+for \fIabrt\fP.
+.P
+This plugin is used to mail the data about the crash
+to a specified mail address.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file. It can take
+one parameter, a subject of the mail (if it differs from the
+one specified in the \fIMailx.conf\fP configuration file).
+.SH CONFIGURATION
+The \fIMailx.conf\fP configuration file contains
+several entries in a format "Option = Value". The options are:
+.SS Subject
+The subject of the mail.
+.SS Parameters
+The \fIMailx\fP plugin executes the external "mailx" command to
+send the mail. This option defines some additional command line
+parameters, which should be added to the program invocation, if any.
+.SS EmailFrom
+The address from which the email is sent.
+.SS EmailTo
+The address to which the email is sent.
+.SS SendBinaryData
+Can be "yes" or "no". If set to "yes", the email will also
+contain the binary files associated with the crash. Warning:
+this can cause the emails to be large! (several MB)
+.SH EXAMPLES
+.P
+These are snippets from the \fIabrt.conf\fP configuration file.
+.P
+1) Each time a crash happens, a mail is sent
+.PP
+[common]
+.br
+ActionsAndReporters = Mailx("[abrt] a crash occurs")
+.P
+2) When a program in a specific package (in this case "httpd") crashes,
+send a mail about it.
+.PP
+[AnalyzerActionsAndReporters]
+.br
+CCpp:httpd = Mailx("[abrt] Apache crash")
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-ReportUploader.7 b/lib/plugins/abrt-ReportUploader.7
new file mode 100644
index 00000000..bd91f266
--- /dev/null
+++ b/lib/plugins/abrt-ReportUploader.7
@@ -0,0 +1,55 @@
+.TH abrt "7" "9 July 2009" ""
+.SH NAME
+ReportUploader plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon which watches for application crashes. When a crash occurs,
+it collects the crash data and performs some actions according to
+the configuration. This manual page describes the \fIReportUploader\fP plugin
+for \fIabrt\fP.
+.P
+This plugin will send a report to an anonymous FTP site. It's intended
+for use in cases where a ticketing system is associated with the FTP site,
+but the ticketing system has no way to automatically create new tickets,
+or add to existing tickets. Customer Name is put in config file.
+Ticket name (or number) is also put in config file. If no ticket
+name is configured, assume ticketing system should create a new ticket.
+This information is added to the report, the report is copied into a
+compressed, optionally encrypted, tarball. Then the tarball is FTP'd
+to the upload site. Then a status string is displayed to the user
+showing the name of the file on the FTP site, it's MD5SUM, and
+it's encryption key. This information can be pasted into a ticket
+in the ticketing system.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+No parameters are necessary.
+.SH CONFIGURATION
+The \fIReportUploader.conf\fP configuration file contains
+entries in a format "Option = Value". The options are:
+.SS Customer
+This is the customer's name or other customer identifier.
+.SS Ticket
+This is the ticket name or number.
+.SS Encrypt
+"yes" for encrypt upload, anything else for not.
+.SS Upload
+"yes" for for upload to FTP site, anything else for copy to local /tmp.
+.SS URL
+URL of upload site (ie. ftp://support.com/upload).
+.SH EXAMPLES
+.P
+This is a snippet from the \fIabrt.conf\fP configuration file.
+Log all the C/C++ application crashes:
+.P
+[AnalyzerActionsAndReporters]
+.br
+CCpp = RHUpload
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Plugin and man page by Gavin Romig-Koch <gavin@redhat.com>.
+
+
diff --git a/lib/plugins/abrt-RunApp.7 b/lib/plugins/abrt-RunApp.7
new file mode 100644
index 00000000..56a8d2b0
--- /dev/null
+++ b/lib/plugins/abrt-RunApp.7
@@ -0,0 +1,43 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+RunApp plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fIRunApp\fP plugin
+for \fIabrt\fP.
+.P
+This plugin is used to run a specified application when the crash occurs.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file.
+The first parameter is the command to run. The second, optional
+parameter specifies an output file, to which the standard
+output of the program is saved.
+.SH CONFIGURATION
+There is no configuration file, the plugin parameters are
+sufficient.
+.SH EXAMPLES
+.P
+These are snippets from the \fIabrt.conf\fP configuration file.
+.P
+1) Each time something crashes, print (and save in a text file)
+which processes are running on the system.
+.PP
+[common]
+.br
+ActionsAndReporters = RunApp("ps ax","processes.txt")
+.P
+2) When proftpd crashes, restart it.
+.PP
+[AnalyzerActionsAndReporters]
+.br
+CCpp:proftpd = RunApp("/etc/init.d/proftpd restart")
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Zdenek Prikryl <zprikryl@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-SQLite3.7 b/lib/plugins/abrt-SQLite3.7
new file mode 100644
index 00000000..c2b39d86
--- /dev/null
+++ b/lib/plugins/abrt-SQLite3.7
@@ -0,0 +1,36 @@
+.TH abrt "7" "1 Jun 2009" ""
+.SH NAME
+SQLite3 database plugin for abrt(8)
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data and takes action according to
+its configuration. This manual page describes the \fISQLite3\fP database plugin
+for \fIabrt\fP.
+.P
+This is a database plugin: \fIabrt\fP needs a database in which to store
+its metadata. You can choose one by specifying "Database" in
+the \fIabrt.conf\fP configuration file. Currently SQLite3 is
+the only choice supported.
+.SH INVOCATION
+The plugin is invoked in the \fIabrt.conf\fP configuration file, like
+this:
+.br
+[common]
+.br
+Database = SQLite3
+.SH CONFIGURATION
+The \fISQLite3.conf\fP configuration file contains one entry:
+.SS DBPath
+The path to the database.
+.SH EXAMPLES
+see \fBINVOCATION\fP
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-plugins (7)
+.SH AUTHOR
+Written by Zdenek Prikryl <zprikryl@redhat.com> and Jiri
+Moskovcak <jmoskovc@redhat.com>. Manual
+page by Daniel Novotny <dnovotny@redhat.com>.
diff --git a/lib/plugins/abrt-plugins.7 b/lib/plugins/abrt-plugins.7
new file mode 100644
index 00000000..3a99dcbb
--- /dev/null
+++ b/lib/plugins/abrt-plugins.7
@@ -0,0 +1,44 @@
+.TH abrt "8" "28 May 2009" ""
+.SH NAME
+abrt-plugins \- plugins for the abrt crash reporter program
+.SH DESCRIPTION
+.P
+.I abrt
+is a daemon that watches for application crashes. When a crash occurs,
+it collects the crash data (core file, application's command line etc.)
+and takes action according to the type of application that
+crashed and according to the configuration specified in the
+.I abrt.conf
+configuration file.
+.P
+Plugins allow abrt to perform various actions: for example,
+to report the crash to Bugzilla, to mail the report, to transfer
+the report via FTP or SCP, or to run a program that you specify.
+.P
+This manual page provides a list of all the manual pages for
+these plugins.
+.P
+If you want to create your own plugin, refer to the PLUGINS-HOWTO
+file in the documentation directory.
+.SH INVOCATION
+Each plugin is invoked in the \fIabrt.conf\fP configuration
+file, in the section that is appropriate to what you
+want abrt to do.
+.SH CONFIGURATION
+Almost every plugin has its configuration file,
+stored in the \fI/etc/abrt/plugins\fP directory.
+.SH "SEE ALSO"
+.IR abrt (8),
+.IR abrt.conf (5),
+.IR abrt-Bugzilla (7),
+.IR abrt-FileTransfer (7),
+.IR abrt-KerneloopsReporter (7),
+.IR abrt-KerneloopsScanner (7),
+.IR abrt-Logger (7),
+.IR abrt-Mailx (7),
+.IR abrt-RunApp (7),
+.IR abrt-SQLite3 (7)
+.SH AUTHOR
+\fIabrt\fP written by Zdeněk Přikryl <zprikryl@redhat.com> and
+Jiří Moskovčák <jmoskovc@redhat.com>. Manual page written by Daniel
+Novotný <dnovotny@redhat.com>.