Move the regex #defines to configfile.c
[browser-switch] / launcher.c
1 /*
2  * launcher.c -- functions for launching web browsers for browser-switchboard
3  *
4  * Copyright (C) 2009 Steven Luo
5  * Derived from a Python implementation by Jason Simpson and Steven Luo
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (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,
20  * USA.
21  */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <dbus/dbus-glib.h>
30
31 #include "browser-switchboard.h"
32 #include "launcher.h"
33 #include "dbus-server-bindings.h"
34
35 #define DEFAULT_BROWSER "/usr/bin/tear"
36 #define LAUNCH_DEFAULT_BROWSER launch_tear
37
38 static void launch_tear(struct swb_context *ctx, char *uri) {
39         int status;
40         static DBusGProxy *tear_proxy = NULL;
41         GError *error = NULL;
42         pid_t pid;
43
44         if (!uri)
45                 uri = "new_window";
46
47         printf("launch_tear with uri '%s'\n", uri);
48
49         /* We should be able to just call the D-Bus service to open Tear ...
50            but if Tear's not open, that cuases D-Bus to star Tear and then pass
51            it the OpenAddress call, which results in two browser windows.
52            Properly fixing this probably requires Tear to provide a D-Bus
53            method that opens an address in an existing window, but for now work
54            around by just invoking Tear with exec() if it's not running. */
55         status = system("pidof tear > /dev/null");
56         if (WIFEXITED(status) && !WEXITSTATUS(status)) {
57                 if (!tear_proxy)
58                         tear_proxy = dbus_g_proxy_new_for_name(ctx->session_bus,
59                                         "com.nokia.tear", "/com/nokia/tear",
60                                         "com.nokia.Tear");
61                 dbus_g_proxy_call(tear_proxy, "OpenAddress", &error,
62                                   G_TYPE_STRING, uri, G_TYPE_INVALID);
63                 if (!ctx->continuous_mode)
64                         exit(0);
65         } else {
66                 if (ctx->continuous_mode) {
67                         if ((pid = fork()) != 0) {
68                                 /* Parent process or error in fork() */
69                                 printf("child: %d\n", (int)pid);
70                                 return;
71                         }
72                         /* Child process */
73                         setsid();
74                 }
75                 execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
76         }
77 }
78
79 void launch_microb(struct swb_context *ctx, char *uri) {
80         int kill_browserd = 0;
81         int status;
82         pid_t pid;
83
84         if (!uri)
85                 uri = "new_window";
86
87         printf("launch_microb with uri '%s'\n", uri);
88
89         /* Launch browserd if it's not running */
90         status = system("pidof /usr/sbin/browserd > /dev/null");
91         if (WIFEXITED(status) && WEXITSTATUS(status)) {
92                 kill_browserd = 1;
93                 system("/usr/sbin/browserd -d");
94         }
95
96         /* Release the osso_browser D-Bus name so that MicroB can take it */
97         dbus_release_osso_browser_name(ctx);
98
99         if ((pid = fork()) == -1) {
100                 perror("fork");
101                 exit(1);
102         }
103         if (pid > 0) {
104                 /* Parent process */
105                 waitpid(pid, &status, 0);
106         } else {
107                 /* Child process */
108                 /* exec maemo-invoker directly instead of relying on the
109                    /usr/bin/browser symlink, since /usr/bin/browser may have
110                    been replaced with a shell script calling us via D-Bus */
111                 if (!strcmp(uri, "new_window")) {
112                         execl("/usr/bin/maemo-invoker",
113                               "browser", (char *)NULL);
114                 } else {
115                         execl("/usr/bin/maemo-invoker",
116                               "browser", "--url", uri, (char *)NULL);
117                 }
118         }
119
120         /* Kill off browserd if we started it */
121         if (kill_browserd)
122                 system("kill `pidof /usr/sbin/browserd`");
123
124         if (!ctx || !ctx->continuous_mode) 
125                 exit(0);
126
127         dbus_request_osso_browser_name(ctx);
128 }
129
130 static void launch_other_browser(struct swb_context *ctx, char *uri) {
131         char *command;
132         char *quoted_uri, *quote;
133
134         size_t cmdlen, urilen;
135         size_t quoted_uri_size;
136         size_t offset;
137
138         if (!uri || !strcmp(uri, "new_window"))
139                 uri = "";
140
141         printf("launch_other_browser with uri '%s'\n", uri);
142
143         if ((urilen = strlen(uri)) > 0) {
144                 /* Quote the URI to prevent the shell from interpreting it */
145                 /* urilen+3 = length of URI + 2x \' + \0 */
146                 if (!(quoted_uri = calloc(urilen+3, sizeof(char))))
147                         exit(1);
148                 snprintf(quoted_uri, urilen+3, "'%s'", uri);
149
150                 /* If there are any 's in the original URI, URL-escape them
151                    (replace them with %27) */
152                 quoted_uri_size = urilen + 3;
153                 quote = quoted_uri + 1;
154                 while ((quote = strchr(quote, '\'')) &&
155                        (offset = quote-quoted_uri) < strlen(quoted_uri)-1) {
156                         /* Check to make sure we don't shrink the memory area
157                            as a result of integer overflow */
158                         if (quoted_uri_size+2 <= quoted_uri_size)
159                                 exit(1);
160
161                         /* Grow the memory area;
162                            2 = strlen("%27")-strlen("'") */
163                         if (!(quoted_uri = realloc(quoted_uri,
164                                                    quoted_uri_size+2)))
165                                 exit(1);
166                         quoted_uri_size = quoted_uri_size + 2;
167
168                         /* Recalculate the location of the ' character --
169                            realloc() may have moved the string in memory */
170                         quote = quoted_uri + offset;
171
172                         /* Move the string after the ', including the \0,
173                            over two chars */
174                         memmove(quote+3, quote+1, strlen(quote));
175                         memcpy(quote, "%27", 3);
176                         quote = quote + 3;
177                 }
178                 urilen = strlen(quoted_uri);
179         } else
180                 quoted_uri = uri;
181
182         cmdlen = strlen(ctx->other_browser_cmd);
183
184         /* cmdlen+urilen+1 is normally two bytes longer than we need (uri will
185            replace "%s"), but is needed in the case other_browser_cmd has no %s
186            and urilen < 2 */
187         if (!(command = calloc(cmdlen+urilen+1, sizeof(char))))
188                 exit(1);
189         snprintf(command, cmdlen+urilen+1, ctx->other_browser_cmd, quoted_uri);
190         printf("command: '%s'\n", command);
191
192         if (ctx->continuous_mode) {
193                 if (fork() != 0) {
194                         /* Parent process or error in fork() */
195                         if (urilen > 0)
196                                 free(quoted_uri);
197                         free(command);  
198                         return;
199                 }
200                 /* Child process */
201                 setsid();
202         }
203         execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
204 }
205
206 /* Use launch_other_browser as the default browser launcher, with the string
207    passed in as the other_browser_cmd
208    Resulting other_browser_cmd is always safe to free(), even if a pointer
209    to a string constant is passed in */
210 static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) {
211         size_t len = strlen(cmd);
212
213         free(ctx->other_browser_cmd);
214         ctx->other_browser_cmd = calloc(len+1, sizeof(char));
215         if (!ctx->other_browser_cmd) {
216                 printf("malloc failed!\n");
217                 ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
218         } else {
219                 ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
220                                                  cmd, len+1);
221                 ctx->default_browser_launcher = launch_other_browser;
222         }
223 }
224
225 void update_default_browser(struct swb_context *ctx, char *default_browser) {
226         if (!ctx)
227                 return;
228
229         if (!default_browser) {
230                 /* No default_browser configured -- use DEFAULT_BROWSER if
231                    installed, otherwise launch MicroB */
232                 if (!access(DEFAULT_BROWSER, X_OK))
233                         ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
234                 else
235                         ctx->default_browser_launcher = launch_microb;
236                 return;
237         }
238
239         if (!strcmp(default_browser, "tear"))
240                 ctx->default_browser_launcher = launch_tear;
241         else if (!strcmp(default_browser, "microb"))
242                 ctx->default_browser_launcher = launch_microb;
243         else if (!strcmp(default_browser, "fennec"))
244                 /* Cheat and reuse launch_other_browser, since we don't appear
245                    to need to do anything special */
246                 use_other_browser_cmd(ctx, "fennec %s");
247         else if (!strcmp(default_browser, "midori"))
248                 use_other_browser_cmd(ctx, "midori %s");
249         else if (!strcmp(default_browser, "other")) {
250                 if (ctx->other_browser_cmd)
251                         ctx->default_browser_launcher = launch_other_browser;
252                 else {
253                         printf("default_browser is 'other', but no other_browser_cmd set -- using default\n");
254                         ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
255                 }
256         } else {
257                 printf("Unknown default_browser %s, using default", default_browser);
258                 ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
259         }
260 }
261
262 void launch_browser(struct swb_context *ctx, char *uri) {
263         if (ctx && ctx->default_browser_launcher)
264                 ctx->default_browser_launcher(ctx, uri);
265 }