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>
45 #include <libgnomeui/gnome-window-icon.h>
46 #include <libgnomevfs/gnome-vfs-utils.h>
47 #include <gtk/gtkbuilder.h>
48 #include <gdk-pixbuf/gdk-pixbuf.h>
49 #include <libgnomecanvas/gnome-canvas-pixbuf.h>
50 #include <libgnome/libgnometypebuiltins.h>
53 #include <libxml/tree.h>
54 #include <libxml/parser.h>
56 #include <gconf/gconf-client.h>
58 #ifdef HAVE_NETWORKMANAGER
59 #include <libnm_glib.h>
62 #include <libsoup/soup.h>
63 #include <libsoup/soup-xmlrpc-message.h>
65 #include <sys/types.h>
70 #define USE_PROXY_KEY "/system/http_proxy/use_http_proxy"
71 #define PROXY_HOST_KEY "/system/http_proxy/host"
72 #define PROXY_PORT_KEY "/system/http_proxy/port"
73 #define USE_PROXY_AUTH "/system/http_proxy/use_authentication"
74 #define PROXY_USER "/system/http_proxy/authentication_user"
75 #define PROXY_PASSWORD "/system/http_proxy/authentication_password"
76 #define ACCESSIBILITY_KEY "/desktop/gnome/interface/accessibility"
77 #define GTK_THEME_KEY "/desktop/gnome/interface/gtk_theme"
78 #define ICON_THEME_KEY "/desktop/gnome/interface/icon_theme"
79 #define DESKTOP_IS_HOME_DIR "/apps/nautilus/preferences/desktop_is_home_dir"
81 static GOptionData gopt_data
;
82 static int bug_count
= 0;
83 static GHashTable
*apps
= NULL
;
85 static const GOptionEntry options
[] = {
86 { "name", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.name
, N_("Name of contact"), N_("NAME") },
87 { "email", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.email
, N_("Email address of contact"), N_("EMAIL") },
88 { "package", '\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.package
, N_("Package containing the program"), N_("PACKAGE") },
89 { "package-ver",'\0', 0, G_OPTION_ARG_STRING
, &gopt_data
.package_ver
, N_("Version of the package"), N_("VERSION") },
90 { "appname", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.app_file
, N_("File name of crashed program"), N_("FILE") },
91 { "pid", '\0', 0, G_OPTION_ARG_INT
, &gopt_data
.pid
, N_("PID of crashed program"), N_("PID") },
92 { "core", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.core_file
, N_("Core file from program"), N_("FILE") },
93 { "include", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.include_file
, N_("Text file to include in the report"), N_("FILE") },
94 { "minidump", '\0', 0, G_OPTION_ARG_FILENAME
, &gopt_data
.minidump_file
, N_("MiniDump file with info about the crash"), N_("FILE") },
95 { "kill", '\0', 0, G_OPTION_ARG_INT
, &gopt_data
.kill
, N_("PID of the program to kill after the report"), N_("KILL") },
101 NETWORK_DISCONNECTED
,
105 static void fill_stderr_info (GtkBuilder
*ui
);
106 static void fill_custom_info (BugzillaApplication
*app
, GtkBuilder
*ui
);
107 static void close_callback (GtkWidget
*widget
, gpointer user_data
);
108 static void bug_buddy_quit (GtkBuilder
*ui
);
112 buddy_error (GtkWidget
*parent
, const char *msg
, ...)
119 /* No va_list version of dialog_new, construct the string ourselves. */
120 va_start (args
, msg
);
121 s
= g_strdup_vprintf (msg
, args
);
124 w
= gtk_message_dialog_new (GTK_WINDOW (parent
),
131 gtk_dialog_set_default_response (d
, GTK_RESPONSE_OK
);
133 gtk_widget_destroy (w
);
138 lock_text (GtkBuilder
*ui
)
140 GtkTextView
*text_view
;
141 GtkTextBuffer
*buffer
;
144 static GtkTextTag
*tag
= NULL
;
147 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
148 buffer
= gtk_text_view_get_buffer (text_view
);
149 gtk_text_buffer_get_start_iter (buffer
, &start
);
150 gtk_text_buffer_get_end_iter (buffer
, &end
);
151 text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
154 GtkStyle
*style
= gtk_widget_get_style (GTK_WIDGET (text_view
));
155 tag
= gtk_text_buffer_create_tag (buffer
, "lock_tag",
157 /* I don't like how it looks like dimming also fg
158 "foreground-gdk", &style->fg[GTK_STATE_INSENSITIVE], */
159 "background-gdk", &style
->bg
[GTK_STATE_INSENSITIVE
],
163 if (gtk_text_iter_forward_search (&start
, "Backtrace was generated from",
164 GTK_TEXT_SEARCH_TEXT_ONLY
,
166 gtk_text_iter_forward_line (&end
);
167 gtk_text_buffer_apply_tag_by_name (buffer
, "lock_tag", &start
, &end
);
173 search_forbidden_words (GtkBuilder
*ui
)
175 GtkTextView
*text_view
;
176 GtkTextBuffer
*buffer
;
178 gboolean found
= FALSE
;
179 static GtkTextTag
*tag
= NULL
;
181 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
182 buffer
= gtk_text_view_get_buffer (text_view
);
184 tag
= gtk_text_buffer_create_tag (buffer
, "forbbiden_tag",
185 "foreground", "white",
186 "background", "blue",
190 for (i
= 0; forbidden_words
[i
]; i
++) {
194 gtk_text_buffer_get_start_iter (buffer
, &start
);
195 while (gtk_text_iter_forward_search (&start
, forbidden_words
[i
],
196 GTK_TEXT_SEARCH_TEXT_ONLY
,
197 &start
, &end
, NULL
)) {
198 gtk_text_buffer_apply_tag_by_name (buffer
, "forbbiden_tag", &start
, &end
);
210 copy_review (GtkWidget
*button
, gpointer data
)
212 GtkTextView
*text_view
;
213 GtkTextBuffer
*buffer
;
216 GtkBuilder
*ui
= (GtkBuilder
*) data
;
218 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
219 buffer
= gtk_text_view_get_buffer (text_view
);
220 gtk_text_buffer_get_start_iter (buffer
, &start
);
221 gtk_text_buffer_get_end_iter (buffer
, &end
);
222 gtk_text_buffer_select_range (buffer
, &start
, &end
);
223 gtk_text_buffer_copy_clipboard (buffer
, gtk_clipboard_get (GDK_NONE
));
228 edit_review (GtkWidget
*button
, gpointer data
)
230 GtkTextView
*text_view
;
231 GtkBuilder
*ui
= (GtkBuilder
*) data
;
232 gboolean editable
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button
));
234 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
235 gtk_text_view_set_editable (text_view
, editable
);
243 close_review (GtkWidget
*button
, gpointer data
)
245 GtkWidget
*review_dialog
= GTK_WIDGET (data
);
247 gtk_widget_hide (review_dialog
);
251 delete_review (GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
253 gtk_widget_hide (widget
);
255 return TRUE
; /* don't destroy */
259 show_review (GtkWidget
*button
, gpointer data
)
261 GtkWidget
*review_dialog
, *main_window
;
262 GtkWidget
*edit
, *copy
, *close
;
263 GtkBuilder
*ui
= (GtkBuilder
*) data
;
264 static gboolean initialized
= FALSE
;
267 review_dialog
= GTK_WIDGET (gtk_builder_get_object (ui
, "review-dialog"));
268 main_window
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-window"));
269 copy
= GTK_WIDGET (gtk_builder_get_object (ui
, "copy-review-button"));
270 edit
= GTK_WIDGET (gtk_builder_get_object (ui
, "edit-review-button"));
271 close
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-review-button"));
273 gtk_window_set_transient_for (GTK_WINDOW (review_dialog
), GTK_WINDOW (main_window
));
275 g_signal_connect (G_OBJECT (copy
), "clicked", G_CALLBACK (copy_review
), ui
);
276 g_signal_connect (G_OBJECT (edit
), "toggled", G_CALLBACK (edit_review
), ui
);
277 g_signal_connect (G_OBJECT (close
), "clicked", G_CALLBACK (close_review
), review_dialog
);
278 g_signal_connect (G_OBJECT (review_dialog
), "delete-event", G_CALLBACK (delete_review
), NULL
);
285 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "review-dialog")));
289 static GnomeVersionInfo
*
290 get_gnome_version_info (void)
292 GnomeVersionInfo
*version
;
296 guchar
*platform
, *minor
, *micro
, *distributor
, *date
;
298 version
= g_new0 (GnomeVersionInfo
, 1);
300 xml_file
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_DATADIR
,
301 "gnome-about/gnome-version.xml",
305 doc
= xmlParseFile (xml_file
);
311 platform
= minor
= micro
= distributor
= date
= NULL
;
313 for (node
= xmlDocGetRootElement (doc
)->children
; node
; node
= node
->next
) {
314 if (!strcmp ((char *)node
->name
, "platform"))
315 platform
= xmlNodeGetContent (node
);
316 else if (!strcmp ((char *)node
->name
, "minor"))
317 minor
= xmlNodeGetContent (node
);
318 else if (!strcmp ((char *)node
->name
, "micro"))
319 micro
= xmlNodeGetContent (node
);
320 else if (!strcmp ((char *)node
->name
, "distributor"))
321 distributor
= xmlNodeGetContent (node
);
322 else if (!strcmp ((char *)node
->name
, "date"))
323 date
= xmlNodeGetContent (node
);
326 if (platform
&& minor
&& micro
)
327 version
->gnome_platform
= g_strdup_printf ("%s.%s.%s", platform
, minor
, micro
);
329 if (distributor
&& *distributor
)
330 version
->gnome_distributor
= g_strdup ((char *)distributor
);
333 version
->gnome_date
= g_strdup ((char *)date
);
338 xmlFree (distributor
);
347 update_progress_bar (gpointer data
)
349 GtkProgressBar
*pbar
= GTK_PROGRESS_BAR (data
);
351 gtk_progress_bar_pulse (pbar
);
357 save_email (const char *email
)
359 GConfClient
*conf_client
;
361 conf_client
= gconf_client_get_default ();
362 gconf_client_set_string (conf_client
, "/apps/bug-buddy/email_address", email
, NULL
);
363 g_object_unref (conf_client
);
367 link_callback (GtkLinkButton
*button
, gpointer user_data
)
369 const gchar
*link
= gtk_link_button_get_uri (button
);
371 if (gnome_vfs_url_show (link
) != GNOME_VFS_OK
) {
374 text
= g_markup_printf_escaped (_("Bug Buddy was unable to view the link \"%s\"\n"), link
);
375 buddy_error (NULL
, text
);
383 save_to_file (const gchar
*filename
, const gchar
*text
)
385 GError
*error
= NULL
;
387 if (!g_file_set_contents (filename
, text
, -1, &error
)) {
388 g_warning ("Unable to save document %s: %s\n", filename
, error
->message
);
389 g_error_free (error
);
395 network_error (SoupMessage
*msg
, GtkBuilder
*ui
)
401 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
403 dialog
= gtk_message_dialog_new_with_markup (NULL
,
405 GTK_MESSAGE_QUESTION
,
407 _("<span weight=\"bold\">Network Connection Error</span>\n"
408 "Maybe no Network Connection available.\n"
409 "Do you want to store this report until "
410 "a Network Connection is available?"));
411 res
= gtk_dialog_run (GTK_DIALOG (dialog
));
412 gtk_widget_destroy (dialog
);
414 if (res
== GTK_RESPONSE_YES
) {
417 xmlChar
*message_string
;
419 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
420 if (!g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
421 g_mkdir_with_parents (dirname
, 0755);
424 filename
= g_strdup_printf ("%s/%ld", dirname
, (long)time (NULL
));
426 message_string
= soup_xmlrpc_message_to_string (SOUP_XMLRPC_MESSAGE (msg
));
428 save_to_file (filename
, (const gchar
*) message_string
);
430 xmlFree (message_string
);
440 remove_pending_reports (void)
444 GError
*error
= NULL
;
446 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
447 dir
= g_dir_open (dirname
, 0, &error
);
449 const char *name
= g_dir_read_name (dir
);
451 char *path
= g_strdup_printf ("%s/%s", dirname
, name
);
454 name
= g_dir_read_name (dir
);
464 all_sent (GtkBuilder
*ui
)
466 GtkWidget
*close_button
;
468 /* hide the progressbar */
469 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
471 close_button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
472 gtk_widget_show (close_button
);
476 previous_sent (SoupMessage
*msg
, GtkBuilder
*ui
)
478 if (--bug_count
== 0) {
485 bug_sent (SoupMessage
*msg
, GtkBuilder
*ui
)
494 GtkWidget
*urlbutton
;
495 GtkRequisition requisition
;
498 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
499 gtk_button_set_label (GTK_BUTTON (button
), _("_Close"));
500 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
502 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
),
503 gtk_button_set_image (GTK_BUTTON (button
), image
);
505 if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg
->status_code
)) {
506 network_error (msg
, ui
);
508 remove_pending_reports ();
511 /* parse the XML-RPC response from Bugzilla */
512 bugid
= bugzilla_parse_response (msg
, &err
);
515 GtkWidget
*main_vbox
;
517 /* we need a reference to the vbox containing the text so that we
518 * can add a GtkLinkButton to the bug report */
519 main_vbox
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-vbox"));
521 text
= g_strdup_printf ("http://bugzilla.gnome.org/show_bug.cgi?id=%ld", bugid
);
523 /* create a clickable link to the bug report */
524 urlbutton
= gtk_link_button_new (text
);
525 g_signal_connect (G_OBJECT (urlbutton
), "clicked", G_CALLBACK (link_callback
), NULL
);
526 gtk_box_pack_end (GTK_BOX (main_vbox
), urlbutton
, FALSE
, FALSE
, 0);
528 gtk_widget_show (urlbutton
);
531 text
= g_markup_printf_escaped (_("A bug report detailing your software crash has been sent to GNOME. "
532 "This information will allow the developers to understand the cause "
533 "of the crash and prepare a solution for it.\n\n"
534 "You may be contacted by a GNOME developer if more details are "
535 "required about the crash.\n\n"
536 "You can view your bug report and follow its progress with this URL:\n")) ;
538 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), text
);
540 save_email (gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (ui
, "email-entry"))));
542 errmsg
= _("Bug Buddy has encountered an error while submitting your report "
543 "to the Bugzilla server. Details of the error are included below.\n\n");
547 case BUGZILLA_ERROR_RECV_BAD_STATUS
:
548 text
= g_strdup_printf (_("Server returned bad state. This is most likely a server "
549 "issue and should be reported to bugmaster@gnome.org\n\n%s"),
552 case BUGZILLA_ERROR_RECV_PARSE_FAILED
:
553 text
= g_strdup_printf (_("Failed to parse the xml-rpc response. Response follows:\n\n%s"),
556 case BUGZILLA_ERROR_RECV_FAULT
:
557 /* in this case, the error message returned is the faultCode and faultString from
558 * the XML-RPC response, separated by a colon. We construct our error message
559 * based on the faultString */
560 ptr
= strstr (err
->message
, ":");
562 text
= g_strdup_printf (_("Bugzilla reported an error when trying to process your "
563 "request, but was unable to parse the response."));
570 /* see http://cvs.gnome.org/viewcvs/bugzilla-newer/Bugzilla/RPC.pm?view=markup */
571 if (g_str_equal (ptr
, "invalid_username")) {
572 text
= g_strdup_printf (_("The email address you provided is not valid."));
573 } else if (g_str_equal (ptr
, "account_disabled")) {
574 text
= g_strdup_printf (_("The account associated with the email address "
575 "provided has been disabled."));
576 } else if (g_str_equal (ptr
, "product_doesnt_exist")) {
577 text
= g_strdup_printf (_("The product specified doesn't exist or has been "
578 "renamed. Please upgrade to the latest version."));
579 } else if (g_str_equal (ptr
, "component_not_valid")) {
580 text
= g_strdup_printf (_("The component specified doesn't exist or has been "
581 "renamed. Please upgrade to the latest version."));
582 } else if (g_str_equal (ptr
, "require_summary")) {
583 text
= g_strdup_printf (_("The summary is required in your bug report. "
584 "This should not happen with the latest Bug Buddy."));
585 } else if (g_str_equal (ptr
, "description_required")) {
586 text
= g_strdup_printf (_("The description is required in your bug report. "
587 "This should not happen with the latest Bug Buddy."));
589 text
= g_strdup_printf (_("The fault code returned by Bugzilla is not recognized. "
590 "Please report the following information to "
591 "bugzilla.gnome.org manually:\n\n%s"), err
->message
);
596 text
= g_strdup_printf (_("An unknown error occurred. This is most likely a problem with "
597 "bug-buddy. Please report this problem manually at bugzilla."
602 str
= g_strconcat (errmsg
, text
, NULL
);
603 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), str
);
611 if (--bug_count
== 0) {
615 gtk_widget_size_request (GTK_WIDGET (gtk_builder_get_object (ui
, "main-window")), &requisition
);
616 gtk_window_resize (GTK_WINDOW (gtk_builder_get_object (ui
, "main-window")),
617 requisition
.width
, requisition
.height
);
623 set_proxy (SoupSession
*session
)
625 GConfClient
*gconf_client
;
630 char *username
= NULL
;
631 char *password
= NULL
;
634 gconf_client
= gconf_client_get_default ();
636 if (gconf_client_get_bool (gconf_client
, USE_PROXY_KEY
, NULL
) == FALSE
) {
637 g_object_unref (gconf_client
);
641 host
= gconf_client_get_string (gconf_client
, PROXY_HOST_KEY
, NULL
);
643 g_object_unref (gconf_client
);
646 port
= gconf_client_get_int (gconf_client
, PROXY_PORT_KEY
, NULL
);
650 if (gconf_client_get_bool (gconf_client
, USE_PROXY_AUTH
, NULL
)) {
651 username
= gconf_client_get_string (gconf_client
, PROXY_USER
, NULL
);
652 password
= gconf_client_get_string (gconf_client
, PROXY_PASSWORD
, NULL
);
655 if (username
&& password
)
656 proxy_uri
= g_strdup_printf ("http://%s:%s@%s:%d", username
, password
, host
, port
);
658 proxy_uri
= g_strdup_printf ("http://%s:%d", host
, port
);
660 uri
= soup_uri_new (proxy_uri
);
661 g_object_set (G_OBJECT (session
), "proxy-uri", uri
, NULL
);
668 g_object_unref (gconf_client
);
674 create_report_title (BugzillaApplication
*app
, int type
, const char *description
)
681 tmp
= g_malloc0 (256); /* This should be safe enough for 24 UTF-8 chars.
682 * anyway, I miss a g_utf8_strndup :) */
683 size
= g_utf8_strlen (description
, -1);
685 g_utf8_strncpy (tmp
, description
, 24);
687 g_utf8_strncpy (tmp
, description
, size
);
691 if (type
== BUG_TYPE_CRASH
) {
692 title
= g_strdup_printf ("crash in %s: %s%s", app
->cname
,
693 tmp
? tmp
: "empty description",
694 (tmp
&& size
> 24) ? "..." : "");
696 title
= g_strdup_printf ("%s: %s%s", app
->cname
,
697 tmp
? tmp
: "empty description",
698 (tmp
&& size
> 24) ? "..." : "");
706 send_report (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GtkBuilder
*ui
)
708 GtkTextView
*text_view
;
709 GtkTextBuffer
*buffer
;
718 SoupSession
*session
;
719 SoupXmlrpcMessage
*message
;
722 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "pending-reports-check")));
724 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
725 buffer
= gtk_text_view_get_buffer (text_view
);
726 gtk_text_buffer_get_start_iter (buffer
, &start
);
727 gtk_text_buffer_get_end_iter (buffer
, &end
);
728 gdb_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
730 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "details-view"));
731 buffer
= gtk_text_view_get_buffer (text_view
);
732 gtk_text_buffer_get_start_iter (buffer
, &start
);
733 gtk_text_buffer_get_end_iter (buffer
, &end
);
734 details_text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
736 type
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT(ui
), "type"));
737 final_text
= g_strdup_printf ("%s%s\n\n\n%s",
738 type
== BUG_TYPE_CRASH
? "What were you doing when the application crashed?\n" : "",
739 details_text
!= NULL
? details_text
: "",
740 gdb_text
!= NULL
? gdb_text
: "<empty backtrace>");
742 email
= gtk_entry_get_text (GTK_ENTRY (gtk_builder_get_object (ui
, "email-entry")));
743 title
= create_report_title (app
, type
, details_text
);
745 message
= bugzilla_create_report (app
, type
, gnome_version
, email
, title
, final_text
, ui
, &err
);
746 if (message
== NULL
) {
750 text
= g_strdup_printf (_("Unable to create the bug report: %s\n"), err
->message
);
752 text
= g_strdup_printf (_("There was an error creating the bug report\n"));
755 buddy_error (NULL
, text
);
758 g_free (details_text
);
765 session
= soup_session_async_new ();
769 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gtk_builder_get_object (ui
, "pending-reports-check")))) {
772 GError
*error
= NULL
;
774 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
775 dir
= g_dir_open (dirname
, 0, &error
);
777 const char *name
= g_dir_read_name (dir
);
778 while (name
!= NULL
) {
782 path
= g_strdup_printf ("%s/%s", dirname
, name
);
783 if (g_file_get_contents (path
, &contents
, NULL
, NULL
)) {
784 SoupXmlrpcMessage
*msg
;
785 msg
= soup_xmlrpc_message_new ("http://bugzilla.gnome.org/bugbuddy.cgi");
786 soup_xmlrpc_message_from_string (msg
, contents
);
788 soup_xmlrpc_message_persist (msg
);
789 soup_session_queue_message (session
, SOUP_MESSAGE (msg
),
790 (SoupMessageCallbackFn
)previous_sent
, ui
);
794 name
= g_dir_read_name (dir
);
803 soup_xmlrpc_message_persist (message
);
804 soup_session_queue_message (session
, SOUP_MESSAGE (message
),
805 (SoupMessageCallbackFn
)bug_sent
, ui
);
807 g_free (details_text
);
811 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
816 /* A local part is valid if it is one or more valid characters. */
818 email_local_part_is_valid (const char *local_part
)
820 const char *character
;
825 for (character
= local_part
; *character
; character
++) {
826 /* If character is alphanumeric it is valid. */
827 if (g_ascii_isalnum (*character
))
830 /* If character is "-", "_" or "." it is valid. */
831 if (*character
== '-' || *character
== '_' || *character
== '.')
834 /* Not valid character, not valid local part. */
841 /* A domain label is valid if it is one or more valid characters. */
843 email_domain_label_is_valid (const char *domain_label
)
845 const char *character
;
848 /* Validate each character, whilst measuring length, i. */
849 for (i
= 0; *(character
= domain_label
+ i
); i
++) {
851 /* If character is alphanumeric it is valid. */
852 if (g_ascii_isalnum (*character
))
855 /* If it's a hyphen, it's also valid. */
856 if (*character
== '-')
859 /* Anything else is invalid */
863 /* Labels must be between 1 and 63 characters long */
864 if (i
< 1 || i
> 63) {
871 /* A domain is valid if it is one or more valid, dot-separated labels. */
873 email_domain_is_valid (const char *domain
)
878 /* If there is no domain, there are no domain labels and the domain is
883 /* Split the domain on the dot to validate labels. */
884 labels
= g_strsplit (domain
, ".", 0);
886 for (this_label
= labels
; *this_label
; this_label
++) {
887 if (!email_domain_label_is_valid (*this_label
)) {
897 /* Check for *simple* email addresses of the form user@host, with checks
898 * in characters used, and sanity checks on the form of host.
900 /* FIXME: Should we provide a useful error message? */
902 email_is_valid (const char *address
)
909 parts
= g_strsplit (address
, "@", 2);
911 /* Check we have the 2 parts */
912 if (!(local_part
= parts
[0]) || !(domain
= parts
[1])) {
917 is_valid
= email_local_part_is_valid (local_part
)
918 && email_domain_is_valid (domain
);
925 check_email (GtkEditable
*editable
, gpointer data
)
928 GtkBuilder
*ui
= (GtkBuilder
*) data
;
930 email
= gtk_entry_get_text (GTK_ENTRY (editable
));
931 gtk_widget_set_sensitive (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")),
932 email_is_valid (email
));
937 on_send_clicked (GtkWidget
*button
, gpointer data
)
939 BugzillaApplication
*app
;
940 GnomeVersionInfo
*gnome_version
;
941 GtkRequisition requisition
;
942 GtkBuilder
*ui
= (GtkBuilder
*) data
;
944 app
= g_object_get_data (G_OBJECT (ui
), "app");
945 gnome_version
= g_object_get_data (G_OBJECT (ui
), "gnome-version");
947 /* hide the send button immediately so that the user can't click
948 * it more than once (this will create multiple bugs) */
949 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
951 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
952 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
953 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "review-box")));
955 gtk_widget_size_request (GTK_WIDGET (gtk_builder_get_object (ui
, "main-window")), &requisition
);
956 gtk_window_resize (GTK_WINDOW (gtk_builder_get_object (ui
, "main-window")),
957 requisition
.width
, requisition
.height
);
959 send_report (app
, gnome_version
, ui
);
963 gdb_insert_text (const gchar
*stacktrace
, GtkBuilder
*ui
)
965 GtkTextView
*text_view
;
967 GtkTextBuffer
*buffer
;
969 /* FIXME: These strings are gdb specific, we should add here also dbx */
970 const char *bt_step1
= "#1";
971 const char *bt_step2
= "#2";
972 const char *bt_step3
= "#3";
974 if (!g_strrstr (stacktrace
, bt_step1
) &&
975 !g_strrstr (stacktrace
, bt_step2
) &&
976 !g_strrstr (stacktrace
, bt_step3
)) {
982 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
983 buffer
= gtk_text_view_get_buffer (text_view
);
984 gtk_text_buffer_get_end_iter (buffer
, &end
);
986 /* add the stacktrace to the GtkTextView */
987 gtk_text_buffer_insert (buffer
, &end
, stacktrace
, strlen (stacktrace
));
993 show_pending_checkbox_if_pending (GtkBuilder
*ui
)
998 dirname
= g_strdup_printf ("%s/.gnome2/bug-buddy/pending_reports", g_get_home_dir ());
999 if (g_file_test (dirname
, G_FILE_TEST_IS_DIR
)) {
1000 check
= GTK_WIDGET (gtk_builder_get_object (ui
, "pending-reports-check"));
1001 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check
), TRUE
);
1002 gtk_widget_show (check
);
1008 create_debuginfo_link (void)
1010 GtkWidget
*urlbutton
;
1012 /* create a clickable link to the bug report */
1013 urlbutton
= gtk_link_button_new_with_label ("http://live.gnome.org/GettingTraces/DistroSpecificInstructions",
1014 /* Translators: This is the hyperlink which takes to http://live.gnome.org/GettingTraces/DistroSpecificInstructions
1015 * page. Please also mention that the page is in English */
1016 _("Getting useful crash reports"));
1017 g_signal_connect (G_OBJECT (urlbutton
), "clicked", G_CALLBACK (link_callback
), NULL
);
1024 useless_finished (GtkBuilder
*ui
)
1027 GtkWidget
*button
, *image
, *main_vbox
, *urlbutton
;
1028 BugzillaApplication
*app
;
1031 app
= g_object_get_data (G_OBJECT (ui
), "app");
1033 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1035 label_text
= g_markup_printf_escaped (_("The application %s crashed. The bug reporting tool was "
1036 "unable to collect enough information about the crash to be "
1037 "useful to the developers.\n\n"
1038 "In order to submit useful reports, please consider installing "
1039 "debug packages for your distribution.\n"
1040 "Click the link below to get information about how to install "
1041 "these packages:\n"),
1043 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")),
1046 main_vbox
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-vbox"));
1047 urlbutton
= create_debuginfo_link ();
1048 gtk_box_pack_end (GTK_BOX (main_vbox
), urlbutton
, FALSE
, FALSE
, 0);
1050 gtk_widget_show (urlbutton
);
1052 g_free (label_text
);
1054 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
1055 gtk_button_set_label (GTK_BUTTON (button
), _("_Close"));
1056 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
1058 image
= gtk_image_new_from_stock (GTK_STOCK_CLOSE
, GTK_ICON_SIZE_BUTTON
),
1059 gtk_button_set_image (GTK_BUTTON (button
), image
);
1061 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry")));
1066 known_app_finished (GtkBuilder
*ui
)
1068 BugzillaApplication
*app
;
1069 GtkWidget
*email_entry
;
1071 char *default_email
;
1072 char *lang_note
, *label_text
, *s
;
1073 const char *en_lang_note
= N_("\n\nPlease write your report in English, if possible.");
1075 app
= g_object_get_data (G_OBJECT (ui
), "app");
1077 fill_custom_info (app
, ui
);
1078 fill_stderr_info (ui
);
1080 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1081 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1082 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1084 lang_note
= gettext (en_lang_note
);
1086 label_text
= g_strconcat (_("Information about the %s application crash has been successfully collected. "
1087 "Please provide some more details about what you were doing when "
1088 "the application crashed.\n\n"
1090 "A valid email address is required. This will allow the developers to "
1091 "contact you for more information if necessary."),
1092 strcmp (lang_note
, en_lang_note
) ? lang_note
: NULL
,
1095 s
= g_markup_printf_escaped (label_text
, app
->name
);
1097 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")),
1101 g_free (label_text
);
1103 show_pending_checkbox_if_pending (ui
);
1105 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "send-button"));
1106 g_signal_connect (button
, "clicked",
1107 G_CALLBACK (on_send_clicked
), ui
);
1109 email_entry
= GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry"));
1110 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), ui
);
1112 default_email
= get_default_user_email ();
1114 if (default_email
!= NULL
) {
1115 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
1116 g_free (default_email
);
1118 gtk_widget_set_sensitive (button
, FALSE
);
1121 if (search_forbidden_words (ui
)) {
1122 char *review_text
= g_markup_printf_escaped ("<small><i><span weight=\"bold\">%s</span> %s</i></small>",
1124 _("Some sensitive data is likely present in the crash details. "
1125 "Please review and edit the information if you are concerned "
1126 "about transmitting passwords or other sensitive data."));
1127 gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (ui
, "review-label")), review_text
);
1129 g_signal_connect (gtk_builder_get_object (ui
, "review-button"), "clicked", G_CALLBACK (show_review
), ui
);
1130 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "review-box")));
1132 gtk_widget_grab_focus (GTK_WIDGET (gtk_builder_get_object (ui
, "details-view")));
1136 gdb_finished (const gchar
*stacktrace
, gpointer data
)
1138 GtkBuilder
*ui
= (GtkBuilder
*) data
;
1140 if (gdb_insert_text (stacktrace
, ui
)) {
1141 known_app_finished (ui
);
1143 useless_finished (ui
);
1150 on_save_clicked (GtkWidget
*button
, gpointer user_data
)
1152 GtkBuilder
*ui
= (GtkBuilder
*)user_data
;
1154 const char *desktop
;
1156 gboolean desktop_is_home_dir
, saved
;
1157 GConfClient
*gconf_client
;
1161 dialog
= gtk_file_chooser_dialog_new (_("Save File"),
1162 GTK_WINDOW (gtk_builder_get_object (ui
, "main-window")),
1163 GTK_FILE_CHOOSER_ACTION_SAVE
,
1164 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1165 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
1168 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog
), TRUE
);
1170 gconf_client
= gconf_client_get_default ();
1171 desktop_is_home_dir
= gconf_client_get_bool (gconf_client
, DESKTOP_IS_HOME_DIR
, NULL
);
1172 g_object_unref (gconf_client
);
1174 if (desktop_is_home_dir
)
1175 desktop
= g_get_home_dir();
1177 desktop
= g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP
);
1179 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog
), desktop
);
1181 filename
= g_strconcat (gopt_data
.app_file
, _("-bugreport.txt"), NULL
);
1182 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog
), filename
);
1185 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
) {
1187 GtkTextView
*text_view
;
1188 GtkTextBuffer
*buffer
;
1193 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1194 buffer
= gtk_text_view_get_buffer (text_view
);
1195 gtk_text_buffer_get_start_iter (buffer
, &start
);
1196 gtk_text_buffer_get_end_iter (buffer
, &end
);
1197 text
= gtk_text_buffer_get_text (buffer
, &start
, &end
, FALSE
);
1199 filename
= gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog
));
1200 save_to_file (filename
, text
);
1206 gtk_widget_destroy (dialog
);
1208 bug_buddy_quit (ui
);
1213 focus_details (GtkWidget
*widget
, gpointer data
)
1215 gtk_widget_grab_focus (widget
);
1219 unknown_app_finished (GtkBuilder
*ui
)
1224 fill_stderr_info (ui
);
1226 /* don't need user input, so hide these widgets */
1227 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1228 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1230 /* make the send button into a save button :-) */
1231 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "send-button"));
1232 gtk_button_set_label (GTK_BUTTON (button
), _("_Save Bug Report"));
1233 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
1234 g_signal_connect (GTK_BUTTON (button
), "clicked", G_CALLBACK (on_save_clicked
), ui
);
1235 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1237 label_text
= g_markup_printf_escaped (_("The application %s has crashed.\n"
1238 "Information about the crash has been successfully collected.\n\n"
1239 "This application is not known to bug-buddy, therefore the "
1240 "bug report cannot be sent to the GNOME Bugzilla. Please save the "
1241 "bug to a text file and report it to the appropriate bug tracker "
1242 "for this application."), gopt_data
.app_file
);
1243 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), label_text
);
1245 /* FIXME: If we just grab the focus here to the GtkTextView it will crash on the blink_cb because
1246 * the window is nop mapped! Is this a gtk+ bug? are we doing something wrong?
1247 * Let's do a funny Workaround: */
1248 g_signal_connect_after (gtk_builder_get_object (ui
, "details-view"), "realize", G_CALLBACK (focus_details
), NULL
);
1249 gtk_widget_realize (GTK_WIDGET (gtk_builder_get_object (ui
, "details-view")));
1254 send_minidump (GtkBuilder
*ui
)
1256 /* FIXME: Implement the MiniDump sending code */
1259 button
= GTK_WIDGET (gtk_builder_get_object (ui
, "close-button"));
1260 gtk_button_set_label (GTK_BUTTON (button
), _("_Close"));
1261 gtk_button_set_use_underline (GTK_BUTTON (button
), TRUE
);
1263 /* don't need user input, so hide these widgets */
1264 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1265 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1266 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1268 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")),
1269 _("Your application has crashed. Information about the crash has been successfully collected.\n\n"
1270 "However we are working on GNOME debug server to handle correctly this information.\n\n"));
1276 gdb_finished_unknown_app (const gchar
*stacktrace
, gpointer data
)
1278 GtkBuilder
*ui
= (GtkBuilder
*) data
;
1280 gdb_insert_text (stacktrace
, ui
);
1281 unknown_app_finished (ui
);
1285 bug_buddy_quit (GtkBuilder
*ui
)
1289 g_return_if_fail (ui
!= NULL
);
1291 data
= g_object_get_data (G_OBJECT (ui
), "sourceid");
1294 guint source_id
= GPOINTER_TO_UINT (data
);
1296 /* removes the context from the main loop and kills any remaining
1298 if (source_id
> 0) {
1299 g_source_remove (source_id
);
1300 g_object_set_data (G_OBJECT (ui
), "sourceid", GUINT_TO_POINTER (0));
1304 g_hash_table_destroy (apps
);
1306 g_object_unref (ui
);
1312 close_callback (GtkWidget
*widget
, gpointer user_data
)
1314 GtkBuilder
*ui
= (GtkBuilder
*)user_data
;
1316 bug_buddy_quit (ui
);
1320 help_callback (GtkWidget
*widget
, gpointer user_data
)
1322 GnomeProgram
*program
= (GnomeProgram
*)user_data
;
1323 GError
*error
= NULL
;
1325 gnome_help_display_desktop (program
,
1332 GtkWidget
*error_dialog
=
1333 gtk_message_dialog_new (NULL
,
1337 _("There was an error displaying help: %s"),
1340 g_signal_connect (G_OBJECT (error_dialog
), "response",
1341 G_CALLBACK (gtk_widget_destroy
), NULL
);
1343 gtk_window_set_resizable (GTK_WINDOW (error_dialog
), FALSE
);
1345 gtk_widget_show (error_dialog
);
1346 g_error_free (error
);
1352 delete_callback (GtkWidget
*widget
, GdkEvent
*event
, gpointer data
)
1354 close_callback (NULL
, data
);
1359 fill_gnome_info (BugzillaApplication
*app
, GnomeVersionInfo
*gnome_version
, GtkBuilder
*ui
)
1363 GtkTextView
*text_view
;
1365 GtkTextBuffer
*buffer
;
1367 g_return_if_fail (app
!= NULL
);
1368 g_return_if_fail (gnome_version
!= NULL
);
1369 g_return_if_fail (ui
!= NULL
);
1371 distro
= get_distro_name ();
1372 version_info
= g_strdup_printf ("Distribution: %s\n"
1373 "Gnome Release: %s %s (%s)\n"
1374 "BugBuddy Version: %s\n"
1377 gnome_version
->gnome_platform
, gnome_version
->gnome_date
,
1378 gnome_version
->gnome_distributor
, VERSION
);
1382 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1383 buffer
= gtk_text_view_get_buffer (text_view
);
1384 gtk_text_buffer_get_end_iter (buffer
, &end
);
1385 gtk_text_buffer_insert (buffer
, &end
, version_info
, strlen (version_info
));
1387 g_free (version_info
);
1391 fill_custom_info (BugzillaApplication
*app
, GtkBuilder
*ui
)
1393 GtkTextView
*text_view
;
1395 GtkTextBuffer
*buffer
;
1398 gchar
*standard_output
= NULL
;
1399 gchar
*standard_error
= NULL
;
1400 GError
*error
= NULL
;
1402 g_return_if_fail (app
!= NULL
);
1403 g_return_if_fail (ui
!= NULL
);
1405 if (app
->extra_info_script
== NULL
) {
1409 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1410 buffer
= gtk_text_view_get_buffer (text_view
);
1411 gtk_text_buffer_get_end_iter (buffer
, &end
);
1413 if (!g_spawn_command_line_sync (app
->extra_info_script
, &standard_output
, &standard_error
,
1415 gchar
*error_string
= g_strdup_printf ("There was an error running \"%s\" script:\n"
1416 "%s", app
->extra_info_script
, error
->message
);
1417 gtk_text_buffer_insert (buffer
, &end
, error_string
, strlen (error_string
));
1420 g_free (error_string
);
1424 output
= g_strdup_printf ("Output of custom script \"%s\":\n"
1426 app
->extra_info_script
,
1427 standard_output
? standard_output
: "");
1429 gtk_text_buffer_insert (buffer
, &end
, output
, strlen (output
));
1432 g_free (standard_output
);
1433 g_free (standard_error
);
1438 fill_proccess_info (pid_t pid
, GtkBuilder
*ui
)
1440 GtkTextView
*text_view
;
1442 GtkTextBuffer
*buffer
;
1445 char *proccess_info
;
1447 mem
= proccess_get_mem_state (pid
);
1448 time
= proccess_get_time (pid
);
1450 proccess_info
= g_strdup_printf ("%s\n"
1458 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1459 buffer
= gtk_text_view_get_buffer (text_view
);
1460 gtk_text_buffer_get_end_iter (buffer
, &end
);
1461 gtk_text_buffer_insert (buffer
, &end
, proccess_info
, strlen (proccess_info
));
1463 g_free (proccess_info
);
1468 fill_include_file (char *filename
, GtkBuilder
*ui
)
1470 GtkTextView
*text_view
;
1472 GtkTextBuffer
*buffer
;
1474 GError
*error
= NULL
;
1476 if (g_file_get_contents (filename
, &text
, NULL
, &error
) != TRUE
) {
1477 buddy_error (NULL
, error
->message
);
1478 g_error_free (error
);
1482 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1483 buffer
= gtk_text_view_get_buffer (text_view
);
1484 gtk_text_buffer_get_end_iter (buffer
, &end
);
1485 gtk_text_buffer_insert (buffer
, &end
, text
, strlen (text
));
1493 fill_system_info (GtkBuilder
*ui
)
1495 GConfClient
*gconf_client
;
1496 GtkTextView
*text_view
;
1498 GtkTextBuffer
*buffer
;
1499 GString
*system_info
;
1500 struct utsname uts_buf
;
1502 gboolean has_selinux
, enforcing
, a11y
;
1504 g_return_if_fail (ui
!= NULL
);
1506 system_info
= g_string_new ("");
1508 if (uname (&uts_buf
) == 0) {
1509 g_string_append_printf (system_info
, "System: %s %s %s %s\n", uts_buf
.sysname
, uts_buf
.release
, uts_buf
.version
, uts_buf
.machine
);
1512 /* X server checks */
1513 g_string_append_printf (system_info
, "X Vendor: %s\n", ServerVendor (gdk_display
));
1514 g_string_append_printf (system_info
, "X Vendor Release: %d\n", VendorRelease (gdk_display
));
1517 /* Selinux checks */
1518 has_selinux
= FALSE
;
1519 if (g_file_get_contents ("/proc/filesystems", &str
, NULL
, NULL
)) {
1520 has_selinux
= strstr (str
, "selinuxfs") != NULL
;
1525 if (g_file_get_contents ("/selinux/enforce", &str
, NULL
, NULL
)) {
1526 enforcing
= strcmp (str
, "0") != 0;
1529 g_string_append_printf (system_info
, "Selinux: %s\n", enforcing
?"Enforcing":"Permissive");
1531 g_string_append_printf (system_info
, "Selinux: No\n");
1535 gconf_client
= gconf_client_get_default ();
1536 a11y
= gconf_client_get_bool (gconf_client
, ACCESSIBILITY_KEY
, NULL
);
1537 g_string_append_printf (system_info
, "Accessibility: %s\n", a11y
?"Enabled":"Disabled");
1538 str
= gconf_client_get_string (gconf_client
, GTK_THEME_KEY
, NULL
);
1539 g_string_append_printf (system_info
, "GTK+ Theme: %s\n", str
);
1541 str
= gconf_client_get_string (gconf_client
, ICON_THEME_KEY
, NULL
);
1542 g_string_append_printf (system_info
, "Icon Theme: %s\n", str
);
1544 g_object_unref (gconf_client
);
1546 g_string_append (system_info
, "\n");
1548 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1549 buffer
= gtk_text_view_get_buffer (text_view
);
1550 gtk_text_buffer_get_end_iter (buffer
, &end
);
1551 gtk_text_buffer_insert (buffer
, &end
, system_info
->str
, system_info
->len
);
1553 g_string_free (system_info
, TRUE
);
1557 fill_stderr_info (GtkBuilder
*ui
)
1559 GtkTextView
*text_view
;
1561 GtkTextBuffer
*buffer
;
1562 GString
*stderr_info
;
1567 g_return_if_fail (ui
!= NULL
);
1569 stderr_info
= g_string_new ("");
1571 /* .xsession-errors: read file */
1572 file
= g_build_filename (g_get_home_dir (), ".xsession-errors", NULL
);
1573 if (g_file_get_contents (file
, &str
, NULL
, NULL
)) {
1574 lines
= g_strsplit (str
, "\n", -1);
1577 while (lines
[n_lines
] != NULL
) {
1583 char *mtime_age
= NULL
;
1587 if (stat (file
, &buf
) == 0) {
1588 age
= time (NULL
) - buf
.st_mtime
;
1590 mtime_age
= g_strdup_printf (" (%d sec old)", (int) age
);
1594 g_string_append_printf (stderr_info
,
1595 "\n\n----------- .xsession-errors%s ---------------------\n",
1596 mtime_age
?mtime_age
:"");
1600 for (i
= MAX (0, n_lines
-16); i
< n_lines
; i
++) {
1601 if (lines
[i
][0] != 0) {
1602 /* Limit line length to 200 chars to avoid excessive data */
1603 if (strlen (lines
[i
]) > 200) {
1607 g_string_append_printf (stderr_info
, "%s\n", lines
[i
]);
1611 g_string_append (stderr_info
, "--------------------------------------------------\n");
1617 text_view
= GTK_TEXT_VIEW (gtk_builder_get_object (ui
, "gdb-text"));
1618 buffer
= gtk_text_view_get_buffer (text_view
);
1619 gtk_text_buffer_get_end_iter (buffer
, &end
);
1620 gtk_text_buffer_insert (buffer
, &end
, stderr_info
->str
, stderr_info
->len
);
1622 g_string_free (stderr_info
, TRUE
);
1627 load_icon (const char *icon
)
1629 GdkPixbuf
*pixbuf
= NULL
;
1631 if (g_path_is_absolute (icon
)) {
1632 pixbuf
= gdk_pixbuf_new_from_file_at_size (icon
, 48, 48, NULL
);
1634 if (pixbuf
== NULL
) {
1635 pixbuf
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
1638 if (pixbuf
== NULL
&& strrchr (icon
, '.') != NULL
) {
1640 name
= g_strndup (icon
, strlen (icon
) - strlen (strrchr (icon
, '.')));
1641 pixbuf
= gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
1646 if (pixbuf
== NULL
) {
1648 filename
= g_strdup_printf (BUDDY_ICONDIR
"/pixmaps/%s", icon
);
1649 pixbuf
= gdk_pixbuf_new_from_file_at_size (filename
, 48, 48, NULL
);
1658 main (int argc
, char *argv
[])
1661 BugzillaApplication
*app
;
1662 GnomeVersionInfo
*gnome_version
;
1664 GtkWidget
*main_window
;
1665 GOptionContext
*context
;
1666 GnomeProgram
*program
;
1669 GtkBuilder
*ui
= NULL
;
1671 memset (&gopt_data
, 0, sizeof (gopt_data
));
1673 bindtextdomain (PACKAGE
, GNOMELOCALEDIR
);
1674 bind_textdomain_codeset (PACKAGE
, "UTF-8");
1675 textdomain (PACKAGE
);
1677 context
= g_option_context_new (N_("\n\nBug Buddy is a utility that helps report debugging\n"
1678 "information to the GNOME Bugzilla when a program crashes."));
1680 g_option_context_set_translation_domain (context
, GETTEXT_PACKAGE
);
1682 g_option_context_add_main_entries (context
, options
, GETTEXT_PACKAGE
);
1684 program
= gnome_program_init (PACKAGE
, VERSION
,
1687 GNOME_PARAM_GOPTION_CONTEXT
, context
,
1688 GNOME_PARAM_APP_DATADIR
, BUDDY_DATADIR
,
1689 GNOME_CLIENT_PARAM_SM_CONNECT
, FALSE
,
1692 g_set_application_name (_("Bug Buddy"));
1693 gtk_window_set_default_icon_name ("bug-buddy");
1696 s
= gnome_program_locate_file (NULL
, GNOME_FILE_DOMAIN_APP_DATADIR
,
1697 "bug-buddy.gtkbuilder", TRUE
, NULL
);
1699 ui
= gtk_builder_new ();
1700 gtk_builder_add_from_file (ui
, s
, &err
);
1701 gtk_builder_set_translation_domain (ui
, GETTEXT_PACKAGE
);
1706 _("Bug Buddy could not load its user interface file.\n"
1707 "Please make sure Bug Buddy was installed correctly."));
1708 g_object_unref (program
);
1713 main_window
= GTK_WIDGET (gtk_builder_get_object (ui
, "main-window"));
1714 g_signal_connect (main_window
, "delete-event", G_CALLBACK (delete_callback
), ui
);
1716 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1718 progress
= g_timeout_add (100, update_progress_bar
,
1719 gtk_builder_get_object (ui
, "progressbar"));
1720 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
1721 _("Collecting information from your system..."));
1723 if (gopt_data
.app_file
== NULL
&& gopt_data
.package
== NULL
) {
1724 buddy_error (NULL
, _("Either --appname or --package arguments are required.\n"));
1725 g_object_unref (program
);
1729 if (gopt_data
.app_file
&& gopt_data
.pid
== 0 &&
1730 gopt_data
.include_file
== NULL
&&
1731 gopt_data
.minidump_file
== NULL
) {
1732 buddy_error (NULL
, _("Either --pid , --include or --minidump arguments are required.\n"));
1733 g_object_unref (program
);
1737 /* get some information about the gnome version */
1738 gnome_version
= get_gnome_version_info ();
1739 if (gnome_version
== NULL
) {
1740 buddy_error (NULL
, _("Bug Buddy was unable to retrieve information regarding "
1741 "the version of GNOME you are running. This is most likely "
1742 "due to a missing installation of gnome-desktop.\n"));
1743 g_object_unref (program
);
1747 g_object_set_data (G_OBJECT (ui
), "gnome-version", gnome_version
);
1749 /* connect the signal handler for the help button */
1750 g_signal_connect (gtk_builder_get_object (ui
, "help-button"), "clicked",
1751 G_CALLBACK (help_callback
), program
);
1753 gtk_widget_show (main_window
);
1755 apps
= load_applications ();
1757 /* If we have a binary file it is a crash */
1758 if (gopt_data
.app_file
) {
1759 app
= g_hash_table_lookup (apps
, gopt_data
.app_file
);
1761 /* we handle an unknown application (no .desktop file) differently */
1763 s
= g_markup_printf_escaped (_("The %s application has crashed. "
1764 "We are collecting information about the crash to send to the "
1765 "developers in order to fix the problem."), app
->name
);
1766 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), s
);
1769 g_object_set_data (G_OBJECT (ui
), "app", app
);
1773 pixbuf
= load_icon (app
->icon
);
1775 gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_builder_get_object (ui
, "app-image")),
1777 g_object_unref(pixbuf
);
1780 fill_gnome_info (app
, gnome_version
, ui
);
1783 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (gtk_builder_get_object (ui
, "progressbar")),
1784 _("Collecting information from the crash..."));
1786 fill_system_info (ui
);
1788 fill_proccess_info (gopt_data
.pid
, ui
);
1790 if (gopt_data
.pid
> 0) {
1791 /* again, if this is an unknown application, we connect a different callback that
1792 * will allow the user to save the trace rather than sending it to the GNOME Bugzilla */
1794 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, ui
,
1795 gdb_finished_unknown_app
, &err
);
1797 source_id
= gdb_get_trace (gopt_data
.app_file
, gopt_data
.pid
, ui
, gdb_finished
, &err
);
1800 if (source_id
== 0) {
1801 buddy_error (NULL
, _("Bug Buddy encountered the following error when trying "
1802 "to retrieve debugging information: %s\n"), err
->message
);
1804 g_object_unref (program
);
1808 /* connect the close button callback so that we can remove the source from
1809 * the main loop (and kill gdb) if the user wants to quit before gdb is finished */
1810 g_object_set_data (G_OBJECT (ui
), "sourceid", GUINT_TO_POINTER (source_id
));
1812 if (gopt_data
.minidump_file
!= NULL
) {
1814 } else if (app
== NULL
) {
1815 unknown_app_finished (ui
);
1817 known_app_finished (ui
);
1820 g_object_set_data (G_OBJECT (ui
), "type", GINT_TO_POINTER(BUG_TYPE_CRASH
));
1822 /* No binary file, so this is a non-crashing bug. Look the application from the --package arg */
1823 GtkWidget
*email_entry
;
1824 char *default_email
;
1825 char *msg
, *lang_note
;
1826 const char *en_lang_note
= N_("\n\nPlease write your report in English, if possible.");
1828 app
= g_hash_table_find (apps
, (GHRFunc
)bugzilla_search_for_package
, gopt_data
.package
);
1830 /* Fallback to binary name */
1831 app
= g_hash_table_lookup (apps
, gopt_data
.package
);
1834 buddy_error (NULL
, _("Bug Buddy doesn't know how to send a suggestion for the application %s.\n"),
1839 g_object_set_data (G_OBJECT (ui
), "app", app
);
1844 pixbuf
= load_icon (app
->icon
);
1846 gtk_image_set_from_pixbuf (GTK_IMAGE (gtk_builder_get_object (ui
, "app-image")),
1848 g_object_unref (pixbuf
);
1851 fill_gnome_info (app
, gnome_version
, ui
);
1852 fill_custom_info (app
, ui
);
1854 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "final-box")));
1855 gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (ui
, "send-button")));
1856 gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (ui
, "progressbar")));
1858 lang_note
= gettext (en_lang_note
);
1859 msg
= g_strconcat (_("Thank you for helping us improving our software.\n"
1860 "Please fill your suggestions/error information for %s application.\n\n"
1861 "A valid email address is required. This will allow developers to "
1862 "contact you for more information if necessary."),
1863 strcmp (lang_note
, en_lang_note
)? lang_note
: NULL
,
1865 s
= g_markup_printf_escaped (msg
, app
->name
);
1866 gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (ui
, "main-text")), s
);
1870 s
= g_markup_printf_escaped ("<span weight=\"bold\">%s</span>",
1871 _("Suggestion / Error description:"));
1872 gtk_label_set_markup (GTK_LABEL (gtk_builder_get_object (ui
, "main-label")), s
);
1875 show_pending_checkbox_if_pending (ui
);
1876 g_signal_connect (gtk_builder_get_object (ui
, "send-button"), "clicked",
1877 G_CALLBACK (on_send_clicked
), ui
);
1879 email_entry
= GTK_WIDGET (gtk_builder_get_object (ui
, "email-entry"));
1880 g_signal_connect (email_entry
, "changed", G_CALLBACK (check_email
), ui
);
1882 default_email
= get_default_user_email ();
1884 if (default_email
!= NULL
) {
1885 gtk_entry_set_text (GTK_ENTRY (email_entry
), default_email
);
1886 g_free (default_email
);
1888 g_object_set_data (G_OBJECT (ui
), "type", GINT_TO_POINTER(BUG_TYPE_REQUEST
));
1891 if (gopt_data
.include_file
!= NULL
) {
1892 fill_include_file (gopt_data
.include_file
, ui
);
1896 g_signal_connect (gtk_builder_get_object (ui
, "close-button"), "clicked",
1897 G_CALLBACK (close_callback
), ui
);
1901 g_object_unref (program
);