/* bug-buddy bug submitting program
*
* Copyright (C) 2001 Jacob Berkman
* Copyright 2001 Ximian, Inc.
*
* Author: jacob berkman <jacob@bug-buddy.org>
*
* 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 <config.h>
#include "bug-buddy.h"
#include "distribution.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <utime.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <glib/gi18n.h>
#include <gnome.h>
#include <gmenu-tree.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo-activation/bonobo-activation.h>
#include <dirent.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlmemory.h>
#include <libsoup/soup.h>
#include <libsoup/soup-xmlrpc-message.h>
#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 gnome-applications.menu*/
tree = gmenu_tree_lookup (menufile, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
} else {
/* fallback to using the gnome-applications.menu */
tree = gmenu_tree_lookup ("gnome-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 <fault> */
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;
}
syntax highlighted by Code2HTML, v. 0.9.1