>From bccd10f39589870673ffd09de6bc09d6e00fab72 Mon Sep 17 00:00:00 2001
From: Jiri Moskovcak <jmoskovc@redhat.com>
Date: Wed, 18 May 2011 16:55:40 +0200
Subject: [PATCH] report C API improvements

---
 src/applet/applet_gtk.c           |    2 +-
 src/gui-gtk/abrt-gtk.c            |   53 ++++++++++++-
 src/include/report/problem_data.h |    2 +
 src/include/report/report.h       |   10 ++-
 src/lib/problem_data.c            |   32 +++++++
 src/lib/report.c                  |  162 ++++++++++++++++++++++++++++++-------
 6 files changed, 227 insertions(+), 34 deletions(-)

diff --git a/src/applet/applet_gtk.c b/src/applet/applet_gtk.c
index 524aca7..59c21ef 100644
--- a/src/applet/applet_gtk.c
+++ b/src/applet/applet_gtk.c
@@ -86,7 +86,7 @@ static void action_report(NotifyNotification *notification, gchar *action, gpoin
     struct applet *applet = (struct applet *)user_data;
     if (applet->ap_daemon_running)
     {
-        analyze_and_report_dir(applet->ap_last_crash_id);
+        analyze_and_report_dir(applet->ap_last_crash_id, NOWAIT);
         GError *err = NULL;
         notify_notification_close(notification, &err);
         if (err != NULL)
diff --git a/src/gui-gtk/abrt-gtk.c b/src/gui-gtk/abrt-gtk.c
index df94485..a44ca68 100644
--- a/src/gui-gtk/abrt-gtk.c
+++ b/src/gui-gtk/abrt-gtk.c
@@ -29,6 +29,7 @@ static const char help_uri[] = "http://docs.fedoraproject.org/en-US/"
 static GtkListStore *s_dumps_list_store;
 static GtkWidget *s_treeview;
 static GtkWidget *g_main_window;
+static GtkWidget *s_report_window;
 
 enum
 {
@@ -113,7 +114,7 @@ static void on_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path, GtkTre
             gtk_tree_model_get_value(store, &iter, COLUMN_DUMP_DIR, &d_dir);
 
             const char *dirname= g_value_get_string(&d_dir);
-            analyze_and_report_dir(dirname);
+            analyze_and_report_dir(dirname, NOWAIT);
         }
     }
 }
@@ -184,6 +185,53 @@ static void on_menu_help_cb(GtkMenuItem *menuitem, gpointer unused)
     gtk_show_uri(NULL, help_uri, GDK_CURRENT_TIME, NULL);
 }
 
+static void on_button_send_cb(GtkWidget *button, gpointer data)
+{
+    GtkTextView *tev = GTK_TEXT_VIEW(data);
+    GtkTextBuffer *buf = gtk_text_view_get_buffer(tev);
+    GtkTextIter it_start;
+    GtkTextIter it_end;
+    gtk_text_buffer_get_start_iter(buf, &it_start);
+    gtk_text_buffer_get_end_iter(buf, &it_end);
+    gchar *text = gtk_text_buffer_get_text(buf,
+                                           &it_start,
+                                           &it_end,
+                                           false);
+
+    problem_data_t *pd = new_problem_data();
+
+    if (strlen(text) > 0)
+    {
+        add_to_problem_data(pd, "description", text);
+    }
+
+    /* why it doesn't want to hide before report ends? */
+    gtk_widget_destroy(s_report_window);
+
+    int status = report(pd);
+    VERB1 log("Reporting finished with status: %i", status);
+    free_problem_data(pd);
+}
+
+static void on_menu_report_cb(GtkMenuItem *menuitem, gpointer unused)
+{
+
+    s_report_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(s_report_window), _("Problem description"));
+    gtk_window_set_default_size(GTK_WINDOW(s_report_window), 400, 400);
+    GtkWidget *vbox = gtk_vbox_new(false, 0);
+    GtkWidget *button_send = gtk_button_new_with_label(_("Send"));
+    GtkWidget *tev = gtk_text_view_new();
+    g_signal_connect(button_send, "clicked", G_CALLBACK(on_button_send_cb), tev);
+
+    gtk_box_pack_start(GTK_BOX(vbox), tev, true, true, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), button_send, false, false, 5);
+
+    gtk_container_add(GTK_CONTAINER(s_report_window), vbox);
+
+    gtk_widget_show_all(s_report_window);
+}
+
 static void on_menu_about_cb(GtkMenuItem *menuitem, gpointer unused)
 {
     static const char copyright_str[] = "Copyright © 2009, 2010, 2011 Red Hat, Inc";
@@ -330,12 +378,15 @@ GtkWidget *create_menu(void)
     /* help submenu */
     GtkWidget *help_submenu = gtk_menu_new();
     GtkWidget *online_help_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_HELP, NULL);
