1 /* bug-buddy bug submitting program
3 * Copyright (C) 1999 - 2001 Jacob Berkman
4 * Copyright 2000, 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 "eds-buddy.h"
27 #include "bug-buddy.h"
28 #include "distribution.h"
34 #include <sys/utsname.h>
35 #include <sys/types.h>
41 #include <libgnomeui/gnome-window-icon.h>
42 #include <glade/glade.h>
43 #include <glade/glade-build.h>
44 #include <gdk-pixbuf/gdk-pixbuf.h>
45 #include <libgnomecanvas/gnome-canvas-pixbuf.h>
46 #include <libgnome/libgnometypebuiltins.h>
49 #include <libxml/tree.h>
50 #include <libxml/parser.h>
52 #include <gconf/gconf-client.h>
54 #ifdef HAVE_NETWORKMANAGER
55 #include <libnm_glib.h>
58 #include <libsoup/soup.h>
59 #include <libsoup/soup-xmlrpc-message.h>
61 #include <sys/types.h>
66 #define USE_PROXY_KEY "/system/http_proxy/use_http_proxy"
67 #define PROXY_HOST_KEY "/system/http_proxy/host"
68 #define PROXY_PORT_KEY "/system/http_proxy/port"
69 #define USE_PROXY_AUTH "/system/http_proxy/use_authentication"
70 #define PROXY_USER "/system/http_proxy/authentication_user"
71 #define PROXY_PASSWORD "/system/http_proxy/authentication_password"
72 #define ACCESSIBILITY_KEY "/desktop/gnome/interface/accessibility"
74 static GOptionData gopt_data
;
75 static int bug_count
= 0;
77 static const GOptionEntry options
[] = {
78 { "name", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.name
, N_("Name of contact"), N_("NAME") },
79 { "email", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.email
, N_("Email address of contact"), N_("EMAIL") },
80 { "package", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.package
, N_("Package containing the program"), N_("PACKAGE") },
81 { "package-ver",'\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.package_ver
, N_("Version of the package"), N_("VERSION") },
82 { "appname", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.app_file
, N_("File name of crashed program"), N_("FILE") },
83 { "pid", '\0', 0, G_OPTION_ARG_INT
, &gopt_data
.pid
, N_("PID of crashed program"), N_("PID") },
84 { "core", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.core_file
, N_("Core file from program"), N_("FILE") },
85 { "include", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.include_file
,N_("Text file to include in the report"), N_("FILE") },
86 { "kill", '\0', 0, G_OPTION_ARG_INT
, &gopt_data
.kill
, N_("PID of the program to kill after the report"), N_("KILL") },
96 static void fill_stderr_info (GladeXML
*xml
);
100 buddy_error (GtkWidget
*parent
, const char *msg
, ...)
107 /* No va_list version of dialog_new, construct the string ourselves. */
108 va_start (args
, msg
);
109 s
= g_strdup_vprintf (msg
, args
);
112 w
= gtk_message_dialog_new (GTK_WINDOW (parent
),
119 gtk_dialog_set_default_response (d
, GTK_RESPONSE_OK
);
121 gtk_widget_destroy (w
);
125 static GnomeVersionInfo
*
126 get_gnome_version_info (void)
128 GnomeVersionInfo
*version
;
132 guchar
*platform
, *minor
, *micro
, *distributor
, *date
;
134 version
= g_new0 (GnomeVersionInfo
, 1);
136 xml_file
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_DATADIR
,
137 "gnome-about/gnome-version.xml",
141 doc
= xmlParseFile (xml_file
);
147 platform
= minor
= micro
= distributor
= NULL
;
149 for (node
= xmlDocGetRootElement (doc
)->children
; node
; node
= node
->next
) {
150 if (!strcmp ((char *)node
->name
, "platform"))
151 platform
= xmlNodeGetContent (node
);
152 else if (!strcmp ((char *)node
->name
, "minor"))
153 minor
= xmlNodeGetContent (node
);
154 else if (!strcmp ((char *)node
->name
, "micro"))
155 micro
= xmlNodeGetContent (node
);
156 else if (!strcmp ((char *)node
->name
, "distributor"))
157 distributor
= xmlNodeGetContent (node
);
158 else if (!strcmp ((char *)node
->name
, "date"))
159 date
= xmlNodeGetContent (node
);
162 if (platform
&& minor
&& micro
)
163 version
->gnome_platform
= g_strdup_printf ("%s.%s.%s", platform
, minor
, micro
);
165 if (distributor
&& *distributor
)
166 version
->gnome_distributor
= g_strdup ((char *)distributor
);
169 version
->gnome_date
= g_strdup ((char *)date
);
174 xmlFree (distributor
);
183 update_progress_bar (gpointer data
)
185 GtkProgressBar
*pbar
= GTK_PROGRESS_BAR (data
);
187 gtk_progress_bar_pulse (pbar
);
193 save_email (const char *email
)
195 GConfClient
*conf_client
;
197 conf_client
= gconf_client_get_default ();
198 gconf_client_set_string (conf_client
, "/apps/bug-buddy/email_address", email
, NULL
);
199 g_object_unref (conf_client
);
203 link_callback (GtkLinkButton
*button
, gpointer user_data
)
205 const gchar
*link
= gtk_link_button_get_uri (button
);
207 if (gnome_vfs_url_show (link
) != GNOME_VFS_OK
) {
210 text
= g_markup_printf_escaped (_("Bug Buddy was unable to view the link \"%s\"\n"), link
);
211 buddy_error (NULL
, text
);
219 save_to_file (const gchar
*filename
, const gchar
*text
)
223 if ((bugfile
= fopen (filename
, "w")) == NULL
) {
224 g_warning ("Unable to save document %s\n", filename
);
228 fwrite (text
, g_utf8_strlen (text
, -1), 1, bugfile
);
236 network_error (SoupMessage
*msg
, GladeXML
*xml
)
242 gtk_widget_hide (glade_xml_get_widget (xml
, "progressbar"));
243 gtk_statusbar_push (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")),
248 dialog
= gtk_message_dialog_new_with_markup (NULL
,
250 GTK_MESSAGE_QUESTION
,
252 _("<span weight=\"bold\">Network Connection Error</span>\n"
253 "Maybe no Network Connection available.\n"
254 "Do you want to store this report until "
255 "a Network Connection is available?"));
256 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
257 gtk_widget_destroy (dialog
);
259 if (res
== GTK_RESPONSE_YES
) {
262 gchar
*message_string
;
264 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
265 if (!g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
266 g_mkdir_with_parents (dirname
, 0755);
269 filename
= g_strdup_printf ("%s/%ld", dirname
, (long)time (NULL
));
271 message_string
= soup_xmlrpc_message_to_string (SOUP_XMLRPC_MESSAGE (msg
));
273 save_to_file (filename
, message_string
);
275 g_free (message_string
);
284 remove_pending_reports (void)
288 GError
*error
= NULL
;
290 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
291 dir
= g_dir_open (dirname
, 0, &error
);
293 const char *name
= g_dir_read_name (dir
);
295 char *path
= g_strdup_printf ("%s/%s", dirname
, name
);
298 name
= g_dir_read_name (dir
);
308 all_sent (GladeXML
*xml
)
310 GtkWidget
*close_button
;
312 /* hide the progressbar */
313 gtk_widget_hide (glade_xml_get_widget (xml
, "progressbar"));
315 /* connect the closed button to gtk_main_quit() */
316 close_button
= glade_xml_get_widget (xml
, "close-button");
317 g_signal_connect (close_button
, "clicked", gtk_main_quit
, NULL
);
318 gtk_widget_show (close_button
);
319 gtk_statusbar_push (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")),
324 previous_sent (SoupMessage
*msg
, GladeXML
*xml
)
326 if (--bug_count
== 0) {
334 bug_sent (SoupMessage
*msg
, GladeXML
*xml
)
341 GtkWidget
*urlbutton
;
342 GtkRequisition requisition
;
346 if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg
->status_code
)) {
347 network_error (msg
, xml
);
349 remove_pending_reports ();
352 /* parse the XML-RPC response from bugzilla */
353 bugid
= bugzilla_parse_response (msg
, &err
);
356 GtkWidget
*main_vbox
;
358 /* we need a reference to the vbox containing the text so that we
359 * can add a GtkLinkButton to the bug report */
360 main_vbox
= glade_xml_get_widget (xml
, "main-vbox");
362 text
= g_strdup_printf ("http://bugzilla.gnome.org/show_bug.cgi?id=%d", bugid
);
364 /* create a clickable link to the bug report */
365 urlbutton
= gtk_link_button_new (text
);
366 g_signal_connect (G_OBJECT (urlbutton
), "clicked", G_CALLBACK (link_callback
), NULL
);
367 gtk_box_pack_end (GTK_BOX (main_vbox
), urlbutton
, FALSE
, FALSE
, 0);
369 gtk_widget_show (urlbutton
);
372 text
= g_markup_printf_escaped (_("A bug report detailing your software crash has been sent to GNOME. "
373 "This information will allow the developers to understand the cause "
374 "of the crash and prepare a solution for it.\n\n"
375 "You may be contacted by a GNOME developer if more details are "
376 "required about the crash.\n\n"
377 "You can view your bug report and follow its progress with this URL:\n")) ;
379 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")), text
);
381 save_email (gtk_entry_get_text (GTK_ENTRY (glade_xml_get_widget (xml
, "email-entry"))));
383 errmsg
= _("Bug Buddy has encountered an error while submitting your report "
384 "to the Bugzilla server. Details of the error are included below.\n\n");
388 case BUGZILLA_ERROR_RECV_BAD_STATUS
:
389 text
= g_strdup_printf (_("Server returned bad state. This is most likely a server "
390 "issue and should be reported to bugmaster@gnome.org\n\n%s"),
393 case BUGZILLA_ERROR_RECV_PARSE_FAILED
:
394 text
= g_strdup_printf (_("Failed to parse the xml-rpc response. Response follows:\n\n%s"),
397 case BUGZILLA_ERROR_RECV_FAULT
:
398 /* in this case, the error message returned is the faultCode and faultString from
399 * the XML-RPC response, separated by a colon. We construct our error message
400 * based on the faultString */
401 ptr
= strstr (err
->message
, ":");
403 text
= g_strdup_printf (_("Bugzilla reported an error when trying to process your "
404 "request, but was unable to parse the response."));
411 /* see http://cvs.gnome.org/viewcvs/bugzilla-newer/Bugzilla/RPC.pm?view=markup */
412 if (g_str_equal (ptr
, "invalid_username")) {
413 text
= g_strdup_printf (_("The email address you provided is not valid."));
414 } else if (g_str_equal (ptr
, "account_disabled")) {
415 text
= g_strdup_printf (_("The account associated with the email address "
416 "provided has been disabled."));
417 } else if (g_str_equal (ptr
, "product_doesnt_exist")) {
418 text
= g_strdup_printf (_("The product specified doesn't exist or has been "
419 "renamed. Please upgrade to the latest version."));
420 } else if (g_str_equal (ptr
, "component_not_valid")) {
421 text
= g_strdup_printf (_("The component specified doesn't exist or has been "
422 "renamed. Please upgrade to the latest version."));
423 } else if (g_str_equal (ptr
, "require_summary")) {
424 text
= g_strdup_printf (_("The summary is required in your bug report. "
425 "This should not happen with the latest Bug Buddy."));
426 } else if (g_str_equal (ptr
, "description_required")) {
427 text
= g_strdup_printf (_("The description is required in your bug report. "
428 "This should not happen with the latest Bug Buddy."));
430 text
= g_strdup_printf (_("The fault code returned by Bugzilla is not recognized. "
431 "Please report the following information to "
432 "bugzilla.gnome.org manually:\n\n%s"), err
->message
);
437 text
= g_strdup_printf (_("An unknown error occurred. This is most likely a problem with "
438 "bug-buddy. Please report this problem manually at bugzilla."
443 str
= g_strconcat (errmsg
, text
, NULL
);
444 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")), str
);
452 if (--bug_count
== 0) {
456 gtk_widget_size_request (glade_xml_get_widget (xml
, "main-window"), &requisition
);
457 gtk_window_resize (GTK_WINDOW (glade_xml_get_widget (xml
, "main-window")),
458 requisition
.width
, requisition
.height
);
464 set_proxy (SoupSession
*session
)
466 GConfClient
*gconf_client
;
471 char *username
= NULL
;
472 char *password
= NULL
;
475 gconf_client
= gconf_client_get_default ();
477 if (gconf_client_get_bool (gconf_client
, USE_PROXY_KEY
, NULL
) == FALSE
) {
478 g_object_unref (gconf_client
);
482 host
= gconf_client_get_string (gconf_client
, PROXY_HOST_KEY
, NULL
);
484 g_object_unref (gconf_client
);
487 port
= gconf_client_get_int (gconf_client
, PROXY_PORT_KEY
, NULL
);
491 if (gconf_client_get_bool (gconf_client
, USE_PROXY_AUTH
, NULL
)) {
492 username
= gconf_client_get_string (gconf_client
, PROXY_USER
, NULL
);
493 password
= gconf_client_get_string (gconf_client
, PROXY_PASSWORD
, NULL
);
496 if (username
&& password
)
497 proxy_uri
= g_strdup_printf ("http://%s:%s@%s:%d", username
, password
, host
, port
);
499 proxy_uri
= g_strdup_printf ("http://%s:%d", host
, port
);
501 uri
= soup_uri_new (proxy_uri
);
502 g_object_set (G_OBJECT (session
), "proxy-uri", uri
, NULL
);
509 g_object_unref (gconf_client
);
515 create_report_title (BugzillaApplication
*app
, int type
, const char *description
)
523 tmp
= g_malloc0 (256); /* This should be safe enough for 24 UTF-8 chars.
524 * anyway, I miss a g_utf8_strndup :) */
525 size
= g_utf8_strlen (description
, -1);
527 g_utf8_strncpy (tmp
, description
, 24);
529 g_utf8_strncpy (tmp
, description
, size
);
533 if (type
== BUG_TYPE_CRASH
) {
534 title
= g_strdup_printf ("crash in %s: %s%s", app
->cname
,
535 tmp
? tmp
: "empty description",
536 (tmp
&& size
> 24) ? "..." : "");
538 title
= g_strdup_printf ("%s: %s%s", app
->cname
,
539 tmp
? tmp
: "empty description",
540 (tmp
&& size
> 24) ? "..." : "");
548 send_report (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GladeXML
*xml
)
550 GtkTextView
*text_view
;
551 GtkTextBuffer
*buffer
;
560 SoupSession
*session
;
561 SoupXmlrpcMessage
*message
;
562 SoupXmlrpcResponse
*response
;
565 gtk_widget_hide (glade_xml_get_widget (xml
, "pending-reports-check"));
567 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
568 buffer
= gtk_text_view_get_buffer (text_view
);
569 gtk_text_buffer_get_start_iter (buffer
, &start
);
570 gtk_text_buffer_get_end_iter (buffer
, &end
);
571 gdb_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
573 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "details-view"));
574 buffer
= gtk_text_view_get_buffer (text_view
);
575 gtk_text_buffer_get_start_iter (buffer
, &start
);
576 gtk_text_buffer_get_end_iter (buffer
, &end
);
577 details_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
579 final_text
= g_strdup_printf ("What were you doing when the application crashed?\n%s\n\n\n%s",
580 details_text
!= NULL
? details_text
: "",
581 gdb_text
!= NULL
? gdb_text
: "<empty backtrace>");
583 email
= gtk_entry_get_text (GTK_ENTRY (glade_xml_get_widget (xml
, "email-entry")));
584 type
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT(xml
), "type"));
585 title
= create_report_title (app
, type
, details_text
);
587 message
= bugzilla_create_report (app
, gnome_version
, email
, title
, final_text
, xml
, &err
);
588 if (message
== NULL
) {
592 text
= g_strdup_printf (_("Unable to create the bug report: %s\n"), err
->message
);
594 text
= g_strdup_printf (_("There was an error creating the bug report\n"));
597 buddy_error (NULL
, text
);
600 g_free (details_text
);
607 session
= soup_session_async_new ();
611 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (glade_xml_get_widget (xml
, "pending-reports-check")))) {
614 GError
*error
= NULL
;
616 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
617 dir
= g_dir_open (dirname
, 0, &error
);
619 const char *name
= g_dir_read_name (dir
);
620 while (name
!= NULL
) {
624 path
= g_strdup_printf ("%s/%s", dirname
, name
);
625 if (g_file_get_contents (path
, &contents
, NULL
, NULL
)) {
626 SoupXmlrpcMessage
*msg
;
627 msg
= soup_xmlrpc_message_new ("http://bugzilla.gnome.org/bugbuddy.cgi");
628 soup_xmlrpc_message_from_string (msg
, contents
);
630 soup_xmlrpc_message_persist (msg
);
631 soup_session_queue_message (session
, SOUP_MESSAGE (msg
),
632 (SoupMessageCallbackFn
)previous_sent
, xml
);
636 name
= g_dir_read_name (dir
);
645 soup_xmlrpc_message_persist (message
);
646 soup_session_queue_message (session
, SOUP_MESSAGE (message
),
647 (SoupMessageCallbackFn
)bug_sent
, xml
);
649 g_free (details_text
);
653 gtk_statusbar_push (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")),
654 SENDING
, _("Sending..."));
660 /* Check for *simple* email addresses of the form user@host, with checks
661 * in characters used, and sanity checks on the form of host.
663 /* FIXME: Should we provide a useful error message? */
665 email_is_valid (const char *addy
)
667 const char *tlds
[] = {
668 "com", "org", "net", "edu", "mil", "gov", "int", "arpa",
669 "aero", "biz", "coop", "info", "museum", "name", "pro",
673 gboolean is_dot_ok
= FALSE
;
674 gboolean seen_at
= FALSE
;
675 const char *tld
= NULL
;
679 for (s
= addy
; *s
; s
++) {
680 /* Must be in acceptable ASCII range. */
681 if ((unsigned char)*s
<= 32 || (unsigned char)*s
>= 128)
684 /* No dots at start, after at, or two in a row. */
690 } else if (*s
== '@') {
691 /* Only one at, not allowed if a dot isn't. */
692 if (!is_dot_ok
|| seen_at
)
697 /* Can put a dot or at after this character. */
701 if (!seen_at
|| tld
== NULL
)
704 /* Make sure they're not root. */
705 if (!strncmp (addy
, "root@", 5))
708 /* We'll believe any old ccTLD. */
709 if (strlen (tld
) == 2)
711 /* Otherwise, check the TLD list. */
712 for (t
=tlds
; *t
; t
++) {
713 if (!g_ascii_strcasecmp (tld
, *t
))
716 /* Unrecognised. :( */
722 check_email (GtkEditable
*editable
, gpointer data
)
725 GladeXML
*xml
= (GladeXML
*) data
;
727 email
= gtk_entry_get_text (GTK_ENTRY (editable
));
728 gtk_widget_set_sensitive (glade_xml_get_widget (xml
, "send-button"),
729 email_is_valid (email
));
733 on_send_clicked (GtkWidget
*button
, gpointer data
)
735 BugzillaApplication
*app
;
736 GnomeVersionInfo
*gnome_version
;
737 GtkRequisition requisition
;
738 GladeXML
*xml
= (GladeXML
*) data
;
740 app
= g_object_get_data (G_OBJECT (xml
), "app");
741 gnome_version
= g_object_get_data (G_OBJECT (xml
), "gnome-version");
743 /* hide the send button immediately so that the user can't click
744 * it more than once (this will create multiple bugs) */
745 gtk_widget_hide (glade_xml_get_widget (xml
, "send-button"));
747 gtk_widget_show (glade_xml_get_widget (xml
, "progressbar"));
748 gtk_widget_hide (glade_xml_get_widget (xml
, "final-box"));
749 gtk_widget_hide (glade_xml_get_widget (xml
, "expander1"));
751 gtk_widget_size_request (glade_xml_get_widget (xml
, "main-window"), &requisition
);
752 gtk_window_resize (GTK_WINDOW (glade_xml_get_widget (xml
, "main-window")),
753 requisition
.width
, requisition
.height
);
755 send_report (app
, gnome_version
, xml
);
759 gdb_insert_text (const gchar
*stacktrace
, GladeXML
*xml
)
761 GtkTextView
*text_view
;
763 GtkTextBuffer
*buffer
;
765 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
766 buffer
= gtk_text_view_get_buffer (text_view
);
767 gtk_text_buffer_get_end_iter (buffer
, &end
);
769 /* add the stacktrace to the GtkTextView */
770 gtk_text_buffer_insert (buffer
, &end
, stacktrace
, strlen (stacktrace
));
775 show_pending_checkbox_if_pending (GladeXML
*xml
)
780 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
781 if (g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
782 check
= glade_xml_get_widget (xml
, "pending-reports-check");
783 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check
), TRUE
);
784 gtk_widget_show (check
);
790 known_app_finished (GladeXML
*xml
)
792 GtkWidget
*email_entry
;
795 fill_stderr_info (xml
);
797 gtk_widget_show (glade_xml_get_widget (xml
, "final-box"));
798 gtk_widget_show (glade_xml_get_widget (xml
, "send-button"));
799 gtk_widget_hide (glade_xml_get_widget (xml
, "progressbar"));
800 gtk_statusbar_pop (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")), DEBUGGING
);
803 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")),
804 _("The information about the crash has been successfully collected. "
805 "Please provide some background about what you were doing when "
806 "the application crashed.\n\n"
807 "A valid email address is required. This will allow developers to "
808 "contact you for more information if necessary.\n\n"
809 "Sensitive data may be present in the debug information, so please "
810 "review details below if you are concerned about transmitting "
811 "passwords or other sensitive data.\n"));
813 show_pending_checkbox_if_pending (xml
);
815 g_signal_connect (glade_xml_get_widget (xml
, "send-button"), "clicked",
816 G_CALLBACK (on_send_clicked
), xml
);
818 email_entry
= glade_xml_get_widget (xml
, "email-entry");
819 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), xml
);
821 default_email
= get_default_user_email ();
823 if (default_email
!= NULL
) {
824 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
825 g_free (default_email
);
828 gtk_widget_grab_focus (glade_xml_get_widget (xml
, "details-view"));
832 gdb_finished (const gchar
*stacktrace
, gpointer data
)
834 GladeXML
*xml
= (GladeXML
*) data
;
836 gdb_insert_text (stacktrace
, xml
);
837 known_app_finished (xml
);
843 on_save_clicked (GtkWidget
*button
, gpointer user_data
)
845 GladeXML
*xml
= (GladeXML
*)user_data
;
849 gboolean saved
= FALSE
;
851 dialog
= gtk_file_chooser_dialog_new ("Save File",
852 GTK_WINDOW (glade_xml_get_widget (xml
, "main-window")),
853 GTK_FILE_CHOOSER_ACTION_SAVE
,
854 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
855 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
858 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
860 desktop
= g_strconcat (g_getenv ("HOME"), "/Desktop", NULL
);
861 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
), desktop
);
863 filename
= g_strconcat (gopt_data
.app_file
, _("-bugreport.txt"), NULL
);
864 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), filename
);
868 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
870 GtkTextView
*text_view
;
871 GtkTextBuffer
*buffer
;
876 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
877 buffer
= gtk_text_view_get_buffer (text_view
);
878 gtk_text_buffer_get_start_iter (buffer
, &start
);
879 gtk_text_buffer_get_end_iter (buffer
, &end
);
880 text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
882 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
883 save_to_file (filename
, text
);
889 gtk_widget_destroy (dialog
);
896 focus_details (GtkWidget
*widget
, gpointer data
)
898 gtk_widget_grab_focus (widget
);
902 unknown_app_finished (GladeXML
*xml
)
906 fill_stderr_info (xml
);
908 /* don't need user input, so hide these widgets */
909 gtk_widget_hide (glade_xml_get_widget (xml
, "final-box"));
910 gtk_widget_hide (glade_xml_get_widget (xml
, "progressbar"));
912 /* make the send button into a save button :-) */
913 button
= glade_xml_get_widget (xml
, "send-button");
914 gtk_button_set_label (GTK_BUTTON (button
), _("Save"));
915 g_signal_connect (GTK_BUTTON (button
), "clicked", G_CALLBACK (on_save_clicked
), xml
);
916 gtk_widget_show (glade_xml_get_widget (xml
, "send-button"));
918 /* expand the details about the stack trace */
919 gtk_expander_set_expanded (GTK_EXPANDER (glade_xml_get_widget (xml
, "expander1")), TRUE
);
921 gtk_statusbar_pop (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")), DEBUGGING
);
923 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")),
924 _("The information about the crash has been successfully collected.\n\n"
925 "The application that crashed is not known to bug-buddy, therefore the "
926 "bug report can not be sent to the GNOME Bugzilla. Please save the "
927 "bug to a text file and report it to the appropriate bug tracker "
928 "for this application."));
930 /* FIXME: If we just grab the focus here to the GtkTextView it will crash on the blink_cb because
931 * the window is nop mapped! Is this a gtk+ bug? are we doing something wrong?
932 * Let's do a funny Workaround: */
933 g_signal_connect_after (glade_xml_get_widget (xml
, "details-view"), "realize", G_CALLBACK (focus_details
), NULL
);
934 gtk_widget_realize (glade_xml_get_widget (xml
, "details-view"));
938 gdb_finished_unknown_app (const gchar
*stacktrace
, gpointer data
)
940 GladeXML
*xml
= (GladeXML
*) data
;
942 gdb_insert_text (stacktrace
, xml
);
943 unknown_app_finished (xml
);
950 close_callback (GtkWidget
*widget
, gpointer user_data
)
952 GladeXML
*xml
= (GladeXML
*)user_data
;
955 g_return_if_fail (xml
!= NULL
);
957 data
= g_object_get_data (G_OBJECT (xml
), "sourceid");
960 guint source_id
= GPOINTER_TO_UINT (data
);
962 /* removes the context from the main loop and kills any remaining
965 g_source_remove (source_id
);
966 g_object_set_data (G_OBJECT (xml
), "sourceid", GUINT_TO_POINTER (0));
974 delete_callback (GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
976 close_callback (NULL
, data
);
981 fill_gnome_info (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GladeXML
*xml
)
985 GtkTextView
*text_view
;
987 GtkTextBuffer
*buffer
;
989 g_return_if_fail (app
!= NULL
);
990 g_return_if_fail (gnome_version
!= NULL
);
991 g_return_if_fail (xml
!= NULL
);
993 distro
= get_distro_name ();
994 version_info
= g_strdup_printf ("Distribution: %s\n"
995 "Gnome Release: %s %s (%s)\n"
996 "BugBuddy Version: %s\n"
999 gnome_version
->gnome_platform
, gnome_version
->gnome_date
,
1000 gnome_version
->gnome_distributor
, VERSION
);
1004 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
1005 buffer
= gtk_text_view_get_buffer (text_view
);
1006 gtk_text_buffer_get_end_iter (buffer
, &end
);
1007 gtk_text_buffer_insert (buffer
, &end
, version_info
, strlen (version_info
));
1009 g_free (version_info
);
1014 fill_proccess_info (pid_t pid
, GladeXML
*xml
)
1016 GtkTextView
*text_view
;
1018 GtkTextBuffer
*buffer
;
1021 char *proccess_info
;
1023 mem
= proccess_get_mem_state (pid
);
1024 time
= proccess_get_time (pid
);
1026 proccess_info
= g_strdup_printf ("%s\n"
1034 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
1035 buffer
= gtk_text_view_get_buffer (text_view
);
1036 gtk_text_buffer_get_end_iter (buffer
, &end
);
1037 gtk_text_buffer_insert (buffer
, &end
, proccess_info
, strlen (proccess_info
));
1039 g_free (proccess_info
);
1044 fill_include_file (char *filename
, GladeXML
*xml
)
1046 GtkTextView
*text_view
;
1048 GtkTextBuffer
*buffer
;
1050 GError
*error
= NULL
;
1052 if (g_file_get_contents (filename
, &text
, NULL
, &error
) != TRUE
) {
1053 buddy_error (NULL
, error
->message
);
1054 g_error_free (error
);
1058 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
1059 buffer
= gtk_text_view_get_buffer (text_view
);
1060 gtk_text_buffer_get_end_iter (buffer
, &end
);
1061 gtk_text_buffer_insert (buffer
, &end
, text
, strlen (text
));
1069 fill_system_info (GladeXML
*xml
)
1071 GConfClient
*gconf_client
;
1072 GtkTextView
*text_view
;
1074 GtkTextBuffer
*buffer
;
1075 GString
*system_info
;
1076 struct utsname uts_buf
;
1078 gboolean has_selinux
, enforcing
, a11y
;
1080 g_return_if_fail (xml
!= NULL
);
1082 system_info
= g_string_new ("");
1084 if (uname (&uts_buf
) == 0) {
1085 g_string_append_printf (system_info
, "System: %s %s %s %s\n", uts_buf
.sysname
, uts_buf
.release
, uts_buf
.version
, uts_buf
.machine
);
1088 /* X server checks */
1089 g_string_append_printf (system_info
, "X Vendor: %s\n", ServerVendor (gdk_display
));
1090 g_string_append_printf (system_info
, "X Vendor Release: %d\n", VendorRelease (gdk_display
));
1093 /* Selinux checks */
1094 has_selinux
= FALSE
;
1095 if (g_file_get_contents ("/proc/filesystems", &str
, NULL
, NULL
)) {
1096 has_selinux
= strstr (str
, "selinuxfs") != NULL
;
1101 if (g_file_get_contents ("/selinux/enforce", &str
, NULL
, NULL
)) {
1102 enforcing
= strcmp (str
, "0") != 0;
1105 g_string_append_printf (system_info
, "Selinux: %s\n", enforcing
?"Enforcing":"Permissive");
1107 g_string_append_printf (system_info
, "Selinux: No\n");
1110 gconf_client
= gconf_client_get_default ();
1111 a11y
= gconf_client_get_bool (gconf_client
, ACCESSIBILITY_KEY
, NULL
);
1112 g_object_unref (gconf_client
);
1113 g_string_append_printf (system_info
, "Accessibility: %s\n", a11y
?"Enabled":"Disabled");
1115 g_string_append (system_info
, "\n");
1117 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
1118 buffer
= gtk_text_view_get_buffer (text_view
);
1119 gtk_text_buffer_get_end_iter (buffer
, &end
);
1120 gtk_text_buffer_insert (buffer
, &end
, system_info
->str
, system_info
->len
);
1122 g_string_free (system_info
, TRUE
);
1126 fill_stderr_info (GladeXML
*xml
)
1128 GtkTextView
*text_view
;
1130 GtkTextBuffer
*buffer
;
1131 GString
*stderr_info
;
1136 g_return_if_fail (xml
!= NULL
);
1138 stderr_info
= g_string_new ("");
1140 /* .xsession-errors: read file */
1141 file
= g_build_filename (g_get_home_dir (), ".xsession-errors", NULL
);
1142 if (g_file_get_contents (file
, &str
, NULL
, NULL
)) {
1143 lines
= g_strsplit (str
, "\n", -1);
1146 while (lines
[n_lines
] != NULL
) {
1152 char *mtime_age
= NULL
;
1156 if (stat (file
, &buf
) == 0) {
1157 age
= time (NULL
) - buf
.st_mtime
;
1159 mtime_age
= g_strdup_printf (" (%d sec old)", age
);
1163 g_string_append_printf (stderr_info
,
1164 "\n\n----------- .xsession-errors%s ---------------------\n",
1165 mtime_age
?mtime_age
:"");
1169 for (i
= MAX (0, n_lines
-16); i
< n_lines
; i
++) {
1170 if (lines
[i
][0] != 0) {
1171 /* Limit line length to 200 chars to avoid excessive data */
1172 if (strlen (lines
[i
]) > 200) {
1176 g_string_append_printf (stderr_info
, "%s\n", lines
[i
]);
1180 g_string_append (stderr_info
, "--------------------------------------------------\n");
1186 text_view
= GTK_TEXT_VIEW (glade_xml_get_widget (xml
, "gdb-text"));
1187 buffer
= gtk_text_view_get_buffer (text_view
);
1188 gtk_text_buffer_get_end_iter (buffer
, &end
);
1189 gtk_text_buffer_insert (buffer
, &end
, stderr_info
->str
, stderr_info
->len
);
1191 g_string_free (stderr_info
, TRUE
);
1198 main (int argc
, char *argv
[])
1200 GtkIconInfo
*icon_info
;
1205 GtkTextView
*text_view
;
1206 BugzillaApplication
*app
;
1207 GnomeVersionInfo
*gnome_version
;
1209 GtkWidget
*main_window
, *statusbar
;
1210 GOptionContext
*context
;
1211 GnomeProgram
*program
;
1215 memset (&gopt_data
, 0, sizeof (gopt_data
));
1217 bindtextdomain (PACKAGE
, GNOMELOCALEDIR
);
1218 bind_textdomain_codeset (PACKAGE
, "UTF-8");
1219 textdomain (PACKAGE
);
1221 context
= g_option_context_new (_("\n\nBug Buddy is a utility that helps report debugging\n"
1222 "information to the GNOME bugzilla when a program crashes."));
1224 g_option_context_add_main_entries (context
, options
, GETTEXT_PACKAGE
);
1226 program
= gnome_program_init (PACKAGE
, VERSION
,
1229 GNOME_PARAM_GOPTION_CONTEXT
, context
,
1230 GNOME_PARAM_APP_DATADIR
, REAL_DATADIR
,
1233 g_set_application_name (_("Bug Buddy"));
1234 gtk_window_set_default_icon_name ("bug-buddy");
1236 s
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_APP_DATADIR
,
1237 "bug-buddy/bug-buddy.glade", TRUE
, NULL
);
1239 xml
= glade_xml_new (s
, NULL
, GETTEXT_PACKAGE
);
1245 _("Bug Buddy could not load its user interface file (%s).\n"
1246 "Please make sure Bug Buddy was installed correctly."),
1248 g_object_unref (program
);
1253 main_window
= glade_xml_get_widget (xml
, "main-window");
1254 g_signal_connect (main_window
, "delete-event", G_CALLBACK (delete_callback
), xml
);
1255 gtk_window_set_default_size (GTK_WINDOW (main_window
), 400, 400);
1257 gtk_widget_hide (glade_xml_get_widget (xml
, "final-box"));
1259 statusbar
= glade_xml_get_widget (xml
, "statusbar");
1260 gtk_statusbar_push (GTK_STATUSBAR (statusbar
),
1261 LOADING_INFO
, _("Collecting info from your system..."));
1262 progress
= g_timeout_add (100, update_progress_bar
,
1263 glade_xml_get_widget (xml
, "progressbar"));
1265 if (gopt_data
.app_file
== NULL
&& gopt_data
.package
== NULL
) {
1266 buddy_error (NULL
, _("Either --appname or --package arguments are required.\n"));
1267 g_object_unref (program
);
1271 if (gopt_data
.app_file
&& gopt_data
.pid
== 0 && gopt_data
.include_file
== NULL
) {
1272 buddy_error (NULL
, _("Either --pid or --include arguments are required.\n"));
1273 g_object_unref (program
);
1277 /* get some information about the gnome version */
1278 gnome_version
= get_gnome_version_info ();
1279 if (gnome_version
== NULL
) {
1280 buddy_error (NULL
, _("Bug Buddy was unable to retrieve information regarding "
1281 "the version of GNOME you are running. This is most likely "
1282 "due to a missing installation of gnome-desktop.\n"));
1283 g_object_unref (program
);
1287 g_object_set_data (G_OBJECT (xml
), "gnome-version", gnome_version
);
1289 gtk_widget_show (main_window
);
1291 apps
= load_applications ();
1293 /* If we have a binary file it is a crash */
1294 if (gopt_data
.app_file
) {
1295 app
= g_hash_table_lookup (apps
, gopt_data
.app_file
);
1297 /* we handle an unknown application (no .desktop file) differently */
1299 s
= g_markup_printf_escaped (_("The application %s has crashed.\n"
1300 "We are collecting information about the crash to send it to the\n"
1301 "developers in order to fix the problem."), app
->name
);
1302 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")), s
);
1305 g_object_set_data (G_OBJECT (xml
), "app", app
);
1308 gtk_image_set_from_pixbuf (GTK_IMAGE (glade_xml_get_widget (xml
, "app-image")),
1310 fill_gnome_info (app
, gnome_version
, xml
);
1313 gtk_statusbar_push (GTK_STATUSBAR (statusbar
),
1314 DEBUGGING
, _("Collecting info from the crash..."));
1316 fill_system_info (xml
);
1318 fill_proccess_info (gopt_data
.pid
, xml
);
1320 if (gopt_data
.pid
> 0) {
1321 /* again, if this is an unknown application, we connect a different callback that
1322 * will allow the user to save the trace rather than sending it to the GNOME bugzilla */
1324 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, xml
,
1325 gdb_finished_unknown_app
, &err
);
1327 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, xml
, gdb_finished
, &err
);
1330 if (source_id
== 0) {
1331 buddy_error (NULL
, _("Bug Buddy encountered the following error when trying "
1332 "to retrieve debugging information: %s\n"), err
->message
);
1334 g_object_unref (program
);
1338 /* connect the close button callback so that we can remove the source from
1339 * the main loop (and kill gdb) if the user wants to quit before gdb is finished */
1340 g_object_set_data (G_OBJECT (xml
), "sourceid", GUINT_TO_POINTER (source_id
));
1343 unknown_app_finished (xml
);
1345 known_app_finished (xml
);
1348 g_object_set_data (G_OBJECT (xml
), "type", GINT_TO_POINTER(BUG_TYPE_CRASH
));
1350 /* No binary file, so this is a non-crashing bug. Look the application from the --package arg */
1351 GtkWidget
*email_entry
;
1352 char *default_email
;
1354 app
= g_hash_table_find (apps
, (GHRFunc
)bugzilla_search_for_package
, gopt_data
.package
);
1356 /* Fallback to binary name */
1357 app
= g_hash_table_lookup (apps
, gopt_data
.package
);
1360 buddy_error (NULL
, _("Bug Buddy doesn't know how to send a suggestion for the application %s.\n"),
1364 s
= g_markup_printf_escaped (_("Thank you for helping us improving our software.\n"
1365 "Please fill your suggestions/error information for %s application.\n"),
1367 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")), s
);
1370 g_object_set_data (G_OBJECT (xml
), "app", app
);
1373 gtk_image_set_from_pixbuf (GTK_IMAGE (glade_xml_get_widget (xml
, "app-image")),
1375 fill_gnome_info (app
, gnome_version
, xml
);
1377 gtk_widget_show (glade_xml_get_widget (xml
, "final-box"));
1378 gtk_widget_show (glade_xml_get_widget (xml
, "send-button"));
1379 gtk_widget_hide (glade_xml_get_widget (xml
, "progressbar"));
1380 gtk_statusbar_pop (GTK_STATUSBAR (glade_xml_get_widget (xml
, "statusbar")), DEBUGGING
);
1382 s
= g_markup_printf_escaped (_("Thank you for helping us improving our software.\n"
1383 "Please fill your suggestions/error information for %s application.\n\n"
1384 "A valid email address is required. This will allow developers to "
1385 "contact you for more information if necessary.\n\n"),
1387 gtk_label_set_text (GTK_LABEL (glade_xml_get_widget (xml
, "main-text")), s
);
1390 s
= g_markup_printf_escaped (_("<span weight=\"bold\">Suggestion / Error description:</span>"));
1391 gtk_label_set_markup (GTK_LABEL (glade_xml_get_widget (xml
, "main-label")), s
);
1394 show_pending_checkbox_if_pending (xml
);
1395 g_signal_connect (glade_xml_get_widget (xml
, "send-button"), "clicked",
1396 G_CALLBACK (on_send_clicked
), xml
);
1398 email_entry
= glade_xml_get_widget (xml
, "email-entry");
1399 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), xml
);
1401 default_email
= get_default_user_email ();
1403 if (default_email
!= NULL
) {
1404 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
1405 g_free (default_email
);
1407 g_object_set_data (G_OBJECT (xml
), "type", GINT_TO_POINTER(BUG_TYPE_REQUEST
));
1410 if (gopt_data
.include_file
!= NULL
) {
1411 fill_include_file (gopt_data
.include_file
, xml
);
1415 g_signal_connect (glade_xml_get_widget (xml
, "close-button"), "clicked",
1416 G_CALLBACK (close_callback
), xml
);
1420 g_hash_table_destroy (apps
);
1422 g_object_unref (program
);