[l10n] Updated Estonian translation
[bug-buddy.git] / src / bugzilla.c
blobd2dec25f2e9045b9f885fc34ff472bbd0f133806
1 /* bug-buddy bug submitting program
3 * Copyright (C) 2001 Jacob Berkman
4 * Copyright 2001 Ximian, Inc.
6 * Author: jacob berkman <jacob@bug-buddy.org>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public
10 * License as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22 #include <config.h>
24 #include "bug-buddy.h"
25 #include "distribution.h"
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <utime.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <ctype.h>
35 #include <glib/gi18n.h>
37 #include <bonobo/bonobo-exception.h>
38 #include <bonobo-activation/bonobo-activation.h>
40 #include <libxml/tree.h>
41 #include <libxml/parser.h>
42 #include <libxml/xmlmemory.h>
44 #include <libsoup/soup.h>
47 #define APPLET_REQUIREMENTS \
48 "has_all (repo_ids, ['IDL:Bonobo/Control:1.0'," \
49 " 'IDL:GNOME/Vertigo/PanelAppletShell:1.0']) && " \
50 "defined (panel:icon)"
52 #define DESKTOP_ENTRY "Desktop Entry"
53 #define DESKTOP_NAME "Name"
54 #define DESKTOP_COMMENT "Comment"
55 #define DESKTOP_ICON "Icon"
56 #define DESKTOP_EXEC "Exec"
58 #define BUGZILLA_BUGZILLA "X-GNOME-Bugzilla-Bugzilla"
59 #define BUGZILLA_PRODUCT "X-GNOME-Bugzilla-Product"
60 #define BUGZILLA_COMPONENT "X-GNOME-Bugzilla-Component"
61 #define BUGZILLA_EMAIL "X-GNOME-Bugzilla-Email"
62 #define BUGZILLA_VERSION "X-GNOME-Bugzilla-Version"
63 #define BUGZILLA_OTHER_BINARIES "X-GNOME-Bugzilla-OtherBinaries"
64 #define BUGZILLA_EXTRA_INFO_SCRIPT "X-GNOME-Bugzilla-ExtraInfoScript"
66 #define BA_BUGZILLA_BUGZILLA "bugzilla:bugzilla"
67 #define BA_BUGZILLA_PRODUCT "bugzilla:product"
68 #define BA_BUGZILLA_COMPONENT "bugzilla:component"
69 #define BA_BUGZILLA_VERSION "bugzilla:version"
70 #define BA_BUGZILLA_OTHER_BINARIES "bugzilla:other_binaries"
71 #define BA_BUGZILLA_EXTRA_INFO_SCRIPT "bugzilla:extra_info_script"
73 static void
74 add_bugzilla_application (GHashTable *hash,
75 const char *name,
76 const char *cname,
77 const char *comment,
78 const char *bugzilla,
79 const char *product,
80 const char *component,
81 const char *version,
82 const char *icon,
83 const char *program,
84 const char *other_programs,
85 const char *extra_info_script)
87 BugzillaApplication *app;
88 char **programv;
89 int i;
91 app = g_new0 (BugzillaApplication, 1);
93 app->name = g_strdup (name);
94 app->cname = g_strdup (cname);
95 app->comment = g_strdup (comment);
96 app->icon = g_strdup (icon);
97 app->bugzilla = g_strdup (bugzilla);
98 app->product = g_strdup (product);
99 app->component = g_strdup (component);
100 app->version = g_strdup (version);
101 app->extra_info_script = g_strdup (extra_info_script);
103 if (program) {
104 g_shell_parse_argv (program, &i, &programv, NULL);
105 if (programv[0]) {
106 char *s;
107 s = strrchr (programv[0], G_DIR_SEPARATOR);
108 s = s ? s+1 : programv[0];
109 app->ref_count += 1;
110 g_hash_table_insert (hash, g_strdup (s), app);
111 } else {
112 g_free (app);
113 return;
115 if (programv)
116 g_strfreev (programv);
119 if (other_programs) {
120 programv = g_strsplit (other_programs, ";", -1);
121 for (i=0; programv[i]; i++) {
122 app->ref_count += 1;
123 g_hash_table_insert (hash, g_strdup (programv[i]), app);
125 g_strfreev (programv);
129 static void
130 application_free (BugzillaApplication *app)
132 app->ref_count -= 1;
133 if (app->ref_count > 0)
134 return;
136 g_free (app->name);
137 g_free (app->cname);
138 g_free (app->comment);
139 g_free (app->icon);
140 g_free (app->bugzilla);
141 g_free (app->product);
142 g_free (app->component);
143 g_free (app->version);
144 g_free (app->extra_info_script);
145 g_free (app);
151 static const GSList *
152 get_i18n_slist (void)
154 const char * const *langs;
155 guint i;
156 static GSList *langs_gslist = NULL;
158 if (langs_gslist)
159 return langs_gslist;
161 langs = g_get_language_names ();
162 for (i = 0; langs[i] != 0; ++i) {
163 langs_gslist = g_slist_append (langs_gslist, (gpointer) langs[i]);
166 return langs_gslist;
169 static void
170 load_applets (GHashTable *hash)
172 Bonobo_ServerInfoList *info_list;
173 Bonobo_ServerInfo *info;
174 CORBA_Environment ev;
175 GSList *langs;
176 int i;
177 gchar *name;
179 CORBA_exception_init (&ev);
180 info_list = bonobo_activation_query (APPLET_REQUIREMENTS, NULL, &ev);
181 if (BONOBO_EX (&ev)) {
182 g_warning ("Applet list query failed: %s", BONOBO_EX_REPOID (&ev));
183 CORBA_exception_free (&ev);
184 return;
186 CORBA_exception_free (&ev);
188 langs = (GSList *)get_i18n_slist ();
190 for (i = 0; i < info_list->_length; i++) {
191 info = info_list->_buffer + i;
192 if (!bonobo_server_info_prop_lookup (info,
193 BA_BUGZILLA_BUGZILLA,
194 NULL)) {
195 continue;
198 name = g_strdup (bonobo_server_info_prop_lookup (info, "name", langs));
199 /*FIXME:
200 for (l = applications; l; l = l->next) {
201 BugzillaApplication *app = l->data;
203 if (strcmp (app->name, name) == 0) {
204 g_free (name);
205 name = g_strdup_printf (_("%s (Panel Applet)"), bonobo_server_info_prop_lookup (info, "name", langs));
207 break;
211 add_bugzilla_application (hash,
212 name,
213 bonobo_server_info_prop_lookup (info, "name", NULL),
214 bonobo_server_info_prop_lookup (info, "description", langs),
215 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_BUGZILLA, NULL),
216 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_PRODUCT, NULL),
217 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_COMPONENT, NULL),
218 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_VERSION, NULL),
219 bonobo_server_info_prop_lookup (info, "panel:icon", NULL),
220 NULL,
221 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_OTHER_BINARIES, NULL),
222 bonobo_server_info_prop_lookup (info, BA_BUGZILLA_EXTRA_INFO_SCRIPT, NULL));
224 g_free (name);
227 CORBA_free (info_list);
230 static void
231 add_bugzilla_application_from_desktop_file (GHashTable *hash,
232 const char *desktop_file_path)
234 GKeyFile *key_file;
235 GError *error = NULL;
236 char *name;
237 char *cname;
238 char *comment;
239 char *bugzilla;
240 char *product;
241 char *component;
242 char *version;
243 char *icon;
244 char *exec;
245 char *other_binaries;
246 char *extra_info_script;
248 key_file = g_key_file_new ();
249 g_key_file_load_from_file (key_file, desktop_file_path,
250 G_KEY_FILE_NONE, &error);
251 if (error) {
252 g_warning ("Couldn't load %s: %s", desktop_file_path,
253 error->message);
254 g_error_free (error);
255 error = NULL;
256 return;
259 if (!g_key_file_has_group (key_file, DESKTOP_ENTRY) ||
260 !g_key_file_has_key (key_file, DESKTOP_ENTRY, BUGZILLA_BUGZILLA, &error)) {
261 g_key_file_free (key_file);
262 if (error)
263 g_error_free (error);
264 return;
267 name = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY, DESKTOP_NAME, NULL, NULL);
268 cname = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_NAME, NULL);
269 comment = g_key_file_get_locale_string (key_file, DESKTOP_ENTRY, DESKTOP_COMMENT, NULL, NULL);
270 bugzilla = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_BUGZILLA, NULL);
271 product = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_PRODUCT, NULL);
272 component = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_COMPONENT, NULL);
273 version = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_VERSION, NULL);
274 icon = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_ICON, NULL);
275 exec = g_key_file_get_string (key_file, DESKTOP_ENTRY, DESKTOP_EXEC, NULL);
276 other_binaries = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_OTHER_BINARIES, NULL);
277 extra_info_script = g_key_file_get_string (key_file, DESKTOP_ENTRY, BUGZILLA_EXTRA_INFO_SCRIPT, NULL);
279 add_bugzilla_application (hash,
280 name,
281 cname,
282 comment,
283 bugzilla,
284 product,
285 component,
286 version,
287 icon,
288 exec,
289 other_binaries,
290 extra_info_script);
291 g_free (name);
292 g_free (cname);
293 g_free (comment);
294 g_free (bugzilla);
295 g_free (product);
296 g_free (component);
297 g_free (version);
298 g_free (icon);
299 g_free (exec);
300 g_free (other_binaries);
301 g_free (extra_info_script);
302 g_key_file_free (key_file);
305 static void
306 load_applications_from_dir (GHashTable *hash,
307 const char *path)
309 GDir *dir;
310 char *name;
311 char *desktop_file;
313 dir = g_dir_open (path, 0, NULL);
314 if (!dir)
315 return;
317 while ((name = g_dir_read_name (dir))) {
318 if (g_main_context_pending (NULL)) {
319 g_main_context_iteration (NULL, FALSE);
322 if (!g_str_has_suffix (name, ".desktop")) {
323 continue;
326 desktop_file = g_build_filename (path, name, NULL);
327 add_bugzilla_application_from_desktop_file (hash, desktop_file);
328 g_free (desktop_file);
331 g_dir_close (dir);
334 static char **
335 get_all_directories (void)
337 GPtrArray *dirs;
338 const char * const *system_config_dirs;
339 const char * const *system_data_dirs;
340 int i;
342 dirs = g_ptr_array_new ();
344 system_data_dirs = g_get_system_data_dirs ();
345 for (i = 0; system_data_dirs[i]; i++) {
346 g_ptr_array_add (dirs,
347 g_build_filename (system_data_dirs[i],
348 "applications", NULL));
349 g_ptr_array_add (dirs,
350 g_build_filename (system_data_dirs[i],
351 "gnome", "autostart", NULL));
354 system_config_dirs = g_get_system_config_dirs ();
355 for (i = 0; system_config_dirs[i]; i++) {
356 g_ptr_array_add (dirs,
357 g_build_filename (system_config_dirs[i],
358 "autostart", NULL));
361 g_ptr_array_add (dirs, NULL);
363 return (char **) g_ptr_array_free (dirs, FALSE);
366 GQuark
367 bugzilla_error_quark (void)
369 return g_quark_from_static_string ("bugzilla_error");
372 GHashTable *
373 load_applications (void)
375 char **directories;
376 int i;
378 GHashTable *program_to_application = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) application_free);
380 directories = get_all_directories ();
381 for (i = 0; directories[i] != NULL; i++)
382 load_applications_from_dir (program_to_application, directories[i]);
383 g_strfreev (directories);
385 load_applets (program_to_application);
387 return program_to_application;
390 gboolean
391 bugzilla_search_for_package (gpointer key, gpointer value, const char *package)
393 BugzillaApplication *app = (BugzillaApplication*) value;
395 if (!strcmp (app->product, package))
396 return TRUE;
398 return FALSE;
402 char *
403 bugzilla_parse_response (SoupMessage *msg, GError **err)
405 GValue value;
406 int bugid = 0;
407 char *url = NULL;
409 g_return_val_if_fail ((err == NULL || *err == NULL), NULL);
411 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
412 g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_BAD_STATUS,
413 _("HTTP Response returned bad status code %d"), msg->status_code);
414 return NULL;
417 if (!soup_xmlrpc_parse_method_response (msg->response_body->data,
418 msg->response_body->length,
419 &value, err))
420 return NULL;
422 if (G_VALUE_HOLDS_INT (&value))
423 bugid = g_value_get_int (&value);
424 else if (G_VALUE_HOLDS_STRING (&value))
425 url = g_value_dup_string (&value);
426 else {
427 g_value_unset (&value);
428 g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_RECV_PARSE_FAILED,
429 _("Unable to parse XML-RPC response\n\n%s"),
430 msg->response_body->data);
431 return NULL;
433 g_value_unset (&value);
435 return bugid ? g_strdup_printf ("%d", bugid) : url;
438 SoupMessage*
439 bugzilla_create_report (BugzillaApplication *app, int type, GnomeVersionInfo *gnome_version,
440 const char *username, const char *title, const char *text,
441 GtkBuilder *ui, GError **err)
443 SoupMessage *message;
444 const char *uri;
445 GHashTable *report;
446 char *user_agent;
447 char *os_version;
448 const gchar *crt;
449 GString *rv;
451 g_return_val_if_fail (app != NULL, NULL);
452 g_return_val_if_fail (gnome_version != NULL, NULL);
453 g_return_val_if_fail (username != NULL, NULL);
454 g_return_val_if_fail (text != NULL, NULL);
455 g_return_val_if_fail (ui != NULL, NULL);
456 g_return_val_if_fail (err == NULL || *err == NULL, NULL);
458 /* FIXME: Hardcoded right now */
459 if (app->bugzilla == NULL || strcmp (app->bugzilla, "GNOME") != 0) {
460 g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP,
461 _("Application does not track its bugs in the GNOME Bugzilla."));
462 return NULL;
465 if (!app->product || !app->component) {
466 g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_NOTSUPPORTED_APP,
467 _("Product or component not specified."));
468 return NULL;
471 report = soup_value_hash_new ();
473 soup_value_hash_insert (report, "version", G_TYPE_STRING,
474 app->version ? app->version : "unspecified");
475 soup_value_hash_insert (report, "product", G_TYPE_STRING, app->product);
476 soup_value_hash_insert (report, "component", G_TYPE_STRING, app->component);
477 soup_value_hash_insert (report, "gnome_version", G_TYPE_STRING,
478 gnome_version->gnome_platform);
479 soup_value_hash_insert (report, "reporter", G_TYPE_STRING, username);
481 os_version = get_distro_name ();
482 soup_value_hash_insert (report, "os_version", G_TYPE_STRING, os_version);
483 g_free (os_version);
485 if (type == BUG_TYPE_CRASH) {
486 soup_value_hash_insert (report, "priority", G_TYPE_STRING, "High");
487 soup_value_hash_insert (report, "bug_severity", G_TYPE_STRING, "critical");
490 soup_value_hash_insert (report, "short_desc", G_TYPE_STRING, title);
492 /* Skip UTF-8 control chars that are not valid in XML.*/
493 rv = g_string_new (NULL);
494 crt = text;
495 while (*crt) {
496 gchar *next = g_utf8_next_char (crt);
497 gunichar uni = g_utf8_get_char (crt);
499 if (!g_unichar_iscntrl (uni) || (uni == '\n') || (uni == '\t'))
500 g_string_append_len (rv, crt, next - crt);
502 crt = next;
505 soup_value_hash_insert (report, "comment", G_TYPE_STRING, rv->str);
506 g_string_free (rv, TRUE);
508 uri = "http://bugzilla.gnome.org/bugbuddy.cgi";
510 message = soup_xmlrpc_request_new (uri, "BugBuddy.createBug",
511 G_TYPE_HASH_TABLE, report,
512 G_TYPE_INVALID);
513 g_hash_table_destroy (report);
514 if (message == NULL) {
515 g_set_error (err, BUGZILLA_ERROR, BUGZILLA_ERROR_SEND_ERROR,
516 _("Unable to create XML-RPC message."));
517 return NULL;
520 /* FIXME: wrong User-Agent syntax. Should be "Bug-Buddy (VERSION)" */
521 user_agent = g_strdup_printf ("Bug-Buddy: %s", VERSION);
522 soup_message_headers_append (message->request_headers,
523 "User-Agent", user_agent);
524 g_free (user_agent);
526 return message;