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