From 1fd085ced737612ea2cd38c8aa565f995c4a46a0 Mon Sep 17 00:00:00 2001
From: robk-tahoe <robk-tahoe@allmydata.com>
Date: Thu, 25 Sep 2008 07:12:24 -0700
Subject: [PATCH] gui/macapp: rough cut of ui tweaks; configurability,
 auto-mount

chatting with peter, two things the mac gui needed were the ability to mount
the 'allmydata drive' automatically upon launching the app, and open the
Finder to reveal it.  (also a request to hide the debug 'open webroot' stuff)

this (somewhat rough) patch implements all the above as default behaviour

it also contains a quick configuration mechanism for the gui - rather than a
preferences gui, running with a more 'tahoe' styled mechanism, the contents
of a few optional files can modify the default behaviour, specifically file
in ~/.tahoe/gui.conf control behaviour as follows:

auto-mount (bool): if set (the default) then the mac app will, upon launch
automatically mount the 'tahoe:' alias with the display name 'Allmydata'
using a mountpoint of ~/.tahoe/mnt/__auto__

auto-open (bool): if set (the default) then upon mounting a file system
(including the auto-mount if set) finder will be opened to the mountpoint
of the filesystem, which essentially reveals the newly mounted drive in a
Finder window

show-webopen (bool): if set (false by default) then the 'open webroot'
action will be made available in both the dock and file menus of the app

daemon-timout (int): sets the daemon-timeout option passed into tahoe fuse
when a filesystem is mounted. this defaults to 5 min

files of type (int) much, naturally contain a parsable int representation.
files of type (bool) are considered true if their (case-insensitive) contents
are any of ['y', 'yes', 'true', 'on', '1'] and considered false otherwise.
---
 src/allmydata/gui/macapp.py | 122 ++++++++++++++++++++++++++++++++++--
 1 file changed, 118 insertions(+), 4 deletions(-)

diff --git a/src/allmydata/gui/macapp.py b/src/allmydata/gui/macapp.py
index 5846b5974..63b64f942 100644
--- a/src/allmydata/gui/macapp.py
+++ b/src/allmydata/gui/macapp.py
@@ -280,6 +280,56 @@ class MountFrame(wx.Frame):
     def on_close(self, event):
         self.Show(False)
 
+
+def check_mount(proc):
+    message = [ 'pid: %s' % (proc.pid,),
+                'ret: %s' % (proc.returncode,),
+                'stdout:\n%s' % (proc.stdout.read(),),
+                'stderr:\n%s' % (proc.stderr.read(),),
+                ]
+    log.msg('\n'.join(['spawned process:'] + message))
+
+def mount_filesystem(basedir, alias_name, mountpoint, display_name=None, timeout=None):
+    log.msg('mount_filesystem(%r,%r,%r,%r)' % (basedir, alias_name, mountpoint, display_name))
+    log.msg('sys.exec = %r' % (sys.executable,))
+    if not sys.executable.endswith('Allmydata.app/Contents/MacOS/python'):
+        log.msg("can't find allmydata.app: sys.executable = %r" % (sys.executable,))
+        wx.MessageBox("Can't determine location of Allmydata.app")
+        return False
+    bin_path = sys.executable[:-6] + 'Allmydata'
+    log.msg('%r exists: %r' % (bin_path, os.path.exists(bin_path),))
+
+    foptions = []
+    foptions.append('-olocal') # required to display in Finder on leopard
+
+    if display_name is None:
+        display_name = alias_name
+    foptions.append('-ovolname=%s' % (display_name,))
+
+    if timeout is None:
+        timeout = DEFAULT_FUSE_TIMEOUT
+    if timeout:
+        foptions.append('-odaemon_timeout=%d' % (timeout,))
+
+    icns_path = os.path.join(basedir, 'private', alias_name+'.icns')
+    if not os.path.exists(icns_path):
+        icns_path = os.path.normpath(os.path.join(os.path.dirname(sys.executable),
+                                                  '../Resources/allmydata.icns'))
+        log.msg('set icns_path=%s' % (icns_path,))
+        log.msg('icns_path exists: %s' % os.path.exists(icns_path))
+    if os.path.exists(icns_path):
+        foptions.append('-ovolicon=%s' % (icns_path,))
+
+    command = [bin_path, 'fuse', '--alias', alias_name] + foptions + [mountpoint]
+    log.msg('spawning command %r' % (command,))
+    proc = subprocess.Popen(command,
+                            cwd=basedir,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.PIPE)
+    log.msg('spawned process, pid %s' % (proc.pid,))
+    wx.FutureCall(4096, check_mount, proc)
+    return True
+
 class MountPanel(wx.Panel):
     def __init__(self, parent, on_close, app):
         wx.Panel.__init__(self, parent, -1)
@@ -338,6 +388,15 @@ class MountPanel(wx.Panel):
         dlg.Destroy()
 
     def do_mount(self, alias_name, mountpoint):
