Merge documentation updates from 2.2 into c-implementation
[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 LAUNCH_DEFAULT_BROWSER launch_tear
36
37 static void launch_tear(struct swb_context *ctx, char *uri) {
38         int status;
39         static DBusGProxy *tear_proxy = NULL;
40         GError *error = NULL;
41         pid_t pid;
42
43         if (!uri)
44                 uri = "new_window";
45
46         printf("launch_tear with uri '%s'\n", uri);
47
48         status = system("pidof tear > /dev/null");
49         if (WIFEXITED(status) && !WEXITSTATUS(status)) {
50                 if (!tear_proxy)
51                         tear_proxy = dbus_g_proxy_new_for_name(ctx->session_bus,
52                                         "com.nokia.tear", "/com/nokia/tear",
53                                         "com.nokia.Tear");
54                 dbus_g_proxy_call(tear_proxy, "OpenAddress", &error,
55                                   G_TYPE_STRING, uri, G_TYPE_INVALID);
56                 if (!ctx->continuous_mode)
57                         exit(0);
58         } else {
59                 if (ctx->continuous_mode) {
60                         if ((pid = fork()) != 0) {
61                                 /* Parent process or error in fork() */
62                                 printf("child: %d\n", (int)pid);
63                                 return;
64                         }
65                         /* Child process */
66                         setsid();
67                 }
68                 execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
69         }
70 }
71
72 void launch_microb(struct swb_context *ctx, char *uri) {
73         int kill_browserd = 0;
74         int status;
75         pid_t pid;
76
77         if (!uri)
78                 uri = "new_window";
79
80         status = system("pidof /usr/sbin/browserd > /dev/null");
81         if (WIFEXITED(status) && WEXITSTATUS(status)) {
82                 kill_browserd = 1;
83                 system("/usr/sbin/browserd -d");
84         }
85
86         dbus_release_osso_browser_name(ctx);
87
88         if ((pid = fork()) == -1) {
89                 perror("fork");
90                 exit(1);
91         }
92         if (pid > 0) {
93                 /* Parent process */
94                 waitpid(pid, &status, 0);
95         } else {
96                 /* Child process */
97                 if (!strcmp(uri, "new_window")) {
98                         execl("/usr/bin/maemo-invoker",
99                               "browser", (char *)NULL);
100                 } else {
101                         execl("/usr/bin/maemo-invoker",
102                               "browser", "--url", uri, (char *)NULL);
103                 }
104         }
105
106         if (kill_browserd)
107                 system("kill `pidof /usr/sbin/browserd`");
108
109         if (!ctx || !ctx->continuous_mode) 
110                 exit(0);
111
112         dbus_request_osso_browser_name(ctx);
113 }
114
115 static void launch_other_browser(struct swb_context *ctx, char *uri) {
116         char *command;
117         char *quoted_uri, *quote;
118
119         size_t cmdlen, urilen;
120         size_t quoted_uri_size;
121         size_t offset;
122
123         if (!uri || !strcmp(uri, "new_window"))
124                 uri = "";
125         urilen = strlen(uri);
126         if (urilen > 0) {
127                 /* Quote the URI */
128                 /* urilen+3 = length of URI + 2x \' + \0 */
129                 if (!(quoted_uri = calloc(urilen+3, sizeof(char))))
130                         exit(1);
131                 snprintf(quoted_uri, urilen+3, "'%s'", uri);
132
133                 /* If there are any 's in the original URI, URL-escape them
134                    (replace them with %27) */
135                 quoted_uri_size = urilen + 3;
136                 quote = quoted_uri + 1;
137                 while ((quote = strchr(quote, '\'')) &&
138                        (offset = quote-quoted_uri) < strlen(quoted_uri)-1) {
139                         /* Check to make sure we don't shrink the memory area
140                            as a result of integer overflow */
141                         if (quoted_uri_size+2 <= quoted_uri_size)
142                                 exit(1);
143
144                         /* Grow the memory area;
145                            2 = strlen("%27")-strlen("'") */
146                         if (!(quoted_uri = realloc(quoted_uri,
147                                                    quoted_uri_size+2)))
148                                 exit(1);
149                         quoted_uri_size = quoted_uri_size + 2;
150
151                         /* Recalculate the location of the ' character --
152                            realloc() may have moved the string in memory */
153                         quote = quoted_uri + offset;
154
155                         /* Move the string after the ', including the \0,
156                            over two chars */
157                         memmove(quote+3, quote+1, strlen(quote));
158                         memcpy(quote, "%27", 3);
159                         quote = quote + 3;
160                 }
161                 urilen = strlen(quoted_uri);
162         } else
163                 quoted_uri = uri;
164
165         cmdlen = strlen(ctx->other_browser_cmd);
166
167         /* cmdlen+urilen+1 is normally two bytes longer than we need (uri will
168            replace "%s"), but is needed in the case other_browser_cmd has no %s
169            and urilen < 2 */
170         if (!(command = calloc(cmdlen+urilen+1, sizeof(char))))
171                 exit(1);
172         snprintf(command, cmdlen+urilen+1, ctx->other_browser_cmd, quoted_uri);
173         printf("command: '%s'\n", command);
174
175         if (ctx->continuous_mode) {
176                 if (fork() != 0) {
177                         /* Parent process or error in fork() */
178                         if (urilen > 0)
179                                 free(quoted_uri);
180                         free(command);  
181                         return;
182                 }
183                 /* Child process */
184                 setsid();
185         }
186         execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
187 }
188
189 static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) {
190         size_t len = strlen(cmd);
191
192         free(ctx->other_browser_cmd);
193         ctx->other_browser_cmd = calloc(len+1, sizeof(char));
194         if (!ctx->other_browser_cmd) {
195                 printf("malloc failed!\n");
196                 ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
197         } else {
198                 ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
199                                                  cmd, len+1);
200                 ctx->default_browser_launcher = launch_other_browser;
201         }
202 }
203
204 void update_default_browser(struct swb_context *ctx, char *default_browser) {
205         if (!ctx)
206                 return;
207
208         if (!default_browser) {
209                 ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
210                 return;
211         }
212
213         if (!strcmp(default_browser, "tear"))
214                 ctx->default_browser_launcher = launch_tear;
215         else if (!strcmp(default_browser, "microb"))
216                 ctx->default_browser_launcher = launch_microb;
217         else if (!strcmp(default_browser, "fennec"))
218                 use_other_browser_cmd(ctx, "fennec %s");
219         else if (!strcmp(default_browser, "midori"))
220                 use_other_browser_cmd(ctx, "midori %s");
221         else if (!strcmp(default_browser, "other")) {
222                 if (ctx->other_browser_cmd)
223                         ctx->default_browser_launcher = launch_other_browser;
224                 else {
225                         printf("default_browser is 'other', but no other_browser_cmd set -- using default\n");
226                         ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
227                 }
228         } else {
229                 printf("Unknown default_browser %s, using default", default_browser);
230                 ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
231         }
232 }
233
234 void launch_browser(struct swb_context *ctx, char *uri) {
235         if (ctx && ctx->default_browser_launcher)
236                 ctx->default_browser_launcher(ctx, uri);
237 }