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 "${@}"
This commit is contained in:
robk-tahoe 2008-01-22 19:32:26 -07:00
parent 6ef4606534
commit 68c2d54c0b
9 changed files with 749 additions and 0 deletions

View File

@ -420,4 +420,19 @@ windows-installer-upload:
chmod -R o+rx windows/dist/installer
rsync -av -e /usr/bin/ssh windows/dist/installer/ amduser@dev:/home/amduser/public_html/dist/tahoe/windows/
# These targets provide for mac native builds
.PHONY: mac-exe mac-upload mac-cleanup mac-dbg
mac-exe: .built
$(MAKE) -C mac clean
VERSION=$(VER) $(PP) $(MAKE) -C mac build
mac-dist:
VERSION=$(VER) $(MAKE) -C mac diskimage upload
mac-cleanup:
VERSION=$(VER) $(MAKE) -C mac cleanup
mac-dbg:
cd mac && $(PP) $(PYTHON)w allmydata_tahoe.py

29
mac/Makefile Normal file
View File

@ -0,0 +1,29 @@
.PHONY: all dist build diskimage upload cleanup clean
all: build
dist: build diskimage upload cleanup
build:
python setup.py py2app --no-chdir
diskimage:
mv dist Allmydata-$(VERSION)
hdiutil create -ov -srcfolder Allmydata-$(VERSION) allmydata-rw.dmg
hdiutil convert -ov allmydata-rw.dmg -format UDRO -o allmydata-ro.dmg
hdiutil convert -ov allmydata-ro.dmg -format UDZO -o Allmydata-$(VERSION).dmg
hdiutil internet-enable -yes Allmydata-$(VERSION).dmg
rm -r Allmydata-$(VERSION) allmydata-rw.dmg allmydata-ro.dmg
upload:
mkdir $(VERSION)
mv Allmydata-$(VERSION).dmg $(VERSION)/
chmod go+rx $(VERSION)/
rsync -av $(VERSION) amduser@svn.allmydata.com:/home/amduser/public_html/dist/tahoe/mac/
cleanup:
rm -r $(VERSION)
clean:
rm -rf build dist

BIN
mac/allmydata.icns Normal file

Binary file not shown.

95
mac/allmydata_tahoe.py Normal file
View File

