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"
25 #include "gdb-buddy.h"
27 #include "bug-buddy.h"
28 #include "distribution.h"
30 #include "forbidden-words.h"
35 #include <sys/utsname.h>
36 #include <sys/types.h>
42 #include <glib/gstdio.h>
43 #include <glib/gi18n.h>
47 #include <gdk-pixbuf/gdk-pixbuf.h>
48 #include <libgnome/libgnometypebuiltins.h>
52 #include <libxml/tree.h>
53 #include <libxml/parser.h>
55 #include <gconf/gconf-client.h>
57 #include <libsoup/soup.h>
59 #define USE_PROXY_KEY "/system/http_proxy/use_http_proxy"
60 #define PROXY_HOST_KEY "/system/http_proxy/host"
61 #define PROXY_PORT_KEY "/system/http_proxy/port"
62 #define USE_PROXY_AUTH "/system/http_proxy/use_authentication"
63 #define PROXY_USER "/system/http_proxy/authentication_user"
64 #define PROXY_PASSWORD "/system/http_proxy/authentication_password"
65 #define ACCESSIBILITY_KEY "/desktop/gnome/interface/accessibility"
66 #define GTK_THEME_KEY "/desktop/gnome/interface/gtk_theme"
67 #define ICON_THEME_KEY "/desktop/gnome/interface/icon_theme"
68 #define DESKTOP_IS_HOME_DIR "/apps/nautilus/preferences/desktop_is_home_dir"
69 #define MIN_REPORT_DETAILS_CHARS 10
71 static GOptionData gopt_data
;
72 static int bug_count
= 0;
73 static GHashTable
*apps
= NULL
;
75 static const GOptionEntry options
[] = {
76 { "package", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.package
, N_("Package containing the program"), N_("PACKAGE") },
77 { "appname", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.app_file
, N_("File name of crashed program"), N_("FILE") },
78 { "pid", '\0', 0, G_OPTION_ARG_INT
, &gopt_data
.pid
, N_("PID of crashed program"), N_("PID") },
79 { "include", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.include_file
, N_("Text file to include in the report"), N_("FILE") },
80 { "minidump", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.minidump_file
, N_("MiniDump file with info about the crash"), N_("FILE") },
84 static void fill_stderr_info (GtkBuilder
*ui
);
85 static void fill_custom_info (BugzillaApplication
*app
, GtkBuilder
*ui
);
86 static void close_callback (GtkWidget
*widget
, gpointer user_data
);
87 static void bug_buddy_quit (GtkBuilder
*ui
);
91 buddy_error (GtkWidget
*parent
, const char *msg
, ...)
98 /* No va_list version of dialog_new, construct the string ourselves. */
100 s
= g_strdup_vprintf (msg
, args
);
103 w
= gtk_message_dialog_new (GTK_WINDOW (parent
),
110 gtk_dialog_set_default_response (d
, GTK_RESPONSE_OK
);
112 gtk_widget_destroy (w
);
117 lock_text (GtkBuilder
*ui
)
119 GtkTextView
*text_view
;
120 GtkTextBuffer
*buffer
;
123 static GtkTextTag
*tag
= NULL
;
126 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
127 buffer
= gtk_text_view_get_buffer (text_view
);
128 gtk_text_buffer_get_start_iter (buffer
, &start
);
129 gtk_text_buffer_get_end_iter (buffer
, &end
);
130 text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
133 GtkStyle
*style
= gtk_widget_get_style (GTK_WIDGET (text_view
));
134 tag
= gtk_text_buffer_create_tag (buffer
, "lock_tag",
136 /* I don't like how it looks like dimming also fg
137 "foreground-gdk", &style->fg[GTK_STATE_INSENSITIVE], */
138 "background-gdk", &style
->bg
[GTK_STATE_INSENSITIVE
],
142 if (gtk_text_iter_forward_search (&start
, "Backtrace was generated from",
143 GTK_TEXT_SEARCH_TEXT_ONLY
,
145 gtk_text_iter_forward_line (&end
);
146 gtk_text_buffer_apply_tag_by_name (buffer
, "lock_tag", &start
, &end
);
152 search_forbidden_words (GtkBuilder
*ui
)
154 GtkTextView
*text_view
;
155 GtkTextBuffer
*buffer
;
157 gboolean found
= FALSE
;
158 static GtkTextTag
*tag
= NULL
;
160 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
161 buffer
= gtk_text_view_get_buffer (text_view
);
163 tag
= gtk_text_buffer_create_tag (buffer
, "forbbiden_tag",
164 "foreground", "white",
165 "background", "blue",
169 for (i
= 0; forbidden_words
[i
]; i
++) {
173 gtk_text_buffer_get_start_iter (buffer
, &start
);
174 while (gtk_text_iter_forward_search (&start
, forbidden_words
[i
],
175 GTK_TEXT_SEARCH_TEXT_ONLY
,
176 &start
, &end
, NULL
)) {
177 gtk_text_buffer_apply_tag_by_name (buffer
, "forbbiden_tag", &start
, &end
);
189 copy_review (GtkWidget
*button
, gpointer data
)
191 GtkTextView
*text_view
;
192 GtkTextBuffer
*buffer
;
195 GtkBuilder
*ui
= (GtkBuilder
*) data
;
197 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
198 buffer
= gtk_text_view_get_buffer (text_view
);
199 gtk_text_buffer_get_start_iter (buffer
, &start
);
200 gtk_text_buffer_get_end_iter (buffer
, &end
);
201 gtk_text_buffer_select_range (buffer
, &start
, &end
);
202 gtk_text_buffer_copy_clipboard (buffer
, gtk_clipboard_get (GDK_NONE
));
207 edit_review (GtkWidget
*button
, gpointer data
)
209 GtkTextView
*text_view
;
210 GtkBuilder
*ui
= (GtkBuilder
*) data
;
211 gboolean editable
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button
));
213 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
214 gtk_text_view_set_editable (text_view
, editable
);
222 close_review (GtkWidget
*button
, gpointer data
)
224 GtkWidget
*review_dialog
= GTK_WIDGET (data
);
226 gtk_widget_hide (review_dialog
);
230 delete_review (GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
232 gtk_widget_hide (widget
);
234 return TRUE
; /* don't destroy */
238 show_review (GtkWidget
*button
, gpointer data
)
240 GtkWidget
*review_dialog
, *main_window
;
241 GtkWidget
*edit
, *copy
, *close
;
242 GtkBuilder
*ui
= (GtkBuilder
*) data
;
243 static gboolean initialized
= FALSE
;
246 review_dialog
= GTK_WIDGET (gtk_builder_get_object (ui
, "review-dialog"));
247 main_window
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-window"));
248 copy
= GTK_WIDGET (gtk_builder_get_object (ui
, "copy-review-button"));
249 edit
= GTK_WIDGET (gtk_builder_get_object (ui
, "edit-review-button"));
250 close
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-review-button"));
252 gtk_window_set_transient_for (GTK_WINDOW (review_dialog
), GTK_WINDOW (main_window
));
254 g_signal_connect (G_OBJECT (copy
), "clicked", G_CALLBACK (copy_review
), ui
);
255 g_signal_connect (G_OBJECT (edit
), "toggled", G_CALLBACK (edit_review
), ui
);
256 g_signal_connect (G_OBJECT (close
), "clicked", G_CALLBACK (close_review
), review_dialog
);
257 g_signal_connect (G_OBJECT (review_dialog
), "delete-event", G_CALLBACK (delete_review
), NULL
);
264 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "review-dialog")));
268 static GnomeVersionInfo
*
269 get_gnome_version_info (void)
271 GnomeVersionInfo
*version
;
275 guchar
*platform
, *minor
, *micro
, *distributor
, *date
;
277 version
= g_new0 (GnomeVersionInfo
, 1);
279 xml_file
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_DATADIR
,
280 "gnome-about/gnome-version.xml",
284 doc
= xmlParseFile (xml_file
);
290 platform
= minor
= micro
= distributor
= date
= NULL
;
292 for (node
= xmlDocGetRootElement (doc
)->children
; node
; node
= node
->next
) {
293 if (!strcmp ((char *)node
->name
, "platform"))
294 platform
= xmlNodeGetContent (node
);
295 else if (!strcmp ((char *)node
->name
, "minor"))
296 minor
= xmlNodeGetContent (node
);
297 else if (!strcmp ((char *)node
->name
, "micro"))
298 micro
= xmlNodeGetContent (node
);
299 else if (!strcmp ((char *)node
->name
, "distributor"))
300 distributor
= xmlNodeGetContent (node
);
301 else if (!strcmp ((char *)node
->name
, "date"))
302 date
= xmlNodeGetContent (node
);
305 if (platform
&& minor
&& micro
)
306 version
->gnome_platform
= g_strdup_printf ("%s.%s.%s", platform
, minor
, micro
);
308 if (distributor
&& *distributor
)
309 version
->gnome_distributor
= g_strdup ((char *)distributor
);
312 version
->gnome_date
= g_strdup ((char *)date
);
317 xmlFree (distributor
);
326 update_progress_bar (gpointer data
)
328 GtkProgressBar
*pbar
= GTK_PROGRESS_BAR (data
);
330 gtk_progress_bar_pulse (pbar
);
336 save_email (const char *email
)
338 GConfClient
*conf_client
;
340 conf_client
= gconf_client_get_default ();
341 gconf_client_set_string (conf_client
, "/apps/bug-buddy/email_address", email
, NULL
);
342 g_object_unref (conf_client
);
346 copy_link_item_activated_cb (GtkMenuItem
*menu_item
,
349 GtkClipboard
*clipboard
;
351 clipboard
= gtk_clipboard_get (GDK_SELECTION_CLIPBOARD
);
352 gtk_clipboard_set_text (clipboard
, link
, -1);
356 build_link_menu (const gchar
*link
)
362 menu
= gtk_menu_new ();
363 item
= gtk_image_menu_item_new_with_mnemonic (_("Copy _Link Address"));
364 image
= gtk_image_new_from_stock (GTK_STOCK_COPY
, GTK_ICON_SIZE_MENU
);
365 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item
), image
);
366 gtk_widget_show_all (item
);
367 g_signal_connect (item
, "activate",
368 G_CALLBACK (copy_link_item_activated_cb
), (char *) link
);
369 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
375 link_button_press_event_cb (GtkWidget
*widget
,
376 GdkEventButton
*event
,
379 const gchar
*link
= gtk_link_button_get_uri (GTK_LINK_BUTTON (widget
));
381 if (event
->type
== GDK_BUTTON_PRESS
&& event
->button
== 3) {
384 popup
= build_link_menu (link
);
385 gtk_menu_popup (GTK_MENU (popup
), NULL
, NULL
, NULL
, NULL
,
386 event
->button
, event
->time
);
395 link_callback (GtkLinkButton
*button
, gpointer user_data
)
397 const gchar
*link
= gtk_link_button_get_uri (button
);
398 #if GTK_CHECK_VERSION (2,13,0)
399 GdkAppLaunchContext
*context
;
401 context
= gdk_app_launch_context_new ();
402 gdk_app_launch_context_set_screen (context
,
403 gtk_widget_get_screen (GTK_WIDGET (button
)));
404 gdk_app_launch_context_set_timestamp (context
,
405 gtk_get_current_event_time ());
407 if (!g_app_info_launch_default_for_uri (link
,
408 G_APP_LAUNCH_CONTEXT (context
),
411 if (!g_app_info_launch_default_for_uri (link
, NULL
, NULL
))
416 text
= g_markup_printf_escaped (_("Bug Buddy was unable to view the link \"%s\"\n"), link
);
417 buddy_error (NULL
, text
);
421 #if GTK_CHECK_VERSION (2,13,0)
422 g_object_unref (context
);
429 save_to_file (const gchar
*filename
, const gchar
*text
)
431 GError
*error
= NULL
;
433 if (!g_file_set_contents (filename
, text
, -1, &error
)) {
434 g_warning ("Unable to save document %s: %s\n", filename
, error
->message
);
435 g_error_free (error
);
441 network_error (SoupMessage
*msg
, GtkBuilder
*ui
)
447 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
449 dialog
= gtk_message_dialog_new_with_markup (NULL
,
453 _("There has been a network error while sending the report. "
454 "Do you want to save this report and send it later?"));
455 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog
),
456 "%s", _("Please ensure that your Internet connection is active "
457 "and working correctly."));
458 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
459 gtk_widget_destroy (dialog
);
461 if (res
== GTK_RESPONSE_YES
) {
465 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
466 if (!g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
467 g_mkdir_with_parents (dirname
, 0755);
470 filename
= g_strdup_printf ("%s/%ld", dirname
, (long)time (NULL
));
472 save_to_file (filename
, msg
->request_body
->data
);
483 remove_pending_reports (void)
487 GError
*error
= NULL
;
489 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
490 dir
= g_dir_open (dirname
, 0, &error
);
492 const char *name
= g_dir_read_name (dir
);
494 char *path
= g_strdup_printf ("%s/%s", dirname
, name
);
497 name
= g_dir_read_name (dir
);
507 all_sent (GtkBuilder
*ui
)
509 GtkWidget
*close_button
;
511 /* hide the progressbar */
512 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
514 close_button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
515 gtk_widget_show (close_button
);
519 previous_sent (SoupSession
*session
, SoupMessage
*msg
, GtkBuilder
*ui
)
521 if (--bug_count
== 0) {
528 bug_sent (SoupSession
*session
, SoupMessage
*msg
, GtkBuilder
*ui
)
538 GtkWidget
*urlbutton
;
539 GtkRequisition requisition
;
542 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
543 gtk_button_set_label (GTK_BUTTON (button
), _("_Close"));
544 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
546 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
),
547 gtk_button_set_image (GTK_BUTTON (button
), image
);
549 if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg
->status_code
)) {
550 network_error (msg
, ui
);
552 remove_pending_reports ();
555 /* parse the XML-RPC response */
556 response
= bugzilla_parse_response (msg
, &err
);
557 if (response
!= NULL
) {
558 bugid
= strtol (response
, &tmp
, 10);
559 GtkWidget
*main_vbox
;
561 /* we need a reference to the vbox containing the text so that we
562 * can add a GtkLinkButton to the bug report */
563 main_vbox
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-vbox"));
565 if (response
== tmp
) {
567 url
= strstr (response
, "ViewURL=");
569 url
+= strlen ("ViewURL=");
573 text
= g_strdup (url
);
575 text
= g_strdup_printf ("http://bugzilla.gnome.org/show_bug.cgi?id=%ld", bugid
);
577 /* create a clickable link to the bug report */
578 urlbutton
= gtk_link_button_new (text
);
579 g_signal_connect (G_OBJECT (urlbutton
), "clicked", G_CALLBACK (link_callback
), NULL
);
580 g_signal_connect (G_OBJECT (urlbutton
), "button-press-event",
581 G_CALLBACK (link_button_press_event_cb
), NULL
);
582 gtk_box_pack_end (GTK_BOX (main_vbox
), urlbutton
, FALSE
, FALSE
, 0);
584 gtk_widget_show (urlbutton
);
587 text
= g_markup_printf_escaped (_("A bug report detailing your software crash has been sent to GNOME. "
588 "This information will allow the developers to understand the cause "
589 "of the crash and prepare a solution for it.\n\n"
590 "You may be contacted by a GNOME developer if more details are "
591 "required about the crash.\n\n"
592 "You can view your bug report and follow its progress with this URL:\n")) ;
594 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), text
);
596 save_email (gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (ui
, "email-entry"))));
598 errmsg
= _("Bug Buddy has encountered an error while submitting your report "
599 "to the Bugzilla server. Details of the error are included below.\n\n");
601 if (err
&& err
->domain
== SOUP_XMLRPC_FAULT
) {
602 /* see http://cvs.gnome.org/viewcvs/bugzilla-newer/Bugzilla/RPC.pm?view=markup */
603 if (err
->message
== NULL
) {
604 text
= g_strdup_printf (_("Bugzilla reported an error when trying to process your "
605 "request, but was unable to parse the response."));
606 } else if (g_str_equal (err
->message
, "invalid_username")) {
607 text
= g_strdup_printf (_("The email address you provided is not valid."));
608 } else if (g_str_equal (err
->message
, "account_disabled")) {
609 text
= g_strdup_printf (_("The account associated with the email address "
610 "provided has been disabled."));
611 } else if (g_str_equal (err
->message
, "product_doesnt_exist")) {
612 text
= g_strdup_printf (_("The product specified doesn't exist or has been "
613 "renamed. Please upgrade to the latest version."));
614 } else if (g_str_equal (err
->message
, "component_not_valid")) {
615 text
= g_strdup_printf (_("The component specified doesn't exist or has been "
616 "renamed. Please upgrade to the latest version."));
617 } else if (g_str_equal (err
->message
, "require_summary")) {
618 text
= g_strdup_printf (_("The summary is required in your bug report. "
619 "This should not happen with the latest Bug Buddy."));
620 } else if (g_str_equal (err
->message
, "description_required")) {
621 text
= g_strdup_printf (_("The description is required in your bug report. "
622 "This should not happen with the latest Bug Buddy."));
624 text
= g_strdup_printf (_("The fault code returned by Bugzilla is not recognized. "
625 "Please report the following information to "
626 "bugzilla.gnome.org manually:\n\n%s"), err
->message
);
630 case BUGZILLA_ERROR_RECV_BAD_STATUS
:
631 text
= g_strdup_printf (_("Server returned bad state. This is most likely a server "
632 "issue and should be reported to bugmaster@gnome.org\n\n%s"),
635 case BUGZILLA_ERROR_RECV_PARSE_FAILED
:
636 text
= g_strdup_printf (_("Failed to parse the xml-rpc response. Response follows:\n\n%s"),
640 text
= g_strdup_printf (_("An unknown error occurred. This is most likely a problem with "
641 "bug-buddy. Please report this problem manually at bugzilla."
646 str
= g_strconcat (errmsg
, text
, NULL
);
647 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), str
);
655 if (--bug_count
== 0) {
659 gtk_widget_size_request (GTK_WIDGET (gtk_builder_get_object (ui
, "main-window")), &requisition
);
660 gtk_window_resize (GTK_WINDOW (gtk_builder_get_object (ui
, "main-window")),
661 requisition
.width
, requisition
.height
);
669 set_proxy (SoupSession
*session
)
671 GConfClient
*gconf_client
;
676 char *username
= NULL
;
677 char *password
= NULL
;
680 gconf_client
= gconf_client_get_default ();
682 if (gconf_client_get_bool (gconf_client
, USE_PROXY_KEY
, NULL
) == FALSE
) {
683 g_object_unref (gconf_client
);
687 host
= gconf_client_get_string (gconf_client
, PROXY_HOST_KEY
, NULL
);
689 g_object_unref (gconf_client
);
692 port
= gconf_client_get_int (gconf_client
, PROXY_PORT_KEY
, NULL
);
696 if (gconf_client_get_bool (gconf_client
, USE_PROXY_AUTH
, NULL
)) {
697 username
= gconf_client_get_string (gconf_client
, PROXY_USER
, NULL
);
698 password
= gconf_client_get_string (gconf_client
, PROXY_PASSWORD
, NULL
);
701 if (username
&& password
)
702 proxy_uri
= g_strdup_printf ("http://%s:%s@%s:%d", username
, password
, host
, port
);
704 proxy_uri
= g_strdup_printf ("http://%s:%d", host
, port
);
706 uri
= soup_uri_new (proxy_uri
);
707 g_object_set (G_OBJECT (session
), "proxy-uri", uri
, NULL
);
714 g_object_unref (gconf_client
);
720 create_report_title (BugzillaApplication
*app
, int type
, const char *description
)
727 tmp
= g_malloc0 (256); /* This should be safe enough for 24 UTF-8 chars.
728 * anyway, I miss a g_utf8_strndup :) */
729 size
= g_utf8_strlen (description
, -1);
731 g_utf8_strncpy (tmp
, description
, 24);
733 g_utf8_strncpy (tmp
, description
, size
);
737 if (type
== BUG_TYPE_CRASH
) {
738 title
= g_strdup_printf ("crash in %s: %s%s", app
->cname
,
739 tmp
? tmp
: "empty description",
740 (tmp
&& size
> 24) ? "..." : "");
742 title
= g_strdup_printf ("%s: %s%s", app
->cname
,
743 tmp
? tmp
: "empty description",
744 (tmp
&& size
> 24) ? "..." : "");
752 send_report (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GtkBuilder
*ui
)
754 GtkTextView
*text_view
;
755 GtkTextBuffer
*buffer
;
764 SoupSession
*session
;
765 SoupMessage
*message
;
768 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "pending-reports-check")));
770 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
771 buffer
= gtk_text_view_get_buffer (text_view
);
772 gtk_text_buffer_get_start_iter (buffer
, &start
);
773 gtk_text_buffer_get_end_iter (buffer
, &end
);
774 gdb_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
776 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "details-view"));
777 buffer
= gtk_text_view_get_buffer (text_view
);
778 gtk_text_buffer_get_start_iter (buffer
, &start
);
779 gtk_text_buffer_get_end_iter (buffer
, &end
);
780 details_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
782 type
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT(ui
), "type"));
783 final_text
= g_strdup_printf ("%s%s\n\n\n%s",
784 type
== BUG_TYPE_CRASH
? "What were you doing when the application crashed?\n" : "",
785 details_text
!= NULL
? details_text
: "",
786 gdb_text
!= NULL
? gdb_text
: "<empty backtrace>");
788 email
= gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (ui
, "email-entry")));
789 title
= create_report_title (app
, type
, details_text
);
791 message
= bugzilla_create_report (app
, type
, gnome_version
, email
, title
, final_text
, ui
, gopt_data
.minidump_file
, &err
);
792 if (message
== NULL
) {
796 text
= g_strdup_printf (_("Unable to create the bug report: %s\n"), err
->message
);
798 text
= g_strdup_printf (_("There was an error creating the bug report\n"));
801 buddy_error (NULL
, text
);
804 g_free (details_text
);
811 session
= soup_session_async_new ();
815 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (ui
, "pending-reports-check")))) {
818 GError
*error
= NULL
;
820 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
821 dir
= g_dir_open (dirname
, 0, &error
);
823 const char *name
= g_dir_read_name (dir
);
824 while (name
!= NULL
) {
829 path
= g_strdup_printf ("%s/%s", dirname
, name
);
830 if (g_file_get_contents (path
, &contents
, &length
, NULL
)) {
832 msg
= soup_message_new ("POST", "http://bugzilla.gnome.org/bugbuddy.cgi");
833 soup_message_set_request (msg
, "text/xml",
837 soup_session_queue_message (session
, SOUP_MESSAGE (msg
),
838 (SoupSessionCallback
)previous_sent
, ui
);
841 name
= g_dir_read_name (dir
);
850 soup_session_queue_message (session
, message
,
851 (SoupSessionCallback
)bug_sent
, ui
);
853 g_free (details_text
);
857 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
862 /* A local part is valid if it is one or more valid characters. */
864 email_local_part_is_valid (const char *local_part
)
866 const char *character
;
871 for (character
= local_part
; *character
; character
++) {
872 /* RFC 3696 says *any* printable ASCII character can
873 * appear in local-part, subject to quoting rules. */
874 if (g_ascii_isprint (*character
))
877 /* Not valid character, not valid local part. */
884 /* A domain label is valid if it is one or more valid characters. */
886 email_domain_label_is_valid (const char *domain_label
)
888 const char *character
;
891 /* Validate each character, whilst measuring length, i. */
892 for (i
= 0; *(character
= domain_label
+ i
); i
++) {
894 /* If character is alphanumeric it is valid. */
895 if (g_ascii_isalnum (*character
))
898 /* If it's a hyphen, it's also valid. */
899 if (*character
== '-')
902 /* Anything else is invalid */
906 /* Labels must be between 1 and 63 characters long */
907 if (i
< 1 || i
> 63) {
914 /* A domain is valid if it is one or more valid, dot-separated labels. */
916 email_domain_is_valid (const char *domain
)
920 gboolean retval
= FALSE
;
922 /* If there is no domain, there are no domain labels and the domain is
927 /* Split the domain on the dot to validate labels. */
928 labels
= g_strsplit (domain
, ".", 0);
929 if (g_strv_length (labels
) == 1) {
930 /* the domain doesn't contain any dot: it's not valid */
934 for (this_label
= labels
; *this_label
; this_label
++) {
935 if (!email_domain_label_is_valid (*this_label
)) {
947 /* Check for *simple* email addresses of the form user@host, with checks
948 * in characters used, and sanity checks on the form of host.
950 /* FIXME: Should we provide a useful error message? */
952 email_is_valid (const char *address
)
959 /* Split on the *last* '@' character: */
960 at_sign
= strrchr (address
, '@');
965 local_part
= g_strndup (address
, at_sign
- address
);
966 domain
= g_strdup (at_sign
+ 1);
968 /* Check each part is valid */
969 is_valid
= email_local_part_is_valid (local_part
)
970 && email_domain_is_valid (domain
);
979 check_email (GtkEditable
*editable
, gpointer data
)
982 GtkBuilder
*ui
= (GtkBuilder
*) data
;
984 email
= gtk_entry_get_text (GTK_ENTRY (editable
));
985 gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")),
986 email_is_valid (email
));
991 on_send_clicked (GtkWidget
*button
, gpointer data
)
993 BugzillaApplication
*app
;
994 GnomeVersionInfo
*gnome_version
;
995 GtkRequisition requisition
;
996 GtkBuilder
*ui
= (GtkBuilder
*) data
;
1000 app
= g_object_get_data (G_OBJECT (ui
), "app");
1001 gnome_version
= g_object_get_data (G_OBJECT (ui
), "gnome-version");
1003 details
= GTK_WIDGET (gtk_builder_get_object (ui
, "details-view"));
1004 i
= gtk_text_buffer_get_char_count (
1005 gtk_text_view_get_buffer (GTK_TEXT_VIEW (details
)));
1007 if (i
< MIN_REPORT_DETAILS_CHARS
) {
1014 dialog
= gtk_message_dialog_new (NULL
,
1018 _("The description you provided for the "
1019 "crash is very short. Are you sure you want "
1022 /* Secondary text */
1023 gtk_message_dialog_format_secondary_text
1024 (GTK_MESSAGE_DIALOG (dialog
),
1025 _("A short description is probably not of much help "
1026 "to the developers investigating your report. "
1027 "If you provide a better one, for instance "
1028 "specifying a way to reproduce the crash, the "
1029 "issue can be more easily resolved."));
1032 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
1033 _("_Review description"),
1034 GTK_RESPONSE_CANCEL
);
1035 icon
= gtk_image_new_from_stock
1036 (GTK_STOCK_EDIT
, GTK_ICON_SIZE_BUTTON
);
1037 gtk_button_set_image (GTK_BUTTON (button
), icon
);
1038 gtk_widget_show (button
);
1040 /* Send anyway button */
1041 button
= gtk_dialog_add_button (GTK_DIALOG (dialog
),
1044 icon
= gtk_image_new_from_stock
1045 (GTK_STOCK_OK
, GTK_ICON_SIZE_BUTTON
);
1046 gtk_button_set_image (GTK_BUTTON (button
), icon
);
1047 gtk_widget_show (button
);
1049 gtk_dialog_set_default_response (GTK_DIALOG (dialog
),
1052 i
= gtk_dialog_run (GTK_DIALOG (dialog
));
1054 gtk_widget_destroy (dialog
);
1056 if (i
!= GTK_RESPONSE_OK
)
1060 /* hide the send button immediately so that the user can't click
1061 * it more than once (this will create multiple bugs).
1063 gtk_widget_hide (GTK_WIDGET (
1064 gtk_builder_get_object (ui
, "send-button")));
1066 gtk_widget_show (GTK_WIDGET (
1067 gtk_builder_get_object (ui
, "progressbar")));
1068 gtk_widget_hide (GTK_WIDGET (
1069 gtk_builder_get_object (ui
, "final-box")));
1070 gtk_widget_hide (GTK_WIDGET (
1071 gtk_builder_get_object (ui
, "review-box")));
1073 gtk_widget_size_request (GTK_WIDGET (
1074 gtk_builder_get_object (ui
, "main-window")),
1077 gtk_window_resize (GTK_WINDOW (
1078 gtk_builder_get_object (ui
, "main-window")),
1079 requisition
.width
, requisition
.height
);
1081 send_report (app
, gnome_version
, ui
);
1086 gdb_insert_text (const gchar
*stacktrace
, GtkBuilder
*ui
)
1088 GtkTextView
*text_view
;
1090 GtkTextBuffer
*buffer
;
1092 /* FIXME: These strings are gdb specific, we should add here also dbx */
1093 const char *bt_step1
= "#1";
1094 const char *bt_step2
= "#2";
1095 const char *bt_step3
= "#3";
1097 if (!g_strrstr (stacktrace
, bt_step1
) &&
1098 !g_strrstr (stacktrace
, bt_step2
) &&
1099 !g_strrstr (stacktrace
, bt_step3
)) {
1105 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1106 buffer
= gtk_text_view_get_buffer (text_view
);
1107 gtk_text_buffer_get_end_iter (buffer
, &end
);
1109 /* add the stacktrace to the GtkTextView */
1110 gtk_text_buffer_insert (buffer
, &end
, stacktrace
, strlen (stacktrace
));
1116 show_pending_checkbox_if_pending (GtkBuilder
*ui
)
1121 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
1122 if (g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
1123 check
= GTK_WIDGET (gtk_builder_get_object (ui
, "pending-reports-check"));
1124 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check
), TRUE
);
1125 gtk_widget_show (check
);
1131 create_debuginfo_link (void)
1133 GtkWidget
*urlbutton
;
1135 /* create a clickable link to the bug report */
1136 urlbutton
= gtk_link_button_new_with_label ("http://live.gnome.org/GettingTraces/DistroSpecificInstructions",
1137 /* Translators: This is the hyperlink which takes to http://live.gnome.org/GettingTraces/DistroSpecificInstructions
1138 * page. Please also mention that the page is in English */
1139 _("Getting useful crash reports"));
1140 g_signal_connect (G_OBJECT (urlbutton
), "clicked", G_CALLBACK (link_callback
), NULL
);
1147 add_minidump_text_output (const char *minidump_file
, GtkBuilder
*ui
)
1149 GtkTextView
*text_view
;
1151 GtkTextBuffer
*buffer
;
1152 gchar
*command_line
;
1153 gchar
*standard_output
, *standard_error
;
1158 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1159 buffer
= gtk_text_view_get_buffer (text_view
);
1160 gtk_text_buffer_get_end_iter (buffer
, &end
);
1162 command_line
= g_strdup_printf ("minidump_dump %s", minidump_file
);
1163 if (g_spawn_command_line_sync (command_line
, &standard_output
, &standard_error
,
1164 &exit_status
, &error
)) {
1165 gtk_text_buffer_insert (buffer
, &end
, standard_output
, strlen (standard_output
));
1166 g_free (standard_output
);
1167 g_free (standard_error
);
1169 g_error_free (error
);
1179 useless_finished (GtkBuilder
*ui
)
1182 GtkWidget
*button
, *image
, *main_vbox
, *urlbutton
;
1183 BugzillaApplication
*app
;
1186 app
= g_object_get_data (G_OBJECT (ui
), "app");
1188 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1190 label_text
= g_markup_printf_escaped (_("The application %s crashed. The bug reporting tool was "
1191 "unable to collect enough information about the crash to be "
1192 "useful to the developers.\n\n"
1193 "In order to submit useful reports, please consider installing "
1194 "debug packages for your distribution.\n"
1195 "Click the link below to get information about how to install "
1196 "these packages:\n"),
1198 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")),
1201 main_vbox
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-vbox"));
1202 urlbutton
= create_debuginfo_link ();
1203 gtk_box_pack_end (GTK_BOX (main_vbox
), urlbutton
, FALSE
, FALSE
, 0);
1205 gtk_widget_show (urlbutton
);
1207 g_free (label_text
);
1209 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
1210 gtk_button_set_label (GTK_BUTTON (button
), _("_Close"));
1211 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
1213 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
),
1214 gtk_button_set_image (GTK_BUTTON (button
), image
);
1216 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry")));
1221 known_app_finished (GtkBuilder
*ui
)
1223 BugzillaApplication
*app
;
1224 GtkWidget
*email_entry
;
1226 char *default_email
;
1227 char *lang_note
, *label_text
, *s
;
1228 const char *en_lang_note
= N_("\n\nPlease write your report in English, if possible.");
1230 app
= g_object_get_data (G_OBJECT (ui
), "app");
1232 fill_custom_info (app
, ui
);
1233 fill_stderr_info (ui
);
1235 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1236 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1237 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1239 lang_note
= gettext (en_lang_note
);
1241 label_text
= g_strconcat (_("Information about the %s application crash has been successfully collected. "
1242 "Please provide some more details about what you were doing when "
1243 "the application crashed.\n\n"
1245 "A valid email address is required. This will allow the developers to "
1246 "contact you for more information if necessary."),
1247 strcmp (lang_note
, en_lang_note
) ? lang_note
: NULL
,
1250 s
= g_markup_printf_escaped (label_text
, app
->name
);
1252 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")),
1256 g_free (label_text
);
1258 show_pending_checkbox_if_pending (ui
);
1260 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "send-button"));
1261 g_signal_connect (button
, "clicked",
1262 G_CALLBACK (on_send_clicked
), ui
);
1264 email_entry
= GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry"));
1265 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), ui
);
1267 default_email
= get_default_user_email ();
1269 if (default_email
!= NULL
) {
1270 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
1271 g_free (default_email
);
1273 gtk_widget_set_sensitive (button
, FALSE
);
1276 if (search_forbidden_words (ui
)) {
1277 char *review_text
= g_markup_printf_escaped ("<small><i><span weight=\"bold\">%s</span> %s</i></small>",
1279 _("Some sensitive data is likely present in the crash details. "
1280 "Please review and edit the information if you are concerned "
1281 "about transmitting passwords or other sensitive data."));
1282 gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (ui
, "review-label")), review_text
);
1284 g_signal_connect (gtk_builder_get_object (ui
, "review-button"), "clicked", G_CALLBACK (show_review
), ui
);
1285 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "review-box")));
1287 gtk_widget_grab_focus (GTK_WIDGET (gtk_builder_get_object (ui
, "details-view")));
1291 gdb_finished (const gchar
*stacktrace
, gpointer data
)
1293 GtkBuilder
*ui
= (GtkBuilder
*) data
;
1295 if (gdb_insert_text (stacktrace
, ui
)) {
1296 known_app_finished (ui
);
1298 useless_finished (ui
);
1305 on_save_clicked (GtkWidget
*button
, gpointer user_data
)
1307 GtkBuilder
*ui
= (GtkBuilder
*)user_data
;
1309 const char *desktop
;
1311 gboolean desktop_is_home_dir
, saved
;
1312 GConfClient
*gconf_client
;
1316 dialog
= gtk_file_chooser_dialog_new (_("Save File"),
1317 GTK_WINDOW (gtk_builder_get_object (ui
, "main-window")),
1318 GTK_FILE_CHOOSER_ACTION_SAVE
,
1319 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1320 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
1323 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
1325 gconf_client
= gconf_client_get_default ();
1326 desktop_is_home_dir
= gconf_client_get_bool (gconf_client
, DESKTOP_IS_HOME_DIR
, NULL
);
1327 g_object_unref (gconf_client
);
1329 if (desktop_is_home_dir
)
1330 desktop
= g_get_home_dir();
1332 desktop
= g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP
);
1334 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
), desktop
);
1336 filename
= g_strconcat (gopt_data
.app_file
, _("-bugreport.txt"), NULL
);
1337 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), filename
);
1340 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
1342 GtkTextView
*text_view
;
1343 GtkTextBuffer
*buffer
;
1348 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1349 buffer
= gtk_text_view_get_buffer (text_view
);
1350 gtk_text_buffer_get_start_iter (buffer
, &start
);
1351 gtk_text_buffer_get_end_iter (buffer
, &end
);
1352 text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
1354 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
1355 save_to_file (filename
, text
);
1361 gtk_widget_destroy (dialog
);
1363 bug_buddy_quit (ui
);
1368 focus_details (GtkWidget
*widget
, gpointer data
)
1370 gtk_widget_grab_focus (widget
);
1376 unknown_app_finished (GtkBuilder
*ui
)
1381 fill_stderr_info (ui
);
1383 if (gopt_data
.minidump_file
) {
1384 add_minidump_text_output (gopt_data
.minidump_file
, ui
);
1387 /* don't need user input, so hide these widgets */
1388 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1389 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1391 /* make the send button into a save button :-) */
1392 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "send-button"));
1393 gtk_button_set_label (GTK_BUTTON (button
), _("_Save Bug Report"));
1394 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
1395 g_signal_connect (GTK_BUTTON (button
), "clicked", G_CALLBACK (on_save_clicked
), ui
);
1396 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1398 label_text
= g_markup_printf_escaped (_("The application %s has crashed.\n"
1399 "Information about the crash has been successfully collected.\n\n"
1400 "This application is not known to bug-buddy, therefore the "
1401 "bug report cannot be sent to the GNOME Bugzilla. Please save the "
1402 "bug to a text file and report it to the appropriate bug tracker "
1403 "for this application."), gopt_data
.app_file
);
1404 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), label_text
);
1406 /* FIXME: If we just grab the focus here to the GtkTextView it will crash on the blink_cb because
1407 * the window is nop mapped! Is this a gtk+ bug? are we doing something wrong?
1408 * Let's do a funny Workaround: */
1409 g_signal_connect_after (gtk_builder_get_object (ui
, "details-view"), "realize", G_CALLBACK (focus_details
), NULL
);
1410 gtk_widget_realize (GTK_WIDGET (gtk_builder_get_object (ui
, "details-view")));
1417 gdb_finished_unknown_app (const gchar
*stacktrace
, gpointer data
)
1419 GtkBuilder
*ui
= (GtkBuilder
*) data
;
1421 gdb_insert_text (stacktrace
, ui
);
1422 unknown_app_finished (ui
);
1426 bug_buddy_quit (GtkBuilder
*ui
)
1430 g_return_if_fail (ui
!= NULL
);
1432 data
= g_object_get_data (G_OBJECT (ui
), "sourceid");
1435 guint source_id
= GPOINTER_TO_UINT (data
);
1437 /* removes the context from the main loop and kills any remaining
1439 if (source_id
> 0) {
1440 g_source_remove (source_id
);
1441 g_object_set_data (G_OBJECT (ui
), "sourceid", GUINT_TO_POINTER (0));
1445 g_hash_table_destroy (apps
);
1447 g_object_unref (ui
);
1453 keypress_callback (GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1455 if (event
->keyval
== GDK_Escape
) {
1456 close_callback (NULL
, data
);
1459 /* let others handle the event */
1464 close_callback (GtkWidget
*widget
, gpointer user_data
)
1466 GtkBuilder
*ui
= (GtkBuilder
*)user_data
;
1468 bug_buddy_quit (ui
);
1472 help_callback (GtkWidget
*widget
, gpointer user_data
)
1474 GnomeProgram
*program
= (GnomeProgram
*)user_data
;
1475 GError
*error
= NULL
;
1477 gnome_help_display_desktop (program
,
1484 GtkWidget
*error_dialog
=
1485 gtk_message_dialog_new (NULL
,
1489 _("There was an error displaying help: %s"),
1492 g_signal_connect (G_OBJECT (error_dialog
), "response",
1493 G_CALLBACK (gtk_widget_destroy
), NULL
);
1495 gtk_window_set_resizable (GTK_WINDOW (error_dialog
), FALSE
);
1497 gtk_widget_show (error_dialog
);
1498 g_error_free (error
);
1504 delete_callback (GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
1506 close_callback (NULL
, data
);
1511 fill_gnome_info (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GtkBuilder
*ui
)
1515 GtkTextView
*text_view
;
1517 GtkTextBuffer
*buffer
;
1519 g_return_if_fail (app
!= NULL
);
1520 g_return_if_fail (gnome_version
!= NULL
);
1521 g_return_if_fail (ui
!= NULL
);
1523 distro
= get_distro_name ();
1524 version_info
= g_strdup_printf ("Distribution: %s\n"
1525 "Gnome Release: %s %s (%s)\n"
1526 "BugBuddy Version: %s\n"
1529 gnome_version
->gnome_platform
, gnome_version
->gnome_date
,
1530 gnome_version
->gnome_distributor
, VERSION
);
1534 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1535 buffer
= gtk_text_view_get_buffer (text_view
);
1536 gtk_text_buffer_get_end_iter (buffer
, &end
);
1537 gtk_text_buffer_insert (buffer
, &end
, version_info
, strlen (version_info
));
1539 g_free (version_info
);
1543 fill_custom_info (BugzillaApplication
*app
, GtkBuilder
*ui
)
1545 GtkTextView
*text_view
;
1547 GtkTextBuffer
*buffer
;
1550 gchar
*standard_output
= NULL
;
1551 gchar
*standard_error
= NULL
;
1552 GError
*error
= NULL
;
1554 g_return_if_fail (app
!= NULL
);
1555 g_return_if_fail (ui
!= NULL
);
1557 if (app
->extra_info_script
== NULL
) {
1561 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1562 buffer
= gtk_text_view_get_buffer (text_view
);
1563 gtk_text_buffer_get_end_iter (buffer
, &end
);
1565 if (!g_spawn_command_line_sync (app
->extra_info_script
, &standard_output
, &standard_error
,
1567 gchar
*error_string
= g_strdup_printf ("There was an error running \"%s\" script:\n"
1568 "%s", app
->extra_info_script
, error
->message
);
1569 gtk_text_buffer_insert (buffer
, &end
, error_string
, strlen (error_string
));
1572 g_free (error_string
);
1576 output
= g_strdup_printf ("Output of custom script \"%s\":\n"
1578 app
->extra_info_script
,
1579 standard_output
? standard_output
: "");
1581 gtk_text_buffer_insert (buffer
, &end
, output
, strlen (output
));
1584 g_free (standard_output
);
1585 g_free (standard_error
);
1590 fill_proccess_info (pid_t pid
, GtkBuilder
*ui
)
1592 GtkTextView
*text_view
;
1594 GtkTextBuffer
*buffer
;
1597 char *proccess_info
;
1599 mem
= proccess_get_mem_state (pid
);
1600 time
= proccess_get_time (pid
);
1602 proccess_info
= g_strdup_printf ("%s\n"
1610 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1611 buffer
= gtk_text_view_get_buffer (text_view
);
1612 gtk_text_buffer_get_end_iter (buffer
, &end
);
1613 gtk_text_buffer_insert (buffer
, &end
, proccess_info
, strlen (proccess_info
));
1615 g_free (proccess_info
);
1620 fill_include_file (char *filename
, GtkBuilder
*ui
)
1622 GtkTextView
*text_view
;
1624 GtkTextBuffer
*buffer
;
1626 GError
*error
= NULL
;
1628 if (g_file_get_contents (filename
, &text
, NULL
, &error
) != TRUE
) {
1629 buddy_error (NULL
, error
->message
);
1630 g_error_free (error
);
1634 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1635 buffer
= gtk_text_view_get_buffer (text_view
);
1636 gtk_text_buffer_get_end_iter (buffer
, &end
);
1637 gtk_text_buffer_insert (buffer
, &end
, text
, strlen (text
));
1645 fill_system_info (GtkBuilder
*ui
)
1647 GConfClient
*gconf_client
;
1648 GtkTextView
*text_view
;
1650 GtkTextBuffer
*buffer
;
1651 GString
*system_info
;
1652 struct utsname uts_buf
;
1654 gboolean has_selinux
, enforcing
, a11y
;
1656 g_return_if_fail (ui
!= NULL
);
1658 system_info
= g_string_new ("");
1660 if (uname (&uts_buf
) == 0) {
1661 g_string_append_printf (system_info
, "System: %s %s %s %s\n", uts_buf
.sysname
, uts_buf
.release
, uts_buf
.version
, uts_buf
.machine
);
1664 /* X server checks */
1665 g_string_append_printf (system_info
, "X Vendor: %s\n", ServerVendor (gdk_display
));
1666 g_string_append_printf (system_info
, "X Vendor Release: %d\n", VendorRelease (gdk_display
));
1669 /* Selinux checks */
1670 has_selinux
= FALSE
;
1671 if (g_file_get_contents ("/proc/filesystems", &str
, NULL
, NULL
)) {
1672 has_selinux
= strstr (str
, "selinuxfs") != NULL
;
1677 if (g_file_get_contents ("/selinux/enforce", &str
, NULL
, NULL
)) {
1678 enforcing
= strcmp (str
, "0") != 0;
1681 g_string_append_printf (system_info
, "Selinux: %s\n", enforcing
?"Enforcing":"Permissive");
1683 g_string_append_printf (system_info
, "Selinux: No\n");
1687 gconf_client
= gconf_client_get_default ();
1688 a11y
= gconf_client_get_bool (gconf_client
, ACCESSIBILITY_KEY
, NULL
);
1689 g_string_append_printf (system_info
, "Accessibility: %s\n", a11y
?"Enabled":"Disabled");
1690 str
= gconf_client_get_string (gconf_client
, GTK_THEME_KEY
, NULL
);
1691 g_string_append_printf (system_info
, "GTK+ Theme: %s\n", str
);
1693 str
= gconf_client_get_string (gconf_client
, ICON_THEME_KEY
, NULL
);
1694 g_string_append_printf (system_info
, "Icon Theme: %s\n", str
);
1696 g_object_unref (gconf_client
);
1698 g_string_append (system_info
, "\n");
1700 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1701 buffer
= gtk_text_view_get_buffer (text_view
);
1702 gtk_text_buffer_get_end_iter (buffer
, &end
);
1703 gtk_text_buffer_insert (buffer
, &end
, system_info
->str
, system_info
->len
);
1705 g_string_free (system_info
, TRUE
);
1709 fill_stderr_info (GtkBuilder
*ui
)
1711 GtkTextView
*text_view
;
1713 GtkTextBuffer
*buffer
;
1714 GString
*stderr_info
;
1719 g_return_if_fail (ui
!= NULL
);
1721 stderr_info
= g_string_new ("");
1723 /* .xsession-errors: read file */
1724 file
= g_build_filename (g_get_home_dir (), ".xsession-errors", NULL
);
1725 if (g_file_get_contents (file
, &str
, NULL
, NULL
)) {
1726 lines
= g_strsplit (str
, "\n", -1);
1729 while (lines
[n_lines
] != NULL
) {
1735 char *mtime_age
= NULL
;
1739 if (stat (file
, &buf
) == 0) {
1740 age
= time (NULL
) - buf
.st_mtime
;
1742 mtime_age
= g_strdup_printf (" (%d sec old)", (int) age
);
1746 g_string_append_printf (stderr_info
,
1747 "\n\n----------- .xsession-errors%s ---------------------\n",
1748 mtime_age
?mtime_age
:"");
1752 for (i
= MAX (0, n_lines
-16); i
< n_lines
; i
++) {
1753 if (lines
[i
][0] != 0) {
1754 /* Limit line length to 200 chars to avoid excessive data */
1755 if (strlen (lines
[i
]) > 200) {
1759 g_string_append_printf (stderr_info
, "%s\n", lines
[i
]);
1763 g_string_append (stderr_info
, "--------------------------------------------------\n");
1769 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1770 buffer
= gtk_text_view_get_buffer (text_view
);
1771 gtk_text_buffer_get_end_iter (buffer
, &end
);
1772 gtk_text_buffer_insert (buffer
, &end
, stderr_info
->str
, stderr_info
->len
);
1774 g_string_free (stderr_info
, TRUE
);
1778 main (int argc
, char *argv
[])
1781 BugzillaApplication
*app
;
1782 GnomeVersionInfo
*gnome_version
;
1784 GtkWidget
*main_window
;
1785 GOptionContext
*context
;
1786 GnomeProgram
*program
;
1789 GtkBuilder
*ui
= NULL
;
1791 memset (&gopt_data
, 0, sizeof (gopt_data
));
1793 bindtextdomain (PACKAGE
, GNOMELOCALEDIR
);
1794 bind_textdomain_codeset (PACKAGE
, "UTF-8");
1795 textdomain (PACKAGE
);
1797 context
= g_option_context_new (N_("\n\nBug Buddy is a utility that helps report debugging\n"
1798 "information to the GNOME Bugzilla when a program crashes."));
1800 g_option_context_set_translation_domain (context
, GETTEXT_PACKAGE
);
1802 g_option_context_add_main_entries (context
, options
, GETTEXT_PACKAGE
);
1804 program
= gnome_program_init (PACKAGE
, VERSION
,
1807 GNOME_PARAM_GOPTION_CONTEXT
, context
,
1808 GNOME_PARAM_APP_DATADIR
, BUDDY_DATADIR
,
1809 GNOME_CLIENT_PARAM_SM_CONNECT
, FALSE
,
1812 g_set_application_name (_("Bug Buddy"));
1813 gtk_window_set_default_icon_name ("bug-buddy");
1816 s
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_APP_DATADIR
,
1817 "bug-buddy.gtkbuilder", TRUE
, NULL
);
1819 ui
= gtk_builder_new ();
1820 gtk_builder_add_from_file (ui
, s
, &err
);
1821 gtk_builder_set_translation_domain (ui
, GETTEXT_PACKAGE
);
1828 _("Bug Buddy could not load its user interface file.\n"
1829 "Please make sure Bug Buddy was installed correctly."));
1833 g_object_unref (program
);
1837 main_window
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-window"));
1838 g_signal_connect (main_window
, "delete-event", G_CALLBACK (delete_callback
), ui
);
1839 g_signal_connect (main_window
, "key-press-event", G_CALLBACK (keypress_callback
), ui
);
1841 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1843 progress
= g_timeout_add (100, update_progress_bar
,
1844 gtk_builder_get_object (ui
, "progressbar"));
1845 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
1846 _("Collecting information from your system..."));
1848 if (gopt_data
.app_file
== NULL
&& gopt_data
.package
== NULL
) {
1849 buddy_error (NULL
, _("Either --appname or --package arguments are required.\n"));
1850 g_object_unref (program
);
1854 if (gopt_data
.app_file
&& gopt_data
.pid
== 0 &&
1855 gopt_data
.include_file
== NULL
&&
1856 gopt_data
.minidump_file
== NULL
) {
1857 buddy_error (NULL
, _("Either --pid , --include or --minidump arguments are required.\n"));
1858 g_object_unref (program
);
1862 /* get some information about the gnome version */
1863 gnome_version
= get_gnome_version_info ();
1864 if (gnome_version
== NULL
) {
1865 buddy_error (NULL
, _("Bug Buddy was unable to retrieve information regarding "
1866 "the version of GNOME you are running. This is most likely "
1867 "due to a missing installation of gnome-desktop.\n"));
1868 g_object_unref (program
);
1872 g_object_set_data (G_OBJECT (ui
), "gnome-version", gnome_version
);
1874 /* connect the signal handler for the help button */
1875 g_signal_connect (gtk_builder_get_object (ui
, "help-button"), "clicked",
1876 G_CALLBACK (help_callback
), program
);
1878 gtk_widget_show (main_window
);
1880 apps
= load_applications ();
1882 /* If we have a binary file it is a crash */
1883 if (gopt_data
.app_file
) {
1884 app
= g_hash_table_lookup (apps
, gopt_data
.app_file
);
1886 /* we handle an unknown application (no .desktop file) differently */
1888 s
= g_markup_printf_escaped (_("The %s application has crashed. "
1889 "We are collecting information about the crash to send to the "
1890 "developers in order to fix the problem."), app
->name
);
1891 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), s
);
1894 g_object_set_data (G_OBJECT (ui
), "app", app
);
1897 gtk_image_set_from_icon_name (GTK_IMAGE (gtk_builder_get_object (ui
, "app-image")),
1899 GTK_ICON_SIZE_DIALOG
);
1901 fill_gnome_info (app
, gnome_version
, ui
);
1904 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
1905 _("Collecting information from the crash..."));
1907 fill_system_info (ui
);
1909 fill_proccess_info (gopt_data
.pid
, ui
);
1911 if (gopt_data
.pid
> 0) {
1912 /* again, if this is an unknown application, we connect a different callback that
1913 * will allow the user to save the trace rather than sending it to the GNOME Bugzilla */
1915 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, ui
,
1916 gdb_finished_unknown_app
, &err
);
1918 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, ui
, gdb_finished
, &err
);
1921 if (source_id
== 0) {
1922 buddy_error (NULL
, _("Bug Buddy encountered the following error when trying "
1923 "to retrieve debugging information: %s\n"), err
->message
);
1925 g_object_unref (program
);
1929 /* connect the close button callback so that we can remove the source from
1930 * the main loop (and kill gdb) if the user wants to quit before gdb is finished */
1931 g_object_set_data (G_OBJECT (ui
), "sourceid", GUINT_TO_POINTER (source_id
));
1934 unknown_app_finished (ui
);
1936 known_app_finished (ui
);
1939 g_object_set_data (G_OBJECT (ui
), "type", GINT_TO_POINTER(BUG_TYPE_CRASH
));
1941 /* No binary file, so this is a non-crashing bug. Look the application from the --package arg */
1942 GtkWidget
*email_entry
;
1943 char *default_email
;
1944 char *msg
, *lang_note
;
1945 const char *en_lang_note
= N_("\n\nPlease write your report in English, if possible.");
1947 app
= g_hash_table_find (apps
, (GHRFunc
)bugzilla_search_for_package
, gopt_data
.package
);
1949 /* Fallback to binary name */
1950 app
= g_hash_table_lookup (apps
, gopt_data
.package
);
1953 buddy_error (NULL
, _("Bug Buddy doesn't know how to send a suggestion for the application %s.\n"),
1958 g_object_set_data (G_OBJECT (ui
), "app", app
);
1961 gtk_image_set_from_icon_name (GTK_IMAGE (gtk_builder_get_object (ui
, "app-image")),
1963 GTK_ICON_SIZE_DIALOG
);
1965 fill_gnome_info (app
, gnome_version
, ui
);
1966 fill_custom_info (app
, ui
);
1968 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1969 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1970 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1972 lang_note
= gettext (en_lang_note
);
1973 msg
= g_strconcat (_("Thank you for helping us improving our software.\n"
1974 "Please fill your suggestions/error information for %s application.\n\n"
1975 "A valid email address is required. This will allow developers to "
1976 "contact you for more information if necessary."),
1977 strcmp (lang_note
, en_lang_note
)? lang_note
: NULL
,
1979 s
= g_markup_printf_escaped (msg
, app
->name
);
1980 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), s
);
1984 s
= g_markup_printf_escaped ("<span weight=\"bold\">%s</span>",
1985 _("Suggestion / Error description:"));
1986 gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (ui
, "main-label")), s
);
1989 show_pending_checkbox_if_pending (ui
);
1990 g_signal_connect (gtk_builder_get_object (ui
, "send-button"), "clicked",
1991 G_CALLBACK (on_send_clicked
), ui
);
1993 email_entry
= GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry"));
1994 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), ui
);
1996 default_email
= get_default_user_email ();
1998 if (default_email
!= NULL
) {
1999 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
2000 g_free (default_email
);
2002 g_object_set_data (G_OBJECT (ui
), "type", GINT_TO_POINTER(BUG_TYPE_REQUEST
));
2005 if (gopt_data
.include_file
!= NULL
) {
2006 fill_include_file (gopt_data
.include_file
, ui
);
2010 g_signal_connect (gtk_builder_get_object (ui
, "close-button"), "clicked",
2011 G_CALLBACK (close_callback
), ui
);
2015 g_object_unref (program
);