Merge documentation updates from 2.2 into c-implementation
authorSteven Luo <steven+maemo@steven676.net>
Mon, 14 Dec 2009 11:27:51 +0000 (03:27 -0800)
committerSteven Luo <steven+maemo@steven676.net>
Mon, 14 Dec 2009 11:27:51 +0000 (03:27 -0800)
Makefile [new file with mode: 0644]
browser-switchboard [deleted file]
browser-switchboard.h [new file with mode: 0644]
dbus-server-bindings.c [new file with mode: 0644]
dbus-server-bindings.h [new file with mode: 0644]
dbus-server-glue.xml [new file with mode: 0644]
launcher.c [new file with mode: 0644]
launcher.h [new file with mode: 0644]
main.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..8974eb2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+CC = gcc
+CFLAGS = -Wall -Os -mcpu=arm1136jf-s -mthumb
+CPPFLAGS = `pkg-config --cflags dbus-glib-1`
+LDFLAGS = `pkg-config --libs dbus-glib-1`
+PREFIX = /usr/local
+
+APP = browser-switchboard
+obj = main.o launcher.o dbus-server-bindings.o
+
+all: $(APP)
+
+$(APP): dbus-server-glue.h $(obj)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $(APP) $(obj)
+
+dbus-server-glue.h:
+       dbus-binding-tool --mode=glib-server --prefix="osso_browser" \
+           dbus-server-glue.xml > dbus-server-glue.h
+
+strip: $(APP)
+       strip $(APP)
+
+install: all
+       mkdir -p $(DESTDIR)$(PREFIX)/bin
+       install -c -m 0755 browser-switchboard $(DESTDIR)$(PREFIX)/bin
+       install -c -m 0644 com.nokia.osso_browser.service $(DESTDIR)$(PREFIX)/share/dbus-1/services
+       install -c -m 0755 browser $(DESTDIR)$(PREFIX)/bin
+
+clean:
+       rm -f $(APP) *.o dbus-server-glue.h
+
+.PHONY: strip install
diff --git a/browser-switchboard b/browser-switchboard
deleted file mode 100755 (executable)
index e922cee..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/python
-# browser-switchboard
-# version 2.2 by steven676 and xiojason
-
-# simple python-dbus service that proxies osso_browser events to Tear
-# based on code from http://paste.lisp.org/display/45824
-
-# Copyright (C) 2009 Jason Simpson
-# Copyright (C) 2009 Steven Luo
-
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
-# On Maemo systems, a copy of the GPL can be found in
-# /usr/share/common-licenses/GPL .
-
-
-import os
-import gobject
-import dbus
-import dbus.service
-import dbus.glib
-
-# Set default configuration values
-# These can be overriden by the config file $HOME/.config/browser-switchboard
-def setconfigdefaults():
-    global continuous_mode, default_browser, other_browser_cmd
-
-    # continuous_mode: 0 -- close after handling a request; 1 -- run
-    # continuously in the background
-    continuous_mode = 0
-    # default_browser: "tear", "microb", "fennec", "midori", or "other"
-    # empty string is handled specially -- see below
-    default_browser = ""
-    # If default browser is "other", what program to run (%s will be replaced
-    # by URI)
-    other_browser_cmd = ""
-
-class BrowserLauncher:
-    def LaunchTear(self, uri):
-        # We should be able to just call the D-Bus service to open Tear ...
-        # but if Tear's not open, that causes D-Bus to start Tear and then
-        # pass it the OpenAddress call, which results in two browser windows.
-        # Properly fixing this probably requires Tear to provide a D-Bus method
-        # that opens an address in an existing window, but for now work around
-        # by just opening Tear via the command line if it's not running.
-        if os.system("pidof tear > /dev/null") == 0:
-            dbus.SessionBus().get_object('com.nokia.tear', '/com/nokia/tear').OpenAddress(uri, dbus_interface='com.nokia.Tear')
-            if continuous_mode == 0:
-                quit()
-        else:
-            if continuous_mode:
-                if os.fork() != 0:
-                    # parent process doesn't need to do anything
-                    return
-                # child process
-                os.setsid()
-            os.execl('/usr/bin/tear', '/usr/bin/tear', uri)
-
-    def LaunchMicroB(self, uri):
-        if os.system("pidof /usr/sbin/browserd > /dev/null") != 0:
-            kill_browserd = 1
-            os.system("/usr/sbin/browserd -d")
-        else:
-            kill_browserd = 0
-        dbus.SessionBus().release_name("com.nokia.osso_browser")
-        if uri == "new_window":
-            # exec maemo-invoker directly instead of relying on the
-            # /usr/bin/browser symlink, since /usr/bin/browser may have been
-            # replaced with a shell script calling us via D-Bus
-            os.spawnl(os.P_WAIT, '/usr/bin/maemo-invoker', '/usr/bin/browser')
-        else:
-            os.spawnl(os.P_WAIT, '/usr/bin/maemo-invoker', '/usr/bin/browser', '--url', uri)
-        if kill_browserd:
-            os.system("kill `pidof /usr/sbin/browserd`")
-        if continuous_mode:
-            dbus.SessionBus().request_name("com.nokia.osso_browser")
-        else:
-            quit()
-
-    def LaunchOtherBrowser(self, uri):
-        if uri == "new_window":
-            uri = ""
-        try:
-            # URI must be quoted and quotes in the URI must be URL-escaped
-            # to prevent the shell from interpreting any part of the URI
-            command = other_browser_cmd % "'%s'" % uri.replace("'", "%27")
-        except TypeError:
-            # Couldn't substitute URI in, just launch the browser
-            print "other_browser_cmd should have a %s placeholder for the URI"
-            command = other_browser_cmd
-        import subprocess
-        subprocess.Popen(command, shell=True)
-        if continuous_mode == 0:
-            quit()
-
-    def UpdateDefaultBrowser(self):
-        global other_browser_cmd
-        if default_browser == "tear":
-            self.LaunchBrowser = self.LaunchTear
-        elif default_browser == "microb":
-            self.LaunchBrowser = self.LaunchMicroB
-        elif default_browser == "fennec":
-            # Cheat and reuse LaunchOtherBrowser, since we don't appear to
-            # need to do anything special
-            # TODO: does Fennec have a D-Bus API or signaling mechanism?
-            # Invoking the fennec binary appears to be somewhat expensive
-            other_browser_cmd = "fennec '%s'"
-            self.LaunchBrowser = self.LaunchOtherBrowser
-        elif default_browser == "midori":
-            other_browser_cmd = "midori '%s'"
-            self.LaunchBrowser = self.LaunchOtherBrowser
-        elif default_browser == "other":
-            if other_browser_cmd != "":
-                self.LaunchBrowser = self.LaunchOtherBrowser
-            else:
-                print "default_browser is 'other', but no other_browser_cmd set -- using default"
-                self.LaunchBrowser = self.LaunchTear
-        elif default_browser == "":
-            # If default_browser is empty, use Tear as the default if
-            # installed, otherwise use MicroB
-            if os.access("/usr/bin/tear", os.X_OK):
-                self.LaunchBrowser = self.LaunchTear
-            else:
-                self.LaunchBrowser = self.LaunchMicroB
-        else:
-            print "Unknown default_browser %s, using default" % default_browser
-            self.LaunchBrowser = self.LaunchTear
-
-    def __init__(self):
-        self.UpdateDefaultBrowser()
-
-class ProxyBrowserService(dbus.service.Object):
-    def __init__(self):
-        bus_name = dbus.service.BusName('com.nokia.osso_browser', bus=dbus.SessionBus())
-        dbus.service.Object.__init__(self, bus_name, '/com/nokia/osso_browser')
-        dbus.service.Object.__init__(self, bus_name, '/com/nokia/osso_browser/request')
-
-    def OpenAddress(self, uri):
-        print uri
-
-        if uri[0] == '/':
-            print "prefixing apparent local path with file://"
-            uri = "file://" + uri
-
-        launcher.LaunchBrowser(uri)
-
-    @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
-    def load_url(self, uri):
-        print "load_url"
-        self.OpenAddress(uri)
-
-    @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
-    def mime_open(self, uri):
-        print "mime_open"
-        self.OpenAddress(uri)
-
-    @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
-    def open_new_window(self, uri):
-        print "open_new_window"
-        self.OpenAddress(uri)
-
-    @dbus.service.method(dbus_interface='com.nokia.osso_browser')
-    def top_application(self):
-        print "top_application"
-        launcher.LaunchMicroB("new_window")
-
-    # This method is an "undocumented", non-standard extension to the
-    # osso_browser D-Bus API, intended solely to allow a wrapper script
-    # replacing /usr/bin/browser to implement --url
-    @dbus.service.method(dbus_interface='com.nokia.osso_browser')
-    def switchboard_launch_microb(self, uri="new_window"):
-        print "switchboard_launch_microb"
-        launcher.LaunchMicroB(uri)
-
-def readconfigfile(signalnum=None, frame=None):
-    # reset configuration to defaults
-    setconfigdefaults()
-
-    # read configuration from the config file, if available
-    try:
-        execfile(os.getenv("HOME", "/home/user") + "/.config/browser-switchboard", globals())
-    except:
-        # Try the legacy config file location
-        try:
-            execfile(os.getenv("HOME", "/home/user") + "/.config/browser-proxy", globals())
-        except:
-            # No valid config file available
-            pass
-    launcher.UpdateDefaultBrowser()
-
-setconfigdefaults()
-
-launcher = BrowserLauncher()
-pbrowser = ProxyBrowserService()
-
-readconfigfile()
-
-if continuous_mode:
-    import signal
-    def waitforzombies(signalnum, frame):
-        try:
-            while os.waitpid(-1, os.WNOHANG) != (0, 0):
-                continue
-        except OSError:
-            pass
-    signal.signal(signal.SIGCHLD, waitforzombies)
-    signal.signal(signal.SIGHUP, readconfigfile)
-
-loop = gobject.MainLoop()
-loop.run()
diff --git a/browser-switchboard.h b/browser-switchboard.h
new file mode 100644 (file)
index 0000000..726a553
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * browser-switchboard.h -- definitions common to all of browser-switchboard
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _BROWSER_SWITCHBOARD_H
+#define _BROWSER_SWITCHBOARD_H 1
+
+struct swb_context {
+       int continuous_mode;
+       void (*default_browser_launcher)(struct swb_context *, char *);
+       char *other_browser_cmd;
+       DBusGConnection *session_bus;
+       DBusGProxy *dbus_proxy;
+};
+
+#endif /* _BROWSER_SWITCHBOARD_H */
diff --git a/dbus-server-bindings.c b/dbus-server-bindings.c
new file mode 100644 (file)
index 0000000..b29b1ab
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * dbus-server-bindings.c -- osso_browser D-Bus interface implementation
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dbus/dbus-glib.h>
+
+#include "browser-switchboard.h"
+#include "launcher.h"
+#include "dbus-server-bindings.h"
+
+extern struct swb_context ctx;
+
+G_DEFINE_TYPE(OssoBrowser, osso_browser, G_TYPE_OBJECT);
+static void osso_browser_init(OssoBrowser *obj)
+{
+}
+
+static void osso_browser_class_init(OssoBrowserClass *klass)
+{
+}
+
+#include "dbus-server-glue.h"
+
+
+static void open_address(const char *uri) {
+       char *new_uri;
+       size_t new_uri_len;
+
+       if (!uri)
+               /* Not much to do in this case ... */
+               return;
+
+       if (uri[0] == '/') {
+               new_uri_len = strlen("file://") + strlen(uri) + 1;
+               if (!(new_uri = calloc(new_uri_len, sizeof(char))))
+                       exit(1);
+               snprintf(new_uri, new_uri_len, "%s%s", "file://", uri);
+
+               launch_browser(&ctx, new_uri);
+               /* If launch_browser didn't exec something in this process,
+                  we need to clean up after ourselves */
+               free(new_uri);
+       } else {
+               printf("open_address '%s'\n", uri);
+               launch_browser(&ctx, (char *)uri);
+       }
+}
+
+
+/*
+ * The com.nokia.osso_browser D-Bus interface
+ */
+gboolean osso_browser_load_url(OssoBrowser *obj,
+               const char *uri, GError **error) {
+       open_address(uri);
+       return TRUE;
+}
+
+gboolean osso_browser_mime_open(OssoBrowser *obj,
+               const char *uri, GError **error) {
+       open_address(uri);
+       return TRUE;
+}
+
+gboolean osso_browser_open_new_window(OssoBrowser *obj,
+               const char *uri, GError **error) {
+       open_address(uri);
+       return TRUE;
+}
+
+gboolean osso_browser_top_application(OssoBrowser *obj,
+               GError **error) {
+       launch_microb(&ctx, "new_window");
+       return TRUE;
+}
+
+/* This is a "undocumented", non-standard extension to the API, ONLY
+   for use by /usr/bin/browser wrapper to implement --url */
+gboolean osso_browser_switchboard_launch_microb(OssoBrowser *obj,
+               const char *uri, GError **error) {
+       launch_microb(&ctx, (char *)uri);
+       return TRUE;
+}
+
+
+/* Register the name com.nokia.osso_browser on the D-Bus session bus */
+void dbus_request_osso_browser_name(struct swb_context *ctx) {
+       GError *error = NULL;
+       guint result;
+
+       if (!ctx || !ctx->dbus_proxy)
+               return;
+
+       if (!dbus_g_proxy_call(ctx->dbus_proxy, "RequestName", &error,
+                              G_TYPE_STRING, "com.nokia.osso_browser",
+                              G_TYPE_UINT, DBUS_NAME_FLAG_REPLACE_EXISTING|DBUS_NAME_FLAG_DO_NOT_QUEUE,
+                              G_TYPE_INVALID,
+                              G_TYPE_UINT, &result,
+                              G_TYPE_INVALID)) {
+               printf("Couldn't acquire name com.nokia.osso_browser\n");
+               exit(1);
+       }
+       if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {  
+               printf("Couldn't acquire name com.nokia.osso_browser\n");
+               exit(1);
+       }
+}
+
+/* Release the name com.nokia.osso_browser on the D-Bus session bus */
+void dbus_release_osso_browser_name(struct swb_context *ctx) {
+       GError *error = NULL;
+       guint result;
+
+       if (!ctx || !ctx->dbus_proxy)
+               return;
+
+       dbus_g_proxy_call(ctx->dbus_proxy, "ReleaseName", &error,
+                         G_TYPE_STRING, "com.nokia.osso_browser",
+                         G_TYPE_INVALID,
+                         G_TYPE_UINT, &result,
+                         G_TYPE_INVALID);
+}
diff --git a/dbus-server-bindings.h b/dbus-server-bindings.h
new file mode 100644 (file)
index 0000000..232ac5d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * dbus-server-bindings.h -- definitions for the osso_browser interface
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _DBUS_SERVER_BINDINGS_H
+#define _DBUS_SERVER_BINDINGS_H 1
+
+#include "browser-switchboard.h"
+
+GType osso_browser_get_type(void);
+#define OSSO_BROWSER_TYPE (osso_browser_get_type())
+typedef struct _OssoBrowser {
+       GObject parent;
+} OssoBrowser;
+typedef struct _OssoBrowserClass {
+       GObjectClass parent;
+} OssoBrowserClass;
+
+/* The com.nokia.osso_browser D-Bus interface */
+gboolean osso_browser_load_url(OssoBrowser *obj,
+                const char *uri, GError **error);
+gboolean osso_browser_mime_open(OssoBrowser *obj,
+                const char *uri, GError **error);
+gboolean osso_browser_open_new_window(OssoBrowser *obj,
+                const char *uri, GError **error);
+gboolean osso_browser_top_application(OssoBrowser *obj, GError **error);
+/* This is an "undocumented", non-standard extension; DO NOT USE */
+gboolean osso_browser_switchboard_launch_microb(OssoBrowser *obj,
+                const char *uri, GError **error);
+
+void dbus_request_osso_browser_name(struct swb_context *ctx);
+void dbus_release_osso_browser_name(struct swb_context *ctx);
+
+const DBusGObjectInfo dbus_glib_osso_browser_object_info;
+
+#endif /* _DBUS_SERVER_BINDINGS_H */
diff --git a/dbus-server-glue.xml b/dbus-server-glue.xml
new file mode 100644 (file)
index 0000000..1fd3062
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/com/nokia/osso_browser">
+  <interface name="com.nokia.osso_browser">
+    <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="osso_browser" />
+    <method name="load_url">
+      <arg type="s" name="uri" direction="in" />
+    </method>
+    <method name="mime_open">
+      <arg type="s" name="uri" direction="in" />
+    </method>
+    <method name="open_new_window">
+      <arg type="s" name="uri" direction="in" />
+    </method>
+    <method name="top_application" />
+    <method name="switchboard_launch_microb">
+      <arg type="s" name="uri" direction="in" />
+    </method>
+  </interface>
+</node>
diff --git a/launcher.c b/launcher.c
new file mode 100644 (file)
index 0000000..24dff0a
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * launcher.c -- functions for launching web browsers for browser-switchboard
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dbus/dbus-glib.h>
+
+#include "browser-switchboard.h"
+#include "launcher.h"
+#include "dbus-server-bindings.h"
+
+#define LAUNCH_DEFAULT_BROWSER launch_tear
+
+static void launch_tear(struct swb_context *ctx, char *uri) {
+       int status;
+       static DBusGProxy *tear_proxy = NULL;
+       GError *error = NULL;
+       pid_t pid;
+
+       if (!uri)
+               uri = "new_window";
+
+       printf("launch_tear with uri '%s'\n", uri);
+
+       status = system("pidof tear > /dev/null");
+       if (WIFEXITED(status) && !WEXITSTATUS(status)) {
+               if (!tear_proxy)
+                       tear_proxy = dbus_g_proxy_new_for_name(ctx->session_bus,
+                                       "com.nokia.tear", "/com/nokia/tear",
+                                       "com.nokia.Tear");
+               dbus_g_proxy_call(tear_proxy, "OpenAddress", &error,
+                                 G_TYPE_STRING, uri, G_TYPE_INVALID);
+               if (!ctx->continuous_mode)
+                       exit(0);
+       } else {
+               if (ctx->continuous_mode) {
+                       if ((pid = fork()) != 0) {
+                               /* Parent process or error in fork() */
+                               printf("child: %d\n", (int)pid);
+                               return;
+                       }
+                       /* Child process */
+                       setsid();
+               }
+               execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
+       }
+}
+
+void launch_microb(struct swb_context *ctx, char *uri) {
+       int kill_browserd = 0;
+       int status;
+       pid_t pid;
+
+       if (!uri)
+               uri = "new_window";
+
+       status = system("pidof /usr/sbin/browserd > /dev/null");
+       if (WIFEXITED(status) && WEXITSTATUS(status)) {
+               kill_browserd = 1;
+               system("/usr/sbin/browserd -d");
+       }
+
+       dbus_release_osso_browser_name(ctx);
+
+       if ((pid = fork()) == -1) {
+               perror("fork");
+               exit(1);
+       }
+       if (pid > 0) {
+               /* Parent process */
+               waitpid(pid, &status, 0);
+       } else {
+               /* Child process */
+               if (!strcmp(uri, "new_window")) {
+                       execl("/usr/bin/maemo-invoker",
+                             "browser", (char *)NULL);
+               } else {
+                       execl("/usr/bin/maemo-invoker",
+                             "browser", "--url", uri, (char *)NULL);
+               }
+       }
+
+       if (kill_browserd)
+               system("kill `pidof /usr/sbin/browserd`");
+
+       if (!ctx || !ctx->continuous_mode) 
+               exit(0);
+
+       dbus_request_osso_browser_name(ctx);
+}
+
+static void launch_other_browser(struct swb_context *ctx, char *uri) {
+       char *command;
+       char *quoted_uri, *quote;
+
+       size_t cmdlen, urilen;
+       size_t quoted_uri_size;
+       size_t offset;
+
+       if (!uri || !strcmp(uri, "new_window"))
+               uri = "";
+       urilen = strlen(uri);
+       if (urilen > 0) {
+               /* Quote the URI */
+               /* urilen+3 = length of URI + 2x \' + \0 */
+               if (!(quoted_uri = calloc(urilen+3, sizeof(char))))
+                       exit(1);
+               snprintf(quoted_uri, urilen+3, "'%s'", uri);
+
+               /* If there are any 's in the original URI, URL-escape them
+                  (replace them with %27) */
+               quoted_uri_size = urilen + 3;
+               quote = quoted_uri + 1;
+               while ((quote = strchr(quote, '\'')) &&
+                      (offset = quote-quoted_uri) < strlen(quoted_uri)-1) {
+                       /* Check to make sure we don't shrink the memory area
+                          as a result of integer overflow */
+                       if (quoted_uri_size+2 <= quoted_uri_size)
+                               exit(1);
+
+                       /* Grow the memory area;
+                          2 = strlen("%27")-strlen("'") */
+                       if (!(quoted_uri = realloc(quoted_uri,
+                                                  quoted_uri_size+2)))
+                               exit(1);
+                       quoted_uri_size = quoted_uri_size + 2;
+
+                       /* Recalculate the location of the ' character --
+                          realloc() may have moved the string in memory */
+                       quote = quoted_uri + offset;
+
+                       /* Move the string after the ', including the \0,
+                          over two chars */
+                       memmove(quote+3, quote+1, strlen(quote));
+                       memcpy(quote, "%27", 3);
+                       quote = quote + 3;
+               }
+               urilen = strlen(quoted_uri);
+       } else
+               quoted_uri = uri;
+
+       cmdlen = strlen(ctx->other_browser_cmd);
+
+       /* cmdlen+urilen+1 is normally two bytes longer than we need (uri will
+          replace "%s"), but is needed in the case other_browser_cmd has no %s
+          and urilen < 2 */
+       if (!(command = calloc(cmdlen+urilen+1, sizeof(char))))
+               exit(1);
+       snprintf(command, cmdlen+urilen+1, ctx->other_browser_cmd, quoted_uri);
+       printf("command: '%s'\n", command);
+
+       if (ctx->continuous_mode) {
+               if (fork() != 0) {
+                       /* Parent process or error in fork() */
+                       if (urilen > 0)
+                               free(quoted_uri);
+                       free(command);  
+                       return;
+               }
+               /* Child process */
+               setsid();
+       }
+       execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
+}
+
+static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) {
+       size_t len = strlen(cmd);
+
+       free(ctx->other_browser_cmd);
+       ctx->other_browser_cmd = calloc(len+1, sizeof(char));
+       if (!ctx->other_browser_cmd) {
+               printf("malloc failed!\n");
+               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
+       } else {
+               ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
+                                                cmd, len+1);
+               ctx->default_browser_launcher = launch_other_browser;
+       }
+}
+
+void update_default_browser(struct swb_context *ctx, char *default_browser) {
+       if (!ctx)
+               return;
+
+       if (!default_browser) {
+               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
+               return;
+       }
+
+       if (!strcmp(default_browser, "tear"))
+               ctx->default_browser_launcher = launch_tear;
+       else if (!strcmp(default_browser, "microb"))
+               ctx->default_browser_launcher = launch_microb;
+       else if (!strcmp(default_browser, "fennec"))
+               use_other_browser_cmd(ctx, "fennec %s");
+       else if (!strcmp(default_browser, "midori"))
+               use_other_browser_cmd(ctx, "midori %s");
+       else if (!strcmp(default_browser, "other")) {
+               if (ctx->other_browser_cmd)
+                       ctx->default_browser_launcher = launch_other_browser;
+               else {
+                       printf("default_browser is 'other', but no other_browser_cmd set -- using default\n");
+                       ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
+               }
+       } else {
+               printf("Unknown default_browser %s, using default", default_browser);
+               ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
+       }
+}
+
+void launch_browser(struct swb_context *ctx, char *uri) {
+       if (ctx && ctx->default_browser_launcher)
+               ctx->default_browser_launcher(ctx, uri);
+}
diff --git a/launcher.h b/launcher.h
new file mode 100644 (file)
index 0000000..c3ad5b9
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * launcher.h -- definitions for the browser launching functions
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _LAUNCHER_H
+#define _LAUNCHER_H 1
+
+#include "browser-switchboard.h"
+
+void launch_microb(struct swb_context *ctx, char *uri);
+void launch_browser(struct swb_context *ctx, char *uri);
+void update_default_browser(struct swb_context *ctx, char *default_browser);
+
+#endif /* _LAUNCHER_H */
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..73ca570
--- /dev/null
+++ b/main.c
@@ -0,0 +1,221 @@
+/*
+ * main.c -- config file parsing and main loop for browser-switchboard
+ *
+ * Copyright (C) 2009 Steven Luo
+ * Derived from a Python implementation by Jason Simpson and Steven Luo
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <regex.h>
+#include <dbus/dbus-glib.h>
+
+#include "browser-switchboard.h"
+#include "launcher.h"
+#include "dbus-server-bindings.h"
+
+#define DEFAULT_HOMEDIR "/home/user"
+#define CONFIGFILE_LOC "/.config/browser-switchboard"
+#define MAXLINE 1024
+
+struct swb_context ctx;
+
+static void set_config_defaults(struct swb_context *ctx) {
+       if (!ctx)
+               return;
+       free(ctx->other_browser_cmd);
+       ctx->continuous_mode = 0;
+       ctx->default_browser_launcher = NULL;
+       ctx->other_browser_cmd = NULL;
+}
+
+static void waitforzombies(int signalnum) {
+       while (waitpid(-1, NULL, WNOHANG) > 0)
+               printf("Waited for a zombie\n");
+}
+
+static void read_config(int signalnum) {
+       char *homedir, *configfile;
+       size_t len;
+       char buf[MAXLINE];
+       char *key, *value;
+       char *default_browser = NULL;
+       FILE *fp;
+       regex_t re_ignore, re_config1, re_config2;
+       regmatch_t substrs[3];
+
+       set_config_defaults(&ctx);
+
+       if (!(homedir = getenv("HOME")))
+               homedir = DEFAULT_HOMEDIR;
+       len = strlen(homedir) + strlen(CONFIGFILE_LOC) + 1;
+       if (!(configfile = calloc(len, sizeof(char))))
+               goto out_noopen;
+       snprintf(configfile, len, "%s%s", homedir, CONFIGFILE_LOC);
+
+       if (!(fp = fopen(configfile, "r")))
+               goto out_noopen;
+
+       /* compile regex matching blank lines or comments */
+       if (regcomp(&re_ignore, "^[[:space:]]*(#|$)", REG_EXTENDED|REG_NOSUB))
+               goto out_nore;
+       /* compile regex matching foo = "bar", with arbitrary whitespace at
+          beginning and end of line and surrounding the = */
+       if (regcomp(&re_config1,
+                   "^[[:space:]]*([^=[:space:]]+)[[:space:]]*=[[:space:]]*\"(.*)\"[[:space:]]*$",
+                   REG_EXTENDED)) {
+               regfree(&re_ignore);
+               goto out_nore;
+       }
+       /* compile regex matching foo = bar, with arbitrary whitespace at
+          beginning of line and surrounding the = */
+       if (regcomp(&re_config2,
+                   "^[[:space:]]*([^=[:space:]]+)[[:space:]]*=[[:space:]]*(.*)$",
+                   REG_EXTENDED|REG_NEWLINE)) {
+               regfree(&re_ignore);
+               regfree(&re_config1);
+               goto out_nore;
+       }
+
+       /* Read in the config file one line at a time and parse it
+          XXX doesn't deal with lines longer than MAXLINE */
+       while (fgets(buf, MAXLINE, fp)) {
+               /* skip blank lines and comments */
+               if (!regexec(&re_ignore, buf, 0, NULL, 0))
+                       continue;
+
+               /* Find the substrings corresponding to the key and value
+                  If the line doesn't match our idea of a config file entry,
+                  skip it */
+               if (regexec(&re_config1, buf, 3, substrs, 0) &&
+                   regexec(&re_config2, buf, 3, substrs, 0))
+                       continue;
+               if (substrs[1].rm_so == -1 || substrs[2].rm_so == -1)
+                       continue;
+
+               /* copy the config value into a new string */
+               len = substrs[2].rm_eo - substrs[2].rm_so;
+               if (!(value = calloc(len+1, sizeof(char))))
+                       goto out;
+               strncpy(value, buf+substrs[2].rm_so, len);
+               /* calloc() zeroes the memory, so string is automatically
+                  null terminated */
+
+               /* make key point to a null-terminated string holding the 
+                  config key */
+               key = buf + substrs[1].rm_so;
+               buf[substrs[1].rm_eo] = '\0';
+
+               if (!strcmp(key, "continuous_mode")) {
+                       ctx.continuous_mode = atoi(value);
+                       free(value);
+               } else if (!strcmp(key, "default_browser")) {
+                       if (!default_browser)
+                               default_browser = value;
+               } else if (!strcmp(key, "other_browser_cmd")) {
+                       if (!ctx.other_browser_cmd)
+                               ctx.other_browser_cmd = value;
+               } else {
+                       /* Don't need this line's contents */
+                       free(value);
+               }
+               value = NULL;
+       }
+
+       printf("continuous_mode: %d\n", ctx.continuous_mode);
+       printf("default_browser: '%s'\n", default_browser?default_browser:"NULL");
+       printf("other_browser_cmd: '%s'\n", ctx.other_browser_cmd?ctx.other_browser_cmd:"NULL");
+
+out:
+       regfree(&re_ignore);
+       regfree(&re_config1);
+       regfree(&re_config2);
+out_nore:
+       fclose(fp);
+out_noopen:
+       update_default_browser(&ctx, default_browser);
+       free(configfile);
+       free(default_browser);
+       return;
+}
+
+int main() {
+       OssoBrowser *obj_osso_browser, *obj_osso_browser_req;
+       GMainLoop *mainloop;
+       GError *error = NULL;
+
+       read_config(0);
+
+       if (ctx.continuous_mode) {
+               struct sigaction act;
+               act.sa_flags = SA_RESTART;
+               sigemptyset(&(act.sa_mask));
+
+               act.sa_handler = waitforzombies;
+               if (sigaction(SIGCHLD, &act, NULL) == -1) {
+                       printf("Installing signal handler failed\n");
+                       return 1;
+               }
+
+               act.sa_handler = read_config;
+               if (sigaction(SIGHUP, &act, NULL) == -1) {
+                       printf("Installing signal handler failed\n");
+                       return 1;
+               }
+       }
+
+       g_type_init();
+
+       dbus_g_object_type_install_info(OSSO_BROWSER_TYPE,
+                       &dbus_glib_osso_browser_object_info);
+
+       ctx.session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (!ctx.session_bus) {
+               printf("Couldn't get a D-Bus bus connection\n");
+               return 1;
+       }
+       ctx.dbus_proxy = dbus_g_proxy_new_for_name(ctx.session_bus,
+                       "org.freedesktop.DBus", "/org/freedesktop/DBus",
+                       "org.freedesktop.DBus");
+       if (!ctx.dbus_proxy) {
+               printf("Couldn't get an org.freedesktop.DBus proxy\n");
+               return 1;
+       }
+
+       dbus_request_osso_browser_name(&ctx);
+
+       obj_osso_browser = g_object_new(OSSO_BROWSER_TYPE, NULL);
+       obj_osso_browser_req = g_object_new(OSSO_BROWSER_TYPE, NULL);
+       dbus_g_connection_register_g_object(ctx.session_bus,
+                       "/com/nokia/osso_browser", G_OBJECT(obj_osso_browser));
+       dbus_g_connection_register_g_object(ctx.session_bus,
+                       "/com/nokia/osso_browser/request",
+                       G_OBJECT(obj_osso_browser_req));
+
+       mainloop = g_main_loop_new(NULL, FALSE);
+       printf("Starting main loop\n");
+       g_main_loop_run(mainloop);
+       printf("Main loop completed\n");
+
+       return 0;
+}