1 /* bug-buddy bug submitting program
3 * Copyright (C) 2001 Jacob Berkman
4 * Copyright 2001 Ximian, Inc.
6 * Author: jacob berkman <jacob@bug-buddy.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
24 #include "bug-buddy.h"
25 #include "distribution.h"
28 #include <sys/types.h>
35 #include <glib/gi18n.h>
38 #include <gmenu-tree.h>
40 #include <bonobo/bonobo-exception.h>
41 #include <bonobo-activation/bonobo-activation.h>
45 #include <libxml/tree.h>
46 #include <libxml/parser.h>
47 #include <libxml/xmlmemory.h>
49 #include <libsoup/soup.h>
52 #define APPLET_REQUIREMENTS \
53 "has_all (repo_ids, ['IDL:Bonobo/Control:1.0'," \
54 " 'IDL:GNOME/Vertigo/PanelAppletShell:1.0']) && " \
55 "defined (panel:icon)"
57 #define DESKTOP_ENTRY "Desktop Entry"
58 #define DESKTOP_NAME "Name"
59 #define DESKTOP_COMMENT "Comment"
60 #define DESKTOP_ICON "Icon"
61 #define DESKTOP_EXEC "Exec"
63 #define BUGZILLA_BUGZILLA "X-GNOME-Bugzilla-Bugzilla"
64 #define BUGZILLA_PRODUCT "X-GNOME-Bugzilla-Product"
65 #define BUGZILLA_COMPONENT "X-GNOME-Bugzilla-Component"
66 #define BUGZILLA_EMAIL "X-GNOME-Bugzilla-Email"
67 #define BUGZILLA_VERSION "X-GNOME-Bugzilla-Version"
68 #define BUGZILLA_OTHER_BINARIES "X-GNOME-Bugzilla-OtherBinaries"
69 #define BUGZILLA_EXTRA_INFO_SCRIPT "X-GNOME-Bugzilla-ExtraInfoScript"
71 #define BA_BUGZILLA_BUGZILLA "bugzilla:bugzilla"
72 #define BA_BUGZILLA_PRODUCT "bugzilla:product"
73 #define BA_BUGZILLA_COMPONENT "bugzilla:component"
74 #define BA_BUGZILLA_VERSION "bugzilla:version"
75 #define BA_BUGZILLA_OTHER_BINARIES "bugzilla:other_binaries"
76 #define BA_BUGZILLA_EXTRA_INFO_SCRIPT "bugzilla:extra_info_script"
79 add_bugzilla_application (GHashTable
*hash
,
85 const char *component
,
89 const char *other_programs
,
90 const char *extra_info_script
)
92 BugzillaApplication
*app
;
96 app
= g_new0 (BugzillaApplication
, 1);
98 app
->name
= g_strdup (name
);
99 app
->cname
= g_strdup (cname
);
100 app
->comment
= g_strdup (comment
);
101 app
->icon
= g_strdup (icon
);
102 app
->bugzilla
= g_strdup (bugzilla
);
103 app
->product
= g_strdup (product
);
104 app
->component
= g_strdup (component
);
105 app
->version
= g_strdup (version
);
106 app
->extra_info_script
= g_strdup (extra_info_script
);
109 g_shell_parse_argv (program
, &i
, &programv
, NULL
);
112 s
= strrchr (programv
[0], G_DIR_SEPARATOR
);
113 s
= s
? s
+1 : programv
[0];
115 g_hash_table_insert (hash
, g_strdup (s
), app
);
121 g_strfreev (programv
);
124 if (other_programs
) {
125 programv
= g_strsplit (other_programs
, ";", -1);
126 for (i
=0; programv
[i
]; i
++) {
128 g_hash_table_insert (hash
, g_strdup (programv
[i
]), app
);
130 g_strfreev (programv
);
135 application_free (BugzillaApplication
*app
)
138 if (app
->ref_count
> 0)
143 g_free (app
->comment
);
145 g_free (app
->bugzilla
);
146 g_free (app
->product
);
147 g_free (app
->component
);
148 g_free (app
->version
);
149 g_free (app
->extra_info_script
);
156 static const GSList
*
157 get_i18n_slist (void)
159 const char * const *langs
;
161 static GSList
*langs_gslist
= NULL
;
166 langs
= g_get_language_names ();
167 for (i
= 0; langs
[i
] != 0; ++i
) {
168 langs_gslist
= g_slist_append (langs_gslist
, (gpointer
) langs
[i
]);
175 load_applets (GHashTable
*hash
)
177 Bonobo_ServerInfoList
*info_list
;
178 Bonobo_ServerInfo
*info
;
179 CORBA_Environment ev
;
184 CORBA_exception_init (&ev
);
185 info_list
= bonobo_activation_query (APPLET_REQUIREMENTS
, NULL
, &ev
);
186 if (BONOBO_EX (&ev
)) {
187 g_warning ("Applet list query failed: %s", BONOBO_EX_REPOID (&ev
));
188 CORBA_exception_free (&ev
);
191 CORBA_exception_free (&ev
);
193 langs
= (GSList
*)get_i18n_slist ();
195 for (i
= 0; i
< info_list
->_length
; i
++) {
196 info
= info_list
->_buffer
+ i
;
197 if (!bonobo_server_info_prop_lookup (info
,
198 BA_BUGZILLA_BUGZILLA
,
203 name
= g_strdup (bonobo_server_info_prop_lookup (info
, "name", langs
));
205 for (l = applications; l; l = l->next) {
206 BugzillaApplication *app = l->data;
208 if (strcmp (app->name, name) == 0) {
210 name = g_strdup_printf (_("%s (Panel Applet)"), bonobo_server_info_prop_lookup (info, "name", langs));
216 add_bugzilla_application (hash
,
218 bonobo_server_info_prop_lookup (info
, "name", NULL
),
219 bonobo_server_info_prop_lookup (info
, "description", langs
),
220 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_BUGZILLA
, NULL
),
221 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_PRODUCT
, NULL
),
222 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_COMPONENT
, NULL
),
223 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_VERSION
, NULL
),
224 bonobo_server_info_prop_lookup (info
, "panel:icon", NULL
),
226 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_OTHER_BINARIES
, NULL
),
227 bonobo_server_info_prop_lookup (info
, BA_BUGZILLA_EXTRA_INFO_SCRIPT
, NULL
));
232 CORBA_free (info_list
);
236 compare_applications (GMenuTreeEntry
*a
,
239 return g_utf8_collate (gmenu_tree_entry_get_name (a
),
240 gmenu_tree_entry_get_name (b
));
243 static GSList
*get_all_applications_from_dir (GMenuTreeDirectory
*directory
,
247 get_all_applications_from_alias (GMenuTreeAlias
*alias
,
250 GMenuTreeItem
*aliased_item
;
252 aliased_item
= gmenu_tree_alias_get_item (alias
);
254 switch (gmenu_tree_item_get_type (aliased_item
)) {
255 case GMENU_TREE_ITEM_DIRECTORY
:
256 list
= get_all_applications_from_dir (GMENU_TREE_DIRECTORY (aliased_item
), list
);
259 case GMENU_TREE_ITEM_ENTRY
:
260 list
= g_slist_append (list
, gmenu_tree_item_ref (aliased_item
));
267 gmenu_tree_item_unref (aliased_item
);
273 get_all_applications_from_dir (GMenuTreeDirectory
*directory
,
279 if (g_main_context_pending (NULL
)) {
280 g_main_context_iteration (NULL
, FALSE
);
283 items
= gmenu_tree_directory_get_contents (directory
);
284 for (l
= items
; l
; l
= l
->next
) {
285 GMenuTreeItem
*item
= l
->data
;
287 switch (gmenu_tree_item_get_type (item
)) {
288 case GMENU_TREE_ITEM_DIRECTORY
:
289 list
= get_all_applications_from_dir (GMENU_TREE_DIRECTORY (item
), list
);
292 case GMENU_TREE_ITEM_ENTRY
:
293 list
= g_slist_append (list
, gmenu_tree_item_ref (item
));
296 case GMENU_TREE_ITEM_ALIAS
:
297 list
= get_all_applications_from_alias (GMENU_TREE_ALIAS (item
), list
);
304 gmenu_tree_item_unref (item
);
307 g_slist_free (items
);
313 get_all_applications (void)
316 GMenuTreeDirectory
*root
;
318 const char *menufile
= BUDDY_DATADIR
"/bug-buddy.menu";
320 if (g_file_test (menufile
, G_FILE_TEST_IS_REGULAR
)) {
321 /* use a custom menu file to scan desktop entry files so we aren't limited
322 * to reporting bugs only present in the applications menu*/
323 tree
= gmenu_tree_lookup (menufile
, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY
);
325 /* fallback to using the applications menu */
326 tree
= gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY
);
329 root
= gmenu_tree_get_root_directory (tree
);
331 retval
= get_all_applications_from_dir (root
, NULL
);
333 gmenu_tree_item_unref (root
);
334 gmenu_tree_unref (tree
);
336 retval
= g_slist_sort (retval
, (GCompareFunc
) compare_applications
);
342 bugzilla_error_quark (void)
344 return g_quark_from_static_string ("bugzilla_error");
348 load_applications (void)
350 GSList
*all_applications
;
352 char *prev_name
= NULL
;
353 GError
*error
= NULL
;
355 GHashTable
*program_to_application
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, (GDestroyNotify
) application_free
);
357 all_applications
= get_all_applications ();
358 for (l
= all_applications
; l
; l
= l
->next
) {
369 char *other_binaries
;
370 char *extra_info_script
;
371 GMenuTreeEntry
*entry
= l
->data
;
373 if (g_main_context_pending (NULL
)) {
374 g_main_context_iteration (NULL
, FALSE
);
377 if (prev_name
&& strcmp (gmenu_tree_entry_get_name (entry
), prev_name
) == 0) {
378 gmenu_tree_item_unref (entry
);
381 key_file
= g_key_file_new ();
382 g_key_file_load_from_file (key_file
, gmenu_tree_entry_get_desktop_file_path (entry
),
383 G_KEY_FILE_NONE
, &error
);
385 g_warning ("Couldn't load %s: %s", gmenu_tree_entry_get_desktop_file_path (entry
),
387 g_error_free (error
);
389 gmenu_tree_item_unref (entry
);
393 if (!g_key_file_has_group (key_file
, DESKTOP_ENTRY
) || !g_key_file_has_key (key_file
, DESKTOP_ENTRY
, BUGZILLA_BUGZILLA
, &error
)) {
394 g_key_file_free (key_file
);
395 gmenu_tree_item_unref (entry
);
397 g_error_free (error
);
401 name
= g_key_file_get_locale_string (key_file
, DESKTOP_ENTRY
, DESKTOP_NAME
, NULL
, NULL
);
402 cname
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, DESKTOP_NAME
, NULL
);
403 comment
= g_key_file_get_locale_string (key_file
, DESKTOP_ENTRY
, DESKTOP_COMMENT
, NULL
, NULL
);
404 bugzilla
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_BUGZILLA
, NULL
);
405 product
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_PRODUCT
, NULL
);
406 component
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_COMPONENT
, NULL
);
407 version
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_VERSION
, NULL
);
408 icon
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, DESKTOP_ICON
, NULL
);
409 exec
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, DESKTOP_EXEC
, NULL
);
410 other_binaries
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_OTHER_BINARIES
, NULL
);
411 extra_info_script
= g_key_file_get_string (key_file
, DESKTOP_ENTRY
, BUGZILLA_EXTRA_INFO_SCRIPT
, NULL
);
413 add_bugzilla_application (program_to_application
,
434 g_free (other_binaries
);
435 g_free (extra_info_script
);
437 prev_name
= g_strdup (gmenu_tree_entry_get_name (entry
));
438 g_key_file_free (key_file
);
439 gmenu_tree_item_unref (entry
);
441 g_slist_free (all_applications
);
443 load_applets (program_to_application
);
445 return program_to_application
;
449 bugzilla_search_for_package (gpointer key
, gpointer value
, const char *package
)
451 BugzillaApplication
*app
= (BugzillaApplication
*) value
;
453 if (!strcmp (app
->product
, package
))
461 bugzilla_parse_response (SoupMessage
*msg
, GError
**err
)
467 g_return_val_if_fail ((err
== NULL
|| *err
== NULL
), NULL
);
469 if (!SOUP_STATUS_IS_SUCCESSFUL (msg
->status_code
)) {
470 g_set_error (err
, BUGZILLA_ERROR
, BUGZILLA_ERROR_RECV_BAD_STATUS
,
471 _("HTTP Response returned bad status code %d"), msg
->status_code
);
475 if (!soup_xmlrpc_parse_method_response (msg
->response_body
->data
,
476 msg
->response_body
->length
,
480 if (G_VALUE_HOLDS_INT (&value
))
481 bugid
= g_value_get_int (&value
);
482 else if (G_VALUE_HOLDS_STRING (&value
))
483 url
= g_value_dup_string (&value
);
485 g_value_unset (&value
);
486 g_set_error (err
, BUGZILLA_ERROR
, BUGZILLA_ERROR_RECV_PARSE_FAILED
,
487 _("Unable to parse XML-RPC Response\n\n%s"),
488 msg
->response_body
->data
);
491 g_value_unset (&value
);
493 return bugid
? g_strdup_printf ("%d", bugid
) : url
;
497 bugzilla_create_report (BugzillaApplication
*app
, int type
, GnomeVersionInfo
*gnome_version
,
498 const char *username
, const char *title
, const char *text
,
499 GtkBuilder
*ui
, const char *minidump_file
, GError
**err
)
501 SoupMessage
*message
;
509 g_return_val_if_fail (app
!= NULL
, NULL
);
510 g_return_val_if_fail (gnome_version
!= NULL
, NULL
);
511 g_return_val_if_fail (username
!= NULL
, NULL
);
512 g_return_val_if_fail (text
!= NULL
, NULL
);
513 g_return_val_if_fail (ui
!= NULL
, NULL
);
514 g_return_val_if_fail (err
== NULL
|| *err
== NULL
, NULL
);
516 /* FIXME: Hardcoded right now */
517 if (app
->bugzilla
== NULL
|| strcmp (app
->bugzilla
, "GNOME") != 0) {
518 g_set_error (err
, BUGZILLA_ERROR
, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP
,
519 _("Application does not track its bugs in the GNOME Bugzilla."));
523 if (!app
->product
|| !app
->component
) {
524 g_set_error (err
, BUGZILLA_ERROR
, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP
,
525 _("Product or component not specified."));
529 report
= soup_value_hash_new ();
531 soup_value_hash_insert (report
, "version", G_TYPE_STRING
,
532 app
->version
? app
->version
: "unspecified");
533 soup_value_hash_insert (report
, "product", G_TYPE_STRING
, app
->product
);
534 soup_value_hash_insert (report
, "component", G_TYPE_STRING
, app
->component
);
535 soup_value_hash_insert (report
, "gnome_version", G_TYPE_STRING
,
536 gnome_version
->gnome_platform
);
537 soup_value_hash_insert (report
, "reporter", G_TYPE_STRING
, username
);
539 os_version
= get_distro_name ();
540 soup_value_hash_insert (report
, "os_version", G_TYPE_STRING
, os_version
);
543 if (type
== BUG_TYPE_CRASH
) {
544 soup_value_hash_insert (report
, "priority", G_TYPE_STRING
, "High");
545 soup_value_hash_insert (report
, "bug_severity", G_TYPE_STRING
, "critical");
548 soup_value_hash_insert (report
, "short_desc", G_TYPE_STRING
, "title");
550 /* Skip UTF-8 control chars that are not valid in XML.*/
551 rv
= g_string_new (NULL
);
554 gchar
*next
= g_utf8_next_char (crt
);
555 gunichar uni
= g_utf8_get_char (crt
);
557 if (!g_unichar_iscntrl (uni
) || (uni
== '\n') || (uni
== '\t'))
558 g_string_append_len (rv
, crt
, next
- crt
);
563 soup_value_hash_insert (report
, "comment", G_TYPE_STRING
, rv
->str
);
564 g_string_free (rv
, TRUE
);
570 gchar
*base64
= NULL
;
572 if (g_file_get_contents (minidump_file
, &minidumpbuf
, &length
, &error
)) {
573 base64
= g_base64_encode ((guchar
*)minidumpbuf
, length
);
574 g_free (minidumpbuf
);
576 g_error_free (error
);
584 basename
= g_path_get_basename (minidump_file
);
585 id
= g_strndup (basename
, strlen (basename
) - strlen (".dmp"));
587 soup_value_hash_insert (report
, "minidump-id",
590 /* FIXME: This is broken; for some reason
591 * bug-buddy is base64-encoding the minidump
592 * *twice*. (Once itself, and once by asking
593 * libsoup to do it.) Fix this after verifying
594 * that the server side can deal with the
597 ba
= g_byte_array_sized_new (strlen (base64
));
598 g_byte_array_append (ba
, (guchar
*)base64
, strlen (base64
));
599 soup_value_hash_insert (report
, "minidump",
600 SOUP_TYPE_BYTE_ARRAY
, ba
);
601 g_byte_array_free (ba
, TRUE
);
609 //uri = "http://localhost/breakpad/xmlrpc.py";
610 uri
= "http://socorro.gnome.org/collect.py";
612 uri
= "http://bugzilla.gnome.org/bugbuddy.cgi";
614 message
= soup_xmlrpc_request_new (uri
, "BugBuddy.createBug",
615 G_TYPE_HASH_TABLE
, report
,
617 g_hash_table_destroy (report
);
618 if (message
== NULL
) {
619 g_set_error (err
, BUGZILLA_ERROR
, BUGZILLA_ERROR_SEND_ERROR
,
620 _("Unable to create XML-RPC message."));
624 /* FIXME: wrong User-Agent syntax. Should be "Bug-Buddy (VERSION)" */
625 user_agent
= g_strdup_printf ("Bug-Buddy: %s", VERSION
);
626 soup_message_headers_append (message
->request_headers
,
627 "User-Agent", user_agent
);