@ -0,0 +1,95 @@
import pkgreshook # override the pkg_resources zip provider for py2app deployment
pkgreshook.install() # this is done before nevow is imported by depends
import depends # import dependencies so that py2exe finds them
_junk = depends # appease pyflakes
import sys
import os
def run_default_node():
import operator
import os
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)
if not os.path.exists(os.path.join(basedir, 'webport')):
f = file(os.path.join(basedir, 'webport'), 'wb')
f.write('8123')
f.close()
def files_exist(file_list):
extant_conf = [ os.path.exists(os.path.join(basedir, f)) for f in file_list ]
return reduce(operator.__and__, extant_conf)
def is_config_incomplete():
necessary_conf_files = ['introducer.furl', 'private/root_dir.cap']
need_config = not files_exist(necessary_conf_files)
if need_config:
print 'some config is missing from basedir (%s): %s' % (basedir, necessary_conf_files)
return need_config
if is_config_incomplete():
#import wx
from confwiz import ConfWizApp
app = ConfWizApp()
app.MainLoop()
if is_config_incomplete():
print 'config still incomplete; confwiz cancelled, exiting'
return 1
from twisted.internet import reactor
from twisted.python import log, logfile
from allmydata import client
# set up twisted logging. this will become part of the node rsn.
logdir = os.path.join(basedir, 'logs')
if not os.path.exists(logdir):
os.makedirs(logdir)
lf = logfile.LogFile('tahoesvc.log', logdir)
log.startLogging(lf)
def webopen():
if files_exist(['node.url', 'private/root_dir.cap']):
def read_file(f):
fh = file(f, 'rb')
contents = fh.read().strip()
fh.close()
return contents
import urllib, webbrowser
nodeurl = read_file(os.path.join(basedir, 'node.url'))
if nodeurl[-1] != "/":
nodeurl += "/"
root_dir = read_file(os.path.join(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'
# run the node itself
os.chdir(basedir)
c = client.Client(basedir)
reactor.callLater(0, c.startService) # after reactor startup
reactor.callLater(4, webopen) # give node a chance to connect before loading root dir
reactor.run()
return 0
def main(argv):
if len(argv) == 1:
# then we were given no args; do default mac node startup
sys.exit(run_default_node())
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)

69
mac/amdicon.py Normal file
View File

@ -0,0 +1,69 @@
#----------------------------------------------------------------------
# This file was generated by encode_icons.py
#
from wx import ImageFromStream, BitmapFromImage
from wx import EmptyIcon
import cStringIO
def getData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x06\x00\
\x00\x00szz\xf4\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\x04\
\x1aIDATX\x85\xad\x97MLTW\x14\xc7\xff\xef\xbd\x997|\x8c\x86\xb1\x888\x0c\x06\
\x17\x80\x03\xa1\x8a\t\xa9\x8d5a\xd3t\xd3\xa4\xed\xae+\xe3\xa6\xe9\x02v\x8d\
\xdd\xb8\xd2%D\xb1\x06\x02]\xd9.\xba\xd0\xa4iBqaHHM\xcbnbGZeH\x8d\x16\xad\
\xed\x14\x91\xe1c\x98\xb9\xf7\xbdw\xcf\xe9\x02\x9c\xf2\xf1\xde\x9b7\xc8\x99L\
2\x99{\xce\xb9\xbfs\xee\xb9\xe7\xde\xabi\xba\x81\xa0\x12\x1eN2\xd9!\x90\x1d\
\xc2\xdb\x87\x92\xa8\xab\x8abY\xe41\xb34\x0b=\xec\xc00mX}\x19-\xb0C\x00\xd0t\
#\xd0\xd7\xb8\xde\xcd-\xa3\x1f\xf3\xcd\x99\t\xce\x895\xde*9\xb1\xc67g&\xb8e\
\xec#\x0e}u\x8a\x83\xfa\xd4t#\x18\x80q\xed4_\xb8s\x85\xcbIN\xac\xf1\x85;W\
\xd8\xb8v:0D\xf9\xc9\x87\xba\xf9\xfc\xc4eV;>S\xf3)\x9e\x9aO\xf1\xfdlf\xd7\
\xd8\xf9\x89\xcbl\\\xef\x0e\x06\xe1W\x03\xa1\x1b\x1d|&\xf6.\xee~:T\xfao\xe2\
\x8fi\\\xfci\x04\x7f\x15\x9e\x03:\x03\xa4!Q\xd3\x8c\xc1\xde>|\xd8z\xb6\xa4w\
\xe6\xdb\xcf\x90\x113\xb0\xfbg\xfdk\xa2\\\xea\xc7\x9fL\xf1\x92\xbd\xc8K\xf6"\
\x8f\xa4o\xb16\xd8\xc3\x91\xd1\xcem\xd1EF;Y\x1b\xec\xe1\x91\xf4\xad\x92\xee\
\xf8\x93\xa9@K\xa1\xfb\xc1\xc5\xab\xe2\xe8ij\x83$\x81\x97\xc5W\xb8to\x0cf\
\xb4\xb0\xab\xd2\xad\xbe\x8cfF\x0b\xb8to\x0c/\x8b\xaf I\xa0\xa7\xa9\r\xf1\
\xea\xb8\xff\x0e\x00\xbc\x01B7:\xf8\x9dD\x12\x92\x05$\x0b|\xf7p\x12y\xe4<\
\xb7\x99\xd5\x97\xd1\xf2\xc8\xe1\xc7\xc7\xd3%\x9bd\xfd1\x84\x87\x93\xbc\'\
\x00\x008z \x06I\x02\x92\x04rb\x15\x9a\xa1|\xa3\xd1\x0c\x85\xf9\x95l\xc9\xa6\
\xbd>\x01&\xff\x12\x08\xf9\r:\xec@\x92(\xfd\x0e"\x95\xda\x94\x01\xb0\xb78\
\xb3\x03\x02Tf\xe3\t\xa0\xa4\t\x87\x1d\x08*\x02\x00\xea\xa3Qt\x1e\xec\x00\
\xbe\xe9\xf0]\xd3\xfah\xb4d\xe3\xb0\x03%M_\x00\xd7>\xa0\r\x9e\xe2/\xce~\x82\
\x0f\xdaO\x96\x8d\xa0\x9c\xdc\x9d{\x80\xab\xd3?\x80/\xa6]\x8b\xc15\x03-\x07\
\xe3\xe8mm/\xa5\xf2M\xa4\xb7\xb5\x1d\xdf\xff\x16\xc7S\xa4]\xc7]\x01\xaa\xcd\
\xf0\xbeL\xbe\xd5\x9f\x97\xb8\x020\x13\xc4>\x020Se\x00\x04\x82do\x80\xb9\xec\
\x02&\x1feP\xb0,\xd4\x98&\xde\xef8\x81\xf6\xc6\x06O}B\xa5\x00L\xa5J\xde)\x0f\
\x9e\xfd\x83\xaf\x7f\xfe\x05\xe1\xda"\xec\xfeY-<\x9c\xe4\xfb\x93\x7f\xe2\xf3\
s\xef\xe1\xe4\xb1\xa3\xee\x00{\xca\x80\xcb\x12,;K\xb8\x9dJo;\x0f\xec\xfeY\
\xcd\x1c9\xc1\xb7S\xbf\xe2\xad\x06\x0bu\xa1\x18\xc2\xba\xb9\xcb\xdf\x1e\x00$\
\x14;X\xb0\xb3\xf8\xd7z\x81U\xb5\x02b\x85e\xa1\x83\xbf\xdc}\x18\xd9\x03\xd5<\
\x99\x1b\x07\x81P\xa5W\xe3p\xf8\x08\x8eW\xb5\xe1\x88\x19\xaf\x1c\x80\x99\xf0\
B>\xc3\xbc|\x0c\x8b,\x10\x14\x14\x13\x88\x15\x98j\\\x1d1i\x90,A\xacP\xa0u,\
\xda\x0bx\xb8\x9e\xc6a\xb3\x11k\x8ew}\xb8\x1eFy\xb5\x86\xb9\xe2\xef(\xa8\xf5\
\xcd\x83E\xc2\xa2\x8d\x13\xae\xb1Y\xc0\x18\xea\xda\xd6\r\x8d\xa1.\x8e5\xadl\
\xeaHX$7\xecX\xe2\xb9x\x8aU\xb5R\x19\xc0\xeb~.Y\xc2\xe2\xff\x9dY$\xd1p|\t\
\xb5Q\xa7\x04a\x0cuq\xa4V\xa2\xae\xe5\xefM\x1d\x01\x8b$\x1c\xb672\x06\xdf\
\xce\xed]\x03\x16I((\x10+\x106\xd2\xafX\x81\x0cB]3!\xff\xa8\x11\x00\xc0\xac\
\xe1@"\x0bG/\x80\xc9\x7f\xb2\xe0\x00\xbc\xd1\x07\x88is\xfd\xb7C\xd8\xac\xef\
\xd2/\x17iE\x00\xbc3\x03L\xdb\xb2\xe1\x90wk\xdd\x17\x00\x02C\x92\x00\x816\
\xa3W`\xe6=GY9\x00\x13,\x96\xbe\xa9%+\x04m\xa0\x9b\xc9\xaa\xec%\x16\x08\x80\
\xc1P\xec}\xff\x8b\xd4\xad"q.\xf5F\x13\xbf\x16\xd7mHN\xf0\x07k\x10\xf1\xf3\
\xe7\n\xa0\xa4\x89\xe2bl_&/.\xc6\xa0D\xc4s\xdc\xf3i\xa6\rt\xb3Q%\xa1\x87\xfc\
\xaf\xe2~B\x8e\x01%M\xcf\xeb\x18\x00\xfc\x07\x9aY\xdf_&\xed\xfd\xe8\x00\x00\
\x00\x00IEND\xaeB`\x82'
def getBitmap():
return BitmapFromImage(getImage())
def getImage():
stream = cStringIO.StringIO(getData())
return ImageFromStream(stream)
def getIcon():
icon = EmptyIcon()
icon.CopyFromBitmap(getBitmap())
return icon

