#!/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()
