[PATCH] Move reporters from abrt to libreport
by Miroslav Lichvar
---
configure.ac | 9 +
libreport.spec.in | 102 +++++++
src/Makefile.am | 2 +-
src/lib/Makefile.am | 28 ++
src/lib/abrt_curl.c | 435 ++++++++++++++++++++++++++++++
src/lib/abrt_curl.h | 103 +++++++
src/lib/abrt_xmlrpc.c | 137 ++++++++++
src/lib/abrt_xmlrpc.h | 51 ++++
src/plugins/Bugzilla.conf | 12 +
src/plugins/Kerneloops.conf | 3 +
src/plugins/Makefile.am | 208 +++++++++++++++
src/plugins/Upload.conf | 9 +
src/plugins/abrt_rh_support.c | 460 ++++++++++++++++++++++++++++++++
src/plugins/abrt_rh_support.h | 55 ++++
src/plugins/mailx_events.conf | 1 +
src/plugins/print_events.conf | 2 +
src/plugins/report_Bugzilla.conf | 4 +
src/plugins/report_Bugzilla.xml.in | 36 +++
src/plugins/report_Kerneloops.xml.in | 20 ++
src/plugins/report_Logger.conf | 2 +
src/plugins/report_Mailx.xml.in | 35 +++
src/plugins/report_RHTSupport.xml.in | 35 +++
src/plugins/reporter-bugzilla.c | 291 ++++++++++++++++++++
src/plugins/reporter-bugzilla.txt | 67 +++++
src/plugins/reporter-kerneloops.c | 166 ++++++++++++
src/plugins/reporter-kerneloops.txt | 68 +++++
src/plugins/reporter-mailx.c | 162 +++++++++++
src/plugins/reporter-mailx.txt | 91 +++++++
src/plugins/reporter-print.c | 95 +++++++
src/plugins/reporter-print.txt | 63 +++++
src/plugins/reporter-rhtsupport.c | 342 ++++++++++++++++++++++++
src/plugins/reporter-upload.c | 287 ++++++++++++++++++++
src/plugins/reporter-upload.txt | 67 +++++
src/plugins/rhbz.c | 488 ++++++++++++++++++++++++++++++++++
src/plugins/rhbz.h | 103 +++++++
src/plugins/rhtsupport_events.conf | 1 +
36 files changed, 4039 insertions(+), 1 deletions(-)
create mode 100644 src/lib/abrt_curl.c
create mode 100644 src/lib/abrt_curl.h
create mode 100644 src/lib/abrt_xmlrpc.c
create mode 100644 src/lib/abrt_xmlrpc.h
create mode 100644 src/plugins/Bugzilla.conf
create mode 100644 src/plugins/Kerneloops.conf
create mode 100644 src/plugins/Makefile.am
create mode 100644 src/plugins/Upload.conf
create mode 100644 src/plugins/abrt_rh_support.c
create mode 100644 src/plugins/abrt_rh_support.h
create mode 100644 src/plugins/mailx_events.conf
create mode 100644 src/plugins/print_events.conf
create mode 100644 src/plugins/report_Bugzilla.conf
create mode 100644 src/plugins/report_Bugzilla.xml.in
create mode 100644 src/plugins/report_Kerneloops.xml.in
create mode 100644 src/plugins/report_Logger.conf
create mode 100644 src/plugins/report_Mailx.xml.in
create mode 100644 src/plugins/report_RHTSupport.xml.in
create mode 100644 src/plugins/reporter-bugzilla.c
create mode 100644 src/plugins/reporter-bugzilla.txt
create mode 100644 src/plugins/reporter-kerneloops.c
create mode 100644 src/plugins/reporter-kerneloops.txt
create mode 100644 src/plugins/reporter-mailx.c
create mode 100644 src/plugins/reporter-mailx.txt
create mode 100644 src/plugins/reporter-print.c
create mode 100644 src/plugins/reporter-print.txt
create mode 100644 src/plugins/reporter-rhtsupport.c
create mode 100644 src/plugins/reporter-upload.c
create mode 100644 src/plugins/reporter-upload.txt
create mode 100644 src/plugins/rhbz.c
create mode 100644 src/plugins/rhbz.h
create mode 100644 src/plugins/rhtsupport_events.conf
diff --git a/configure.ac b/configure.ac
index f2569ca..61f16ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -61,6 +61,9 @@ PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.21])
PKG_CHECK_MODULES([DBUS], [dbus-1])
PKG_CHECK_MODULES([LIBXML], [libxml-2.0])
PKG_CHECK_MODULES([NSS], [nss])
+PKG_CHECK_MODULES([XMLRPC], [xmlrpc])
+PKG_CHECK_MODULES([XMLRPC_CLIENT], [xmlrpc_client])
+PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([GNOME_KEYRING], [gnome-keyring-1])
# Just PKG_CHECK_MODULES([PYTHON], [python]) works only with python2.7+
# Below, if python is not found, we set up for python2.6 w/o checking:
@@ -72,9 +75,13 @@ PKG_CHECK_MODULES([NEWT], [libnewt])
PKG_PROG_PKG_CONFIG
+AC_CHECK_HEADER([libtar.h], [],
+ [AC_MSG_ERROR([libtar.h is needed to build libreport])])
+
CONF_DIR='${sysconfdir}/${PACKAGE_NAME}'
VAR_RUN='${localstatedir}/run'
PLUGINS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/plugins'
+REPORT_PLUGINS_CONF_DIR='${sysconfdir}/libreport/plugins'
EVENTS_DIR='${sysconfdir}/${PACKAGE_NAME}/events'
EVENTS_CONF_DIR='${sysconfdir}/${PACKAGE_NAME}/events.d'
PLUGINS_LIB_DIR='${libdir}/${PACKAGE_NAME}'
@@ -90,6 +97,7 @@ AC_ARG_WITH(debugdumpsdir,
AC_SUBST(CONF_DIR)
AC_SUBST(VAR_RUN)
AC_SUBST(PLUGINS_CONF_DIR)
+AC_SUBST(REPORT_PLUGINS_CONF_DIR)
AC_SUBST(EVENTS_CONF_DIR)
AC_SUBST(EVENTS_DIR)
AC_SUBST(PLUGINS_LIB_DIR)
@@ -117,6 +125,7 @@ AC_CONFIG_FILES([
src/gui-wizard-gtk/Makefile
src/cli/Makefile
src/report-newt/Makefile
+ src/plugins/Makefile
po/Makefile.in
])
diff --git a/libreport.spec.in b/libreport.spec.in
index c5bb390..40c119a 100644
--- a/libreport.spec.in
+++ b/libreport.spec.in
@@ -98,6 +98,63 @@ Requires: libreport-gtk = %{version}-%{release}
%description gtk-devel
Development libraries and headers for libreport-gtk
+%package plugin-kerneloops
+Summary: %{name}'s kerneloops reporter plugin
+Group: System Environment/Libraries
+Requires: curl
+Requires: %{name} = %{version}-%{release}
+
+%description plugin-kerneloops
+This package contains plugin which sends kernel crash information to specified
+server, usually to kerneloops.org.
+
+%package plugin-logger
+Summary: %{name}'s logger reporter plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Obsoletes: abrt-plugin-logger < 2.0.4
+
+%description plugin-logger
+The simple reporter plugin which writes a report to a specified file.
+
+%package plugin-mailx
+Summary: %{name}'s mailx reporter plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: mailx
+Obsoletes: abrt-plugin-mailx < 2.0.4
+
+%description plugin-mailx
+The simple reporter plugin which sends a report via mailx to a specified
+email address.
+
+%package plugin-bugzilla
+Summary: %{name}'s bugzilla plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Obsoletes: abrt-plugin-bugzilla < 2.0.4
+
+%description plugin-bugzilla
+Plugin to report bugs into the bugzilla.
+
+%package plugin-rhtsupport
+Summary: %{name}'s RHTSupport plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Obsoletes: abrt-plugin-rhtsupport < 2.0.4
+
+%description plugin-rhtsupport
+Plugin to report bugs into RH support system.
+
+%package plugin-reportuploader
+Summary: %{name}'s reportuploader plugin
+Group: System Environment/Libraries
+Requires: %{name} = %{version}-%{release}
+Obsoletes: abrt-plugin-reportuploader < 2.0.4
+
+%description plugin-reportuploader
+Plugin to report bugs into anonymous FTP site associated with ticketing system.
+
%prep
%setup -q
@@ -154,6 +211,8 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
%config(noreplace) %{_sysconfdir}/%{name}/report_event.conf
%{_libdir}/libreport.so.*
%{_libdir}/libabrt_dbus.so.*
+%{_libdir}/libabrt_web.so.*
+%exclude %{_libdir}/libabrt_web.so
%files devel
%defattr(-,root,root,-)
@@ -196,6 +255,49 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
%{_includedir}/libreport/internal_libreport_gtk.h
%{_libdir}/pkgconfig/libreport-gtk.pc
+%files plugin-kerneloops
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/libreport/plugins/Kerneloops.conf
+%{_sysconfdir}/libreport/events/report_Kerneloops.xml
+%{_mandir}/man*/reporter-kerneloops.*
+%{_bindir}/reporter-kerneloops
+
+%files plugin-logger
+%defattr(-,root,root,-)
+%{_sysconfdir}/libreport/events/report_Logger.conf
+%config(noreplace) %{_sysconfdir}/libreport/events.d/print_events.conf
+%{_bindir}/reporter-print
+%{_mandir}/man*/reporter-print.*
+
+%files plugin-mailx
+%defattr(-,root,root,-)
+%{_sysconfdir}/libreport/events/report_Mailx.xml
+%config(noreplace) %{_sysconfdir}/libreport/events.d/mailx_events.conf
+%{_mandir}/man*/reporter-mailx.*
+%{_bindir}/reporter-mailx
+
+%files plugin-bugzilla
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/libreport/plugins/Bugzilla.conf
+%{_sysconfdir}/libreport/events/report_Bugzilla.xml
+%config(noreplace) %{_sysconfdir}/libreport/events/report_Bugzilla.conf
+# FIXME: remove with the old gui
+%{_mandir}/man1/reporter-bugzilla.1.gz
+%{_bindir}/reporter-bugzilla
+
+%files plugin-rhtsupport
+%defattr(-,root,root,-)
+%{_sysconfdir}/libreport/events/report_RHTSupport.xml
+%config(noreplace) %{_sysconfdir}/libreport/events.d/rhtsupport_events.conf
+# {_mandir}/man7/abrt-RHTSupport.7.gz
+%{_bindir}/reporter-rhtsupport
+
+%files plugin-reportuploader
+%defattr(-,root,root,-)
+%config(noreplace) %{_sysconfdir}/libreport/plugins/Upload.conf
+%{_mandir}/man*/reporter-upload.*
+%{_bindir}/reporter-upload
+
%changelog
* Wed Jun 01 2011 Jiri Moskovcak <jmoskovc(a)redhat.com> 2.0.2-1
- initial packaging
diff --git a/src/Makefile.am b/src/Makefile.am
index cba73c0..24dfeb2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1 +1 @@
-SUBDIRS = include lib report-python gtk-helpers gui-wizard-gtk cli report-newt
+SUBDIRS = include lib plugins report-python gtk-helpers gui-wizard-gtk cli report-newt
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index ea86dc3..7b870d9 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,6 +1,7 @@
# libreport - the stuff shared among most of abrt (like xmalloc, logging)
lib_LTLIBRARIES = \
libreport.la \
+ libabrt_web.la \
libabrt_dbus.la
# Not used just yet:
@@ -90,6 +91,33 @@ libabrt_dbus_la_LIBADD = \
$(GLIB_LIBS) \
$(DBUS_LIBS)
+libabrt_web_la_SOURCES = \
+ abrt_curl.h abrt_curl.c \
+ abrt_xmlrpc.h abrt_xmlrpc.c
+libabrt_web_la_CPPFLAGS = \
+ -Wall -Wwrite-strings -Werror \
+ -I$(srcdir)/../include \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DEVENTS_DIR=\"$(EVENTS_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(CURL_CFLAGS) \
+ $(LIBXML_CFLAGS) \
+ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE
+libabrt_web_la_LDFLAGS = \
+ -version-info 0:1:0
+libabrt_web_la_LIBADD = \
+ $(GLIB_LIBS) \
+ $(CURL_LIBS) \
+ $(LIBXML_LIBS) \
+ $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS)
+
$(DESTDIR)/$(DEBUG_DUMPS_DIR):
$(mkdir_p) '$@'
# no need to chmod it here
diff --git a/src/lib/abrt_curl.c b/src/lib/abrt_curl.c
new file mode 100644
index 0000000..9831862
--- /dev/null
+++ b/src/lib/abrt_curl.c
@@ -0,0 +1,435 @@
+/*
+ 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 "internal_libreport.h"
+#include "abrt_curl.h"
+
+/*
+ * Utility functions
+ */
+CURL* xcurl_easy_init()
+{
+ CURL* curl = curl_easy_init();
+ if (!curl)
+ {
+ error_msg_and_die("Can't create curl handle");
+ }
+ return curl;
+}
+
+static char*
+check_curl_error(CURLcode err, const char* msg)
+{
+ if (err)
+ return xasprintf("%s: %s", msg, curl_easy_strerror(err));
+ return NULL;
+}
+
+static void
+die_if_curl_error(CURLcode err)
+{
+ if (err) {
+ char *msg = check_curl_error(err, "curl");
+ error_msg_and_die("%s", msg);
+ }
+}
+
+static void
+xcurl_easy_setopt_ptr(CURL *handle, CURLoption option, const void *parameter)
+{
+ CURLcode err = curl_easy_setopt(handle, option, parameter);
+ if (err) {
+ char *msg = check_curl_error(err, "curl");
+ error_msg_and_die("%s", msg);
+ }
+}
+static inline void
+xcurl_easy_setopt_long(CURL *handle, CURLoption option, long parameter)
+{
+ xcurl_easy_setopt_ptr(handle, option, (void*)parameter);
+}
+
+/*
+ * post_state utility functions
+ */
+
+abrt_post_state_t *new_abrt_post_state(int flags)
+{
+ abrt_post_state_t *state = (abrt_post_state_t *)xzalloc(sizeof(*state));
+ state->flags = flags;
+ return state;
+}
+
+void free_abrt_post_state(abrt_post_state_t *state)
+{
+ char **headers = state->headers;
+ if (headers)
+ {
+ while (*headers)
+ free(*headers++);
+ free(state->headers);
+ }
+ free(state->curl_error_msg);
+ free(state->body);
+ free(state);
+}
+
+char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str)
+{
+ char **headers = state->headers;
+ if (headers)
+ {
+ unsigned len = strlen(str);
+ while (*headers)
+ {
+ if (strncmp(*headers, str, len) == 0)
+ return skip_whitespace(*headers + len);
+ headers++;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * abrt_post: perform HTTP POST transaction
+ */
+
+/* "save headers" callback */
+static size_t
+save_headers(void *buffer_pv, size_t count, size_t nmemb, void *ptr)
+{
+ abrt_post_state_t* state = (abrt_post_state_t*)ptr;
+ size_t size = count * nmemb;
+
+ char *h = xstrndup((char*)buffer_pv, size);
+ strchrnul(h, '\r')[0] = '\0';
+ strchrnul(h, '\n')[0] = '\0';
+
+ unsigned cnt = state->header_cnt;
+
+ /* Check for the case when curl follows a redirect:
+ * header 0: 'HTTP/1.1 301 Moved Permanently'
+ * header 1: 'Connection: close'
+ * header 2: 'Location: NEW_URL'
+ * header 3: ''
+ * header 0: 'HTTP/1.1 200 OK' <-- we need to forget all hdrs and start anew
+ */
+ if (cnt != 0
+ && strncmp(h, "HTTP/", 5) == 0
+ && state->headers[cnt-1][0] == '\0' /* prev header is an empty string */
+ ) {
+ char **headers = state->headers;
+ if (headers)
+ {
+ while (*headers)
+ free(*headers++);
+ }
+ cnt = 0;
+ }
+
+ VERB3 log("save_headers: header %d: '%s'", cnt, h);
+ state->headers = (char**)xrealloc(state->headers, (cnt+2) * sizeof(state->headers[0]));
+ state->headers[cnt] = h;
+ state->header_cnt = ++cnt;
+ state->headers[cnt] = NULL;
+
+ return size;
+}
+
+/* "read local data from a file" callback */
+static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ static time_t last_t; // hack
+
+ FILE *fp = (FILE*)userdata;
+ time_t t = time(NULL);
+
+ // Report current file position every 16 seconds
+ if (!(t & 0xf) && last_t != t)
+ {
+ last_t = t;
+ off_t cur_pos = ftello(fp);
+ fseeko(fp, 0, SEEK_END);
+ off_t sz = ftello(fp);
+ fseeko(fp, cur_pos, SEEK_SET);
+ log(_("Uploaded: %llu of %llu kbytes"),
+ (unsigned long long)cur_pos / 1024,
+ (unsigned long long)sz / 1024);
+ }
+
+ return fread(ptr, size, nmemb, fp);
+}
+
+static int curl_debug(CURL *handle, curl_infotype it, char *buf, size_t bufsize, void *unused)
+{
+ if (logmode == 0)
+ return 0;
+
+ switch (it) {
+ case CURLINFO_TEXT: /* The data is informational text. */
+ log("curl: %.*s", (int) bufsize, buf);
+ break;
+ case CURLINFO_HEADER_IN: /* The data is header (or header-like) data received from the peer. */
+ log("curl rcvd header: '%.*s'", (int) bufsize, buf);
+ break;
+ case CURLINFO_HEADER_OUT: /* The data is header (or header-like) data sent to the peer. */
+ log("curl sent header: '%.*s'", (int) bufsize, buf);
+ break;
+ case CURLINFO_DATA_IN: /* The data is protocol data received from the peer. */
+ if (g_verbose >= 3)
+ log("curl rcvd data: '%.*s'", (int) bufsize, buf);
+ else
+ log("curl rcvd data %u bytes", (int) bufsize);
+ break;
+ case CURLINFO_DATA_OUT: /* The data is protocol data sent to the peer. */
+ if (g_verbose >= 3)
+ log("curl sent data: '%.*s'", (int) bufsize, buf);
+ else
+ log("curl sent data %u bytes", (int) bufsize);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int
+abrt_post(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char **additional_headers,
+ const char *data,
+ off_t data_size)
+{
+ CURLcode curl_err;
+ long response_code;
+ abrt_post_state_t localstate;
+
+ VERB3 log("abrt_post('%s','%s')", url, data);
+
+ if (!state)
+ {
+ memset(&localstate, 0, sizeof(localstate));
+ state = &localstate;
+ }
+
+ state->http_resp_code = response_code = -1;
+
+ CURL *handle = xcurl_easy_init();
+
+ // Buffer[CURL_ERROR_SIZE] curl stores human readable error messages in.
+ // This may be more helpful than just return code from curl_easy_perform.
+ // curl will need it until curl_easy_cleanup.
+ state->errmsg[0] = '\0';
+ xcurl_easy_setopt_ptr(handle, CURLOPT_ERRORBUFFER, state->errmsg);
+ // Shut off the built-in progress meter completely
+ xcurl_easy_setopt_long(handle, CURLOPT_NOPROGRESS, 1);
+
+ if (g_verbose >= 2) {
+ // "Display a lot of verbose information about its operations.
+ // Very useful for libcurl and/or protocol debugging and understanding.
+ // The verbose information will be sent to stderr, or the stream set
+ // with CURLOPT_STDERR"
+ xcurl_easy_setopt_long(handle, CURLOPT_VERBOSE, 1);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_DEBUGFUNCTION, curl_debug);
+ }
+
+ // TODO: do we need to check for CURLE_URL_MALFORMAT error *here*,
+ // not in curl_easy_perform?
+ xcurl_easy_setopt_ptr(handle, CURLOPT_URL, url);
+
+ // Auth if configured
+ if (state->username) {
+ // bitmask of allowed auth methods
+ xcurl_easy_setopt_long(handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_USERNAME, state->username);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_PASSWORD, (state->password ? state->password : ""));
+ }
+
+ // Do a regular HTTP post. This also makes curl use
+ // a "Content-Type: application/x-www-form-urlencoded" header.
+ // (This is by far the most commonly used POST method).
+ xcurl_easy_setopt_long(handle, CURLOPT_POST, 1);
+ // Supply POST data...
+ struct curl_httppost* post = NULL;
+ struct curl_httppost* last = NULL;
+ FILE* data_file = NULL;
+ if (data_size == ABRT_POST_DATA_FROMFILE) {
+ // ...from a file
+ data_file = fopen(data, "r");
+ if (!data_file)
+//FIXME:
+ perror_msg_and_die("Can't open '%s'", data);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READDATA, data_file);
+ // Want to use custom read function
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting);
+ } else if (data_size == ABRT_POST_DATA_FROMFILE_AS_FORM_DATA) {
+ // ...from a file, in multipart/formdata format
+ const char *basename = strrchr(data, '/');
+ if (basename) basename++;
+ else basename = data;
+#if 0
+ // Simple way, without custom reader function
+ CURLFORMcode curlform_err = curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "file", // element name
+ CURLFORM_FILE, data, // filename to read from
+ CURLFORM_CONTENTTYPE, content_type,
+ CURLFORM_FILENAME, basename, // filename to put in the form
+ CURLFORM_END);
+#else
+ data_file = fopen(data, "r");
+ if (!data_file)
+//FIXME:
+ perror_msg_and_die("Can't open '%s'", data);
+ // Want to use custom read function
+ xcurl_easy_setopt_ptr(handle, CURLOPT_READFUNCTION, (const void*)fread_with_reporting);
+ // Need to know file size
+ fseeko(data_file, 0, SEEK_END);
+ off_t sz = ftello(data_file);
+ fseeko(data_file, 0, SEEK_SET);
+ // Create formdata
+ CURLFORMcode curlform_err = curl_formadd(&post, &last,
+ CURLFORM_PTRNAME, "file", // element name
+ // use CURLOPT_READFUNCTION for reading, pass data_file as its last param:
+ CURLFORM_STREAM, data_file,
+ CURLFORM_CONTENTSLENGTH, (long)sz, // a must if we use CURLFORM_STREAM option
+ CURLFORM_CONTENTTYPE, content_type,
+ CURLFORM_FILENAME, basename, // filename to put in the form
+ CURLFORM_END);
+#endif
+ if (curlform_err != 0)
+//FIXME:
+ error_msg_and_die("out of memory or read error (curl_formadd error code: %d)", (int)curlform_err);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPPOST, post);
+ } else {
+ // .. from a blob in memory
+ xcurl_easy_setopt_ptr(handle, CURLOPT_POSTFIELDS, data);
+ // note1: if data_size == ABRT_POST_DATA_STRING == -1, curl will use strlen(data)
+ xcurl_easy_setopt_long(handle, CURLOPT_POSTFIELDSIZE, data_size);
+ // note2: CURLOPT_POSTFIELDSIZE_LARGE can't be used: xcurl_easy_setopt_long()
+ // truncates data_size on 32-bit arch. Need xcurl_easy_setopt_long_long()?
+ // Also, I'm not sure CURLOPT_POSTFIELDSIZE_LARGE special-cases -1.
+ }
+
+ struct curl_slist *httpheader_list = NULL;
+
+ // Override "Content-Type:"
+ if (data_size != ABRT_POST_DATA_FROMFILE_AS_FORM_DATA)
+ {
+ char *content_type_header = xasprintf("Content-Type: %s", content_type);
+ // Note: curl_slist_append() copies content_type_header
+ httpheader_list = curl_slist_append(httpheader_list, content_type_header);
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+ free(content_type_header);
+ }
+
+ for (; additional_headers && *additional_headers; additional_headers++)
+ {
+ httpheader_list = curl_slist_append(httpheader_list, *additional_headers);
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+ }
+
+ // Add User-Agent: ABRT/N.M
+ httpheader_list = curl_slist_append(httpheader_list, "User-Agent: ABRT/"VERSION);
+ if (!httpheader_list)
+ error_msg_and_die("out of memory");
+
+ if (httpheader_list)
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HTTPHEADER, httpheader_list);
+
+// Disabled: was observed to also handle "305 Use proxy" redirect,
+// apparently with POST->GET remapping - which server didn't like at all.
+// Attempted to suppress remapping on 305 using CURLOPT_POSTREDIR of -1,
+// but it still did not work.
+#if 0
+ // Please handle 301/302 redirects for me
+ xcurl_easy_setopt_long(handle, CURLOPT_FOLLOWLOCATION, 1);
+ xcurl_easy_setopt_long(handle, CURLOPT_MAXREDIRS, 10);
+ // Bitmask to control how libcurl acts on redirects after POSTs.
+ // Bit 0 set (value CURL_REDIR_POST_301) makes libcurl
+ // not convert POST requests into GET requests when following
+ // a 301 redirection. Bit 1 (value CURL_REDIR_POST_302) makes libcurl
+ // maintain the request method after a 302 redirect.
+ // CURL_REDIR_POST_ALL is a convenience define that sets both bits.
+ // The non-RFC behaviour is ubiquitous in web browsers, so the library
+ // does the conversion by default to maintain consistency.
+ // However, a server may require a POST to remain a POST.
+ xcurl_easy_setopt_long(handle, CURLOPT_POSTREDIR, -1L /*CURL_REDIR_POST_ALL*/ );
+#endif
+
+ // Prepare for saving information
+ if (state->flags & ABRT_POST_WANT_HEADERS)
+ {
+ xcurl_easy_setopt_ptr(handle, CURLOPT_HEADERFUNCTION, (void*)save_headers);
+ xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEHEADER, state);
+ }
+ FILE* body_stream = NULL;
+ if (state->flags & ABRT_POST_WANT_BODY)
+ {
+ body_stream = open_memstream(&state->body, &state->body_size);
+ if (!body_stream)
+ error_msg_and_die("out of memory");
+ xcurl_easy_setopt_ptr(handle, CURLOPT_WRITEDATA, body_stream);
+ }
+ if (!(state->flags & ABRT_POST_WANT_SSL_VERIFY))
+ {
+ xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYPEER, 0);
+ xcurl_easy_setopt_long(handle, CURLOPT_SSL_VERIFYHOST, 0);
+ }
+
+ // This is the place where everything happens.
+ // Here errors are not limited to "out of memory", can't just die.
+ curl_err = curl_easy_perform(handle);
+ if (curl_err)
+ {
+ VERB2 log("curl_easy_perform: error %d", (int)curl_err);
+ if (state->flags & ABRT_POST_WANT_ERROR_MSG)
+ {
+ state->curl_error_msg = check_curl_error(curl_err, "curl_easy_perform");
+ VERB3 log("curl_easy_perform: error_msg: %s", state->curl_error_msg);
+ }
+ goto ret;
+ }
+
+ // curl-7.20.1 doesn't do it, we get NULL body in the log message below
+ // unless we fflush the body memstream ourself
+ if (body_stream)
+ fflush(body_stream);
+
+ // Headers/body are already saved (if requested), extract more info
+ curl_err = curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &response_code);
+ die_if_curl_error(curl_err);
+ state->http_resp_code = response_code;
+ VERB3 log("after curl_easy_perform: http code %ld body:'%s'", response_code, state->body);
+
+ ret:
+ curl_easy_cleanup(handle);
+ if (httpheader_list)
+ curl_slist_free_all(httpheader_list);
+ if (body_stream)
+ fclose(body_stream);
+ if (data_file)
+ fclose(data_file);
+ if (post)
+ curl_formfree(post);
+
+ return response_code;
+}
diff --git a/src/lib/abrt_curl.h b/src/lib/abrt_curl.h
new file mode 100644
index 0000000..ad2afc1
--- /dev/null
+++ b/src/lib/abrt_curl.h
@@ -0,0 +1,103 @@
+/*
+ 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 ABRT_CURL_H_
+#define ABRT_CURL_H_
+
+#include <curl/curl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CURL* xcurl_easy_init();
+
+typedef struct abrt_post_state {
+ /* Supplied by caller: */
+ int flags;
+ const char *username;
+ const char *password;
+ /* Results of POST transaction: */
+ int http_resp_code;
+ unsigned header_cnt;
+ char **headers;
+ char *curl_error_msg;
+ char *body;
+ size_t body_size;
+ char errmsg[CURL_ERROR_SIZE];
+} abrt_post_state_t;
+
+abrt_post_state_t *new_abrt_post_state(int flags);
+void free_abrt_post_state(abrt_post_state_t *state);
+char *find_header_in_abrt_post_state(abrt_post_state_t *state, const char *str);
+
+enum {
+ ABRT_POST_WANT_HEADERS = (1 << 0),
+ ABRT_POST_WANT_ERROR_MSG = (1 << 1),
+ ABRT_POST_WANT_BODY = (1 << 2),
+ ABRT_POST_WANT_SSL_VERIFY = (1 << 3),
+};
+enum {
+ /* Must be -1! CURLOPT_POSTFIELDSIZE interprets -1 as "use strlen" */
+ ABRT_POST_DATA_STRING = -1,
+ ABRT_POST_DATA_FROMFILE = -2,
+ ABRT_POST_DATA_FROMFILE_AS_FORM_DATA = -3,
+};
+int
+abrt_post(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char **additional_headers,
+ const char *data,
+ off_t data_size);
+static inline int
+abrt_post_string(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char **additional_headers,
+ const char *str)
+{
+ return abrt_post(state, url, content_type, additional_headers,
+ str, ABRT_POST_DATA_STRING);
+}
+static inline int
+abrt_post_file(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char **additional_headers,
+ const char *filename)
+{
+ return abrt_post(state, url, content_type, additional_headers,
+ filename, ABRT_POST_DATA_FROMFILE);
+}
+static inline int
+abrt_post_file_as_form(abrt_post_state_t *state,
+ const char *url,
+ const char *content_type,
+ const char **additional_headers,
+ const char *filename)
+{
+ return abrt_post(state, url, content_type, additional_headers,
+ filename, ABRT_POST_DATA_FROMFILE_AS_FORM_DATA);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/abrt_xmlrpc.c b/src/lib/abrt_xmlrpc.c
new file mode 100644
index 0000000..0bdb4bd
--- /dev/null
+++ b/src/lib/abrt_xmlrpc.c
@@ -0,0 +1,137 @@
+/*
+ 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 "internal_libreport.h"
+#include "abrt_xmlrpc.h"
+
+void abrt_xmlrpc_die(xmlrpc_env *env)
+{
+ error_msg_and_die("fatal: XML-RPC(%d): %s", env->fault_code, env->fault_string);
+}
+
+void abrt_xmlrpc_error(xmlrpc_env *env)
+{
+ error_msg("error: XML-RPC (%d): %s", env->fault_code, env->fault_string);
+}
+
+struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ struct abrt_xmlrpc *ax = xzalloc(sizeof(struct abrt_xmlrpc));
+
+ /* This should be done at program startup, once. We do it in main */
+ /* xmlrpc_client_setup_global_const(&env); */
+
+ /* URL - bugzilla.redhat.com/show_bug.cgi?id=666893 Unable to make sense of
+ * XML-RPC response from server
+ *
+ * By default, XML data from the network may be no larger than 512K.
+ * XMLRPC_XML_SIZE_LIMIT_DEFAULT is #defined to (512*1024) in xmlrpc-c/base.h
+ *
+ * Users reported trouble with 733402 byte long responses, hope raising the
+ * limit to 2*512k is enough
+ */
+ xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 2 * XMLRPC_XML_SIZE_LIMIT_DEFAULT);
+
+ struct xmlrpc_curl_xportparms curl_parms;
+ memset(&curl_parms, 0, sizeof(curl_parms));
+ /* curlParms.network_interface = NULL; - done by memset */
+ curl_parms.no_ssl_verifypeer = !ssl_verify;
+ curl_parms.no_ssl_verifyhost = !ssl_verify;
+#ifdef VERSION
+ curl_parms.user_agent = PACKAGE_NAME"/"VERSION;
+#else
+ curl_parms.user_agent = "abrt";
+#endif
+
+ struct xmlrpc_clientparms client_parms;
+ memset(&client_parms, 0, sizeof(client_parms));
+ client_parms.transport = "curl";
+ client_parms.transportparmsP = &curl_parms;
+ client_parms.transportparm_size = XMLRPC_CXPSIZE(user_agent);
+
+ xmlrpc_client_create(&env, XMLRPC_CLIENT_NO_FLAGS,
+ PACKAGE_NAME, VERSION,
+ &client_parms, XMLRPC_CPSIZE(transportparm_size),
+ &ax->ax_client);
+
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ ax->ax_server_info = xmlrpc_server_info_new(&env, url);
+ if (env.fault_occurred)
+ {
+ xmlrpc_client_destroy(ax->ax_client);
+ abrt_xmlrpc_die(&env);
+ }
+
+ return ax;
+}
+
+void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax)
+{
+ if (!ax)
+ return;
+
+ if (ax->ax_server_info)
+ xmlrpc_server_info_free(ax->ax_server_info);
+
+ if (ax->ax_client)
+ xmlrpc_client_destroy(ax->ax_client);
+
+ free(ax);
+}
+
+/* die or return expected results */
+xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax,
+ const char* method, const char* format, ...)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value* param = NULL;
+ const char* suffix;
+ va_list args;
+
+ va_start(args, format);
+ xmlrpc_build_value_va(&env, format, args, ¶m, &suffix);
+ va_end(args);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ xmlrpc_value* result = NULL;
+ if (*suffix != '\0')
+ {
+ xmlrpc_env_set_fault_formatted(
+ &env, XMLRPC_INTERNAL_ERROR, "Junk after the argument "
+ "specifier: '%s'. There must be exactly one argument.",
+ suffix);
+ }
+ else
+ {
+ xmlrpc_client_call2(&env, ax->ax_client, ax->ax_server_info, method,
+ param, &result);
+ }
+ xmlrpc_DECREF(param);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ return result;
+}
diff --git a/src/lib/abrt_xmlrpc.h b/src/lib/abrt_xmlrpc.h
new file mode 100644
index 0000000..5c94360
--- /dev/null
+++ b/src/lib/abrt_xmlrpc.h
@@ -0,0 +1,51 @@
+/*
+ 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 ABRT_XMLRPC_H_
+#define ABRT_XMLRPC_H_ 1
+
+/* include/stdint.h: typedef int int32_t;
+ * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32;
+ */
+
+#include <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct abrt_xmlrpc {
+ xmlrpc_client *ax_client;
+ xmlrpc_server_info *ax_server_info;
+};
+
+struct abrt_xmlrpc *abrt_xmlrpc_new_client(const char *url, int ssl_verify);
+void abrt_xmlrpc_free_client(struct abrt_xmlrpc *ax);
+void abrt_xmlrpc_die(xmlrpc_env *env) __attribute__((noreturn));
+void abrt_xmlrpc_error(xmlrpc_env *env);
+
+/* die or return expected results */
+xmlrpc_value *abrt_xmlrpc_call(struct abrt_xmlrpc *ax,
+ const char *method, const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/Bugzilla.conf b/src/plugins/Bugzilla.conf
new file mode 100644
index 0000000..76e0d1d
--- /dev/null
+++ b/src/plugins/Bugzilla.conf
@@ -0,0 +1,12 @@
+# Description: Reports bugs to bugzilla
+
+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/src/plugins/Kerneloops.conf b/src/plugins/Kerneloops.conf
new file mode 100644
index 0000000..a0b86e8
--- /dev/null
+++ b/src/plugins/Kerneloops.conf
@@ -0,0 +1,3 @@
+Enabled = yes
+
+SubmitURL = http://submit.kerneloops.org/submitoops.php
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
new file mode 100644
index 0000000..e078d62
--- /dev/null
+++ b/src/plugins/Makefile.am
@@ -0,0 +1,208 @@
+-include ../../config.mak
+
+pluginslibdir = $(PLUGINS_LIB_DIR)
+
+bin_PROGRAMS = \
+ reporter-bugzilla \
+ reporter-rhtsupport \
+ reporter-kerneloops \
+ reporter-upload \
+ reporter-mailx \
+ reporter-print
+
+pluginsconfdir = $(PLUGINS_CONF_DIR)
+
+reportpluginsconfdir = $(REPORT_PLUGINS_CONF_DIR)
+
+dist_reportpluginsconf_DATA = \
+ Kerneloops.conf \
+ Bugzilla.conf \
+ Upload.conf
+
+eventsdir = $(EVENTS_DIR)
+
+dist_events_DATA = \
+ report_Bugzilla.xml \
+ report_Bugzilla.conf \
+ report_Logger.conf \
+ report_Mailx.xml \
+ report_RHTSupport.xml \
+ report_Kerneloops.xml
+
+@INTLTOOL_XML_RULE@
+
+eventsconfdir = $(EVENTS_CONF_DIR)
+
+dist_eventsconf_DATA = \
+ mailx_events.conf \
+ print_events.conf \
+ rhtsupport_events.conf
+
+MAN_TXT = \
+ reporter-print.txt \
+ reporter-mailx.txt \
+ reporter-bugzilla.txt \
+ reporter-kerneloops.txt \
+ reporter-upload.txt
+
+# Manual pages are generated from .txt via Docbook
+man1_MANS = ${MAN_TXT:%.txt=%.1}
+
+%.1 %.5 %.7: %.xml
+ $(XMLTO_SILENT) xmlto man $< 2>&1 | sed '/Note/d'
+
+%.xml: %.txt ../../asciidoc.conf
+ $(ASCIIDOC_SILENT) asciidoc --backend=docbook --doctype=manpage --conf-file ../../asciidoc.conf -aabrt_version=$(PACKAGE_VERSION) -o $@ $<
+
+
+CLEANFILES = $(man1_MANS)
+
+EXTRA_DIST = \
+ $(man_MANS) \
+ $(MAN_TXT) \
+ $(man1_MANS) \
+ report_Bugzilla.xml.in \
+ report_Bugzilla.conf \
+ report_Logger.conf \
+ report_Mailx.xml.in \
+ report_RHTSupport.xml.in \
+ report_Kerneloops.xml.in
+
+$(DESTDIR)/$(DEBUG_INFO_DIR):
+ $(mkdir_p) '$@'
+
+reporter_bugzilla_SOURCES = \
+ reporter-bugzilla.c rhbz.c rhbz.h
+reporter_bugzilla_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings
+reporter_bugzilla_LDADD = \
+ $(GLIB_LIBS) \
+ ../lib/libabrt_web.la \
+ ../lib/libreport.la
+
+reporter_rhtsupport_SOURCES = \
+ abrt_rh_support.h abrt_rh_support.c \
+ reporter-rhtsupport.c
+reporter_rhtsupport_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ $(XMLRPC_CFLAGS) $(XMLRPC_CLIENT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+reporter_rhtsupport_LDFLAGS = -ltar
+reporter_rhtsupport_LDADD = \
+ $(GLIB_LIBS) \
+ $(XMLRPC_LIBS) $(XMLRPC_CLIENT_LIBS) \
+ ../lib/libabrt_web.la \
+ ../lib/libreport.la
+
+reporter_upload_SOURCES = \
+ reporter-upload.c
+reporter_upload_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(CURL_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+reporter_upload_LDFLAGS = -ltar
+reporter_upload_LDADD = \
+ $(GLIB_LIBS) \
+ $(CURL_LIBS) \
+ ../lib/libreport.la
+
+reporter_kerneloops_SOURCES = \
+ reporter-kerneloops.c
+reporter_kerneloops_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+reporter_kerneloops_LDADD = \
+ ../lib/libabrt_web.la \
+ ../lib/libreport.la
+
+reporter_mailx_SOURCES = \
+ reporter-mailx.c
+reporter_mailx_CPPFLAGS = \
+ -I$(srcdir)/../libreport/src/include/ -I$(srcdir)/../include \
+ -I$(srcdir)/../libreport/src/lib \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+reporter_mailx_LDADD = \
+ ../lib/libreport.la
+
+reporter_print_SOURCES = \
+ reporter-print.c
+reporter_print_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DLOCALSTATEDIR='"$(localstatedir)"' \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(REPORT_PLUGINS_CONF_DIR)\" \
+ $(GLIB_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE \
+ -Wall -Wwrite-strings -Werror
+reporter_print_LDADD = \
+ ../lib/libreport.la
+
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
diff --git a/src/plugins/Upload.conf b/src/plugins/Upload.conf
new file mode 100644
index 0000000..803b0ac
--- /dev/null
+++ b/src/plugins/Upload.conf
@@ -0,0 +1,9 @@
+# Description: Packs problem data into .tar.gz file and uploads it via FTP/SCP/etc
+
+# 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
+# If empty, the prepared archive is stored in /var/run/abrt/
+URL =
diff --git a/src/plugins/abrt_rh_support.c b/src/plugins/abrt_rh_support.c
new file mode 100644
index 0000000..8f92af7
--- /dev/null
+++ b/src/plugins/abrt_rh_support.c
@@ -0,0 +1,460 @@
+/*
+ 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 <libxml/encoding.h>
+#include <libxml/xmlwriter.h>
+#include <curl/curl.h>
+#include "internal_libreport.h"
+#include "abrt_curl.h"
+#include "abrt_rh_support.h"
+
+struct reportfile {
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+};
+
+static void __attribute__((__noreturn__))
+die_xml_oom(void)
+{
+ error_msg_and_die("Can't create XML attribute (out of memory?)");
+}
+
+static xmlBufferPtr
+xxmlBufferCreate(void)
+{
+ xmlBufferPtr r = xmlBufferCreate();
+ if (!r)
+ die_xml_oom();
+ return r;
+}
+
+static xmlTextWriterPtr
+xxmlNewTextWriterMemory(xmlBufferPtr buf /*, int compression*/)
+{
+ xmlTextWriterPtr r = xmlNewTextWriterMemory(buf, /*compression:*/ 0);
+ if (!r)
+ die_xml_oom();
+ return r;
+}
+
+static void
+xxmlTextWriterStartDocument(xmlTextWriterPtr writer,
+ const char * version,
+ const char * encoding,
+ const char * standalone)
+{
+ if (xmlTextWriterStartDocument(writer, version, encoding, standalone) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterEndDocument(xmlTextWriterPtr writer)
+{
+ if (xmlTextWriterEndDocument(writer) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterStartElement(xmlTextWriterPtr writer, const char *name)
+{
+ // these bright guys REDEFINED CHAR (!) to unsigned char...
+ if (xmlTextWriterStartElement(writer, (unsigned char*)name) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterEndElement(xmlTextWriterPtr writer)
+{
+ if (xmlTextWriterEndElement(writer) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterWriteElement(xmlTextWriterPtr writer, const char *name, const char *content)
+{
+ if (xmlTextWriterWriteElement(writer, (unsigned char*)name, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+
+static void
+xxmlTextWriterWriteAttribute(xmlTextWriterPtr writer, const char *name, const char *content)
+{
+ if (xmlTextWriterWriteAttribute(writer, (unsigned char*)name, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+
+#if 0 //unused
+static void
+xxmlTextWriterWriteString(xmlTextWriterPtr writer, const char *content)
+{
+ if (xmlTextWriterWriteString(writer, (unsigned char*)content) < 0)
+ die_xml_oom();
+}
+#endif
+
+//
+// End the reportfile, and prepare it for delivery.
+// No more bindings can be added after this.
+//
+static void
+close_writer(reportfile_t* file)
+{
+ if (!file->writer)
+ return;
+
+ // close off the end of the xml file
+ xxmlTextWriterEndDocument(file->writer);
+ xmlFreeTextWriter(file->writer);
+ file->writer = NULL;
+}
+
+//
+// This allocates a reportfile_t structure and initializes it.
+//
+reportfile_t*
+new_reportfile(void)
+{
+ // create a new reportfile_t
+ reportfile_t* file = (reportfile_t*)xmalloc(sizeof(*file));
+
+ // set up a libxml 'buffer' and 'writer' to that buffer
+ file->buf = xxmlBufferCreate();
+ file->writer = xxmlNewTextWriterMemory(file->buf);
+
+ // start a new xml document:
+ // <report xmlns="http://www.redhat.com/gss/strata">...
+ xxmlTextWriterStartDocument(file->writer, /*version:*/ NULL, /*encoding:*/ NULL, /*standalone:*/ NULL);
+ xxmlTextWriterStartElement(file->writer, "report");
+ xxmlTextWriterWriteAttribute(file->writer, "xmlns", "http://www.redhat.com/gss/strata");
+
+ return file;
+}
+
+static void
+internal_reportfile_start_binding(reportfile_t* file, const char* name, int isbinary, const char* filename)
+{
+ // <binding name=NAME [fileName=FILENAME] type=text/binary...
+ xxmlTextWriterStartElement(file->writer, "binding");
+ xxmlTextWriterWriteAttribute(file->writer, "name", name);
+ if (filename)
+ xxmlTextWriterWriteAttribute(file->writer, "fileName", filename);
+ if (isbinary)
+ xxmlTextWriterWriteAttribute(file->writer, "type", "binary");
+ else
+ xxmlTextWriterWriteAttribute(file->writer, "type", "text");
+}
+
+//
+// Add a new text binding
+//
+void
+reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value)
+{
+ // <binding name=NAME type=text value=VALUE>
+ internal_reportfile_start_binding(file, name, /*isbinary:*/ 0, /*filename:*/ NULL);
+ xxmlTextWriterWriteAttribute(file->writer, "value", value);
+ xxmlTextWriterEndElement(file->writer);
+}
+
+//
+// Add a new binding to a report whose value is represented as a file.
+//
+void
+reportfile_add_binding_from_namedfile(reportfile_t* file,
+ const char* on_disk_filename, /* unused so far */
+ const char* binding_name,
+ const char* recorded_filename,
+ int isbinary)
+{
+ // <binding name=NAME fileName=FILENAME type=text/binary...
+ internal_reportfile_start_binding(file, binding_name, isbinary, recorded_filename);
+ // ... href=content/NAME>
+ char *href_name = concat_path_file("content", binding_name);
+ xxmlTextWriterWriteAttribute(file->writer, "href", href_name);
+ free(href_name);
+}
+
+//
+// Return the contents of the reportfile as a string.
+//
+const char*
+reportfile_as_string(reportfile_t* file)
+{
+ close_writer(file);
+ // unsigned char -> char
+ return (char*)file->buf->content;
+}
+
+void
+reportfile_free(reportfile_t* file)
+{
+ if (!file)
+ return;
+ close_writer(file);
+ xmlBufferFree(file->buf);
+ free(file);
+}
+
+
+//
+// send_report_to_new_case()
+//
+
+static char*
+make_case_data(const char* summary, const char* description,
+ const char* product, const char* version,
+ const char* component)
+{
+ char* retval;
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xxmlBufferCreate();
+ writer = xxmlNewTextWriterMemory(buf);
+
+ xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes");
+ xxmlTextWriterStartElement(writer, "case");
+ xxmlTextWriterWriteAttribute(writer, "xmlns",
+ "http://www.redhat.com/gss/strata");
+
+ xxmlTextWriterWriteElement(writer, "summary", summary);
+ xxmlTextWriterWriteElement(writer, "description", description);
+ if (product) {
+ xxmlTextWriterWriteElement(writer, "product", product);
+ }
+ if (version) {
+ xxmlTextWriterWriteElement(writer, "version", version);
+ }
+ if (component) {
+ xxmlTextWriterWriteElement(writer, "component", component);
+ }
+
+ xxmlTextWriterEndDocument(writer);
+ retval = xstrdup((const char*)buf->content);
+ xmlFreeTextWriter(writer);
+ xmlBufferFree(buf);
+ return retval;
+}
+
+#if 0 //unused
+static char*
+make_response(const char* title, const char* body,
+ const char* actualURL, const char* displayURL)
+{
+ char* retval;
+ xmlTextWriterPtr writer;
+ xmlBufferPtr buf;
+
+ buf = xxmlBufferCreate();
+ writer = xxmlNewTextWriterMemory(buf);
+
+ xxmlTextWriterStartDocument(writer, NULL, "UTF-8", "yes");
+ xxmlTextWriterStartElement(writer, "response");
+ if (title) {
+ xxmlTextWriterWriteElement(writer, "title", title);
+ }
+ if (body) {
+ xxmlTextWriterWriteElement(writer, "body", body);
+ }
+ if (actualURL || displayURL) {
+ xxmlTextWriterStartElement(writer, "URL");
+ if (actualURL) {
+ xxmlTextWriterWriteAttribute(writer, "href", actualURL);
+ }
+ if (displayURL) {
+ xxmlTextWriterWriteString(writer, displayURL);
+ }
+ }
+
+ xxmlTextWriterEndDocument(writer);
+ retval = xstrdup((const char*)buf->content);
+ xmlFreeTextWriter(writer);
+ xmlBufferFree(buf);
+ return retval;
+}
+//Example:
+//<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+//<response><title>Case Created and Report Attached</title><body></body><URL href="http://support-services-devel.gss.redhat.com:8080/Strata/cases/00005129/a...">New Case URL</URL></response>
+#endif
+
+char*
+send_report_to_new_case(const char* baseURL,
+ const char* username,
+ const char* password,
+ bool ssl_verify,
+ const char* summary,
+ const char* description,
+ const char* component,
+ const char* report_file_name)
+{
+ char *case_url = concat_path_file(baseURL, "/cases");
+
+ char *case_data = make_case_data(summary, description,
+ "Red Hat Enterprise Linux", "6.0",
+ component);
+
+ int redirect_count = 0;
+ char *errmsg;
+ char *allocated = NULL;
+ char* retval = NULL;
+ abrt_post_state_t *case_state;
+
+ redirect_case:
+ case_state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ case_state->username = username;
+ case_state->password = password;
+
+ static const char *headers[] = {
+ "Accept: text/plain",
+ NULL
+ };
+
+ abrt_post_string(case_state, case_url, "application/xml", headers, case_data);
+
+ char *case_location = find_header_in_abrt_post_state(case_state, "Location:");
+ switch (case_state->http_resp_code)
+ {
+ case 404:
+ /* Not strictly necessary (default branch would deal with it too),
+ * but makes this typical error less cryptic:
+ * instead of returning html-encoded body, we show short concise message,
+ * and show offending URL (typos in which is a typical cause) */
+ retval = xasprintf("error in case creation, "
+ "HTTP code: 404 (Not found), URL:'%s'", case_url);
+ break;
+
+ case 301: /* "301 Moved Permanently" (for example, used to move http:// to https://) */
+ case 302: /* "302 Found" (just in case) */
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && case_location)
+ {
+ free(case_url);
+ case_url = xstrdup(case_location);
+ free_abrt_post_state(case_state);
+ goto redirect_case;
+ }
+ /* fall through */
+
+ default:
+ errmsg = case_state->curl_error_msg;
+ if (errmsg && errmsg[0])
+ retval = xasprintf("error in case creation: %s", errmsg);
+ else
+ {
+ errmsg = case_state->body;
+ if (errmsg && errmsg[0])
+ retval = xasprintf("error in case creation, HTTP code: %d, server says: '%s'",
+ case_state->http_resp_code, errmsg);
+ else
+ retval = xasprintf("error in case creation, HTTP code: %d",
+ case_state->http_resp_code);
+ }
+ break;
+
+ case 200:
+ case 201: {
+ if (!case_location) {
+ /* Case Creation returned valid code, but no location */
+ retval = xasprintf("error in case creation: no Location URL, HTTP code: %d",
+ case_state->http_resp_code);
+ break;
+ }
+
+ char *atch_url = concat_path_file(case_location, "/attachments");
+ abrt_post_state_t *atch_state;
+ redirect_attach:
+ atch_state = new_abrt_post_state(0
+ + ABRT_POST_WANT_HEADERS
+ + ABRT_POST_WANT_BODY
+ + ABRT_POST_WANT_ERROR_MSG
+ + (ssl_verify ? ABRT_POST_WANT_SSL_VERIFY : 0)
+ );
+ atch_state->username = username;
+ atch_state->password = password;
+
+ abrt_post_file_as_form(atch_state, atch_url, "application/binary", headers,
+ report_file_name);
+
+ char *atch_location = find_header_in_abrt_post_state(atch_state, "Location:");
+ switch (atch_state->http_resp_code)
+ {
+ case 305: /* "305 Use Proxy" */
+ if (++redirect_count < 10 && atch_location)
+ {
+ free(atch_url);
+ atch_url = xstrdup(atch_location);
+ free_abrt_post_state(atch_state);
+ goto redirect_attach;
+ }
+ /* fall through */
+
+ default:
+ /* Case Creation Succeeded, attachement FAILED */
+ errmsg = atch_state->curl_error_msg;
+ if (atch_state->body && atch_state->body[0])
+ {
+ if (errmsg && errmsg[0]
+ && strcmp(errmsg, atch_state->body) != 0
+ ) /* both strata/curl error and body are present (and aren't the same) */
+ allocated = errmsg = xasprintf("%s. %s",
+ atch_state->body,
+ errmsg);
+ else /* only body exists */
+ errmsg = atch_state->body;
+ }
+ /* Note: to prevent URL misparsing, make sure to delimit
+ * case_location only using spaces */
+ retval = xasprintf("Case created: %s but report attachment failed (HTTP code %d)%s%s",
+ case_location,
+ atch_state->http_resp_code,
+ errmsg ? ": " : "",
+ errmsg ? errmsg : ""
+ );
+ break;
+
+ case 200:
+ case 201:
+ // unused
+ //char *body = atch_state->body;
+ //if (case_state->body && case_state->body[0])
+ //{
+ // body = case_state->body;
+ // if (atch_state->body && atch_state->body[0])
+ // allocated = body = xasprintf("%s\n%s",
+ // case_state->body,
+ // atch_state->body);
+ //}
+ retval = xasprintf("Case created: %s", /*body,*/ case_location);
+ } /* switch (attach HTTP code) */
+
+ free_abrt_post_state(atch_state);
+ free(atch_url);
+ } /* case 200/201 */
+
+ } /* switch (case HTTP code) */
+
+ free_abrt_post_state(case_state);
+ free(allocated);
+ free(case_url);
+ return retval;
+}
diff --git a/src/plugins/abrt_rh_support.h b/src/plugins/abrt_rh_support.h
new file mode 100644
index 0000000..db6e9cd
--- /dev/null
+++ b/src/plugins/abrt_rh_support.h
@@ -0,0 +1,55 @@
+/*
+ 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 ABRT_RH_SUPPORT_H_
+#define ABRT_RH_SUPPORT_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct reportfile reportfile_t;
+
+reportfile_t *new_reportfile(void);
+void reportfile_free(reportfile_t* file);
+
+void reportfile_add_binding_from_string(reportfile_t* file, const char* name, const char* value);
+void reportfile_add_binding_from_namedfile(reportfile_t* file,
+ const char* on_disk_filename, /* unused so far */
+ const char* binding_name,
+ const char* recorded_filename,
+ int isbinary);
+
+const char* reportfile_as_string(reportfile_t* file);
+
+char* post_signature(const char* baseURL, bool ssl_verify, const char* signature);
+char*
+send_report_to_new_case(const char* baseURL,
+ const char* username,
+ const char* password,
+ bool ssl_verify,
+ const char* summary,
+ const char* description,
+ const char* component,
+ const char* report_file_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/mailx_events.conf b/src/plugins/mailx_events.conf
new file mode 100644
index 0000000..9b71e83
--- /dev/null
+++ b/src/plugins/mailx_events.conf
@@ -0,0 +1 @@
+EVENT=report_Mailx reporter-mailx
diff --git a/src/plugins/print_events.conf b/src/plugins/print_events.conf
new file mode 100644
index 0000000..7b94aba
--- /dev/null
+++ b/src/plugins/print_events.conf
@@ -0,0 +1,2 @@
+EVENT=report_Logger
+ reporter-print -o "${Log_File:-/tmp/abrt.log}" -a "${Append:-no}" -r
diff --git a/src/plugins/report_Bugzilla.conf b/src/plugins/report_Bugzilla.conf
new file mode 100644
index 0000000..f1a77f5
--- /dev/null
+++ b/src/plugins/report_Bugzilla.conf
@@ -0,0 +1,4 @@
+Bugzilla_BugzillaURL = https://bugzilla.redhat.com
+Bugzilla_Login =
+Bugzilla_Password =
+Bugzilla_SSLVerify = yes
diff --git a/src/plugins/report_Bugzilla.xml.in b/src/plugins/report_Bugzilla.xml.in
new file mode 100644
index 0000000..00ef4a4
--- /dev/null
+++ b/src/plugins/report_Bugzilla.xml.in
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<event>
+ <_name>Bugzilla</_name>
+ <_description>Report to Bugzilla bug tracker</_description>
+
+ <requires-items>component,duphash,os_release</requires-items>
+ <exclude-items-by-default>coredump,count,event_log,reported_to</exclude-items-by-default>
+ <exclude-items-always></exclude-items-always>
+ <exclude-binary-items>yes</exclude-binary-items>
+ <include-items-by-default></include-items-by-default>
+
+ <options>
+ <option type="text" name="Bugzilla_BugzillaURL">
+ <_label>Bugzilla URL</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Address of Bugzilla server</_description>
+ <default-value>https://bugzilla.redhat.com</default-value>
+ <_note-html>You can create bugzilla.redhat.com account <a href="https://bugzilla.redhat.com/createaccount.cgi">here</a></_note-html>
+ </option>
+ <option type="text" name="Bugzilla_Login">
+ <_label>User name</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Bugzilla account user name</_description>
+ </option>
+ <option type="password" name="Bugzilla_Password">
+ <_label>Password</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Bugzilla account password</_description>
+ </option>
+ <option type="bool" name="Bugzilla_SSLVerify">
+ <_label>Verify SSL</_label>
+ <_description>Check SSL key validity</_description>
+ <default-value>yes</default-value>
+ </option>
+ </options>
+</event>
diff --git a/src/plugins/report_Kerneloops.xml.in b/src/plugins/report_Kerneloops.xml.in
new file mode 100644
index 0000000..a4da6b6
--- /dev/null
+++ b/src/plugins/report_Kerneloops.xml.in
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<event>
+ <_name>Kerneloops.org</_name>
+ <_description>Send kernel problems to oops tracker</_description>
+
+ <requires-items>backtrace</requires-items>
+ <exclude-items-by-default></exclude-items-by-default>
+ <exclude-items-always>*</exclude-items-always>
+ <exclude-binary-items>yes</exclude-binary-items>
+ <include-items-by-default></include-items-by-default>
+
+ <options>
+ <option type="text" name="KerneloopsReporter_SubmitURL">
+ <_label>Kerneloops URL</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Oops server url</_description>
+ <default-value>http://submit.kerneloops.org/submitoops.php</default-value>
+ </option>
+ </options>
+</event>
diff --git a/src/plugins/report_Logger.conf b/src/plugins/report_Logger.conf
new file mode 100644
index 0000000..d885ce5
--- /dev/null
+++ b/src/plugins/report_Logger.conf
@@ -0,0 +1,2 @@
+Log_File=/tmp/abrt.log
+Append=yes
diff --git a/src/plugins/report_Mailx.xml.in b/src/plugins/report_Mailx.xml.in
new file mode 100644
index 0000000..2476a3d
--- /dev/null
+++ b/src/plugins/report_Mailx.xml.in
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<event>
+ <_name>Mailx</_name>
+ <_description>Send via email</_description>
+
+ <requires-items></requires-items>
+ <exclude-items-by-default>coredump</exclude-items-by-default>
+ <exclude-items-always></exclude-items-always>
+ <exclude-binary-items>no</exclude-binary-items>
+ <include-items-by-default></include-items-by-default>
+
+ <options>
+ <option type="text" name="Mailx_Subject">
+ <_label>Subject</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Message subject</_description>
+ <default-value>[abrt] detected a crash</default-value>
+ </option>
+ <option type="text" name="Mailx_EmailFrom">
+ <_label>Sender</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Sender's email</_description>
+ </option>
+ <option type="text" name="Mailx_EmailTo">
+ <_label>Recipient</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Recipient's email</_description>
+ </option>
+ <option type="bool" name="Mailx_SendBinaryData">
+ <_label>Send Binary Data</_label>
+ <_description>Send binary files like coredump</_description>
+ <default-value>no</default-value>
+ </option>
+ </options>
+</event>
diff --git a/src/plugins/report_RHTSupport.xml.in b/src/plugins/report_RHTSupport.xml.in
new file mode 100644
index 0000000..834528a
--- /dev/null
+++ b/src/plugins/report_RHTSupport.xml.in
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<event>
+ <_name>Red Hat Customer Support</_name>
+ <_description>Report to Red Hat support</_description>
+
+ <requires-items>package</requires-items>
+ <exclude-items-by-default>event_log,reported_to</exclude-items-by-default>
+ <exclude-items-always></exclude-items-always>
+ <exclude-binary-items>no</exclude-binary-items>
+ <include-items-by-default></include-items-by-default>
+
+ <options>
+ <option type="text" name="RHTSupport_URL">
+ <_label>RH Portal URL</_label>
+ <allow-empty>no</allow-empty>
+ <_description>Address of the Red Hat support portal</_description>
+ <default-value>https://api.access.redhat.com/rs</default-value>
+ </option>
+ <option type="text" name="RHTSupport_Login">
+ <_label>Username</_label>
+ <_description>Red Hat customer user name</_description>
+ <allow-empty>no</allow-empty>
+ </option>
+ <option type="password" name="RHTSupport_Password">
+ <_label>Password</_label>
+ <_description>Red Hat customer password</_description>
+ <allow-empty>no</allow-empty>
+ </option>
+ <option type="bool" name="RHTSupport_SSLVerify">
+ <_label>Verify SSL</_label>
+ <_description>Check SSL key validity</_description>
+ <default-value>yes</default-value>
+ </option>
+ </options>
+</event>
diff --git a/src/plugins/reporter-bugzilla.c b/src/plugins/reporter-bugzilla.c
new file mode 100644
index 0000000..99b03e7
--- /dev/null
+++ b/src/plugins/reporter-bugzilla.c
@@ -0,0 +1,291 @@
+/*
+ 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 "internal_libreport.h"
+#include "abrt_xmlrpc.h"
+#include "rhbz.h"
+
+#define XML_RPC_SUFFIX "/xmlrpc.cgi"
+
+static void report_to_bugzilla(const char *dump_dir_name, map_string_h *settings)
+{
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ const char *env;
+ const char *login;
+ const char *password;
+ const char *bugzilla_xmlrpc;
+ const char *bugzilla_url;
+ bool ssl_verify;
+
+ env = getenv("Bugzilla_Login");
+ login = env ? env : get_map_string_item_or_empty(settings, "Login");
+ env = getenv("Bugzilla_Password");
+ password = env ? env : get_map_string_item_or_empty(settings, "Password");
+ if (!login[0] || !password[0])
+ error_msg_and_die(_("Empty login or password, please check your configuration"));
+
+ env = getenv("Bugzilla_BugzillaURL");
+ bugzilla_url = env ? env : get_map_string_item_or_empty(settings, "BugzillaURL");
+ if (!bugzilla_url[0])
+ bugzilla_url = "https://bugzilla.redhat.com";
+ bugzilla_xmlrpc = xasprintf("%s"XML_RPC_SUFFIX, bugzilla_url);
+
+ env = getenv("Bugzilla_SSLVerify");
+ ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));
+
+ const char *component = get_problem_item_content_or_NULL(problem_data, FILENAME_COMPONENT);
+ const char *duphash = get_problem_item_content_or_NULL(problem_data, FILENAME_DUPHASH);
+//COMPAT, remove after 2.1 release
+ if (!duphash) duphash = get_problem_item_content_or_NULL(problem_data, "global_uuid");
+ if (!duphash)
+ error_msg_and_die(_("Essential file '%s' is missing, can't continue.."),
+ FILENAME_DUPHASH);
+
+ if (!*duphash)
+ error_msg_and_die(_("Essential file '%s' is empty, can't continue.."),
+ FILENAME_DUPHASH);
+
+ const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE);
+ if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
+ release = get_problem_item_content_or_NULL(problem_data, "release");
+
+ struct abrt_xmlrpc *client = abrt_xmlrpc_new_client(bugzilla_xmlrpc, ssl_verify);
+
+ log(_("Logging into Bugzilla at %s"), bugzilla_url);
+ rhbz_login(client, login, password);
+
+ log(_("Checking for duplicates"));
+ char *product = NULL;
+ char *version = NULL;
+ parse_release_for_bz(release, &product, &version);
+ free(version);
+
+ xmlrpc_value *result;
+ if (strcmp(product, "Fedora") == 0)
+ result = rhbz_search_duphash(client, component, product, duphash);
+ else
+ result = rhbz_search_duphash(client, component, NULL, duphash);
+
+ xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ if (!all_bugs)
+ error_msg_and_die(_("Missing mandatory member 'bugs'"));
+
+ int all_bugs_size = rhbz_array_size(all_bugs);
+ // 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 %i reports with same duphash '%s'",
+ all_bugs_size, duphash);
+
+ int bug_id = -1;
+ struct bug_info *bz = NULL;
+ if (all_bugs_size > 0)
+ {
+ bug_id = rhbz_bug_id(all_bugs);
+ xmlrpc_DECREF(all_bugs);
+ bz = rhbz_bug_info(client, bug_id);
+
+ if (strcmp(bz->bi_product, product) != 0)
+ {
+ /* found something, but its a different product */
+ free_bug_info(bz);
+
+ xmlrpc_value *result = rhbz_search_duphash(client, component,
+ product, duphash);
+ xmlrpc_value *all_bugs = rhbz_get_member("bugs", result);
+ xmlrpc_DECREF(result);
+
+ all_bugs_size = rhbz_array_size(all_bugs);
+ if (all_bugs_size > 0)
+ {
+ bug_id = rhbz_bug_id(all_bugs);
+ bz = rhbz_bug_info(client, bug_id);
+ }
+ xmlrpc_DECREF(all_bugs);
+ }
+ }
+ free(product);
+
+ if (all_bugs_size == 0) // Create new bug
+ {
+ log(_("Creating a new bug"));
+ bug_id = rhbz_new_bug(client, problem_data, bug_id);
+
+ log("Adding attachments to bug %i", bug_id);
+ char bug_id_str[sizeof(int)*3 + 2];
+ sprintf(bug_id_str, "%i", bug_id);
+
+ rhbz_attachments(client, bug_id_str, problem_data, RHBZ_NOMAIL_NOTIFY);
+
+ bz = new_bug_info();
+ bz->bi_status = xstrdup("NEW");
+ bz->bi_id = bug_id;
+ goto log_out;
+ }
+
+ // decision based on state
+ log(_("Bug is already reported: %i"), bz->bi_id);
+ if ((strcmp(bz->bi_status, "CLOSED") == 0)
+ && (strcmp(bz->bi_resolution, "DUPLICATE") == 0)
+ ) {
+ struct bug_info *origin;
+ origin = rhbz_find_origin_bug_closed_duplicate(client, bz);
+ if (origin)
+ {
+ free_bug_info(bz);
+ bz = origin;
+ }
+ }
+
+ if (strcmp(bz->bi_status, "CLOSED") != 0)
+ {
+ if ((strcmp(bz->bi_reporter, login) != 0)
+ && (!g_list_find_custom(bz->bi_cc_list, login, (GCompareFunc)g_strcmp0)))
+ {
+ log(_("Add %s to CC list"), login);
+ rhbz_mail_to_cc(client, bz->bi_id, login, RHBZ_NOMAIL_NOTIFY);
+ }
+
+ const char *comment = get_problem_item_content_or_NULL(problem_data, FILENAME_COMMENT);
+ if (comment && comment[0])
+ {
+ const char *package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE);
+ const char *release = get_problem_item_content_or_NULL(problem_data, FILENAME_OS_RELEASE);
+//COMPAT, remove in abrt-2.1 release
+ if (!release)release= get_problem_item_content_or_NULL(problem_data, "release");
+ const char *arch = get_problem_item_content_or_NULL(problem_data, FILENAME_ARCHITECTURE);
+ char *full_dsc = xasprintf("Package: %s\n"
+ "Architecture: %s\n"
+ "OS Release: %s\n"
+ "\n"
+ "Comment\n"
+ "-----\n"
+ "%s\n",
+ package, arch, release, comment
+ );
+ log(_("Adding new comment to bug %d"), bz->bi_id);
+ /* unused code, enable it when gui/cli will be ready
+ int is_priv = is_private && string_to_bool(is_private);
+ const char *is_private = get_problem_item_content_or_NULL(problem_data,
+ "is_private");
+ */
+ rhbz_add_comment(client, bz->bi_id, full_dsc, 0);
+ free(full_dsc);
+ }
+ }
+
+ log_out:
+ log(_("Logging out"));
+ rhbz_logout(client);
+
+ log("Status: %s%s%s %s/show_bug.cgi?id=%u",
+ bz->bi_status,
+ bz->bi_resolution ? " " : "",
+ bz->bi_resolution ? bz->bi_resolution : "",
+ bugzilla_url,
+ bz->bi_id);
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("Bugzilla: URL=%s/show_bug.cgi?id=%u", bugzilla_url, bz->bi_id);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+
+ free_problem_data(problem_data);
+ free_bug_info(bz);
+ abrt_xmlrpc_free_client(client);
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ map_string_h *settings = new_map_string();
+ const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] [-c CONFFILE] -d DIR\n"
+ "\n"
+ "Reports problem to Bugzilla.\n"
+ "\n"
+ "The tool reads DIR. Then it logs in to Bugzilla and tries to find a bug\n"
+ "with the same abrt_hash:HEXSTRING in 'Whiteboard'.\n"
+ "\n"
+ "If such bug is not found, then a new bug is created. Elements of DIR\n"
+ "are stored in the bug as part of bug description or as attachments,\n"
+ "depending on their type and size.\n"
+ "\n"
+ "Otherwise, if such bug is found and it is marked as CLOSED DUPLICATE,\n"
+ "the tool follows the chain of duplicates until it finds a non-DUPLICATE bug.\n"
+ "The tool adds a new comment to found bug.\n"
+ "\n"
+ "The URL to new or modified bug is printed to stdout and recorded in\n"
+ "'reported_to' element.\n"
+ "\n"
+ "CONFFILE lines should have 'PARAM = VALUE' format.\n"
+ "Recognized string parameters: BugzillaURL, Login, Password.\n"
+ "Recognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify.\n"
+ "Parameters can be overridden via $Bugzilla_PARAM environment variables."
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
+ VERB1 log("Initializing XML-RPC library");
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+ xmlrpc_client_setup_global_const(&env);
+ if (env.fault_occurred)
+ error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
+ xmlrpc_env_clean(&env);
+
+ report_to_bugzilla(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return 0;
+}
diff --git a/src/plugins/reporter-bugzilla.txt b/src/plugins/reporter-bugzilla.txt
new file mode 100644
index 0000000..12cc645
--- /dev/null
+++ b/src/plugins/reporter-bugzilla.txt
@@ -0,0 +1,67 @@
+reporter-buzilla(1)
+====================
+
+NAME
+----
+reporter-bugzilla - Reports problem to Bugzilla.
+
+SYNOPSIS
+--------
+'reporter-bugzilla' [-v] [-c CONFFILE] -d DIR
+
+DESCRIPTION
+-----------
+The tool reads problem dump directory DIR. Then it logs in to Bugzilla
+and tries to find a bug with the same abrt_hash:HEXSTRING in 'Whiteboard'.
+
+If such bug is not found, then a new bug is created. Elements of DIR
+are stored in the bug as part of bug description or as attachments,
+depending on their type and size.
+
+Otherwise, if such bug is found and it is marked as CLOSED DUPLICATE,
+the tool follows the chain of duplicates until it finds a non-DUPLICATE bug.
+The tool adds a new comment to found bug.
+
+The URL to new or modified bug is printed to stdout and recorded in
+'reported_to' element in DIR.
+
+Configuration file
+~~~~~~~~~~~~~~~~~~
+Configuration file lines should have 'PARAM = VALUE' format. The parameters are:
+
+'Login'::
+ Login to Bugzilla account.
+
+'Password'::
+ Password to Bugzilla account.
+
+'BugzillaURL'::
+ Bugzilla http(s) address. (default: https://bugzilla.redht.com)
+
+'SSLVerify'::
+ Use yes/true/on/1 to verify Bugzilla ssl certificate. (default: yes)
+
+Parameters can be overridden via $Bugzilla_PARAM environment variables.
+
+Integration with ABRT events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'reporter-bugzilla' can be used as an ABRT reporter. Example
+fragment for /etc/abrt/abrt_events.conf:
+
+------------
+# Report Python crashes
+EVENT=report_Bugzilla analyzer=Python
+ reporter-bugzilla -d . -c /etc/abrt/plugins/Bugzilla.conf
+------------
+
+OPTIONS
+-------
+-d DIR::
+ Path to dump directory.
+
+-c CONFFILE::
+ Path to configration file.
+
+SEE ALSO
+--------
+abrt_event.conf
diff --git a/src/plugins/reporter-kerneloops.c b/src/plugins/reporter-kerneloops.c
new file mode 100644
index 0000000..e03df36
--- /dev/null
+++ b/src/plugins/reporter-kerneloops.c
@@ -0,0 +1,166 @@
+/*
+ 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(a)redhat.com>
+ Arjan van de Ven <arjan(a)linux.intel.com>
+ */
+#include <curl/curl.h>
+#include "internal_libreport.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'", (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 = curl_easy_init();
+ if (!handle)
+ error_msg_and_die("Can't create curl handle");
+
+ 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;
+}
+
+static void report_to_kerneloops(
+ const char *dump_dir_name,
+ map_string_h *settings)
+{
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ const char *backtrace = get_problem_item_content_or_NULL(problem_data, FILENAME_BACKTRACE);
+ if (!backtrace)
+ error_msg_and_die("Error sending kernel oops due to missing backtrace");
+
+ const char *env = getenv("KerneloopsReporter_SubmitURL");
+ const char *submitURL = (env ? env : get_map_string_item_or_empty(settings, "SubmitURL"));
+ if (!submitURL[0])
+ submitURL = "http://submit.kerneloops.org/submitoops.php";
+
+ log(_("Submitting oops report to %s"), submitURL);
+
+ CURLcode ret = http_post_to_kerneloops_site(submitURL, backtrace);
+ if (ret != CURLE_OK)
+ error_msg_and_die("Kernel oops has not been sent due to %s", curl_easy_strerror(ret));
+
+ free_problem_data(problem_data);
+
+ /* Server replies with:
+ * 200 thank you for submitting the kernel oops information
+ * RemoteIP: 34192fd15e34bf60fac6a5f01bba04ddbd3f0558
+ * - no URL or bug ID apparently...
+ */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("kerneloops: URL=%s", submitURL);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+
+ log("Kernel oops report was uploaded");
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ map_string_h *settings = new_map_string();
+ const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] [-c CONFFILE]... -d DIR\n"
+ "\n"
+ "Reports kernel oops to kerneloops.org (or similar) site"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
+ report_to_kerneloops(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return 0;
+}
diff --git a/src/plugins/reporter-kerneloops.txt b/src/plugins/reporter-kerneloops.txt
new file mode 100644
index 0000000..8701dfb
--- /dev/null
+++ b/src/plugins/reporter-kerneloops.txt
@@ -0,0 +1,68 @@
+reporter-kerneloops(1)
+======================
+
+NAME
+----
+reporter-kerneloops - Reports kernel oops to kerneloops.org (or similar)
+site.
+
+SYNOPSIS
+--------
+'reporter-kerneloops' [-v] [-c CONFFILE]... [ -d DIR ]
+
+DESCRIPTION
+-----------
+The tool is used to report the crash to the Kerneloops tracker.
+
+Configuration file
+~~~~~~~~~~~~~~~~~~
+Configuration file contains entries in a format "Option = Value".
+
+The options are:
+
+'SubmitURL'::
+ The URL of the kerneloops tracker, the default is
+ "http://submit.kerneloops.org/submitoops.php".
+
+Integration with ABRT events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'reporter-kerneloops' can be used as a reporter, to allow users to report
+problems to the Kerneloops tracker. This usage is pre-configured in
+/etc/abrt/events.d/koops_events.conf:
+
+------------
+EVENT=report_Kerneloops analyzer=Kerneloops reporter-kerneloops
+------------
+
+It can be also used automatically and immediately without user interaction.
+When this is desired, modify the event configuration file to run the tool on
+the 'post-create' event:
+
+------------
+EVENT=post-create analyzer=Kerneloops reporter-kerneloops
+------------
+
+OPTIONS
+-------
+-v::
+ Be more verbose. Can be given multiple times.
+
+-d DIR::
+ Path to dump directory.
+
+-c CONFFILE::
+ Path to configration file. When used in ABRT event system, the file
+ contains site-wide configuration. Users can change the values via
+ environment variables.
+
+ENVIRONMENT VARIABLES
+---------------------
+Environment variables take precedence over values provided in
+the configuration file.
+
+'KerneloopsReporter_SubmitURL'::
+ The URL of the kerneloops tracker.
+
+AUTHORS
+-------
+* ABRT team
diff --git a/src/plugins/reporter-mailx.c b/src/plugins/reporter-mailx.c
new file mode 100644
index 0000000..99ac586
--- /dev/null
+++ b/src/plugins/reporter-mailx.c
@@ -0,0 +1,162 @@
+/*
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl(a)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 "internal_libreport.h"
+
+static void exec_and_feed_input(const char* text, char **args)
+{
+ int pipein[2];
+
+ pid_t child = fork_execv_on_steroids(
+ EXECFLG_INPUT | EXECFLG_QUIET,
+ args,
+ pipein,
+ /*env_vec:*/ NULL,
+ /*dir:*/ NULL,
+ /*uid (ignored):*/ 0
+ );
+
+ full_write_str(pipein[1], text);
+ close(pipein[1]);
+
+ int status;
+ waitpid(child, &status, 0); /* wait for command completion */
+ if (status != 0)
+ error_msg_and_die("Error running '%s'", args[0]);
+}
+
+static char** append_str_to_vector(char **vec, unsigned *size_p, const char *str)
+{
+ //log("old vec: %p", vec);
+ unsigned size = *size_p;
+ 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;
+ *size_p = size;
+ return vec;
+}
+
+static void create_and_send_email(
+ const char *dump_dir_name,
+ map_string_h *settings)
+{
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ char* env;
+ env = getenv("Mailx_Subject");
+ const char *subject = (env ? env : get_map_string_item_or_NULL(settings, "Subject") ? : "[abrt] full crash report");
+ env = getenv("Mailx_EmailFrom");
+ const char *email_from = (env ? env : get_map_string_item_or_NULL(settings, "EmailFrom") ? : "user@localhost");
+ env = getenv("Mailx_EmailTo");
+ const char *email_to = (env ? env : get_map_string_item_or_NULL(settings, "EmailTo") ? : "root@localhost");
+ env = getenv("Mailx_SendBinaryData");
+ bool send_binary_data = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SendBinaryData"));
+
+ char **args = NULL;
+ unsigned arg_size = 0;
+ args = append_str_to_vector(args, &arg_size, "/bin/mailx");
+
+ char *dsc = make_description_mailx(problem_data);
+
+ if (send_binary_data)
+ {
+ GHashTableIter iter;
+ char *name;
+ struct problem_item *value;
+ g_hash_table_iter_init(&iter, problem_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ if (value->flags & CD_FLAG_BIN)
+ {
+ args = append_str_to_vector(args, &arg_size, "-a");
+ args = append_str_to_vector(args, &arg_size, value->content);
+ }
+ }
+ }
+
+ args = append_str_to_vector(args, &arg_size, "-s");
+ args = append_str_to_vector(args, &arg_size, subject);
+ args = append_str_to_vector(args, &arg_size, "-r");
+ args = append_str_to_vector(args, &arg_size, email_from);
+ args = append_str_to_vector(args, &arg_size, email_to);
+
+ log(_("Sending an email..."));
+ exec_and_feed_input(dsc, args);
+
+ free(dsc);
+
+ while (*args)
+ free(*args++);
+ args -= arg_size;
+ free(args);
+
+ free_problem_data(problem_data);
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("email: %s", email_to);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+ log("Email was sent to: %s", email_to);
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ const char *dump_dir_name = ".";
+ const char *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] -d DIR [-c CONFFILE]\n"
+ "\n"
+ "Sends contents of a dump directory DIR via email"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_STRING('c', NULL, &conf_file , "CONFFILE", _("Config file")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ map_string_h *settings = new_map_string();
+ if (conf_file)
+ load_conf_file(conf_file, settings, /*skip key w/o values:*/ true);
+
+ create_and_send_email(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return 0;
+}
diff --git a/src/plugins/reporter-mailx.txt b/src/plugins/reporter-mailx.txt
new file mode 100644
index 0000000..319a3a1
--- /dev/null
+++ b/src/plugins/reporter-mailx.txt
@@ -0,0 +1,91 @@
+reporter-mailx(1)
+=================
+
+NAME
+----
+reporter-mailx - Sends contents of a dump directory via email.
+
+SYNOPSIS
+--------
+'reporter-mailx' [-v] -d DIR [-c CONFFILE]
+
+DESCRIPTION
+-----------
+The tool reads a problem dump directory, composes an email message
+from the directory contents, and uses mailx to send the message to
+specified recipient.
+
+Properties of email messages can be specified in a configuration file,
+and via environment variables.
+
+Configuration file
+~~~~~~~~~~~~~~~~~~
+Configuration file contains entries in a format "Option = Value".
+
+The options are:
+
+'Subject'::
+ The subject of the email message.
+
+'EmailFrom'::
+ The sender of the email message.
+
+'EmailTo'::
+ The recipient of the email message.
+
+'SendBinaryData'::
+ Use yes/true/on/1 to attach all binary files from the dump
+ directory to the email. This can cause the emails to be very
+ large.
+
+Integration with ABRT events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'reporter-mailx' can be used as a reporter, to allow users report
+problems via email when they decide to do it. This usage is
+pre-configured in /etc/abrt/events.d/mailx_events.conf:
+
+------------
+EVENT=report_Mailx reporter-mailx
+------------
+
+It can also be used to notify administrator automatically when a
+problem happens. When this is desired, modify the event configuration
+file to run the tool on the 'post-create' event:
+
+------------
+EVENT=post-create reporter-mailx
+------------
+
+OPTIONS
+-------
+-d DIR::
+ Path to dump directory.
+
+-c CONFFILE::
+ Path to configration file. When used in ABRT event system, the file
+ contains site-wide configuration. Users can change the values via
+ environment variables.
+
+ENVIRONMENT VARIABLES
+---------------------
+Environment variables take precedence over values provided in
+the configuration file.
+
+'Mailx_Subject'::
+ Subject of the email message.
+
+'Mailx_EmailFrom'::
+ Sender of the email message.
+
+'Mailx_EmailTo'::
+ Recipient of the email message. If nor the environment variable
+ nor the corresponding option is defined, the message is send to
+ "root@localhost".
+
+'Mailx_SendBinaryData'::
+ Use yes/true/on/1 to attach all binary files from the dump
+ directory to the email.
+
+AUTHORS
+-------
+* ABRT team
diff --git a/src/plugins/reporter-print.c b/src/plugins/reporter-print.c
new file mode 100644
index 0000000..c688b32
--- /dev/null
+++ b/src/plugins/reporter-print.c
@@ -0,0 +1,95 @@
+/*
+ Write crash dump to stdout in text form.
+
+ Copyright (C) 2009 Zdenek Prikryl (zprikryl(a)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 "internal_libreport.h"
+
+static const char *dump_dir_name = ".";
+static const char *output_file = NULL;
+static const char *append = "no";
+static const char *open_mode = "w";
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] -d DIR [-o FILE] [-a yes/no] [-r]\n"
+ "\n"
+ "Prints problem information to standard output or FILE"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_o = 1 << 2,
+ OPT_a = 1 << 3,
+ OPT_r = 1 << 4,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_STRING('o', NULL, &output_file , "FILE" , _("Output file")),
+ OPT_STRING('a', NULL, &append , "yes/no", _("Append to, or overwrite FILE")),
+ OPT_BOOL( 'r', NULL, NULL , _("Create reported_to in DIR")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ if (output_file)
+ {
+ if (string_to_bool(append))
+ open_mode = "a";
+ if (!freopen(output_file, open_mode, stdout))
+ perror_msg_and_die("Can't open '%s'", output_file);
+ }
+
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ char *dsc = make_description_logger(problem_data);
+ fputs(dsc, stdout);
+ if (open_mode[0] == 'a')
+ fputs("\nEND:\n\n", stdout);
+ free(dsc);
+ free_problem_data(problem_data);
+
+ if (output_file)
+ {
+ if (opts & OPT_r)
+ {
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("file: %s", output_file);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+ }
+ const char *format = (open_mode[0] == 'a' ? _("The report was appended to %s") : _("The report was stored to %s"));
+ log(format, output_file);
+ }
+
+ return 0;
+}
diff --git a/src/plugins/reporter-print.txt b/src/plugins/reporter-print.txt
new file mode 100644
index 0000000..67c800b
--- /dev/null
+++ b/src/plugins/reporter-print.txt
@@ -0,0 +1,63 @@
+reporter-print(1)
+=================
+
+NAME
+----
+reporter-print - Prints problem information to standard output or FILE.
+
+SYNOPSIS
+--------
+'reporter-print' [-v] [-d DIR] [-o FILE] [-a yes/no] [-r]
+
+DESCRIPTION
+-----------
+The tool reads dump directory DIR and prints its text representation
+to stdout or to a specified FILE.
+
+Integration with ABRT events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'reporter-print' can be used as a reporter, to allow users
+to report problems by writinf them to a file. Example:
+
+------------
+EVENT=report reporter-print -o "${Log_File:-/tmp/abrt.log}"
+------------
+
+OPTIONS
+-------
+-d DIR::
+ Path to dump directory.
+
+-v::
+ Be more verbose. Can be given multiple times.
+
+-o FILE::
+ Output file
+
+-a yes/no::
+ If -o FILE is specified, controls whether FILE is appended to, or overwritten.
+
+-r::
+ Add a recor to 'reported_to' in DIR which specifies that this problem
+ was reported. Some tools use this to differentiate between problems
+ which were and weren't yet reported.
+
+Output format
+~~~~~~~~~~~~~
+The output is designed to be machine-parseable. The elements which have only
+one line are printed in the form
+
+ NAME:<whitespace>VALUE
+
+Elements which have more than one line are printed in the form
+
+ NAME:
+ :LINE1
+ :LINE2
+ :LINE3
+
+Output may contain empty lines for better readability.
+
+AUTHORS
+-------
+* ABRT team
diff --git a/src/plugins/reporter-rhtsupport.c b/src/plugins/reporter-rhtsupport.c
new file mode 100644
index 0000000..4f6d2b6
--- /dev/null
+++ b/src/plugins/reporter-rhtsupport.c
@@ -0,0 +1,342 @@
+/*
+ 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 <libtar.h>
+#include "internal_libreport.h"
+#include "abrt_curl.h"
+#include "abrt_xmlrpc.h"
+#include "abrt_rh_support.h"
+
+static void report_to_rhtsupport(
+ const char *dump_dir_name,
+ map_string_h *settings)
+{
+ problem_data_t *problem_data = create_problem_data_for_reporting(dump_dir_name);
+ if (!problem_data)
+ xfunc_die(); /* create_problem_data_for_reporting already emitted error msg */
+
+ /* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */
+ log(_("Compressing data"));
+
+ const char* errmsg = NULL;
+ TAR* tar = NULL;
+ pid_t child;
+ char* tempfile = NULL;
+ reportfile_t* file = NULL;
+ char* dsc = NULL;
+ char* summary = NULL;
+ const char* function;
+ const char* reason;
+ const char* package;
+
+ char* env;
+ env = getenv("RHTSupport_URL");
+ char *url = xstrdup(env ? env : (get_map_string_item_or_NULL(settings, "URL") ? : "https://api.access.redhat.com/rs"));
+
+ env = getenv("RHTSupport_Login");
+ char *login = xstrdup(env ? env : get_map_string_item_or_empty(settings, "Login"));
+
+ env = getenv("RHTSupport_Password");
+ char *password = xstrdup(env ? env : get_map_string_item_or_empty(settings, "Password"));
+
+ env = getenv("RHTSupport_SSLVerify");
+ bool ssl_verify = string_to_bool(env ? env : get_map_string_item_or_empty(settings, "SSLVerify"));
+
+ if (!login[0] || !password[0])
+ {
+ error_msg_and_die(_("Empty RHTS login or password"));
+ }
+
+ package = get_problem_item_content_or_NULL(problem_data, FILENAME_PACKAGE);
+ reason = get_problem_item_content_or_NULL(problem_data, FILENAME_REASON);
+ function = get_problem_item_content_or_NULL(problem_data, FILENAME_CRASH_FUNCTION);
+
+ {
+ struct strbuf *buf_summary = strbuf_new();
+ strbuf_append_strf(buf_summary, "[abrt] %s", package);
+ if (function && strlen(function) < 30)
+ strbuf_append_strf(buf_summary, ": %s", function);
+ if (reason)
+ strbuf_append_strf(buf_summary, ": %s", reason);
+ summary = strbuf_free_nobuf(buf_summary);
+
+ char *bz_dsc = make_description_bz(problem_data);
+ dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc);
+ free(bz_dsc);
+ }
+ file = new_reportfile();
+ const char *dt_string = iso_date_string(NULL);
+ char tmpdir_name[sizeof("/tmp/rhtsupport-YYYY-MM-DD-hh:mm:ss-XXXXXX")];
+ sprintf(tmpdir_name, "/tmp/rhtsupport-%s-XXXXXX", dt_string);
+ /* mkdtemp does mkdir(xxx, 0700), should be safe (is it?) */
+ if (mkdtemp(tmpdir_name) == NULL)
+ {
+ error_msg_and_die(_("Can't create a temporary directory in /tmp"));
+ }
+
+ /* Starting from here, we must perform cleanup on errors
+ * (delete temp dir)
+ */
+
+ tempfile = xasprintf("%s/tmp-%s-%lu.tar.gz", tmpdir_name, iso_date_string(NULL), (long)getpid());
+
+ int pipe_from_parent_to_child[2];
+ xpipe(pipe_from_parent_to_child);
+ 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]);
+
+ if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile,
+ /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
+ {
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+
+ {
+ GHashTableIter iter;
+ char *name;
+ struct problem_item *value;
+ g_hash_table_iter_init(&iter, problem_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ if (strcmp(name, FILENAME_COUNT) == 0) continue;
+ if (strcmp(name, CD_DUMPDIR) == 0) continue;
+
+ const char *content = value->content;
+ if (value->flags & CD_FLAG_TXT)
+ {
+ reportfile_add_binding_from_string(file, name, content);
+ }
+ else if (value->flags & CD_FLAG_BIN)
+ {
+ const char *basename = strrchr(content, '/');
+ if (basename)
+ basename++;
+ else
+ basename = content;
+ char *xml_name = concat_path_file("content", basename);
+ reportfile_add_binding_from_namedfile(file,
+ /*on_disk_filename */ content,
+ /*binding_name */ name,
+ /*recorded_filename*/ xml_name,
+ /*binary */ 1);
+ if (tar_append_file(tar, (char*)content, xml_name) != 0)
+ {
+ errmsg = "Can't create temporary file in /tmp";
+ free(xml_name);
+ goto ret;
+ }
+ free(xml_name);
+ }
+ }
+ }
+
+ /* 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(NULL));
+ 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 /* writes header block */
+ /* writes content.xml, padded to 512 bytes */
+ || full_write(tar_fd(tar), block, len512) != len512
+ || tar_append_eof(tar) != 0 /* writes EOF blocks */
+ || tar_close(tar) != 0
+ ) {
+ free(block);
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+ tar = NULL;
+ free(block);
+ }
+
+ /* We must be sure gzip finished, and finished successfully */
+ int status;
+ waitpid(child, &status, 0);
+ child = -1;
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ {
+ /* Hopefully, by this time child emitted more meaningful
+ * error message. But just in case it didn't:
+ */
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+
+ /* Send tempfile */
+ {
+ log(_("Creating a new case..."));
+ char* result = send_report_to_new_case(url,
+ login,
+ password,
+ ssl_verify,
+ summary,
+ dsc,
+ package,
+ tempfile
+ );
+ /* Temporary hackish detection of errors. Ideally,
+ * send_report_to_new_case needs to have better error reporting.
+ */
+ if (strncasecmp(result, "error", 5) == 0)
+ {
+ /*
+ * result can contain "...server says: 'multi-line <html> text'"
+ * Replace all '\n' with spaces:
+ * we want this message to be, logically, one log entry.
+ * IOW: one line, not many lines.
+ */
+ char *src, *dst;
+ dst = src = result;
+ while (1)
+ {
+ unsigned char c = *src++;
+ if (c == '\n')
+ c = ' ';
+ *dst++ = c;
+ if (c == '\0')
+ break;
+ }
+ /* Use sanitized string as error message */
+ errmsg = result;
+ goto ret;
+ }
+
+ /* No error */
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (dd)
+ {
+ char *msg = xasprintf("RHTSupport: %s", result);
+ add_reported_to(dd, msg);
+ free(msg);
+ dd_close(dd);
+ }
+ log("%s", result);
+ free(result);
+ }
+
+ ret:
+ /* We must close write fd first, or else child will wait forever */
+ if (tar)
+ tar_close(tar);
+ //close(pipe_from_parent_to_child[1]); - tar_close() does it itself
+
+ /* Now wait for child to exit */
+ if (child > 0)
+ {
+ // Damn, selinux does not allow SIGKILLing our own child! wtf??
+ //kill(child, SIGKILL); /* just in case */
+ waitpid(child, NULL, 0);
+ }
+
+ unlink(tempfile);
+ free(tempfile);
+ reportfile_free(file);
+ rmdir(tmpdir_name);
+
+ free(summary);
+ free(dsc);
+
+ free(url);
+ free(login);
+ free(password);
+ free_problem_data(problem_data);
+
+ if (errmsg)
+ error_msg_and_die("%s", errmsg);
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ map_string_h *settings = new_map_string();
+ const char *dump_dir_name = ".";
+ GList *conf_file = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] -c CONFFILE -d DIR\n"
+ "\n"
+ "Reports a problem to RHTSupport.\n"
+ "\n"
+ "CONFFILE lines should have 'PARAM = VALUE' format.\n"
+ "Recognized string parameters: URL, Login, Password.\n"
+ "Recognized boolean parameter (VALUE should be 1/0, yes/no): SSLVerify.\n"
+ "Parameters can be overridden via $RHTSupport_PARAM environment variables."
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_LIST( 'c', NULL, &conf_file , "FILE", _("Configuration file (may be given many times)")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ while (conf_file)
+ {
+ char *fn = (char *)conf_file->data;
+ VERB1 log("Loading settings from '%s'", fn);
+ load_conf_file(fn, settings, /*skip key w/o values:*/ true);
+ VERB3 log("Loaded '%s'", fn);
+ conf_file = g_list_remove(conf_file, fn);
+ }
+
+ VERB1 log("Initializing XML-RPC library");
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+ xmlrpc_client_setup_global_const(&env);
+ if (env.fault_occurred)
+ error_msg_and_die("XML-RPC Fault: %s(%d)", env.fault_string, env.fault_code);
+ xmlrpc_env_clean(&env);
+
+ report_to_rhtsupport(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return 0;
+}
diff --git a/src/plugins/reporter-upload.c b/src/plugins/reporter-upload.c
new file mode 100644
index 0000000..aea3891
--- /dev/null
+++ b/src/plugins/reporter-upload.c
@@ -0,0 +1,287 @@
+/*
+ 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 <libtar.h>
+#include <curl/curl.h>
+#include "internal_libreport.h"
+
+//TODO: use this for better logging
+#if 0
+/* "read local data from a file" callback */
+static size_t fread_with_reporting(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ static time_t last_t; // hack
+
+ FILE *fp = (FILE*)userdata;
+ time_t t = time(NULL);
+
+ // Report current file position every 16 seconds
+ if (!(t & 0xf) && last_t != t)
+ {
+ last_t = t;
+ off_t cur_pos = ftello(fp);
+ fseeko(fp, 0, SEEK_END);
+ off_t sz = ftello(fp);
+ fseeko(fp, cur_pos, SEEK_SET);
+ log(_("Uploaded: %llu of %llu kbytes"),
+ (unsigned long long)cur_pos / 1024,
+ (unsigned long long)sz / 1024);
+ }
+
+ return fread(ptr, size, nmemb, fp);
+}
+#endif
+
+static int send_file(const char *url, const char *filename)
+{
+ FILE *fp = fopen(filename, "r");
+ if (!fp)
+ {
+ perror_msg("Can't open '%s'", filename);
+ return 1;
+ }
+
+ log(_("Sending %s to %s"), filename, url);
+
+ struct stat stbuf;
+ fstat(fileno(fp), &stbuf); /* never fails */
+ char *whole_url = concat_path_file(url, strrchr(filename, '/') ? : filename);
+
+ CURL *curl = curl_easy_init();
+ if (!curl)
+ {
+ error_msg_and_die("Can't create curl handle");
+ }
+ /* Buffer[CURL_ERROR_SIZE] curl stores human readable error messages in.
+ * This may be more helpful than just return code from curl_easy_perform.
+ * curl will need it until curl_easy_cleanup. */
+ char curl_err_msg[CURL_ERROR_SIZE];
+ curl_err_msg[0] = '\0';
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_msg);
+ /* enable uploading */
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+ /* specify target */
+ curl_easy_setopt(curl, CURLOPT_URL, whole_url);
+ /* FILE handle: passed to the default callback, it will fread() it */
+ curl_easy_setopt(curl, CURLOPT_READDATA, fp);
+ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)stbuf.st_size);
+
+ /* everything is done here; result 0 means success */
+ CURLcode result = curl_easy_perform(curl);
+ free(whole_url);
+ fclose(fp);
+ if (result != 0)
+ error_msg("Error while uploading: '%s'", curl_easy_strerror(result));
+ else
+ /* This ends up a "reporting status message" in abrtd */
+ log(_("Successfully sent %s to %s"), filename, url);
+
+ curl_easy_cleanup(curl);
+
+ return result;
+}
+
+static int create_and_upload_archive(
+ const char *dump_dir_name,
+ map_string_h *settings)
+{
+ int result = 0;
+
+ pid_t child;
+ TAR* tar = NULL;
+ const char* errmsg = NULL;
+ char* tempfile = NULL;
+
+ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+ if (!dd)
+ exit(1); /* error msg is already logged by dd_opendir */
+
+ /* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */
+ log(_("Compressing data"));
+
+//TODO:
+//Encrypt = yes
+//ArchiveType = .tar.bz2
+//ExcludeFiles = foo,bar*,b*z
+ char* env;
+ env = getenv("Upload_URL");
+ const char *url = (env ? env : get_map_string_item_or_empty(settings, "URL"));
+
+ /* Create a child gzip which will compress the data */
+ /* SELinux guys are not happy with /tmp, using /var/run/abrt */
+ /* Reverted back to /tmp for ABRT2 */
+ tempfile = xasprintf("/tmp/abrt-upload-%s-%lu.tar.gz", iso_date_string(NULL), (long)getpid());
+ int pipe_from_parent_to_child[2];
+ xpipe(pipe_from_parent_to_child);
+ child = vfork();
+ if (child == 0)
+ {
+ /* child */
+ close(pipe_from_parent_to_child[1]);
+ xmove_fd(pipe_from_parent_to_child[0], 0);
+ xmove_fd(xopen3(tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600), 1);
+ execlp("gzip", "gzip", NULL);
+ perror_msg_and_die("Can't execute '%s'", "gzip");
+ }
+ close(pipe_from_parent_to_child[0]);
+
+ /* If child died (say, in xopen), then parent might get SIGPIPE.
+ * We want to properly unlock dd, therefore we must not die on SIGPIPE:
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ /* Create tar writer object */
+ if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile,
+ /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
+ {
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+
+ /* Write data to the tarball */
+ {
+ char *exclude_from_report = getenv("EXCLUDE_FROM_REPORT");
+ dd_init_next_file(dd);
+ char *short_name, *full_name;
+ while (dd_get_next_file(dd, &short_name, &full_name))
+ {
+ if (exclude_from_report && is_in_comma_separated_list(short_name, exclude_from_report))
+ goto next;
+
+ // dd_get_next_file guarantees that it's a REG:
+ //struct stat stbuf;
+ //if (stat(full_name, &stbuf) != 0)
+ // || !S_ISREG(stbuf.st_mode)
+ //) {
+ // goto next;
+ //}
+ if (tar_append_file(tar, full_name, short_name) != 0)
+ {
+ errmsg = "Can't create temporary file in /tmp";
+ free(short_name);
+ free(full_name);
+ goto ret;
+ }
+ next:
+ free(short_name);
+ free(full_name);
+ }
+ }
+ dd_close(dd);
+ dd = NULL;
+
+ /* Close tar writer... */
+ if (tar_append_eof(tar) != 0 || tar_close(tar) != 0)
+ {
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+ tar = NULL;
+ /* ...and check that gzip child finished successfully */
+ int status;
+ waitpid(child, &status, 0);
+ child = -1;
+ if (status != 0)
+ {
+ /* We assume the error was out-of-disk-space or out-of-quota */
+ errmsg = "Can't create temporary file in /tmp";
+ goto ret;
+ }
+
+ /* Upload the tarball */
+ if (url && url[0])
+ {
+ result = send_file(url, tempfile);
+ /* cleanup code will delete tempfile */
+ }
+ else
+ {
+ log(_("Archive is created: '%s'"), tempfile);
+ free(tempfile);
+ tempfile = NULL;
+ }
+
+ ret:
+ dd_close(dd);
+ if (tar)
+ tar_close(tar);
+ /* close(pipe_from_parent_to_child[1]); - tar_close() does it itself */
+ if (child > 0)
+ waitpid(child, NULL, 0);
+ if (tempfile)
+ {
+ unlink(tempfile);
+ free(tempfile);
+ }
+ if (errmsg)
+ error_msg_and_die("%s", errmsg);
+
+ return result;
+}
+
+int main(int argc, char **argv)
+{
+ abrt_init(argv);
+
+ const char *dump_dir_name = ".";
+ const char *conf_file = NULL;
+ const char *url = NULL;
+
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "\b [-v] -d DIR [-c CONFFILE] [-u URL]\n"
+ "\n"
+ "Uploads compressed tarball of dump directory DIR to URL.\n"
+ "If URL is not specified, creates tarball in /tmp and exits.\n"
+ "\n"
+ "Files with names listed in $EXCLUDE_FROM_REPORT are not included\n"
+ "into the tarball.\n"
+ "\n"
+ "CONFFILE lines should have 'PARAM = VALUE' format.\n"
+ "Recognized string parameter: URL.\n"
+ "Parameter can be overridden via $Upload_URL."
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_c = 1 << 2,
+ OPT_u = 1 << 3,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Dump directory")),
+ OPT_STRING('c', NULL, &conf_file , "CONFFILE", _("Config file")),
+ OPT_STRING('u', NULL, &url , "URL" , _("Base URL to upload to")),
+ OPT_END()
+ };
+ /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string);
+
+ export_abrt_envvars(0);
+
+ map_string_h *settings = new_map_string();
+ if (url)
+ g_hash_table_replace(settings, xstrdup("URL"), xstrdup(url));
+ if (conf_file)
+ load_conf_file(conf_file, settings, /*skip key w/o values:*/ true);
+
+ int result = create_and_upload_archive(dump_dir_name, settings);
+
+ free_map_string(settings);
+ return result;
+}
diff --git a/src/plugins/reporter-upload.txt b/src/plugins/reporter-upload.txt
new file mode 100644
index 0000000..0a1b8f8
--- /dev/null
+++ b/src/plugins/reporter-upload.txt
@@ -0,0 +1,67 @@
+reporter-upload(1)
+==================
+
+NAME
+----
+reporter-upload - Uploads compressed tarball of dump directory.
+
+SYNOPSIS
+--------
+'reporter-upload' [-c CONFFILE]... [ -d DIR ] [ -u URL ]
+
+DESCRIPTION
+-----------
+The tool is used to create a compressed tarball of the dump directory and
+upload it to a URL. Supported protocols include FTP, FTPS, HTTP, HTTPS, SCP,
+SFTP, TFTP and FILE.
+
+Configuration file
+~~~~~~~~~~~~~~~~~~
+Configuration file contains entries in a format "Option = Value".
+
+The options are:
+
+'URL'::
+ The URL where tarball should be uploaded.
+
+Integration with ABRT events
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+'reporter-upload' can be used as a reporter, to allow users to upload
+compressed tarballs of dump directiories to a configured URL. This usage can be
+configured in /etc/abrt/events.d/upload_events.conf:
+
+------------
+EVENT=report_Upload reporter-upload
+------------
+
+It can be also used on the 'post-create' event to upload it automatically.
+
+------------
+EVENT=post-create reporter-upload
+------------
+
+OPTIONS
+-------
+
+-d DIR::
+ Path to dump directory.
+
+-c CONFFILE::
+ Path to configration file. When used in ABRT event system, the file
+ contains site-wide configuration. Users can change the values via
+ environment variables.
+
+-u URL::
+ The URL where tarball should be uploaded.
+
+ENVIRONMENT VARIABLES
+---------------------
+Environment variables take precedence over values provided in
+the configuration file.
+
+'Upload_URL'::
+ The URL where should be the tarball uploaded.
+
+AUTHORS
+-------
+* ABRT team
diff --git a/src/plugins/rhbz.c b/src/plugins/rhbz.c
new file mode 100644
index 0000000..0a4c28e
--- /dev/null
+++ b/src/plugins/rhbz.c
@@ -0,0 +1,488 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 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 "internal_libreport.h"
+#include "rhbz.h"
+
+#define MAX_HOPS 5
+
+struct bug_info *new_bug_info()
+{
+ struct bug_info *bi = xzalloc(sizeof(struct bug_info));
+ bi->bi_dup_id = -1;
+
+ return bi;
+}
+
+void free_bug_info(struct bug_info *bi)
+{
+ if (!bi)
+ return;
+
+ free(bi->bi_status);
+ free(bi->bi_resolution);
+ free(bi->bi_reporter);
+ free(bi->bi_product);
+
+ list_free_with_free(bi->bi_cc_list);
+
+ bi->bi_status = NULL;
+ bi->bi_resolution = NULL;
+ bi->bi_reporter = NULL;
+ bi->bi_product = NULL;
+
+ bi->bi_cc_list = NULL;
+
+ free(bi);
+}
+
+void rhbz_login(struct abrt_xmlrpc *ax, const char* login, const char* passwd)
+{
+ xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.login", "({s:s,s:s})",
+ "login", login, "password", passwd);
+
+//TODO: with URL like http://bugzilla.redhat.com (that is, with http: instead of https:)
+//we are getting this error:
+//Logging into Bugzilla at http://bugzilla.redhat.com
+//Can't login. Server said: HTTP response code is 301, not 200
+//But this is a 301 redirect! We _can_ follow it if we configure curl to understand that!
+ xmlrpc_DECREF(result);
+}
+
+xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component,
+ const char *product, const char *duphash)
+{
+ char *query = NULL;
+ if (!product)
+ query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\"", component, duphash);
+ else
+ query = xasprintf("ALL component:\"%s\" whiteboard:\"%s\" product:\"%s\"",
+ component, duphash, product);
+
+ VERB3 log("search for '%s'", query);
+ xmlrpc_value *ret = abrt_xmlrpc_call(ax, "Bug.search", "({s:s})",
+ "quicksearch", query);
+ free(query);
+ return ret;
+}
+
+xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value *value = NULL;
+ /* The xmlrpc_struct_find_value functions consider "not found" to be
+ * a normal result. If a member of the structure with the specified key
+ * exists, it returns it as a handle to an xmlrpc_value. If not, it returns
+ * NULL in place of that handle.
+ */
+ xmlrpc_struct_find_value(&env, xml, member, &value);
+ if (env.fault_occurred)
+ abrt_xmlrpc_error(&env);
+
+ return value;
+}
+
+/* The only way this can fail is if arrayP is not actually an array XML-RPC
+ * value. So it is usually not worth checking *envP.
+ * die or return size of array
+ */
+int rhbz_array_size(xmlrpc_value *xml)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ int size = xmlrpc_array_size(&env, xml);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ return size;
+}
+
+/* die or return bug id; each bug must have bug id otherwise xml is corrupted */
+int rhbz_bug_id(xmlrpc_value* xml)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value *item = NULL;
+ xmlrpc_value *bug = NULL;
+ int bug_id = -1;;
+
+ xmlrpc_array_read_item(&env, xml, 0, &item);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ bug = rhbz_get_member("bug_id", item);
+ xmlrpc_DECREF(item);
+ if (!bug)
+ abrt_xmlrpc_die(&env);
+
+ xmlrpc_read_int(&env, bug, &bug_id);
+ xmlrpc_DECREF(bug);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ VERB3 log("found bug_id %i", bug_id);
+ return bug_id;
+}
+
+/* die when mandatory value is missing (set flag RHBZ_MANDATORY_MEMB)
+ * or return appropriate string or NULL when fail;
+ */
+// TODO: npajkovs: add flag to read xmlrpc_read_array_item first
+void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value *member = rhbz_get_member(memb, xml);
+
+ const char *string = NULL;
+
+ if (!member)
+ goto die;
+
+ if (IS_READ_STR(flags))
+ {
+ xmlrpc_read_string(&env, member, &string);
+ xmlrpc_DECREF(member);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ if (!*string)
+ goto die;
+
+ VERB3 log("found %s: '%s'", memb, string);
+ return (void*)string;
+ }
+
+ if (IS_READ_INT(flags))
+ {
+ int *integer = xmalloc(sizeof(int));
+ xmlrpc_read_int(&env, member, integer);
+ xmlrpc_DECREF(member);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ VERB3 log("found %s: '%i'", memb, *integer);
+ return (void*)integer;
+ }
+die:
+ free((void*)string);
+ if (IS_MANDATORY(flags))
+ error_msg_and_die(_("Looks like corrupted xml response, because '%s'"
+ " member is missing."), memb);
+
+ return NULL;
+}
+
+GList *rhbz_bug_cc(xmlrpc_value* result_xml)
+{
+ xmlrpc_env env;
+ xmlrpc_env_init(&env);
+
+ xmlrpc_value* cc_member = rhbz_get_member("cc", result_xml);
+ if (!cc_member)
+ return NULL;
+
+ int array_size = rhbz_array_size(cc_member);
+
+ VERB3 log("count members on cc %i", array_size);
+ GList *cc_list = NULL;
+
+ 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)
+ abrt_xmlrpc_die(&env);
+
+ if (!item)
+ continue;
+
+ const char* cc = NULL;
+ xmlrpc_read_string(&env, item, &cc);
+ xmlrpc_DECREF(item);
+ if (env.fault_occurred)
+ abrt_xmlrpc_die(&env);
+
+ if (*cc != '\0')
+ {
+ cc_list = g_list_append(cc_list, (char*)cc);
+ VERB3 log("member on cc is %s", cc);
+ continue;
+ }
+ free((char*)cc);
+ }
+ xmlrpc_DECREF(cc_member);
+ return cc_list;
+}
+
+struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id)
+{
+ struct bug_info *bz = new_bug_info();
+ xmlrpc_value *xml_bug_response = abrt_xmlrpc_call(ax, "bugzilla.getBug",
+ "(i)", bug_id);
+
+ int *ret = (int*)rhbz_bug_read_item("bug_id", xml_bug_response,
+ RHBZ_MANDATORY_MEMB | RHBZ_READ_INT);
+ bz->bi_id = *ret;
+ free(ret);
+ bz->bi_product = rhbz_bug_read_item("product", xml_bug_response,
+ RHBZ_MANDATORY_MEMB | RHBZ_READ_STR);
+ bz->bi_reporter = rhbz_bug_read_item("reporter", xml_bug_response,
+ RHBZ_MANDATORY_MEMB | RHBZ_READ_STR);
+ bz->bi_status = rhbz_bug_read_item("bug_status", xml_bug_response,
+ RHBZ_MANDATORY_MEMB | RHBZ_READ_STR);
+ bz->bi_resolution = rhbz_bug_read_item("resolution", xml_bug_response,
+ RHBZ_READ_STR);
+
+ if (strcmp(bz->bi_status, "CLOSED") == 0 && !bz->bi_resolution)
+ error_msg_and_die(_("Bug %i is CLOSED, but it has no RESOLUTION"), bz->bi_id);
+
+ ret = (int*)rhbz_bug_read_item("dup_id", xml_bug_response,
+ RHBZ_READ_INT);
+ if (strcmp(bz->bi_status, "CLOSED") == 0
+ && strcmp(bz->bi_resolution, "DUPLICATE") == 0
+ && !ret)
+ {
+ error_msg_and_die(_("Bug %i is CLOSED as DUPLICATE, but it has no DUP_ID"),
+ bz->bi_id);
+ }
+
+ bz->bi_dup_id = (ret) ? *ret: -1;
+ free(ret);
+
+ bz->bi_cc_list = rhbz_bug_cc(xml_bug_response);
+
+ xmlrpc_DECREF(xml_bug_response);
+
+ return bz;
+}
+
+/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
+int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
+ int depend_on_bug)
+{
+ const char *package = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_PACKAGE);
+ const char *component = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_COMPONENT);
+ const char *release = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_OS_RELEASE);
+ if (!release) /* Old dump dir format compat. Remove in abrt-2.1 */
+ release = get_problem_item_content_or_NULL(problem_data, "release");
+ const char *arch = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_ARCHITECTURE);
+ const char *duphash = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_DUPHASH);
+//COMPAT, remove after 2.1 release
+ if (!duphash) duphash = get_problem_item_content_or_NULL(problem_data,
+ "global_uuid");
+ const char *reason = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_REASON);
+ const char *function = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_CRASH_FUNCTION);
+ const char *analyzer = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_ANALYZER);
+ const char *tainted_short = get_problem_item_content_or_NULL(problem_data,
+ FILENAME_TAINTED_SHORT);
+
+ struct strbuf *buf_summary = strbuf_new();
+ strbuf_append_strf(buf_summary, "[abrt] %s", package);
+
+ if (function != NULL && strlen(function) < 30)
+ strbuf_append_strf(buf_summary, ": %s", function);
+
+ if (reason != NULL)
+ strbuf_append_strf(buf_summary, ": %s", reason);
+
+ if (tainted_short && analyzer
+ && (strcmp(analyzer, "Kerneloops") == 0))
+ {
+ strbuf_append_strf(buf_summary, ": TAINTED %s", tainted_short);
+ }
+
+ char *status_whiteboard = xasprintf("abrt_hash:%s", duphash);
+
+ char *bz_dsc = make_description_bz(problem_data);
+ char *full_dsc = xasprintf("abrt version: "VERSION"\n%s", bz_dsc);
+ free(bz_dsc);
+
+ char *product = NULL;
+ char *version = NULL;
+ parse_release_for_bz(release, &product, &version);
+
+ xmlrpc_value* result = NULL;
+ char *summary = strbuf_free_nobuf(buf_summary);
+ if (depend_on_bug > -1)
+ {
+ result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s,s:i})",
+ "product", product,
+ "component", component,
+ "version", version,
+ "summary", summary,
+ "description", full_dsc,
+ "status_whiteboard", status_whiteboard,
+ "platform", arch,
+ "dependson", depend_on_bug);
+ }
+ else
+ {
+ result = abrt_xmlrpc_call(ax, "Bug.create", "({s:s,s:s,s:s,s:s,s:s,s:s,s:s})",
+ "product", product,
+ "component", component,
+ "version", version,
+ "summary", summary,
+ "description", full_dsc,
+ "status_whiteboard", status_whiteboard,
+ "platform", arch);
+ }
+ free(status_whiteboard);
+ free(product);
+ free(version);
+ free(summary);
+ free(full_dsc);
+
+ if (!result)
+ return -1;
+
+ int *r = rhbz_bug_read_item("id", result, RHBZ_MANDATORY_MEMB | RHBZ_READ_INT);
+ xmlrpc_DECREF(result);
+ int new_bug_id = *r;
+ free(r);
+
+ log(_("New bug id: %i"), new_bug_id);
+ return new_bug_id;
+}
+
+/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
+int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename,
+ const char *bug_id, const char *data, int flags)
+{
+ char *encoded64 = encode_base64(data, strlen(data));
+ char *fn = xasprintf("File: %s", filename);
+ xmlrpc_value* result;
+ int nomail_notify = IS_NOMAIL_NOTIFY(flags);
+
+ result= abrt_xmlrpc_call(ax, "bugzilla.addAttachment", "(s{s:s,s:s,s:s,s:s,s:i})",
+ bug_id,
+ "description", fn,
+ "filename", filename,
+ "contenttype", "text/plain",
+ "data", encoded64,
+ "nomail", nomail_notify);
+
+ free(encoded64);
+ free(fn);
+ if (!result)
+ return -1;
+
+ xmlrpc_DECREF(result);
+
+ return 0;
+}
+
+/* suppress mail notify by {s:i} (nomail:1) (driven by flag) */
+int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id,
+ problem_data_t *problem_data, int flags)
+{
+ GHashTableIter iter;
+ char *name;
+ struct problem_item *value;
+ g_hash_table_iter_init(&iter, problem_data);
+ while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value))
+ {
+ const char *content = value->content;
+
+ // We were special-casing FILENAME_BACKTRACE here, but karel says
+ // he can retrieve it in inlined form from comments too.
+ if ((value->flags & CD_FLAG_TXT)
+ && (strlen(content) > CD_TEXT_ATT_SIZE /*|| (strcmp(name, FILENAME_BACKTRACE) == 0)*/)
+ ) {
+ /* check if the attachment failed and try it once more */
+ rhbz_attachment(ax, name, bug_id, content, flags);
+ }
+ }
+
+ return 0;
+}
+
+void rhbz_logout(struct abrt_xmlrpc *ax)
+{
+ xmlrpc_value* result = abrt_xmlrpc_call(ax, "User.logout", "(s)", "");
+ if (result)
+ xmlrpc_DECREF(result);
+}
+
+struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax,
+ struct bug_info *bi)
+{
+ struct bug_info *bi_tmp = new_bug_info();
+ bi_tmp->bi_id = bi->bi_id;
+ bi_tmp->bi_dup_id = bi->bi_dup_id;
+
+ for (int ii = 0; ii <= MAX_HOPS; ii++)
+ {
+ if (ii == MAX_HOPS)
+ error_msg_and_die(_("Bugzilla couldn't find parent of bug %d"), bi->bi_id);
+
+ log("Bug %d is a duplicate, using parent bug %d", bi_tmp->bi_id, bi_tmp->bi_dup_id);
+ int bug_id = bi_tmp->bi_dup_id;
+
+ free_bug_info(bi_tmp);
+ bi_tmp = rhbz_bug_info(ax, bug_id);
+
+ // found a bug which is not CLOSED as DUPLICATE
+ if (bi_tmp->bi_dup_id == -1)
+ break;
+ }
+
+ return bi_tmp;
+}
+
+/* suppress mail notify by {s:i} (nomail:1) */
+void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail, int flags)
+{
+ xmlrpc_value *result;
+ int nomail_notify = IS_NOMAIL_NOTIFY(flags);
+ result = abrt_xmlrpc_call(ax, "Bug.update", "({s:i,s:{s:(s),s:i}})",
+ "ids", bug_id, "updates", "add_cc", mail,
+ "nomail", nomail_notify);
+
+ if (result)
+ xmlrpc_DECREF(result);
+}
+
+void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment,
+ int flags)
+{
+ int private = IS_PRIVATE(flags);
+ int nomail_notify = IS_NOMAIL_NOTIFY(flags);
+
+ xmlrpc_value *result;
+ result = abrt_xmlrpc_call(ax, "Bug.add_comment", "({s:i,s:s,s:b,s:i})",
+ "id", bug_id, "comment", comment,
+ "private", private, "nomail", nomail_notify);
+
+ if (result)
+ xmlrpc_DECREF(result);
+}
diff --git a/src/plugins/rhbz.h b/src/plugins/rhbz.h
new file mode 100644
index 0000000..6a12c68
--- /dev/null
+++ b/src/plugins/rhbz.h
@@ -0,0 +1,103 @@
+/*
+ Copyright (C) 2011 ABRT team
+ Copyright (C) 2011 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 RHBZ_H
+#define RHBZ_H
+
+/* include/stdint.h: typedef int int32_t;
+ * include/xmlrpc-c/base.h: typedef int32_t xmlrpc_int32;
+ */
+
+#include "abrt_xmlrpc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ RHBZ_MANDATORY_MEMB = (1 << 0),
+ RHBZ_READ_STR = (1 << 1),
+ RHBZ_READ_INT = (1 << 2),
+ RHBZ_NOMAIL_NOTIFY = (1 << 3),
+ RHBZ_PRIVATE = (1 << 4),
+};
+
+#define IS_MANDATORY(flags) ((flags) & RHBZ_MANDATORY_MEMB)
+#define IS_READ_STR(flags) ((flags) & RHBZ_READ_STR)
+#define IS_READ_INT(flags) ((flags) & RHBZ_READ_INT)
+#define IS_NOMAIL_NOTIFY(flags) ((flags) & RHBZ_NOMAIL_NOTIFY)
+#define IS_PRIVATE(flags) ((flags) & RHBZ_PRIVATE)
+
+struct bug_info {
+ int bi_id;
+ int bi_dup_id;
+
+ char *bi_status;
+ char *bi_resolution;
+ char *bi_reporter;
+ char *bi_product;
+
+ GList *bi_cc_list;
+};
+
+struct bug_info *new_bug_info();
+void free_bug_info(struct bug_info *bz);
+
+void rhbz_login(struct abrt_xmlrpc *ax, const char *login, const char *passwd);
+
+void rhbz_mail_to_cc(struct abrt_xmlrpc *ax, int bug_id, const char *mail, int flags);
+
+void rhbz_add_comment(struct abrt_xmlrpc *ax, int bug_id, const char *comment,
+ int flags);
+
+void *rhbz_bug_read_item(const char *memb, xmlrpc_value *xml, int flags);
+
+void rhbz_logout(struct abrt_xmlrpc *ax);
+
+xmlrpc_value *rhbz_search_duphash(struct abrt_xmlrpc *ax, const char *component,
+ const char *release, const char *duphash);
+
+xmlrpc_value *rhbz_get_member(const char *member, xmlrpc_value *xml);
+
+int rhbz_array_size(xmlrpc_value *xml);
+
+int rhbz_bug_id(xmlrpc_value *xml);
+
+int rhbz_new_bug(struct abrt_xmlrpc *ax, problem_data_t *problem_data,
+ int depend_on_bug);
+
+int rhbz_attachments(struct abrt_xmlrpc *ax, const char *bug_id,
+ problem_data_t *problem_data, int flags);
+
+int rhbz_attachment(struct abrt_xmlrpc *ax, const char *filename,
+ const char *bug_id, const char *data, int flags);
+
+GList *rhbz_bug_cc(xmlrpc_value *result_xml);
+
+struct bug_info *rhbz_bug_info(struct abrt_xmlrpc *ax, int bug_id);
+
+
+struct bug_info *rhbz_find_origin_bug_closed_duplicate(struct abrt_xmlrpc *ax,
+ struct bug_info *bi);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/plugins/rhtsupport_events.conf b/src/plugins/rhtsupport_events.conf
new file mode 100644
index 0000000..2e9b27e
--- /dev/null
+++ b/src/plugins/rhtsupport_events.conf
@@ -0,0 +1 @@
+EVENT=report_RHTSupport reporter-rhtsupport
--
1.7.5.4
12 years, 9 months
[PATCH] Interactive plugins
by Michal Toman
Hi,
I have a proposal for interactive plugins protocol. The patch consists
of two parts: libreport and abrt (example of usage on abrt-retrace-client).
For those who ignored my previous e-mail, here are some examples:
http://10.34.24.254/question-cli.png
http://10.34.24.254/question-gui.png
http://10.34.24.254/question-gui2.png
http://10.34.24.254/alert-gui.png
The protocol is simple. If REPORT_CLIENT_SLAVE env variable is set,
special messages are prefixed. GUI (or whatever) cuts off the prefixs,
shows a dialog and sends the response to child's stdin depending on the
pressed button. Otherwise standard stdin/stdout is used (awaiting direct
input from terminal). Here is an example of running the same analyzer
from slave and non-slave mode:
//slave - no terminal => stdin not accessible
Querying server settings
Preparing an archive to upload
ASK_YES_NO You are going to upload 16604 bytes. Continue?
//standard - running from terminal
Querying server settings
Preparing an archive to upload
You are going to upload 16604 bytes. Continue? [y/N]
The patch itself is not complete - it works with GUI, but breaks CLI.
This is because GUI and CLI are handling the output in a different way
and in my opinion these two should be changed one for the other.
The thing is that GUI reads output in 257 bytes long blocks instead of
reading line by line - this can easily break the idea of prefixed
messages (with the included patch one question works, multiple are
making problems). On the other hand, CLI reads the input line by line.
This is a problem if the plugin prints a question and waits for the
response on the same line (which seems to be correct for me, eg.
Are you sure you want to quit? [y/N] ). In the current situation the
program stucks waiting with no question displayed.
This is where I would like to begin a discussion - any ideas to figure
this out? Any ideas to improve the protocol? Anything else :)?
Cheers,
Michal
12 years, 9 months