Remove unneeded includes from ui folder
[metze/wireshark/wip.git] / ui / gtk / webbrowser.c
1 /* The GIMP -- an image manipulation program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * Web Browser Plug-in
5  * Copyright (C) 2003  Henrik Brix Andersen <brix@gimp.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
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.
16  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 /* Wireshark - this file is copied from "The GIMP" V2.0.2
23  * You will find the original file in the gimp distribution zip under:
24  * \plug-ins\common\webbrowser.c
25  *
26  * It was modified to suit the Wireshark environment (#if 0)!
27  *
28  * For the UNIX+X11 launcher, see this blog post:
29  *
30  * http://blogs.gnome.org/timj/2006/11/24/24112006-how-to-start-a-web-browser/
31  *
32  * for a discussion of how Beast launches a browser, a link that shows
33  * the rather complicated code it uses, and some information on why it
34  * goes through all that pain.  See also Kevin Krammer's comment, which
35  * notes that the problem might be that the GNOME, KDE, and XFCE
36  * launcher programs always cause the window to be opened in the background,
37  * regardless of whether an instance of the app is running or not (the
38  * app gets launched - in the background - if it's not already running,
39  * and is told to open a new window/tab if it's already running), while
40  * launchers such as sensible-browser, which xdg-open falls back to,
41  * launch the app in the foreground if it's not already running, leading
42  * to the "first window is in the foreground, subsequent windows are in
43  * the background" behavior in non-GNOME/KDE/XFCE environments.
44  *
45  * What Qt's "generic UNIX" openURL does is:
46  *
47  *      if it's a mailto: URL, use the "document lanuncher" if
48  *      known, otherwise, use the results of detectWebBrowser
49  *      as the "document launcher", and use that;
50  *
51  *      otherwise, use the "Web browser" if known, otherwise, use
52  *      the results of detectWebBrowser as the "document launcher",
53  *      and use that.
54  *
55  * detectWebBrowser:
56  *
57  *      looks for xdg-open and, if it finds it, uses that;
58  *
59  *      otherwise, if the DEFAULT_BROWSER or BROWSER environment
60  *      variable is set, use the first of those that's set (in
61  *      that order) and, if that's an executable, uses that;
62  *
63  *      otherwise, if the desktop environment is detected to be
64  *      KDE, uses kfmclient;
65  *
66  *      otherwise, if the desktop environment is detected to
67  *      be GNOME, uses gnome-open;
68  *
69  *      otherwise, tries, in order, google-chrome, firefox,
70  *      mozilla, and opera.
71  *
72  * (Its Windows openURL uses ShellExecute() on non-mailto URLs (it
73  * does more exotic stuff for mailto: URLs).
74  *
75  * Its OS X stuff uses the openURL method of an NSWorkspace (which
76  * probably ends up in Launch Services....).)
77  *
78  * GTK+ has gtk_show_uri(), but that ultimately uses gvfs on UN*X,
79  * so it's not appropriate for non-GNOME UN*Xes (including, but not
80  * limited to, OS X), and ultimately appears to be a stubbed-out
81  * routine in GLib 2.36.0, so it's not very useful for a cross-
82  * platform applicatio n.
83  *
84  * Perhaps the right strategy is to:
85  *
86  *      Check whether we're in a GNOME/KDE/XFCE session and, if
87  *      we are, try xdg-open, as it works around, among other things,
88  *      some kfmclient bugs, and run it synchronously (that will fail
89  *      if we detect a GNOME/KDE/XFCE session but the launcher is
90  *      missing, but so it goes).  If we don't have xdg-open, try
91  *      the appropriate launcher for the environment, but ignore
92  *      the return code from kfmclient, as it might be bogus (that's
93  *      the bug xdg-open works around).
94  *
95  *      Otherwise, try the "broken/unpredictable browser launchers",
96  *      but run them in the background and leave them running, and
97  *      ignore the exit code, and then try x-www-browser, and then
98  *      try directly launching a user-specified browser.  (Beast tries
99  *      a bunch of browsers, with the user not being allowed to
100  *      specify which one they want.)
101  *
102  * On the other hand, see bug 2699, in which xdg-open is itself buggy.
103  */
104
105 #include "config.h"
106 #include <string.h> /* strlen, strstr */
107
108 #include <gtk/gtk.h>
109
110 #include "ui/simple_dialog.h"
111 #include "ui/help_url.h"
112
113 #include "ui/gtk/webbrowser.h"
114
115 #if defined(G_OS_WIN32)
116 /* Win32 - use Windows shell services to start a browser */
117 #include <windows.h>
118 /* We're using Unicode */
119 #include <tchar.h>
120 #include <wsutil/unicode-utils.h>
121 /* if WIN32_LEAN_AND_MEAN is defined, shellapi.h is needed too */
122 #include <shellapi.h>
123 #elif defined (HAVE_OS_X_FRAMEWORKS)
124 /* Mac OS X - use Launch Services to start a browser */
125 #include <CoreFoundation/CoreFoundation.h>
126 #include <ApplicationServices/ApplicationServices.h>
127 #elif defined(HAVE_XDG_OPEN)
128 /* UNIX+X11 desktop with Portland Group stuff - use xdg-open to start a browser */
129 #else
130 /* Everything else - launch the browser ourselves */
131 #define MUST_LAUNCH_BROWSER_OURSELVES
132 #endif
133
134 #ifdef MUST_LAUNCH_BROWSER_OURSELVES
135 static gchar*   strreplace       (const gchar      *string,
136                                   const gchar      *delimiter,
137                                   const gchar      *replacement);
138 #endif
139
140 gboolean
141 browser_needs_pref(void)
142 {
143 #ifdef MUST_LAUNCH_BROWSER_OURSELVES
144     return TRUE;
145 #else
146     return FALSE;
147 #endif
148 }
149
150
151 gboolean
152 browser_open_url (const gchar *url)
153 {
154 #if defined(G_OS_WIN32)
155
156   return ((gint) ShellExecute (HWND_DESKTOP, _T("open"), utf_8to16(url), NULL, NULL, SW_SHOWNORMAL) > 32);
157
158 #elif defined(HAVE_OS_X_FRAMEWORKS)
159
160   CFStringRef url_CFString;
161   CFURLRef url_CFURL;
162   OSStatus status;
163
164   /*
165    * XXX - if URLs passed to "browser_open_url()" contain non-ASCII
166    * characters, we'd have to choose an appropriate value from the
167    * CFStringEncodings enum.
168    */
169   url_CFString = CFStringCreateWithCString(NULL, url, kCFStringEncodingASCII);
170   if (url_CFString == NULL)
171     return (FALSE);
172   url_CFURL = CFURLCreateWithString(NULL, url_CFString, NULL);
173   CFRelease(url_CFString);
174   if (url_CFURL == NULL) {
175     /*
176      * XXX - this could mean that the url_CFString wasn't a valid URL,
177      * or that memory allocation failed.  We can't determine which,
178      * except perhaps by providing our own allocator and somehow
179      * flagging allocation failures.
180      */
181     return (FALSE);
182   }
183   /*
184    * XXX - this is a Launch Services result code, and we should probably
185    * display a dialog box if it's not 0, describing what the error was.
186    * Then again, we should probably do the same for the ShellExecute call,
187    * unless that call itself happens to pop up a dialog box for all errors.
188    */
189   status = LSOpenCFURLRef(url_CFURL, NULL);
190   CFRelease(url_CFURL);
191   return (status == 0);
192
193 #elif defined(HAVE_XDG_OPEN)
194
195   GError      *error = NULL;
196   const gchar *argv[3];
197   gboolean     retval;
198
199   g_return_val_if_fail (url != NULL, FALSE);
200
201   argv[0] = "xdg-open";
202   argv[1] = url;
203   argv[2] = NULL;
204
205   /*
206    * XXX - use g_spawn_on_screen() so the browser window shows up on
207    * the same screen?
208    *
209    * Also, g_spawn_async() shouldn't modify argv but takes it as non-const!
210    */
211   retval = g_spawn_async (NULL, (gchar**) argv, NULL,
212                           G_SPAWN_SEARCH_PATH,
213                           NULL, NULL,
214                           NULL, &error);
215
216   if (! retval)
217     {
218       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
219           "%sCould not execute xdg-open: %s\n\n\"%s\"",
220           simple_dialog_primary_start(), simple_dialog_primary_end(),
221           error->message);
222       g_error_free (error);
223     }
224
225   return retval;
226
227 #elif defined(MUST_LAUNCH_BROWSER_OURSELVES)
228
229   GError    *error = NULL;
230   gchar     *browser;
231   gchar     *argument;
232   gchar     *cmd;
233   gchar    **argv;
234   gboolean   retval;
235
236   g_return_val_if_fail (url != NULL, FALSE);
237
238   /*  browser = gimp_gimprc_query ("web-browser");*/
239   browser = g_strdup(prefs.gui_webbrowser);
240
241   if (browser == NULL || ! strlen (browser))
242     {
243       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
244           "Web browser not specified.\n"
245           "Please correct the web browser setting in the Preferences dialog.\n"
246           "URL: %s", url);
247       g_free (browser);
248       return FALSE;
249     }
250
251   /* quote the url since it might contains special chars */
252   argument = g_shell_quote (url);
253
254   /* replace %s with URL */
255   if (strstr (browser, "%s"))
256     cmd = strreplace (browser, "%s", argument);
257   else
258     cmd = g_strconcat (browser, " ", argument, NULL);
259
260   g_free (argument);
261
262   /* parse the cmd line */
263   if (! g_shell_parse_argv (cmd, NULL, &argv, &error))
264     {
265       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
266           "%sCould not parse web browser command: \"%s\"%s\n\n\"%s\"\n\n%s",
267           simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
268           error->message,
269           "Please correct the web browser setting in the Preferences dialog.");
270       g_error_free (error);
271       return FALSE;
272     }
273
274   /*
275    * XXX - use g_spawn_on_screen() so the browser window shows up on
276    * the same screen?
277    */
278   retval = g_spawn_async (NULL, argv, NULL,
279                           G_SPAWN_SEARCH_PATH,
280                           NULL, NULL,
281                           NULL, &error);
282
283   if (! retval)
284     {
285       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
286           "%sCould not execute web browser: \"%s\"%s\n\n\"%s\"\n\n%s",
287           simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
288           error->message,
289           "Please correct the web browser setting in the Preferences dialog.");
290       g_error_free (error);
291     }
292
293   g_free (browser);
294   g_free (cmd);
295   g_strfreev (argv);
296
297   return retval;
298 #endif
299 }
300
301 /* XXX: Much of this is very similar to browser_open_url - abstract a common
302  * function out of the two of them? */
303 gboolean
304 filemanager_open_directory (const gchar *path)
305 {
306 #if defined(G_OS_WIN32)
307   /* ShellExecute(...,"explore",...) needs path to be explicitly a directory;
308      Otherwise 'explore' will fail if a file exists with a basename matching
309      the provided directory path.
310      (eg: wireshak-gtk2.exe exists in the same directory as  a wireshark-gtk2
311           directory entry).
312   */
313   gint   ret;
314   gchar *xpath;
315   xpath = g_strconcat(path,
316                       g_str_has_suffix(path, "\\") ? "" : "\\",
317                       NULL);
318   ret = (gint) ShellExecute (HWND_DESKTOP, _T("explore"), utf_8to16(xpath), NULL, NULL, SW_SHOWNORMAL);
319   g_free(xpath);
320   return (ret > 32);
321
322 #elif defined(HAVE_OS_X_FRAMEWORKS)
323
324   CFStringRef path_CFString;
325   CFURLRef path_CFURL;
326   OSStatus status;
327
328   path_CFString = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
329   if (path_CFString == NULL)
330     return (FALSE);
331   path_CFURL = CFURLCreateWithFileSystemPath(NULL, path_CFString,
332                                              kCFURLPOSIXPathStyle, true);
333   CFRelease(path_CFString);
334   if (path_CFURL == NULL) {
335     /*
336      * XXX - does this always mean that that memory allocation failed?
337      */
338     return (FALSE);
339   }
340   /*
341    * XXX - this is a Launch Services result code, and we should probably
342    * display a dialog box if it's not 0, describing what the error was.
343    * Then again, we should probably do the same for the ShellExecute call,
344    * unless that call itself happens to pop up a dialog box for all errors.
345    */
346   status = LSOpenCFURLRef(path_CFURL, NULL);
347   CFRelease(path_CFURL);
348   return (status == 0);
349
350 #elif defined(HAVE_XDG_OPEN)
351
352   GError      *error = NULL;
353   const gchar *argv[3];
354   gboolean     retval;
355
356   g_return_val_if_fail (path != NULL, FALSE);
357
358   argv[0] = "xdg-open";
359   argv[1] = path;
360   argv[2] = NULL;
361
362   /*
363    * XXX - use g_spawn_on_screen() so the file managaer window shows up on
364    * the same screen?
365    *
366    * Also, g_spawn_async shouldn't modify argv but takes it as non-const!
367    */
368   retval = g_spawn_async (NULL, (gchar**) argv, NULL,
369                           G_SPAWN_SEARCH_PATH,
370                           NULL, NULL,
371                           NULL, &error);
372
373   if (! retval)
374     {
375       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
376           "%sCould not execute xdg-open: %s\n\n\"%s\"",
377           simple_dialog_primary_start(), simple_dialog_primary_end(),
378           error->message);
379       g_error_free (error);
380     }
381
382   return retval;
383
384 #elif defined(MUST_LAUNCH_BROWSER_OURSELVES)
385
386   GError    *error;
387   gchar     *browser;
388   gchar     *argument;
389   gchar     *cmd;
390   gchar    **argv;
391   gboolean   retval;
392
393   g_return_val_if_fail (path != NULL, FALSE);
394
395   /*  browser = gimp_gimprc_query ("web-browser");*/
396   browser = g_strdup(prefs.gui_webbrowser);
397
398   if (browser == NULL || ! strlen (browser))
399     {
400       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
401           "Web browser not specified.\n"
402           "Please correct the web browser setting in the Preferences dialog.\n"
403           "URL: %s", path);
404       g_free (browser);
405       return FALSE;
406     }
407
408   /* conver the path to a URI */
409   argument = g_filename_to_uri(path, NULL, &error);
410   if (argument == NULL)
411     {
412       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
413           "%sCould not convert \"%s\" to a URI: \"%s\"%s\"",
414           simple_dialog_primary_start(), path, simple_dialog_primary_end(),
415           error->message);
416       g_error_free (error);
417       return FALSE;
418     }
419
420   /* replace %s with URL */
421   if (strstr (browser, "%s"))
422     cmd = strreplace (browser, "%s", argument);
423   else
424     cmd = g_strconcat (browser, " ", argument, NULL);
425
426   g_free (argument);
427
428   /* parse the cmd line */
429   if (! g_shell_parse_argv (cmd, NULL, &argv, &error))
430     {
431       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
432           "%sCould not parse web browser command: \"%s\"%s\n\n\"%s\"\n\n%s",
433           simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
434           error->message,
435           "Please correct the web browser setting in the Preferences dialog.");
436       g_error_free (error);
437       return FALSE;
438     }
439
440   /*
441    * XXX - use g_spawn_on_screen() so the browser window shows up on
442    * the same screen?
443    */
444   retval = g_spawn_async (NULL, argv, NULL,
445                           G_SPAWN_SEARCH_PATH,
446                           NULL, NULL,
447                           NULL, &error);
448
449   if (! retval)
450     {
451       simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
452           "%sCould not execute web browser: \"%s\"%s\n\n\"%s\"\n\n%s",
453           simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
454           error->message,
455           "Please correct the web browser setting in the Preferences dialog.");
456       g_error_free (error);
457     }
458
459   g_free (browser);
460   g_free (cmd);
461   g_strfreev (argv);
462
463   return retval;
464 #endif
465 }
466
467 #ifdef MUST_LAUNCH_BROWSER_OURSELVES
468
469 static gchar*
470 strreplace (const gchar *string,
471             const gchar *delimiter,
472             const gchar *replacement)
473 {
474   gchar  *ret;
475   gchar **tmp;
476
477   g_return_val_if_fail (string != NULL, NULL);
478   g_return_val_if_fail (delimiter != NULL, NULL);
479   g_return_val_if_fail (replacement != NULL, NULL);
480
481   tmp = g_strsplit (string, delimiter, 0);
482   ret = g_strjoinv (replacement, tmp);
483   g_strfreev (tmp);
484
485   return ret;
486 }
487
488 #endif /* MUST_LAUNCH_BROWSER_OURSELVES */
489
490
491 /* browse a file relative to the data dir */
492 void
493 browser_open_data_file(const gchar *filename)
494 {
495     gchar *uri;
496
497     /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */
498
499     uri = data_file_url(filename);
500
501     /* show the uri */
502     browser_open_url (uri);
503
504     g_free(uri);
505 }