mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-28 00:38:52 +00:00
299 lines
9.8 KiB
Python
299 lines
9.8 KiB
Python
|
|
import operator
|
|
import os
|
|
import stat
|
|
import sys
|
|
import thread
|
|
import threading
|
|
import traceback
|
|
import urllib
|
|
import webbrowser
|
|
|
|
import wx
|
|
from twisted.internet import reactor
|
|
from twisted.python import log, logfile
|
|
|
|
import allmydata
|
|
from allmydata import client
|
|
from allmydata.gui.confwiz import ConfWizApp, ACCOUNT_PAGE
|
|
import amdicon
|
|
|
|
|
|
TRY_TO_INSTALL_TAHOE_SCRIPT = True
|
|
TAHOE_SCRIPT = '''#!/bin/bash
|
|
if [ "x${@}x" == "xx" ]
|
|
then
|
|
%(exe)s --help
|
|
else
|
|
%(exe)s "${@}"
|
|
fi
|
|
'''
|
|
|
|
def run_macapp():
|
|
basedir = os.path.expanduser('~/.tahoe')
|
|
if not os.path.isdir(basedir):
|
|
app_supp = os.path.expanduser('~/Library/Application Support/Allmydata Tahoe/')
|
|
if not os.path.isdir(app_supp):
|
|
os.makedirs(app_supp)
|
|
os.symlink(app_supp, basedir)
|
|
|
|
app = App(basedir)
|
|
return app.run()
|
|
|
|
class App(object):
|
|
def __init__(self, basedir):
|
|
self.basedir = basedir
|
|
|
|
def files_exist(self, file_list):
|
|
extant_conf = [ os.path.exists(os.path.join(self.basedir, f)) for f in file_list ]
|
|
return reduce(operator.__and__, extant_conf)
|
|
|
|
def is_config_incomplete(self):
|
|
necessary_conf_files = ['introducer.furl', 'private/root_dir.cap']
|
|
need_config = not self.files_exist(necessary_conf_files)
|
|
if need_config:
|
|
print 'some config is missing from basedir (%s): %s' % (self.basedir, necessary_conf_files)
|
|
return need_config
|
|
|
|
def run(self):
|
|
# handle initial config
|
|
if not os.path.exists(os.path.join(self.basedir, 'webport')):
|
|
f = file(os.path.join(self.basedir, 'webport'), 'wb')
|
|
f.write('8123')
|
|
f.close()
|
|
|
|
if self.is_config_incomplete():
|
|
app = ConfWizApp()
|
|
app.MainLoop()
|
|
|
|
if self.is_config_incomplete():
|
|
print 'config still incomplete; confwiz cancelled, exiting'
|
|
return 1
|
|
|
|
# set up twisted logging. this will become part of the node rsn.
|
|
logdir = os.path.join(self.basedir, 'logs')
|
|
if not os.path.exists(logdir):
|
|
os.makedirs(logdir)
|
|
lf = logfile.LogFile('tahoesvc.log', logdir)
|
|
log.startLogging(lf)
|
|
|
|
if TRY_TO_INSTALL_TAHOE_SCRIPT:
|
|
self.maybe_install_tahoe_script()
|
|
|
|
# actually start up the node and the ui
|
|
os.chdir(self.basedir)
|
|
|
|
self.start_reactor()
|
|
|
|
try:
|
|
guiapp = MacGuiApp(app=self)
|
|
guiapp.MainLoop()
|
|
log.msg('gui mainloop exited')
|
|
except:
|
|
log.err()
|
|
|
|
self.stop_reactor()
|
|
|
|
return 0
|
|
|
|
def start_reactor(self):
|
|
self.reactor_shutdown = threading.Event()
|
|
thread.start_new_thread(self.launch_reactor, ())
|
|
|
|
def launch_reactor(self):
|
|
# run the node itself
|
|
c = client.Client(self.basedir)
|
|
reactor.callLater(0, c.startService) # after reactor startup
|
|
reactor.run(installSignalHandlers=False)
|
|
self.reactor_shutdown.set()
|
|
|
|
def stop_reactor(self):
|
|
# trigger reactor shutdown, and block waiting on it
|
|
reactor.callFromThread(reactor.stop)
|
|
log.msg('waiting for reactor shutdown')
|
|
self.reactor_shutdown.wait()
|
|
log.msg('reactor shut down')
|
|
|
|
def webopen(self):
|
|
if self.files_exist(['node.url', 'private/root_dir.cap']):
|
|
def read_file(f):
|
|
fh = file(f, 'rb')
|
|
contents = fh.read().strip()
|
|
fh.close()
|
|
return contents
|
|
nodeurl = read_file(os.path.join(self.basedir, 'node.url'))
|
|
if nodeurl[-1] != "/":
|
|
nodeurl += "/"
|
|
root_dir = read_file(os.path.join(self.basedir, 'private/root_dir.cap'))
|
|
url = nodeurl + "uri/%s/" % urllib.quote(root_dir)
|
|
webbrowser.open(url)
|
|
else:
|
|
print 'files missing, not opening initial webish root page'
|
|
|
|
def maybe_install_tahoe_script(self):
|
|
path_candidates = ['/usr/local/bin', '~/bin', '~/Library/bin']
|
|
env_path = map(os.path.expanduser, os.environ['PATH'].split(':'))
|
|
if not sys.executable.endswith('/python'):
|
|
print 'not installing tahoe script: unexpected sys.exe "%s"' % (sys.executable,)
|
|
return
|
|
for path_candidate in map(os.path.expanduser, env_path):
|
|
tahoe_path = path_candidate + '/tahoe'
|
|
if os.path.exists(tahoe_path):
|
|
print 'not installing "tahoe": it already exists at "%s"' % (tahoe_path,)
|
|
return
|
|
for path_candidate in map(os.path.expanduser, path_candidates):
|
|
if path_candidate not in env_path:
|
|
print path_candidate, 'not in', env_path
|
|
continue
|
|
tahoe_path = path_candidate + '/tahoe'
|
|
try:
|
|
print 'trying to install "%s"' % (tahoe_path,)
|
|
bin_path = (sys.executable[:-6] + 'Allmydata Tahoe').replace(' ', '\\ ')
|
|
script = TAHOE_SCRIPT % { 'exe': bin_path }
|
|
f = file(tahoe_path, 'wb')
|
|
f.write(script)
|
|
f.close()
|
|
mode = stat.S_IRUSR|stat.S_IXUSR|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH
|
|
os.chmod(tahoe_path, mode)
|
|
print 'installed "%s"' % (tahoe_path,)
|
|
return
|
|
except:
|
|
print 'unable to write %s' % (tahoe_path,)
|
|
traceback.print_exc()
|
|
else:
|
|
print 'no remaining candidate paths for installation of tahoe script'
|
|
|
|
|
|
def DisplayTraceback(message):
|
|
xc = traceback.format_exception(*sys.exc_info())
|
|
wx.MessageBox(u"%s\n (%s)"%(message,''.join(xc)), 'Error')
|
|
|
|
WEBOPEN_ID = wx.NewId()
|
|
ACCOUNT_PAGE_ID = wx.NewId()
|
|
|
|
class SplashFrame(wx.Frame):
|
|
def __init__(self):
|
|
wx.Frame.__init__(self, None, -1, 'Allmydata Tahoe')
|
|
|
|
self.SetSizeHints(100, 100, 600, 800)
|
|
self.SetIcon(amdicon.getIcon())
|
|
self.Bind(wx.EVT_CLOSE, self.on_close)
|
|
|
|
background = wx.Panel(self, -1)
|
|
background.parent = self
|
|
self.login_panel = SplashPanel(background, self.on_close)
|
|
sizer = wx.BoxSizer(wx.VERTICAL)
|
|
background_sizer = wx.BoxSizer(wx.VERTICAL)
|
|
background_sizer.Add(self.login_panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26)
|
|
background.SetSizer(background_sizer)
|
|
sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0)
|
|
self.SetSizer(sizer)
|
|
self.SetAutoLayout(True)
|
|
self.Fit()
|
|
self.Layout()
|
|
|
|
def on_close(self, event):
|
|
self.Show(False)
|
|
|
|
class SplashPanel(wx.Panel):
|
|
def __init__(self, parent, on_close):
|
|
wx.Panel.__init__(self, parent, -1)
|
|
self.parent = parent
|
|
|
|
self.sizer = wx.BoxSizer(wx.VERTICAL)
|
|
|
|
self.label = wx.StaticText(self, -1, 'Allmydata Tahoe')
|
|
font = self.label.GetFont()
|
|
font.SetPointSize(26)
|
|
self.label.SetFont(font)
|
|
self.ver_label = wx.StaticText(self, -1, str(allmydata.__version__))
|
|
self.ok = wx.Button(self, -1, 'Ok')
|
|
self.Bind(wx.EVT_BUTTON, on_close, self.ok)
|
|
self.sizer.Add(self.label, 0, wx.CENTER | wx.ALL, 2)
|
|
self.sizer.Add(self.ver_label, 0, wx.CENTER | wx.ALL, 2)
|
|
self.sizer.Add(wx.Size(42,42), 1, wx.EXPAND | wx.ALL, 2)
|
|
self.sizer.Add(self.ok, 0, wx.CENTER | wx.ALL, 2)
|
|
self.SetSizer(self.sizer)
|
|
self.SetAutoLayout(True)
|
|
|
|
|
|
class MacGuiApp(wx.App):
|
|
def __init__(self, app):
|
|
wx.App.__init__(self)
|
|
self.app = app
|
|
|
|
def OnInit(self):
|
|
try:
|
|
self.frame = SplashFrame()
|
|
self.frame.Show(True)
|
|
self.SetTopWindow(self.frame)
|
|
|
|
wx.FutureCall(4096, self.on_timer, None)
|
|
|
|
self.setup_dock_icon()
|
|
menubar = self.setup_app_menu(self.frame)
|
|
self.frame.SetMenuBar(menubar)
|
|
|
|
return True
|
|
except:
|
|
DisplayTraceback('exception on startup')
|
|
sys.exit()
|
|
|
|
def on_timer(self, event):
|
|
self.frame.Show(False)
|
|
|
|
def setup_dock_icon(self):
|
|
self.tbicon = wx.TaskBarIcon()
|
|
self.tbicon.SetIcon(amdicon.getIcon(), "Allmydata Tahoe")
|
|
wx.EVT_TASKBAR_RIGHT_UP(self.tbicon, self.on_dock_menu)
|
|
|
|
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)
|
|
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(wx.ID_ABOUT, text='About')
|
|
frame.Bind(wx.EVT_MENU, self.on_about, item)
|
|
item = file_menu.Append(wx.ID_EXIT, text='Quit')
|
|
frame.Bind(wx.EVT_MENU, self.on_quit, item)
|
|
menubar.Append(file_menu, 'File')
|
|
return menubar
|
|
|
|
def on_dock_menu(self, event):
|
|
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)
|
|
item = dock_menu.Append(ACCOUNT_PAGE_ID, text='Open Account Page')
|
|
self.tbicon.Bind(wx.EVT_MENU, self.on_account_page, item)
|
|
self.tbicon.PopupMenu(dock_menu)
|
|
|
|
def on_about(self, event):
|
|
self.frame.Show(True)
|
|
|
|
def on_quit(self, event):
|
|
self.ExitMainLoop()
|
|
|
|
def on_webopen(self, event):
|
|
self.app.webopen()
|
|
|
|
def on_account_page(self, event):
|
|
webbrowser.open(ACCOUNT_PAGE)
|
|
|
|
|
|
def main(argv):
|
|
if len(argv) == 1:
|
|
# then we were given no args; do default mac node startup
|
|
sys.exit(run_macapp())
|
|
else:
|
|
# given any cmd line args, do 'tahoe' cli behaviour
|
|
from allmydata.scripts import runner
|
|
sys.exit(runner.runner(argv[1:], install_node_control=False))
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv)
|
|
|