407
mac/confwiz.py Normal file
View File

@ -0,0 +1,407 @@
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()

27
mac/depends.py Normal file
View File

@ -0,0 +1,27 @@
# nevow requires all these for its voodoo module import time adaptor registrations
from nevow import accessors, appserver, static, rend, url, util, query, i18n, flat
from nevow import guard, stan, testutil, context
from nevow.flat import flatmdom, flatstan, twist
from formless import webform, processors, annotate, iformless
from decimal import Decimal
#if sys.platform in ['darwin', ]:
from nevow.flat import flatsax
from xml.parsers import expat
from xml.sax import expatreader, sax2exts
from xml.sax.drivers2 import drv_pyexpat, drv_xmlproc
junk = [ flatsax, expat, expatreader, sax2exts, drv_pyexpat, drv_xmlproc, ]
import allmydata.web
# junk to appease pyflakes's outrage at py2exe's needs
junk = [
accessors, appserver, static, rend, url, util, query, i18n, flat, guard, stan, testutil,
context, flatmdom, flatstan, twist, webform, processors, annotate, iformless, Decimal,
allmydata,
]

46
mac/pkgreshook.py Normal file
View File

@ -0,0 +1,46 @@
def install():
"""
This installs a hook into setuptools' pkg_resources infrastructure, so that resource
files can be found in files relative to the runnin executable, in addition to the
usual egg and source lookup mechanisms. This overrides the ZipProvider, since that
is the lookup mechanism triggered within pkg_resources when running code out of a
py2exe or py2app build's library.zip.
"""
import os, sys
import pkg_resources, zipimport
platform_libdirs = {
'darwin': '../Resources/pkg_resources',
}
exedir = os.path.dirname(sys.executable)
libdir = platform_libdirs.get(sys.platform, 'pkg_resources')
class Provider(pkg_resources.ZipProvider):
def __init__(self, module):
self._module_name = module.__name__
pkg_resources.ZipProvider.__init__(self, module)
def get_resource_filename(self, manager, resource_name):
#print 'get_resource_filename(%s, %s)' % (manager, resource_name)
path = [exedir, libdir] + self._module_name.split('.') + [resource_name]
localfile = os.path.join(*path)
#print ' checking(%s)' % (localfile,)
if os.path.exists(localfile):
#print 'found locally'
return localfile
else:
try:
ret = pkg_resources.ZipProvider.get_resource_filename(self, manager, resource_name)
#print 'returning %s' % (ret,)
return ret
except NotImplementedError:
print 'get_resource_filename(%s,%s): not found' % (self._module_name, resource_name)
import traceback
traceback.print_exc()
return ''
pkg_resources.register_loader_type(zipimport.zipimporter, Provider)

