2 DEFAULT_SERVER_URL = 'https://www.allmydata.com/'
4 BACKEND = 'native_client.php'
5 ACCOUNT_PAGE = 'account'
6 WELCOME_PAGE = 'welcome_install'
7 TAHOESVC_NAME = 'Tahoe'
8 WINFUSESVC_NAME = 'Allmydata SMB'
10 CONVERGENCE_DOMAIN_TAG = "allmydata_root_cap_to_convergence_domain_tag_v1"
19 from urllib import urlencode
23 from allmydata.util.assertutil import precondition
24 from allmydata.util import hashutil, base32
25 from allmydata import uri
31 from foolscap.api import Tub
32 from twisted.python import usage
34 class AuthError(Exception):
37 def unicode_to_utf8(uobj):
38 assert precondition(isinstance(uobj, unicode))
39 return uobj.encode('utf-8')
43 argstr = urlencode(args)
44 conn = urllib2.urlopen(url, argstr)
47 def get_root_cap(url, user, passwd):
49 'action': 'authenticate',
50 'email': unicode_to_utf8(user),
51 'passwd': unicode_to_utf8(passwd),
53 root_cap = post(url, args)
56 elif not uri.is_uri(root_cap):
57 raise ValueError('%r is not a URI' % (root_cap,))
61 def create_account(url, user, passwd, subscribe):
63 'action': 'create_account',
64 'email': unicode_to_utf8(user),
65 'passwd': unicode_to_utf8(passwd),
66 'subscribe': subscribe and 'true' or 'false',
68 result_code = post(url, args)
71 def record_install(url, user, passwd, nodeid, nickname):
73 'action': 'record_install',
74 'email': unicode_to_utf8(user),
75 'passwd': unicode_to_utf8(passwd),
79 result_code = post(url, args)
82 def record_uninstall(url, nodeid):
84 'action': 'record_uninstall',
87 result_code = post(url, args)
90 def get_introducer_furl(url):
91 return post(url, { 'action': 'getintroducerfurl' })
93 def get_config(url, user, passwd):
95 'action': 'get_config',
96 'email': unicode_to_utf8(user),
97 'passwd': unicode_to_utf8(passwd),
99 config = post(url, args)
103 if sys.platform == 'win32':
104 from allmydata.windows import registry
105 return registry.get_base_dir_path()
107 return os.path.expanduser('~/.tahoe')
109 def write_config_file(filename, contents):
110 basedir = get_basedir()
111 path = os.path.join(basedir, filename)
112 dirname = os.path.dirname(path)
113 if not os.path.exists(dirname):
115 iff = file(path, 'wb')
119 def write_root_cap(root_cap):
120 write_config_file('private/root_dir.cap', root_cap+'\n')
121 convergence = base32.b2a(hashutil.tagged_hash(CONVERGENCE_DOMAIN_TAG, root_cap))
122 write_config_file('private/convergence', convergence+'\n')
125 CERTFILE = "node.pem"
126 certfile = os.path.join(get_basedir(), "private", CERTFILE)
127 tub = Tub(certFile=certfile)
128 return tub.getTubID()
132 nnfile = os.path.join(get_basedir(), 'nickname')
133 if os.path.exists(nnfile):
135 fh = file(nnfile, 'rb')
136 nick = fh.read().strip()
139 DisplayTraceback('Failed to read existing nickname file %s' % (nnfile,))
141 nick = socket.gethostname()
144 def maybe_write_file(filename, contents):
145 fname = os.path.join(get_basedir(), filename)
147 if not os.path.exists(fname):
148 fh = file(fname, 'wb')
153 DisplayTraceback('Failed to write file %s' % (fname,))
155 def configure(backend, user, passwd):
156 _config_re = re.compile('^([^:]*): (.*)$')
157 config = get_config(backend, user, passwd)
159 for line in config.split('\n'):
161 m = _config_re.match(line)
163 fname, contents = m.groups()
164 config_dict[fname] = contents
165 for fname, contents in config_dict.items():
166 write_config_file(fname, contents+'\n')
168 def start_windows_service(svc_name):
171 import win32serviceutil as wsu
172 if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING:
173 wsu.StartService(svc_name)
175 DisplayTraceback('Failed to start windows service "%s"' % (svc_name,))
177 def maybe_start_services():
178 if sys.platform == 'win32':
179 start_windows_service(TAHOESVC_NAME)
180 start_windows_service(WINFUSESVC_NAME)
182 def DisplayTraceback(message):
183 xc = traceback.format_exception(*sys.exc_info())
184 wx.MessageBox(u"%s\n (%s)"%(message,''.join(xc)), 'Error')
186 class ConfWizApp(wx.App):
187 def __init__(self, server, open_welcome_page=False):
189 self.show_welcome = open_welcome_page
190 wx.App.__init__(self, 0)
192 def get_backend(self):
193 return self.server + BACKEND
195 def open_welcome_page(self):
196 if self.show_welcome:
197 args = {'v': str(allmydata.__version__),
198 'plat': sys.platform,
200 webbrowser.open(self.server + WELCOME_PAGE + '?' + urlencode(args))
204 wx.InitAllImageHandlers()
206 self.login_frame = WizardFrame(self, LoginPanel)
207 self.login_frame.CenterOnScreen()
208 self.SetTopWindow(self.login_frame)
209 #self.SetExitOnFrameDelete(True)
210 self.login_frame.Show(True)
214 DisplayTraceback('config wizard init threw an exception')
216 def swap_to_register_frame(self):
218 self.login_frame.Show(False)
219 self.regiser_frame = WizardFrame(self, RegisterPanel)
220 self.regiser_frame.CenterOnScreen()
221 self.SetTopWindow(self.regiser_frame)
222 self.SetExitOnFrameDelete(True)
223 self.regiser_frame.Show(True)
225 DisplayTraceback('config wizard threw an exception')
227 class WizardFrame(wx.Frame):
228 def __init__(self, app, panel_class):
229 #title = 'Allmydata Config Wizard'
230 title = 'Setup - Allmydata'
231 wx.Frame.__init__(self, None, -1, title)
233 self.SetIcon(amdlogo.getIcon())
234 self.Bind(wx.EVT_CLOSE, self.close)
236 self.SetSizeHints(500, 360, 600, 800)
238 banner = wx.Panel(self, -1)
239 banner.SetSize((496,58))
240 banner.SetBackgroundColour(wx.WHITE)
242 banner_title = wx.StaticText(banner, -1, panel_class.title)
243 banner_desc = wx.StaticText(banner, -1, " " + panel_class.description)
244 font = banner_title.GetFont()
245 font.SetWeight(wx.FONTWEIGHT_BOLD)
246 banner_title.SetFont(font)
247 banner_icon = wx.StaticBitmap(banner, -1, amdlogo.getBitmap())
248 banner_label_sizer = wx.BoxSizer(wx.VERTICAL)
249 banner_label_sizer.Add(banner_title, 0, wx.EXPAND | wx.ALL, 2)
250 banner_label_sizer.Add(banner_desc, 0, wx.EXPAND | wx.ALL, 2)
252 banner_sizer = wx.BoxSizer(wx.HORIZONTAL)
253 banner_sizer.Add(banner_label_sizer, 1, wx.EXPAND | wx.ALL, 12)
254 banner_sizer.Add(banner_icon, 0, wx.ALL, 12)
255 banner.SetSizer(banner_sizer)
256 banner.SetAutoLayout(True)
258 background = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
259 background.parent = self
261 button_panel = wx.Panel(self, -1)
262 button_panel.SetSize((496, 64))
264 self.panel = panel_class(background, button_panel, app)
265 sizer = wx.BoxSizer(wx.VERTICAL)
266 background_sizer = wx.BoxSizer(wx.VERTICAL)
267 background_sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, self.panel.padding)
268 background_sizer.Add(self.panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26)
269 background_sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, self.panel.padding)
270 background.SetSizer(background_sizer)
273 sizer.Add(banner, 0, wx.EXPAND | wx.HORIZONTAL, 0)
274 sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0)
275 sizer.Add(button_panel, 0, wx.EXPAND | wx.HORIZONTAL, 0)
277 self.SetAutoLayout(True)
281 def close(self, event):
283 self.app.ExitMainLoop()
286 class LoginPanel(wx.Panel):
289 description = 'Sign in to your existing account'
291 def __init__(self, parent, button_panel, app):
292 wx.Panel.__init__(self, parent, -1)
296 self.sizer = wx.BoxSizer(wx.VERTICAL)
298 self.user_label = wx.StaticText(self, -1, 'Email')
299 self.pass_label = wx.StaticText(self, -1, 'Password')
300 self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
301 self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
302 self.warning_label = wx.StaticText(self, -1, '')
303 self.warning_label.SetOwnForegroundColour(wx.RED)
304 wx.EVT_CHAR(self.user_field, self.on_user_entry)
305 wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
306 login_sizer = wx.FlexGridSizer(2, 2, 5, 4)
307 login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
308 login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
309 login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
310 login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
311 self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2)
312 self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
313 self.SetSizer(self.sizer)
314 self.SetAutoLayout(True)
316 self.reg_label = wx.StaticText(button_panel, -1, "Don't have an account?")
317 self.reg_button = wx.Button(button_panel, -1, 'Create Account')
318 self.login_button = wx.Button(button_panel, -1, 'Sign In')
319 button_panel.Bind(wx.EVT_BUTTON, self.on_reg_button, self.reg_button)
320 button_panel.Bind(wx.EVT_BUTTON, self.on_login, self.login_button)
321 btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
322 btn_sizer.Add(wx.Size(2,2), 1, wx.EXPAND | wx.ALL, 12)
323 btn_sizer.Add(self.reg_label, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
324 btn_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
325 btn_sizer.Add(self.login_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
326 button_panel.SetSizer(btn_sizer)
327 self.button_panel = button_panel
329 def on_reg_button(self, event):
330 self.app.swap_to_register_frame()
332 def on_user_entry(self, event):
333 if event.GetKeyCode() == wx.WXK_RETURN:
334 self.pass_field.SetFocus()
338 def on_pass_entry(self, event):
339 if event.GetKeyCode() == wx.WXK_RETURN:
344 def on_login(self, event):
345 user = self.user_field.GetValue()
346 passwd = self.pass_field.GetValue()
347 self.warning_label.SetLabel('Connecting...')
351 backend = self.app.get_backend()
354 self.warning_label.SetLabel('You must enter a password')
355 self.pass_field.SetFocus()
360 root_cap = get_root_cap(backend, user, passwd)
361 write_root_cap(root_cap)
363 self.warning_label.SetLabel('Your email and/or password is incorrect')
364 self.user_field.SetFocus()
368 nodeid = get_nodeid()
369 nickname = get_nickname()
370 ret = record_install(backend, user, passwd, nodeid, nickname)
372 wx.MessageBox('Error "%s" recording this system (%s)' % (ret, nodeid), 'Error')
374 configure(backend, user, passwd)
375 maybe_start_services()
376 maybe_write_file('nickname', nickname)
377 maybe_write_file('accountname', user)
379 self.app.open_welcome_page()
382 self.parent.parent.Close()
384 class RegisterPanel(wx.Panel):
386 title = 'Create account'
387 description = 'Create a new account on Allmydata'
389 def __init__(self, parent, button_panel, app):
390 wx.Panel.__init__(self, parent, -1)
394 self.sizer = wx.BoxSizer(wx.VERTICAL)
396 self.user_label = wx.StaticText(self, -1, 'Email')
397 self.pass_label = wx.StaticText(self, -1, 'Password')
398 self.conf_label = wx.StaticText(self, -1, 'Confirm Password')
399 self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
400 self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
401 self.conf_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
402 self.subscribe_box = wx.CheckBox(self, -1, 'Sign up for our Newsletter')
403 self.subscribe_box.SetValue(True)
404 self.warning_label = wx.StaticText(self, -1, '')
405 self.warning_label.SetOwnForegroundColour(wx.RED)
406 wx.EVT_CHAR(self.user_field, self.on_user_entry)
407 wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
408 wx.EVT_CHAR(self.conf_field, self.on_conf_entry)
409 login_sizer = wx.FlexGridSizer(3, 2, 5, 4)
410 login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
411 login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
412 login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
413 login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
414 login_sizer.Add(self.conf_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
415 login_sizer.Add(self.conf_field, 0, wx.EXPAND | wx.ALL, 2)
416 login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2)
417 self.sizer.Add(login_sizer, 0, wx.EXPAND | wx.ALL, 2)
418 self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
419 self.sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, 4)
420 self.sizer.Add(self.subscribe_box, 0, wx.CENTER | wx.ALL, 2)
421 self.SetSizer(self.sizer)
422 self.SetAutoLayout(True)
424 self.reg_button = wx.Button(button_panel, -1, 'Create Account')
425 button_panel.Bind(wx.EVT_BUTTON, self.on_create_account, self.reg_button)
426 btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
427 btn_sizer.Add(wx.Size(2,2), 1, wx.EXPAND | wx.ALL, 12)
428 btn_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
429 button_panel.SetSizer(btn_sizer)
430 self.button_panel = button_panel
434 def on_user_entry(self, event):
435 if event.GetKeyCode() == wx.WXK_RETURN:
436 self.pass_field.SetFocus()
440 def on_pass_entry(self, event):
441 if event.GetKeyCode() == wx.WXK_RETURN:
442 self.conf_field.SetFocus()
446 def on_conf_entry(self, event):
447 if event.GetKeyCode() == wx.WXK_RETURN:
448 self.on_create_account(event)
452 def on_create_account(self, event):
453 user = self.user_field.GetValue()
454 passwd = self.pass_field.GetValue()
455 pconf = self.conf_field.GetValue()
456 subscribe = self.subscribe_box.IsChecked()
457 self.warning_label.SetLabel('Connecting...')
462 self.warning_label.SetLabel('You must enter a password')
463 self.pass_field.SetFocus()
468 self.warning_label.SetLabel("Passwords don't match")
469 self.pass_field.SetValue('')
470 self.conf_field.SetValue('')
471 self.pass_field.SetFocus()
475 backend = self.app.get_backend()
477 #print 'calling create_account', time.asctime()
478 result_code = create_account(backend, user, passwd, subscribe)
480 if result_code == 'account_exists':
481 # try and log into it; if valid, use it anyway
483 #print 'calling get_root_cap (ae)', time.asctime()
484 root_cap = get_root_cap(backend, user, passwd)
485 write_root_cap(root_cap)
487 self.warning_label.SetLabel('That email address is already registered')
488 self.user_field.SetFocus()
491 elif result_code == 'error':
492 self.warning_label.SetLabel('an error occurred')
493 self.user_field.SetFocus()
496 elif result_code == 'ok':
497 #print 'calling get_root_cap (ok)', time.asctime()
498 root_cap = get_root_cap(backend, user, passwd)
499 write_root_cap(root_cap)
501 self.warning_label.SetLabel('an unexpected error occurred ("%s")' % (result_code,))
502 self.user_field.SetFocus()
506 nodeid = get_nodeid()
507 nickname = get_nickname()
508 ret = record_install(backend, user, passwd, nodeid, nickname)
510 wx.MessageBox('Error "%s" recording this system (%s)' % (ret, nodeid), 'Error')
512 configure(backend, user, passwd)
513 maybe_start_services()
514 maybe_write_file('nickname', nickname)
515 maybe_write_file('accountname', user)
517 self.app.open_welcome_page()
520 self.parent.parent.Close()
522 def do_uninstall(server_url):
523 nodeid = get_nodeid()
524 ret = record_uninstall(server_url + BACKEND, nodeid)
527 print 'Error "%s" recording uninstall of this system (%s)' % (ret, nodeid)
529 class Options(usage.Options):
530 synopsis = "Usage: confwiz [options]"
533 ['uninstall', 'u', 'record uninstall'],
536 ['server', 's', DEFAULT_SERVER_URL, 'url of server to contact'],
542 config.parseOptions(argv[1:])
543 except usage.error, e:
545 print "%s: %s" % (sys.argv[0], e)
548 server = config['server']
549 if not server.endswith('/'):
552 if config['uninstall']:
555 app = ConfWizApp(server)
559 if __name__ == '__main__':