/* bug-buddy bug submitting program * * Copyright (C) 2001 Jacob Berkman * Copyright 2001 Ximian, Inc. * * Author: jacob berkman * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include "bug-buddy.h" #include "distribution.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define APPLET_REQUIREMENTS \ "has_all (repo_ids, ['IDL:Bonobo/Control:1.0'," \ " 'IDL:GNOME/Vertigo/PanelAppletShell:1.0']) && " \ "defined (panel:icon)" #define DESKTOP_ENTRY "Desktop Entry" #define DESKTOP_NAME "Name" #define DESKTOP_COMMENT "Comment" #define DESKTOP_ICON "Icon" #define DESKTOP_EXEC "Exec" #define BUGZILLA_BUGZILLA "X-GNOME-Bugzilla-Bugzilla" #define BUGZILLA_PRODUCT "X-GNOME-Bugzilla-Product" #define BUGZILLA_COMPONENT "X-GNOME-Bugzilla-Component" #define BUGZILLA_EMAIL "X-GNOME-Bugzilla-Email" #define BUGZILLA_VERSION "X-GNOME-Bugzilla-Version" #define BUGZILLA_OTHER_BINARIES "X-GNOME-Bugzilla-OtherBinaries" #define BUGZILLA_EXTRA_INFO_SCRIPT "X-GNOME-Bugzilla-ExtraInfoScript" #define BA_BUGZILLA_BUGZILLA "bugzilla:bugzilla" #define BA_BUGZILLA_PRODUCT "bugzilla:product" #define BA_BUGZILLA_COMPONENT "bugzilla:component" #define BA_BUGZILLA_VERSION "bugzilla:version" #define BA_BUGZILLA_OTHER_BINARIES "bugzilla:other_binaries" #define BA_BUGZILLA_EXTRA_INFO_SCRIPT "bugzilla:extra_info_script" static void add_bugzilla_application (GHashTable *hash, const char *name, const char *cname, const char *comment, const char *bugzilla, const char *product, const char *component, const char *version, const char *icon, const char *program, const char *other_programs, const char *extra_info_script) { BugzillaApplication *app; char **programv; int i; app = g_new0 (BugzillaApplication, 1); app->name = g_strdup (name); app->cname = g_strdup (cname); app->comment = g_strdup (comment); app->icon = g_strdup (icon); app->bugzilla = g_strdup (bugzilla); app->product = g_strdup (product); app->component = g_strdup (component); app->version = g_strdup (version); app->extra_info_script = g_strdup (extra_info_script); if (program) { g_shell_parse_argv (program, &i, &programv, NULL); if (programv[0]) { char *s; s = strrchr (programv[0], G_DIR_SEPARATOR); s = s ? s+1 : programv[0]; app->ref_count += 1; g_hash_table_insert (hash, g_strdup (s), app); } else { g_free (app); return; } if (programv) g_strfreev (programv); } if (other_programs) { programv = g_strsplit (other_programs, ";", -1); for (i=0; programv[i]; i++) { app->ref_count += 1; g_hash_table_insert (hash, g_strdup (programv[i]), app); } g_strfreev (programv); } } static void application_free (BugzillaApplication *app) { app->ref_count -= 1; if (app->ref_count > 0) return; g_free (app->name); g_free (app->cname); g_free (app->comment); g_free (app->icon); g_free (app->bugzilla); g_free (app->product); g_free (app->component); g_free (app->version); g_free (app->extra_info_script); g_free (app); } static const GSList * get_i18n_slist (void) { const char * const *langs; guint i; static GSList *langs_gslist = NULL; if (langs_gslist) return langs_gslist; langs = g_get_language_names (); for (i = 0; langs[i] != 0; ++i) { langs_gslist = g_slist_append (langs_gslist, (gpointer) langs[i]); } return langs_gslist; } static void load_applets (GHashTable *hash) { Bonobo_ServerInfoList *info_list; Bonobo_ServerInfo *info; CORBA_Environment ev; GSList *langs; int i; gchar *name; CORBA_exception_init (&ev); info_list = bonobo_activation_query (APPLET_REQUIREMENTS, NULL, &ev); if (BONOBO_EX (&ev)) { g_warning ("Applet list query failed: %s", BONOBO_EX_REPOID (&ev)); CORBA_exception_free (&ev); return; } CORBA_exception_free (&ev); langs = (GSList *)get_i18n_slist (); for (i = 0; i < info_list->_length; i++) { info = info_list->_buffer + i; if (!bonobo_server_info_prop_lookup (info, BA_BUGZILLA_BUGZILLA, NULL)) { continue; } name = g_strdup (bonobo_server_info_prop_lookup (info, "name", langs)); /*FIXME: for (l = applications; l; l = l->next) { BugzillaApplication *app = l->data; if (strcmp (app->name, name) == 0) { g_free (name); name = g_strdup_printf (_("%s (Panel Applet)"), bonobo_server_info_prop_lookup (info, "name", langs)); break; } }*/ add_bugzilla_application (hash, name, bonobo_server_info_prop_lookup (info, "name", NULL), bonobo_server_info_prop_lookup (info, "description", langs), bonobo_server_info_prop_lookup (info, BA_BUGZILLA_BUGZILLA, NULL), bonobo_server_info_prop_lookup (info, BA_BUGZILLA_PRODUCT, NULL), bonobo_server_info_prop_lookup (info, BA_BUGZILLA_COMPONENT, NULL), bonobo_server_info_prop_lookup (info, BA_BUGZILLA_VERSION, NULL), bonobo_server_info_prop_lookup (info, "panel:icon", NULL), NULL, bonobo_server_info_prop_lookup (info, BA_BUGZILLA_OTHER_BINARIES, NULL), bonobo_server_info_prop_lookup (info, BA_BUGZILLA_EXTRA_INFO_SCRIPT, NULL)); g_free (name); } CORBA_free (info_list); } static int compare_applications (GMenuTreeEntry *a, GMenuTreeEntry *b) { return g_utf8_collate (gmenu_tree_entry_get_name (a), gmenu_tree_entry_get_name (b)); } static GSList *get_all_applications_from_dir (GMenuTreeDirectory *directory, GSList *list); static GSList * get_all_applications_from_alias (GMenuTreeAlias *alias, GSList *list) { GMenuTreeItem *aliased_item; aliased_item = gmenu_tree_alias_get_item (alias); switch (gmenu_tree_item_get_type (aliased_item)) { case GMENU_TREE_ITEM_DIRECTORY: list = get_all_applications_from_dir (GMENU_TREE_DIRECTORY (aliased_item), list); break; case GMENU_TREE_ITEM_ENTRY: list = g_slist_append (list, gmenu_tree_item_ref (aliased_item)); break; default: break; } gmenu_tree_item_unref (aliased_item); return list; } static GSList * get_all_applications_from_dir (GMenuTreeDirectory *directory, GSList *list) { GSList *items; GSList *l; if (g_main_context_pending (NULL)) { g_main_context_iteration (NULL, FALSE); } items = gmenu_tree_directory_get_contents (directory); for (l = items; l; l = l->next) { GMenuTreeItem *item = l->data; switch (gmenu_tree_item_get_type (item)) { case GMENU_TREE_ITEM_DIRECTORY: list = get_all_applications_from_dir (GMENU_TREE_DIRECTORY (item), list); break; case GMENU_TREE_ITEM_ENTRY: list = g_slist_append (list, gmenu_tree_item_ref (item)); break; case GMENU_TREE_ITEM_ALIAS: list = get_all_applications_from_alias (GMENU_TREE_ALIAS (item), list); break; default: break; } gmenu_tree_item_unref (item); } g_slist_free (items); return list; } static GSList * get_all_applications (void) { GMenuTree *tree; GMenuTreeDirectory *root; GSList *retval; const char *menufile = BUDDY_DATADIR "/bug-buddy.menu"; if (g_file_test (menufile, G_FILE_TEST_IS_REGULAR)) { /* use a custom menu file to scan desktop entry files so we aren't limited * to reporting bugs only present in the applications menu*/ tree = gmenu_tree_lookup (menufile, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY); } else { /* fallback to using the applications menu */ tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY); } root = gmenu_tree_get_root_directory (tree); retval = get_all_applications_from_dir (root, NULL); gmenu_tree_item_unref (root); gmenu_tree_unref (tree); retval = g_slist_sort (retval, (GCompareFunc) compare_applications); return retval; } GQuark bugzilla_error_quark (void) { return g_quark_from_static_string ("bugzilla_error"); } GHashTable * load_applications (void) { GSList *all_applications; GSList *l; char *prev_name = NULL; GError *error = NULL; GHashTable *program_to_application = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) application_free); all_applications = get_all_applications (); for (l = all_applications; l; l = l->next) { GKeyFile *key_file; char *name; char *cname; char *comment; char *bugzilla; char *product; char *component; char *version; char *icon; char *exec; char *other_binaries; char *extra_info_script; GMenuTreeEntry *entry = l->data; if (g_main_context_pending (NULL)) { g_main_context_iteration (NULL, FALSE); } if (prev_name && strcmp (gmenu_tree_entry_get_name (entry), prev_name) == 0) { gmenu_tree_item_unref (entry); continue; } key_file = g_key_file_new (); g_key_file_load_from_file (key_file, gmenu_tree_entry_get_desktop_file_path (entry), G_KEY_FILE_NONE, &error); if (error) { g_warning ("Couldn't load %s: %s", gmenu_tree_entry_get_desktop_file_path (entry), error->message); g_error_free (error); error = NULL; gmenu_tree_item_unref (entry); continue; } if (!g_key_file_has_group (key_file, DESKTOP_ENTRY) || !g_key_file_has_key (key_file, DESKTOP_ENTRY, BUGZILLA_BUGZILLA, &error)) { g_key_file_free (key_file); gmenu_tree_item_unref (entry); if (error) g_error_free (error); continue; } name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY, DESKTOP_NAME, NULL, NULL); cname = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_NAME, NULL); comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY, DESKTOP_COMMENT, NULL, NULL); bugzilla = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_BUGZILLA, NULL); product = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_PRODUCT, NULL); component = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_COMPONENT, NULL); version = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_VERSION, NULL); icon = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_ICON, NULL); exec = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_EXEC, NULL); other_binaries = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_OTHER_BINARIES, NULL); extra_info_script = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_EXTRA_INFO_SCRIPT, NULL); add_bugzilla_application (program_to_application, name, cname, comment, bugzilla, product, component, version, icon, exec, other_binaries, extra_info_script); g_free (name); g_free (cname); g_free (comment); g_free (bugzilla); g_free (product); g_free (component); g_free (version); g_free (icon); g_free (exec); g_free (other_binaries); g_free (extra_info_script); g_free (prev_name); prev_name = g_strdup (gmenu_tree_entry_get_name (entry)); g_key_file_free (key_file); gmenu_tree_item_unref (entry); } g_slist_free (all_applications); load_applets (program_to_application); return program_to_application; } gboolean bugzilla_search_for_package (gpointer key, gpointer value, const char *package) { BugzillaApplication *app = (BugzillaApplication*) value; if (!strcmp (app->product, package)) return TRUE; return FALSE; } char * bugzilla_parse_response (SoupMessage *msg, GError **err) { SoupXmlrpcResponse *response; SoupXmlrpcValue *value; long bugid = 0; int debug = 0; char *url; g_return_val_if_fail ((err == NULL || *err == NULL), NULL); if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_BAD_STATUS, _("HTTP Response returned bad status code %d"), msg->status_code); return NULL; } response = soup_xmlrpc_message_parse_response (SOUP_XMLRPC_MESSAGE (msg)); if (!response) { g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_PARSE_FAILED, _("Unable to parse XML-RPC Response")); return NULL; } /* check to see if the XMLRPC response was a */ if (soup_xmlrpc_response_is_fault (response)) { SoupXmlrpcValue *faultCode = NULL; SoupXmlrpcValue *faultString = NULL; GHashTable *fault = NULL; gchar *errormsg = NULL; long errorcode = -1; value = soup_xmlrpc_response_get_value (response); if (!value) { debug = 1; goto parse_error; } /* get the struct representing the fault */ if (!soup_xmlrpc_value_get_struct (value, &fault)) { debug = 2; goto parse_error; } /* get the integer representing the fault code */ faultCode = g_hash_table_lookup (fault, "faultCode"); if (faultCode == NULL || !soup_xmlrpc_value_get_int (faultCode, &errorcode)) { debug = 3; goto parse_error; } /* get the string representing the fault string */ faultString = g_hash_table_lookup (fault, "faultString"); if (faultString == NULL || !soup_xmlrpc_value_get_string (faultString, &errormsg)) { debug = 4; goto parse_error; } /* send back a BUGZILLA_ERROR_FAULT, using the errorcode and errorstring to * construct the GError message */ g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_FAULT, "%ld:%s", errorcode, errormsg); return NULL; } value = soup_xmlrpc_response_get_value (response); if (!value) { debug = 5; goto parse_error; } if (!soup_xmlrpc_value_get_int (value, &bugid)) { if (!soup_xmlrpc_value_get_string (value, &url)) { debug = 6; goto parse_error; } } /* whew, everything checked out, send back the bug id */ g_object_unref (response); return bugid ? g_strdup_printf ("%ld", bugid) : url; parse_error: g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_PARSE_FAILED, _("Unable to parse XML-RPC Response\n\n%d\n\n%s"), debug, soup_xmlrpc_response_to_string (response)); g_object_unref (response); return NULL; } SoupXmlrpcMessage* bugzilla_create_report (BugzillaApplication *app, int type, GnomeVersionInfo *gnome_version, const char *username, const char *title, const char *text, GtkBuilder *ui, const char *minidump_file, GError **err) { SoupXmlrpcMessage *message; char *user_agent; char *os_version; g_return_val_if_fail (app != NULL, NULL); g_return_val_if_fail (gnome_version != NULL, NULL); g_return_val_if_fail (username != NULL, NULL); g_return_val_if_fail (text != NULL, NULL); g_return_val_if_fail (ui != NULL, NULL); g_return_val_if_fail (err == NULL || *err == NULL, NULL); /* FIXME: Hardcoded right now */ if (app->bugzilla == NULL || strcmp (app->bugzilla, "GNOME") != 0) { g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP, _("Application does not track its bugs in the GNOME Bugzilla.")); return NULL; } if (!app->product || !app->component) { g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP, _("Product or component not specified.")); return NULL; } if (minidump_file) //message = soup_xmlrpc_message_new ("http://localhost/breakpad/xmlrpc.py"); message = soup_xmlrpc_message_new ("http://socorro.gnome.org/collect.py"); else message = soup_xmlrpc_message_new ("http://bugzilla.gnome.org/bugbuddy.cgi"); if (message == NULL) { g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_ERROR, _("Unable to create XML-RPC message.")); return NULL; } user_agent = g_strdup_printf ("Bug-Buddy: %s", VERSION); soup_message_add_header (SOUP_MESSAGE(message)->request_headers, "User-Agent", user_agent); g_free (user_agent); soup_xmlrpc_message_start_call (message, "BugBuddy.createBug"); soup_xmlrpc_message_start_param (message); soup_xmlrpc_message_start_struct (message); soup_xmlrpc_message_start_member (message, "version"); soup_xmlrpc_message_write_string (message, app->version ? app->version : "unspecified"); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "product"); soup_xmlrpc_message_write_string (message, app->product); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "component"); soup_xmlrpc_message_write_string (message, app->component); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "gnome_version"); soup_xmlrpc_message_write_string (message, gnome_version->gnome_platform); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "reporter"); soup_xmlrpc_message_write_string (message, username); soup_xmlrpc_message_end_member (message); os_version = get_distro_name (); soup_xmlrpc_message_start_member (message, "os_version"); soup_xmlrpc_message_write_string (message, os_version); soup_xmlrpc_message_end_member (message); g_free (os_version); if (type == BUG_TYPE_CRASH) { soup_xmlrpc_message_start_member (message, "priority"); soup_xmlrpc_message_write_string (message, "High"); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "bug_severity"); soup_xmlrpc_message_write_string (message, "critical"); soup_xmlrpc_message_end_member (message); } soup_xmlrpc_message_start_member (message, "short_desc"); soup_xmlrpc_message_write_string (message, title); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "comment"); soup_xmlrpc_message_write_string (message, text); soup_xmlrpc_message_end_member (message); if (minidump_file) { gchar *minidumpbuf; gsize length; GError *error; gchar *base64 = NULL; if (g_file_get_contents (minidump_file, &minidumpbuf, &length, &error)) { base64 = g_base64_encode ((guchar*)minidumpbuf, length); g_free (minidumpbuf); } else { g_error_free (error); } if (base64) { gchar *basename; gchar *id; basename = g_path_get_basename (minidump_file); id = g_strndup (basename, strlen (basename) - strlen (".dmp")); soup_xmlrpc_message_start_member (message, "minidump-id"); soup_xmlrpc_message_write_string (message, id); soup_xmlrpc_message_end_member (message); soup_xmlrpc_message_start_member (message, "minidump"); soup_xmlrpc_message_write_base64 (message, base64, strlen(base64)); soup_xmlrpc_message_end_member (message); g_free (base64); g_free (basename); g_free (id); } } soup_xmlrpc_message_end_param (message); soup_xmlrpc_message_end_struct (message); soup_xmlrpc_message_end_call (message); return message; }