tahoe-lafs/mac/confwiz.py

408 lines
14 KiB
Python
Raw Normal View History

add mac native build This patch adds support for a mac native build. At the moment it's a fairly simple .app - i.e. so simple as to be unacceptable for a shipping product, but ok for testing and experiment at this point. notably once launched, the app's ui does not respond at all, although its dock icon does allow it to be force-quit. this produces a single .app bundle, which when run will look for a node basedir in ~/.tahoe. If one is not found, one will be created in ~/Library/Application Support/Allmydata Tahoe, and that will be symlinked to ~/.tahoe if the basedir is lacking basic config (introducer.furl and root_dir.cap) then the wx config wizard will be launched to log into an account and to set up those files. if a webport file is not found, the default value of 8123 will be written into it. once the node has started running, a webbrowser will be opened to the webish interface at the users root_dir note that, once configured, the node runs as the main thread of the .app, no daemonisation is done, twistd is not involved. the binary itself, from within the .app bundle, i.e. "Allmydata Tahoe.app/Contents/MacOS/Allmydata Tahoe" can be used from the command line and functions as the 'tahoe' executable would in a unix environment, with one exception - when launched with no args it triggers the default behaviour of running a node, and if necessary config wizard, as if the user had launched the .app one other gotcha to be aware of is that symlinking to this binary from some other place in ones $PATH will most likely not work. when I tried this, something - wx I believe - exploded, since it seems to use argv[0] to figure out where necessary libraries reside and fails if argv[0] isn't in the .app bundle. it's pretty easy to set up a script a la #!/bin/bash /Blah/blah/blah/Allmydata\ Tahoe.app/Contents/MacOS/Allmydata\ Tahoe "${@}"
2008-01-23 02:32:26 +00:00
BACKEND_URL = 'https://www-test.allmydata.com/native_client.php'
#REGISTER_PAGE = 'https://www-test.allmydata.com/register'
TAHOESVC_NAME = 'Tahoe'
WINFUSESVC_NAME = 'Allmydata Tahoe SMB'
import os
import sys
#import time
import traceback
import urllib2
from urllib import urlencode
#import webbrowser
import wx
from allmydata.util.assertutil import precondition
from allmydata import uri
import amdicon
class AuthError(Exception):
pass
def unicode_to_utf8(uobj):
assert precondition(isinstance(uobj, unicode))
return uobj.encode('utf-8')
def post(url, args):
argstr = urlencode(args)
conn = urllib2.urlopen(url, argstr)
return conn.read()
def get_root_cap(url, user, passwd):
args = {
'action': 'authenticate',
'email': unicode_to_utf8(user),
'passwd': unicode_to_utf8(passwd),
}
root_cap = post(url, args)
if root_cap == '0':
raise AuthError()
elif not uri.is_uri(root_cap):
raise ValueError('%r is not a URI' % (root_cap,))
else:
return root_cap
def create_account(url, user, passwd, subscribe):
args = {
'action': 'create_account',
'email': unicode_to_utf8(user),
'passwd': unicode_to_utf8(passwd),
'subscribe': subscribe and 'true' or 'false',
}
result_code = post(url, args)
return result_code
def get_introducer_furl(url):
return post(url, { 'action': 'getintroducerfurl' })
def write_config_file(filename, contents):
if sys.platform == 'win32':
from allmydata.windows import registry
basedir = registry.get_base_dir_path()
else:
basedir = os.path.expanduser('~/.tahoe')
path = os.path.join(basedir, filename)
dirname = os.path.dirname(path)
if not os.path.exists(dirname):
os.makedirs(dirname)
iff = file(path, 'wb')
iff.write(contents)
iff.close()
def DisplayTraceback(message):
xc = traceback.format_exception(*sys.exc_info())
wx.MessageBox(u"%s\n (%s)"%(message,''.join(xc)), 'Error')
class ConfWizApp(wx.App):
def __init__(self):
wx.App.__init__(self, 0)
def OnInit(self):
try:
wx.InitAllImageHandlers()
self.login_frame = LoginFrame(self)
self.login_frame.CenterOnScreen()
self.SetTopWindow(self.login_frame)
#self.SetExitOnFrameDelete(True)
self.login_frame.Show(True)
return True
except:
DisplayTraceback('config wizard init threw an exception')
def swap_to_register_frame(self):
try:
self.login_frame.Show(False)
self.regiser_frame = RegisterFrame(self)
self.regiser_frame.CenterOnScreen()
self.SetTopWindow(self.regiser_frame)
self.SetExitOnFrameDelete(True)
self.regiser_frame.Show(True)
except:
DisplayTraceback('config wizard threw an exception')
class LoginFrame(wx.Frame):
def __init__(self, app):
title = 'Allmydata Tahoe Config Wizard'
wx.Frame.__init__(self, None, -1, title)
self.app = app
self.SetSizeHints(100, 100, 600, 800)
self.SetIcon(amdicon.getIcon())
self.Bind(wx.EVT_CLOSE, self.close)
background = wx.Panel(self, -1)
background.parent = self
self.login_panel = LoginPanel(background)
self.reg_btn_panel = RegisterButtonPanel(background, app)
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_sizer.Add(self.reg_btn_panel, 0, 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 close(self, event):
self.Show(False)
self.app.ExitMainLoop()
class RegisterFrame(wx.Frame):
def __init__(self, app):
title = 'Allmydata Tahoe Config Wizard'
wx.Frame.__init__(self, None, -1, title)
self.app = app
self.SetSizeHints(100, 100, 600, 800)
self.SetIcon(amdicon.getIcon())
self.Bind(wx.EVT_CLOSE, self.close)
background = wx.Panel(self, -1)
background.parent = self
self.register_panel = RegisterPanel(background)
sizer = wx.BoxSizer(wx.VERTICAL)
background_sizer = wx.BoxSizer(wx.VERTICAL)
background_sizer.Add(self.register_panel, 0, 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 close(self, event):
self.Show(False)
self.app.ExitMainLoop()
class LoginPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.parent = parent
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.user_label = wx.StaticText(self, -1, 'Email')
self.pass_label = wx.StaticText(self, -1, 'Password')
self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
self.login_button = wx.Button(self, -1, 'Sign In')
self.warning_label = wx.StaticText(self, -1, '')
self.warning_label.SetOwnForegroundColour(wx.RED)
wx.EVT_CHAR(self.user_field, self.on_user_entry)
wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
self.Bind(wx.EVT_BUTTON, self.on_login, self.login_button)
login_sizer = wx.FlexGridSizer(3, 2, 5, 4)
login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.login_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2)
self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
def on_user_entry(self, event):
if event.GetKeyCode() == wx.WXK_RETURN:
self.pass_field.SetFocus()
else:
event.Skip()
def on_pass_entry(self, event):
if event.GetKeyCode() == wx.WXK_RETURN:
self.on_login(event)
else:
event.Skip()
def on_login(self, event):
user = self.user_field.GetValue()
passwd = self.pass_field.GetValue()
self.warning_label.SetLabel('Connecting...')
self.Layout()
wx.Yield()
if passwd == '':
self.warning_label.SetLabel('You must enter a password')
self.pass_field.SetFocus()
self.Layout()
return
try:
root_cap = get_root_cap(BACKEND_URL, user, passwd)
write_config_file('private/root_dir.cap', root_cap+'\n')
except AuthError:
self.warning_label.SetLabel('Your email and/or password is incorrect')
self.user_field.SetFocus()
self.Layout()
return
# fetch the introducer furl
ifurl = get_introducer_furl(BACKEND_URL)
write_config_file('introducer.furl', ifurl+'\n')
# start service etc.
if sys.platform == 'win32':
self.start_windows_service(TAHOESVC_NAME)
self.start_windows_service(WINFUSESVC_NAME)
# exit
self.parent.parent.Close()
def start_windows_service(self, svc_name):
try:
import win32service
import win32serviceutil as wsu
if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING:
wsu.StartService(svc_name)
except:
DisplayTraceback('Failed to start windows service "%s"' % (svc_name,))
class RegisterButtonPanel(wx.Panel):
def __init__(self, parent, app):
wx.Panel.__init__(self, parent, -1)
self.parent = parent
self.app = app
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.reg_label = wx.StaticText(self, -1, "Don't have an account?")
self.reg_button = wx.Button(self, -1, 'Create Account')
self.Bind(wx.EVT_BUTTON, self.on_reg_button, self.reg_button)
reg_sizer = wx.FlexGridSizer(1, 2, 5, 4)
reg_sizer.Add(self.reg_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
reg_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
self.sizer.Add(reg_sizer, 1, wx.EXPAND | wx.ALL, 2)
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
def on_reg_button(self, event):
#webbrowser.open(REGISTER_PAGE)
self.app.swap_to_register_frame()
class RegisterPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent, -1)
self.parent = parent
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.user_label = wx.StaticText(self, -1, 'Email')
self.pass_label = wx.StaticText(self, -1, 'Password')
self.conf_label = wx.StaticText(self, -1, 'Confirm Password')
self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
self.conf_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
self.create_account_button = wx.Button(self, -1, 'Create Account')
self.subscribe_box = wx.CheckBox(self, -1, 'Sign up for our Newsletter')
self.subscribe_box.SetValue(True)
self.warning_label = wx.StaticText(self, -1, '')
self.warning_label.SetOwnForegroundColour(wx.RED)
wx.EVT_CHAR(self.user_field, self.on_user_entry)
wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
wx.EVT_CHAR(self.conf_field, self.on_conf_entry)
self.Bind(wx.EVT_BUTTON, self.on_create_account, self.create_account_button)
login_sizer = wx.FlexGridSizer(4, 2, 5, 4)
login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
login_sizer.Add(self.conf_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.conf_field, 0, wx.EXPAND | wx.ALL, 2)
login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2)
login_sizer.Add(self.create_account_button, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2)
self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
self.sizer.Add(self.subscribe_box, 0, wx.CENTER | wx.ALL, 2)
self.SetSizer(self.sizer)
self.SetAutoLayout(True)
def on_user_entry(self, event):
if event.GetKeyCode() == wx.WXK_RETURN:
self.pass_field.SetFocus()
else:
event.Skip()
def on_pass_entry(self, event):
if event.GetKeyCode() == wx.WXK_RETURN:
self.conf_field.SetFocus()
else:
event.Skip()
def on_conf_entry(self, event):
if event.GetKeyCode() == wx.WXK_RETURN:
self.on_create_account(event)
else:
event.Skip()
def on_create_account(self, event):
user = self.user_field.GetValue()
passwd = self.pass_field.GetValue()
pconf = self.conf_field.GetValue()
subscribe = self.subscribe_box.IsChecked()
self.warning_label.SetLabel('Connecting...')
self.Layout()
wx.Yield()
if passwd == '':
self.warning_label.SetLabel('You must enter a password')
self.pass_field.SetFocus()
self.Layout()
return
if passwd != pconf:
self.warning_label.SetLabel("Passwords don't match")
self.pass_field.SetValue('')
self.conf_field.SetValue('')
self.pass_field.SetFocus()
self.Layout()
return
#print 'calling create_account', time.asctime()
result_code = create_account(BACKEND_URL, user, passwd, subscribe)
if result_code == 'account_exists':
# try and log into it; if valid, use it anyway
try:
#print 'calling get_root_cap (ae)', time.asctime()
root_cap = get_root_cap(BACKEND_URL, user, passwd)
write_config_file('private/root_dir.cap', root_cap+'\n')
except AuthError:
self.warning_label.SetLabel('That email address is already registered')
self.user_field.SetFocus()
self.Layout()
return
elif result_code == 'error':
self.warning_label.SetLabel('an error occurred')
self.user_field.SetFocus()
self.Layout()
return
elif result_code == 'ok':
#print 'calling get_root_cap (ok)', time.asctime()
root_cap = get_root_cap(BACKEND_URL, user, passwd)
write_config_file('private/root_dir.cap', root_cap+'\n')
else:
self.warning_label.SetLabel('an unexpected error occurred ("%s")' % (result_code,))
self.user_field.SetFocus()
self.Layout()
return
# fetch the introducer furl
#print 'calling get_introducer_furl', time.asctime()
ifurl = get_introducer_furl(BACKEND_URL)
write_config_file('introducer.furl', ifurl+'\n')
# start service etc.
if sys.platform == 'win32':
self.start_windows_service(TAHOESVC_NAME)
self.start_windows_service(WINFUSESVC_NAME)
# exit
self.parent.parent.Close()
def start_windows_service(self, svc_name):
try:
import win32service
import win32serviceutil as wsu
if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING:
wsu.StartService(svc_name)
except:
DisplayTraceback('Failed to start windows service "%s"' % (svc_name,))
def main():
app = ConfWizApp()
app.MainLoop()
if __name__ == '__main__':
main()