+    GtkWidget *report_problem_item = gtk_menu_item_new_with_label(_("Report problem with ABRT"));
     GtkWidget *about_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
     gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), online_help_item);
+    gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), report_problem_item);
     gtk_menu_shell_append(GTK_MENU_SHELL(help_submenu), about_item);
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_submenu);
 
     g_signal_connect(online_help_item, "activate", G_CALLBACK(on_menu_help_cb), NULL);
+    g_signal_connect(report_problem_item, "activate", G_CALLBACK(on_menu_report_cb), NULL);
     g_signal_connect(about_item, "activate", G_CALLBACK(on_menu_about_cb), NULL);
 
     return menu;
diff --git a/src/include/report/problem_data.h b/src/include/report/problem_data.h
index 1481654..c805c37 100644
--- a/src/include/report/problem_data.h
+++ b/src/include/report/problem_data.h
@@ -51,6 +51,8 @@ typedef GHashTable problem_data_t;
 
 problem_data_t *new_problem_data(void);
 
+void add_basics_to_problem_data(problem_data_t *pd);
+
 static inline void free_problem_data(problem_data_t *problem_data)
 {
     if (problem_data)
diff --git a/src/include/report/report.h b/src/include/report/report.h
index 11d1968..f57d527 100644
--- a/src/include/report/report.h
+++ b/src/include/report/report.h
@@ -21,16 +21,22 @@
 
 #include "problem_data.h"
 
+enum {
+    NOWAIT = 0,
+    WAIT = (1 << 0), /* wait for report to finish and reload the problem data */
+};
+
+
 /* analyzes AND reports a problem saved on disk
  * - takes user through all the steps in reporting wizard
  */
-int analyze_and_report_dir(const char* dirname);
+int analyze_and_report_dir(const char* dirname, int flags);
 
 /* analyzes AND reports a problem stored in problem_data_t
  * it's first saved to /tmp and then processed as a dump_dir
  * - takes user through all the steps in reporting wizard
  */
-int analyze_and_report(problem_data_t *pd);
+int analyze_and_report(problem_data_t *pd, int flags);
 
 /* reports a problem saved on disk
  * - shows only reporter selector and progress
diff --git a/src/lib/problem_data.c b/src/lib/problem_data.c
index 42dc770..657044b 100644
--- a/src/lib/problem_data.c
+++ b/src/lib/problem_data.c
@@ -57,6 +57,38 @@ problem_data_t *new_problem_data(void)
                  free, free_problem_item);
 }
 
+void add_basics_to_problem_data(problem_data_t *pd)
+{
+    const char *analyzer = get_problem_item_content_or_NULL(pd, FILENAME_ANALYZER);
+    if (analyzer == NULL)
+        add_to_problem_data(pd, "analyzer", "libreport");
+
+    pid_t pid = getpid();
+    if (pid > 0)
+    {
+        char buf[PATH_MAX+1];
+        char *exe = xasprintf("/proc/%u/exe", pid);
+        ssize_t read = readlink(exe, buf, PATH_MAX);
+        if (read > 0)
+        {
+            buf[read] = 0;
+            VERB2 log("reporting initiated from: %s\n", buf);
+            add_to_problem_data(pd, FILENAME_EXECUTABLE, buf);
+        }
+        free(exe);
+
+//#ifdef WITH_RPM
+        /* FIXME: component should be taken from rpm using
+         * rpm -qf executable
+        */
+        /* Fedora/RHEL rpm specific piece of code */
+        const char *component = get_problem_item_content_or_NULL(pd, FILENAME_ANALYZER);
+        if(component == NULL) // application didn't specify component
+            add_to_problem_data(pd, FILENAME_COMPONENT, "abrt");
+//#endif
+    }
+}
+
 void add_to_problem_data_ext(problem_data_t *problem_data,
                 const char *name,
                 const char *content,
diff --git a/src/lib/report.c b/src/lib/report.c
index a3ba6e9..1a0125e 100644
--- a/src/lib/report.c
+++ b/src/lib/report.c
@@ -18,12 +18,46 @@
 */
 
 #include "abrtlib.h"
+#include "report.h"
 
-int analyze_and_report_dir(const char* dirname)
+static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput)
 {
+    unsigned char hash_bytes[SHA1_RESULT_LEN];
+    sha1_ctx_t sha1ctx;
+    sha1_begin(&sha1ctx);
+    sha1_hash(&sha1ctx, pInput, strlen(pInput));
+    sha1_end(&sha1ctx, hash_bytes);
+
+    unsigned len = SHA1_RESULT_LEN;
+    unsigned char *s = hash_bytes;
+    char *d = hash_str;
+    while (len)
+    {
+        *d++ = "0123456789abcdef"[*s >> 4];
+        *d++ = "0123456789abcdef"[*s & 0xf];
+        s++;
+        len--;
+    }
+    *d = '\0';
+    //log("hash:%s str:'%s'", hash_str, pInput);
+}
+
+static void generate_hash_for_all(gpointer key, gpointer value, gpointer user_data)
+{
+    problem_item *pi = (problem_item *)value;
+    char *hash_str = (char *)user_data;
+    create_hash(hash_str, pi->content);
+}
+
+static int run_reporter_ui(char **args, int flags)
+{
+    char path[PATH_MAX+1];
     /*
-    if is isatty -> run cli reporter
+    if is isatty
+      -> run cli reporter
+      path = "cli"
     */
+
     pid_t pid = vfork();
     if (pid == 0)
     {
@@ -34,66 +68,135 @@ int analyze_and_report_dir(const char* dirname)
          * Note that we do it in the child, so the parent is never affected.
          */
         signal(SIGCHLD, SIG_DFL); // applet still set it to SIG_IGN
-        VERB1 log("Executing: %s %s", "bug-reporting-wizard", dirname);
-        execl(BIN_DIR"/bug-reporting-wizard", "bug-reporting-wizard", "--", dirname, NULL);
-        // note the -o in options which means --report-only
+        strncpy(path, BIN_DIR"/bug-reporting-wizard", PATH_MAX);
+        path[PATH_MAX] = 0;
+        VERB1 log("Executing: %s", path);
+        execv(path, args);
         /* Did not find abrt-gui in installation directory. Oh well */
         /* Trying to find it in PATH */
-        execlp("bug-reporting-wizard", "bug-reporting-wizard", "--", dirname, NULL);
+        strncpy(path, "bug-reporting-wizard", PATH_MAX);
+        execvp(path, args);
         perror_msg_and_die("Can't execute %s", "bug-reporting-wizard");
     }
+    else if(pid > 0)
+    {
+        if (flags & WAIT)
+        {
+            int status = 0;
+            pid_t p = waitpid(pid, &status, WUNTRACED);
+            if(p == -1)
+            {
+                error_msg("can't waitpid");
+                return EXIT_FAILURE;
+            }
+            if (WIFEXITED(status))
+            {
+                VERB2 log("reporting finished with exitcode: status=%d\n", WEXITSTATUS(status));
+                return WEXITSTATUS(status);
+            }
+            else if (WIFSIGNALED(status))
+            {
+                VERB2 log("reporting killed by signal %d\n", WTERMSIG(status));
+            }
+            else if (WIFSTOPPED(status))
+            {
+                /* should parent continue when the reporting is stopped??*/
+                VERB2 log("reporting stopped by signal %d\n", WSTOPSIG(status));
+            }
+            else if (WIFCONTINUED(status))
+            {
+                VERB2 log("continued\n");
+            }
+        }
+    }
+    return 0;
+}
+
+int analyze_and_report_dir(const char* dirname, int flags)
+{
+    char *args[4];
+
+    args[0] = (char *)"bug-reporting-wizard";
+    args[1] = (char *)"--";
+    args[2] = (char *)dirname;
+    args[3] = NULL;
+
+    run_reporter_ui(args, flags);
     return 0;
 }
 
 /* analyzes AND reports a problem saved on disk
  * - takes user through all the steps in reporting wizard
  */