61
mac/setup.py Normal file
View File

@ -0,0 +1,61 @@
from setuptools import setup
import py2app
import glob
import os
import sys
# pull in formless, as best way to grab its .css file depenedency
import formless
def find_formless_css():
fpath = formless.__path__[0]
# first look for it from a regular package install
f = os.path.join(fpath, 'freeform-default.css')
if os.path.exists(f):
return f
# then try looking within .egg structured files
pyver = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
f = os.path.join(fpath, '../lib', pyver, 'site-packages/formless/freeform-default.css')
if os.path.exists(f):
return f
raise RuntimeError("Can't find formless .css file")
data_files = [
('pkg_resources/allmydata/web', glob.glob('../src/allmydata/web/*')),
('pkg_resources/formless', [find_formless_css()]),
]
from setuptools import find_packages
packages = find_packages('../src')
py2app_options = {
'argv_emulation': True,
'iconfile': 'allmydata.icns',
'plist': { 'CFBundleIconFile': 'allmydata.icns', },
}
setup_args = {
'name': 'Allmydata Tahoe',
'description': 'The various parts of the Allmydata Tahoe system',
'author': 'Allmydata, Inc.',
'app': [ 'allmydata_tahoe.py' ],
'options': { 'py2app': py2app_options },
'data_files': data_files,
'setup_requires': [ 'py2app' ],
'packages': packages,
}
if __name__ == '__main__':
if not os.path.exists('allmydata'):
os.symlink('../src/allmydata', 'allmydata')
setup(**setup_args)
junk = [formless, py2app]
del junk