+        log.msg('do_mount(%r, %r)' % (alias_name, mountpoint))
+        # XXX this needs a referential cleanup
+        timeout = self.app.config['daemon-timeout']
+        ok = mount_filesystem(self.app.basedir, alias_name, mountpoint, timeout=timeout)
+        if ok and self.app.config['auto-open']:
+            wx.FutureCall(2048, open_finder, mountpoint)
+        self.parent.parent.Show(False)
+
+    def old_do_mount(self, alias_name, mountpoint):
         log.msg('do_mount(%r, %r)' % (alias_name, mountpoint))
         log.msg('sys.exec = %r' % (sys.executable,))
         if not sys.executable.endswith('Allmydata.app/Contents/MacOS/python'):
@@ -384,20 +443,60 @@ class MountPanel(wx.Panel):
                     ]
         log.msg('\n'.join(['spawned process:'] + message))
 
+def open_finder(path):
+    proc = subprocess.Popen(['/usr/bin/open', path],
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.PIPE)
+
 class MacGuiApp(wx.App):
+    config = {
+        'auto-mount': True,
+        'auto-open': True,
+        'show-webopen': False,
+        'daemon-timeout': DEFAULT_FUSE_TIMEOUT,
+        }
+
     def __init__(self, app):
         self.app = app
+        self.load_config()
         wx.App.__init__(self)
+    # XXX should have a table of all mounted filesystems, so that can unmount on quit
+
+    def load_config(self):
+        log.msg('load_config')
+        confdir = os.path.join(self.app.basedir, 'gui.conf')
+        config = {}
+        config.update(self.config)
+        for k in self.config:
+            f = os.path.join(confdir, k)
+            if os.path.exists(f):
+                val = file(f, 'rb').read().strip()
+                if type(self.config[k]) == bool:
+                    if val.lower() in ['y', 'yes', 'true', 'on', '1']:
+                        val = True
+                    else:
+                        val = False
+                elif type(self.config[k]) == int:
+                    val = int(val)
+                config[k] = val
+        self.config = config
+        #log.msg('loaded config: %r' % (self.config,)) # XXX
 
     def OnInit(self):
+        log.msg('OnInit')
         try:
             self.frame = SplashFrame()
             self.frame.CenterOnScreen()
             self.frame.Show(True)
             self.SetTopWindow(self.frame)
 
+            # self.load_config()
+
             wx.FutureCall(4096, self.on_timer, None)
 
+            # XXX clean up this ref
+            self.app.config = self.config
+
             self.mount_frame = MountFrame(self.app)
 
             self.setup_dock_icon()
@@ -411,6 +510,18 @@ class MacGuiApp(wx.App):
 
     def on_timer(self, event):
         self.frame.Show(False)
+        self.perhaps_automount()
+
+    def perhaps_automount(self):
+        if self.config['auto-mount']:
+            mountpoint = os.path.expanduser('~/.tahoe/mnt/__auto__')
+            if not os.path.isdir(mountpoint):
+                os.makedirs(mountpoint)
+            timeout = self.config['daemon-timeout']
+            mount_filesystem(self.app.basedir, 'tahoe', mountpoint, 'Allmydata', timeout)
+            if self.config['auto-open']:
+                assert os.path.isdir(mountpoint)
+                wx.FutureCall(2048, open_finder, mountpoint)
 
     def setup_dock_icon(self):
         self.tbicon = wx.TaskBarIcon()
@@ -420,8 +531,9 @@ class MacGuiApp(wx.App):
     def setup_app_menu(self, frame):
         menubar = wx.MenuBar()
         file_menu = wx.Menu()
-        item = file_menu.Append(WEBOPEN_ID, text='Open Web Root')
-        frame.Bind(wx.EVT_MENU, self.on_webopen, item)
+        if self.config['show-webopen']:
+            item = file_menu.Append(WEBOPEN_ID, text='Open Web Root')
+            frame.Bind(wx.EVT_MENU, self.on_webopen, item)
         item = file_menu.Append(ACCOUNT_PAGE_ID, text='Open Account Page')
         frame.Bind(wx.EVT_MENU, self.on_account_page, item)
         item = file_menu.Append(MOUNT_ID, text='Mount Filesystem')
@@ -437,8 +549,9 @@ class MacGuiApp(wx.App):
         dock_menu = wx.Menu()
         item = dock_menu.Append(wx.NewId(), text='About')
         self.tbicon.Bind(wx.EVT_MENU, self.on_about, item)
-        item = dock_menu.Append(WEBOPEN_ID, text='Open Web Root')
-        self.tbicon.Bind(wx.EVT_MENU, self.on_webopen, item)
+        if self.config['show-webopen']:
+            item = dock_menu.Append(WEBOPEN_ID, text='Open Web Root')
+            self.tbicon.Bind(wx.EVT_MENU, self.on_webopen, item)
         item = dock_menu.Append(ACCOUNT_PAGE_ID, text='Open Account Page')
         self.tbicon.Bind(wx.EVT_MENU, self.on_account_page, item)
         item = dock_menu.Append(MOUNT_ID, text='Mount Filesystem')
@@ -449,6 +562,7 @@ class MacGuiApp(wx.App):
         self.frame.Show(True)
 
     def on_quit(self, event):
+        #XXX unmount mounted (automounted!) filesystems
         self.ExitMainLoop()
 
     def on_webopen(self, event):