-int analyze_and_report(problem_data_t *pd)
+int analyze_and_report(problem_data_t *pd, int flags)
 {
+    int result = 0;
     struct dump_dir *dd = create_dump_dir_from_problem_data(pd, "/tmp"/* /var/tmp ?? */);
     if (!dd)
         return -1;
     char *dir_name = strdup(dd->dd_dirname);
     dd_close(dd);
     VERB2 log("Temp problem dir: '%s'\n", dir_name);
-    analyze_and_report_dir(dir_name);
+    result = analyze_and_report_dir(dir_name, flags);
+
+    /* if we wait for reporter to finish, we can try to clean the tmp dir */
+    if (flags & WAIT)
+    {
+        dd = dd_opendir(dir_name, 0);
+        if (dd)
+        {
+            if (dd_delete(dd) != 0)
+            {
+                error_msg("Can't remove tmp dir: %s", dd->dd_dirname);
+            }
+        }
+    }
     free(dir_name);
-    return 0;
+    return result;
 }
 
+/* report() and report_dir() don't take flags, because in all known use-cases
+ * it doesn't make sense to not wait for the result
+ *
+*/
+
 /* reports a problem saved on disk
  * - shows only reporter selector and progress
 */
 int report_dir(const char* dirname)
 {
-    pid_t pid = vfork();
-    if (pid == 0)
-    {
-        /* Some callers set SIGCHLD to SIG_IGN.
-         * However, reporting spawns chils processes.
-         * Suppressing chil death notification terribly confuses some of them.
-         * Just in case, undo it.
-         * Note that we do it in the child, so the parent is never affected.
-         */
-        signal(SIGCHLD, SIG_DFL); // applet still set it to SIG_IGN
-        VERB1 log("Executing: %s %s", "bug-reporting-wizard", dirname);
-        execl(BIN_DIR"/bug-reporting-wizard", "bug-reporting-wizard",
-                  "-o", "--", dirname, NULL);
-        // note the -o in options which means --report-only
-        /* Did not find abrt-gui in installation directory. Oh well */
-        /* Trying to find it in PATH */
-        execlp("bug-reporting-wizard", "bug-reporting-wizard",
-                "-o", "--", dirname, NULL);
-        perror_msg_and_die("Can't execute %s", "bug-reporting-wizard");
-    }
+    char *args[5];
+
+    args[0] = (char *)"bug-reporting-wizard";
+    args[1] = (char *)"--report-only";
+    args[2] = (char *)"--";
+    args[3] = (char *)dirname;
+    args[4] = NULL;
+
+    int flags = WAIT;
+    run_reporter_ui(args, flags);
     return 0;
 }
 
 int report(problem_data_t *pd)
 {
+    /* create hash from all components, so we at least eliminate the exact same
+     * reports
+    */
+    char hash_str[SHA1_RESULT_LEN*2 + 1];
+    g_hash_table_foreach(pd, &generate_hash, hash_str);
+    add_to_problem_data(pd, FILENAME_DUPHASH, hash_str);
+
+    /* adds:
+     *  analyzer:libreport
+     *  executable:readlink(/proc/<pid>/exe)
+     * tries to guess component
+    */
+    add_basics_to_problem_data(pd);
     struct dump_dir *dd = create_dump_dir_from_problem_data(pd, "/tmp"/* /var/tmp ?? */);
     if (!dd)
         return -1;
+    dd_create_basic_files(dd, getuid());
     char *dir_name = xstrdup(dd->dd_dirname);
     dd_close(dd);
     VERB2 log("Temp problem dir: '%s'\n", dir_name);
@@ -102,4 +205,3 @@ int report(problem_data_t *pd)
 
     return 0;
 }
-
-- 
1.7.5.1

