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
27 from allmydata.gui import amdlogo
29 from foolscap.api import Tub
30 from twisted.python import usage
32 class AuthError(Exception):
35 def unicode_to_utf8(uobj):
36 assert precondition(isinstance(uobj, unicode))
37 return uobj.encode('utf-8')
41 argstr = urlencode(args)
42 conn = urllib2.urlopen(url, argstr)
45 def get_root_cap(url, user, passwd):
47 'action': 'authenticate',
48 'email': unicode_to_utf8(user),
49 'passwd': unicode_to_utf8(passwd),
51 root_cap = post(url, args)
54 elif not uri.is_uri(root_cap):
55 raise ValueError('%r is not a URI' % (root_cap,))
59 def create_account(url, user, passwd, subscribe):
61 'action': 'create_account',
62 'email': unicode_to_utf8(user),
63 'passwd': unicode_to_utf8(passwd),
64 'subscribe': subscribe and 'true' or 'false',
66 result_code = post(url, args)
69 def record_install(url, user, passwd, nodeid, nickname):
71 'action': 'record_install',
72 'email': unicode_to_utf8(user),
73 'passwd': unicode_to_utf8(passwd),
77 result_code = post(url, args)
80 def record_uninstall(url, nodeid):
82 'action': 'record_uninstall',
85 result_code = post(url, args)
88 def get_introducer_furl(url):
89 return post(url, { 'action': 'getintroducerfurl' })
91 def get_config(url, user, passwd):
93 'action': 'get_config',
94 'email': unicode_to_utf8(user),
95 'passwd': unicode_to_utf8(passwd),
97 config = post(url, args)
101 if sys.platform == 'win32':
102 from allmydata.windows import registry
103 return registry.get_base_dir_path()
105 return os.path.expanduser('~/.tahoe')
107 def write_config_file(filename, contents):
108 basedir = get_basedir()
109 path = os.path.join(basedir, filename)
110 dirname = os.path.dirname(path)
111 if not os.path.exists(dirname):
113 iff = file(path, 'wb')
117 def write_root_cap(root_cap):
118 write_config_file('private/root_dir.cap', root_cap+'\n')
119 convergence = base32.b2a(hashutil.tagged_hash(CONVERGENCE_DOMAIN_TAG, root_cap))
120 write_config_file('private/convergence', convergence+'\n')
123 CERTFILE = "node.pem"
124 certfile = os.path.join(get_basedir(), "private", CERTFILE)
125 tub = Tub(certFile=certfile)
126 return tub.getTubID()
130 nnfile = os.path.join(get_basedir(), 'nickname')
131 if os.path.exists(nnfile):
133 fh = file(nnfile, 'rb')
134 nick = fh.read().strip()
137 DisplayTraceback('Failed to read existing nickname file %s' % (nnfile,))
139 nick = socket.gethostname()
142 def maybe_write_file(filename, contents):
143 fname = os.path.join(get_basedir(), filename)
145 if not os.path.exists(fname):
146 fh = file(fname, 'wb')
151 DisplayTraceback('Failed to write file %s' % (fname,))
153 def configure(backend, user, passwd):
154 _config_re = re.compile('^([^:]*): (.*)$')
155 config = get_config(backend, user, passwd)
157 for line in config.split('\n'):
159 m = _config_re.match(line)
161 fname, contents = m.groups()
162 config_dict[fname] = contents
163 for fname, contents in config_dict.items():
164 write_config_file(fname, contents+'\n')
166 def start_windows_service(svc_name):
169 import win32serviceutil as wsu
170 if wsu.QueryServiceStatus(svc_name)[1] != win32service.SERVICE_RUNNING:
171 wsu.StartService(svc_name)
173 DisplayTraceback('Failed to start windows service "%s"' % (svc_name,))
175 def maybe_start_services():
176 if sys.platform == 'win32':
177 start_windows_service(TAHOESVC_NAME)
178 start_windows_service(WINFUSESVC_NAME)
180 def DisplayTraceback(message):
181 xc = traceback.format_exception(*sys.exc_info())
182 wx.MessageBox(u"%s\n (%s)"%(message,''.join(xc)), 'Error')
184 class ConfWizApp(wx.App):
185 def __init__(self, server, open_welcome_page=False):
187 self.show_welcome = open_welcome_page
188 wx.App.__init__(self, 0)
190 def get_backend(self):
191 return self.server + BACKEND
193 def open_welcome_page(self):
194 if self.show_welcome:
195 args = {'v': str(allmydata.__version__),
196 'plat': sys.platform,
198 webbrowser.open(self.server + WELCOME_PAGE + '?' + urlencode(args))
202 wx.InitAllImageHandlers()
204 self.login_frame = WizardFrame(self, LoginPanel)
205 self.login_frame.CenterOnScreen()
206 self.SetTopWindow(self.login_frame)
207 #self.SetExitOnFrameDelete(True)
208 self.login_frame.Show(True)
212 DisplayTraceback('config wizard init threw an exception')
214 def swap_to_register_frame(self):
216 self.login_frame.Show(False)
217 self.regiser_frame = WizardFrame(self, RegisterPanel)
218 self.regiser_frame.CenterOnScreen()
219 self.SetTopWindow(self.regiser_frame)
220 self.SetExitOnFrameDelete(True)
221 self.regiser_frame.Show(True)
223 DisplayTraceback('config wizard threw an exception')
225 class WizardFrame(wx.Frame):
226 def __init__(self, app, panel_class):
227 #title = 'Allmydata Config Wizard'
228 title = 'Setup - Allmydata'
229 wx.Frame.__init__(self, None, -1, title)
231 self.SetIcon(amdlogo.getIcon())
232 self.Bind(wx.EVT_CLOSE, self.close)
234 self.SetSizeHints(500, 360, 600, 800)
236 banner = wx.Panel(self, -1)
237 banner.SetSize((496,58))
238 banner.SetBackgroundColour(wx.WHITE)
240 banner_title = wx.StaticText(banner, -1, panel_class.title)
241 banner_desc = wx.StaticText(banner, -1, " " + panel_class.description)
242 font = banner_title.GetFont()
243 font.SetWeight(wx.FONTWEIGHT_BOLD)
244 banner_title.SetFont(font)
245 banner_icon = wx.StaticBitmap(banner, -1, amdlogo.getBitmap())
246 banner_label_sizer = wx.BoxSizer(wx.VERTICAL)
247 banner_label_sizer.Add(banner_title, 0, wx.EXPAND | wx.ALL, 2)
248 banner_label_sizer.Add(banner_desc, 0, wx.EXPAND | wx.ALL, 2)
250 banner_sizer = wx.BoxSizer(wx.HORIZONTAL)
251 banner_sizer.Add(banner_label_sizer, 1, wx.EXPAND | wx.ALL, 12)
252 banner_sizer.Add(banner_icon, 0, wx.ALL, 12)
253 banner.SetSizer(banner_sizer)
254 banner.SetAutoLayout(True)
256 background = wx.Panel(self, -1, style=wx.SIMPLE_BORDER)
257 background.parent = self
259 button_panel = wx.Panel(self, -1)
260 button_panel.SetSize((496, 64))
262 self.panel = panel_class(background, button_panel, app)
263 sizer = wx.BoxSizer(wx.VERTICAL)
264 background_sizer = wx.BoxSizer(wx.VERTICAL)
265 background_sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, self.panel.padding)
266 background_sizer.Add(self.panel, 1, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 26)
267 background_sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, self.panel.padding)
268 background.SetSizer(background_sizer)
271 sizer.Add(banner, 0, wx.EXPAND | wx.HORIZONTAL, 0)
272 sizer.Add(background, 0, wx.EXPAND | wx.ALL, 0)
273 sizer.Add(button_panel, 0, wx.EXPAND | wx.HORIZONTAL, 0)
275 self.SetAutoLayout(True)
279 def close(self, event):
281 self.app.ExitMainLoop()
284 class LoginPanel(wx.Panel):
287 description = 'Sign in to your existing account'
289 def __init__(self, parent, button_panel, app):
290 wx.Panel.__init__(self, parent, -1)
294 self.sizer = wx.BoxSizer(wx.VERTICAL)
296 self.user_label = wx.StaticText(self, -1, 'Email')
297 self.pass_label = wx.StaticText(self, -1, 'Password')
298 self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
299 self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
300 self.warning_label = wx.StaticText(self, -1, '')
301 self.warning_label.SetOwnForegroundColour(wx.RED)
302 wx.EVT_CHAR(self.user_field, self.on_user_entry)
303 wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
304 login_sizer = wx.FlexGridSizer(2, 2, 5, 4)
305 login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
306 login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
307 login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
308 login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
309 self.sizer.Add(login_sizer, 1, wx.EXPAND | wx.ALL, 2)
310 self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
311 self.SetSizer(self.sizer)
312 self.SetAutoLayout(True)
314 self.reg_label = wx.StaticText(button_panel, -1, "Don't have an account?")
315 self.reg_button = wx.Button(button_panel, -1, 'Create Account')
316 self.login_button = wx.Button(button_panel, -1, 'Sign In')
317 button_panel.Bind(wx.EVT_BUTTON, self.on_reg_button, self.reg_button)
318 button_panel.Bind(wx.EVT_BUTTON, self.on_login, self.login_button)
319 btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
320 btn_sizer.Add(wx.Size(2,2), 1, wx.EXPAND | wx.ALL, 12)
321 btn_sizer.Add(self.reg_label, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
322 btn_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
323 btn_sizer.Add(self.login_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
324 button_panel.SetSizer(btn_sizer)
325 self.button_panel = button_panel
327 def on_reg_button(self, event):
328 self.app.swap_to_register_frame()
330 def on_user_entry(self, event):
331 if event.GetKeyCode() == wx.WXK_RETURN:
332 self.pass_field.SetFocus()
336 def on_pass_entry(self, event):
337 if event.GetKeyCode() == wx.WXK_RETURN:
342 def on_login(self, event):
343 user = self.user_field.GetValue()
344 passwd = self.pass_field.GetValue()
345 self.warning_label.SetLabel('Connecting...')
349 backend = self.app.get_backend()
352 self.warning_label.SetLabel('You must enter a password')
353 self.pass_field.SetFocus()
358 root_cap = get_root_cap(backend, user, passwd)
359 write_root_cap(root_cap)
361 self.warning_label.SetLabel('Your email and/or password is incorrect')
362 self.user_field.SetFocus()
366 nodeid = get_nodeid()
367 nickname = get_nickname()
368 ret = record_install(backend, user, passwd, nodeid, nickname)
370 wx.MessageBox('Error "%s" recording this system (%s)' % (ret, nodeid), 'Error')
372 configure(backend, user, passwd)
373 maybe_start_services()
374 maybe_write_file('nickname', nickname)
375 maybe_write_file('accountname', user)
377 self.app.open_welcome_page()
380 self.parent.parent.Close()
382 class RegisterPanel(wx.Panel):
384 title = 'Create account'
385 description = 'Create a new account on Allmydata'
387 def __init__(self, parent, button_panel, app):
388 wx.Panel.__init__(self, parent, -1)
392 self.sizer = wx.BoxSizer(wx.VERTICAL)
394 self.user_label = wx.StaticText(self, -1, 'Email')
395 self.pass_label = wx.StaticText(self, -1, 'Password')
396 self.conf_label = wx.StaticText(self, -1, 'Confirm Password')
397 self.user_field = wx.TextCtrl(self, -1, u'', size=(260,-1))
398 self.pass_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
399 self.conf_field = wx.TextCtrl(self, -1, u'', size=(260,-1), style=wx.TE_PASSWORD)
400 self.subscribe_box = wx.CheckBox(self, -1, 'Sign up for our Newsletter')
401 self.subscribe_box.SetValue(True)
402 self.warning_label = wx.StaticText(self, -1, '')
403 self.warning_label.SetOwnForegroundColour(wx.RED)
404 wx.EVT_CHAR(self.user_field, self.on_user_entry)
405 wx.EVT_CHAR(self.pass_field, self.on_pass_entry)
406 wx.EVT_CHAR(self.conf_field, self.on_conf_entry)
407 login_sizer = wx.FlexGridSizer(3, 2, 5, 4)
408 login_sizer.Add(self.user_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
409 login_sizer.Add(self.user_field, 0, wx.EXPAND | wx.ALL, 2)
410 login_sizer.Add(self.pass_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
411 login_sizer.Add(self.pass_field, 0, wx.EXPAND | wx.ALL, 2)
412 login_sizer.Add(self.conf_label, 0, wx.ALIGN_RIGHT | wx.ALL, 2)
413 login_sizer.Add(self.conf_field, 0, wx.EXPAND | wx.ALL, 2)
414 login_sizer.Add(wx.Size(2,2), 0, wx.ALIGN_RIGHT | wx.ALL, 2)
415 self.sizer.Add(login_sizer, 0, wx.EXPAND | wx.ALL, 2)
416 self.sizer.Add(self.warning_label, 0, wx.CENTER | wx.ALL, 2)
417 self.sizer.Add(wx.Size(2,2), 0, wx.EXPAND | wx.ALL, 4)
418 self.sizer.Add(self.subscribe_box, 0, wx.CENTER | wx.ALL, 2)
419 self.SetSizer(self.sizer)
420 self.SetAutoLayout(True)
422 self.reg_button = wx.Button(button_panel, -1, 'Create Account')
423 button_panel.Bind(wx.EVT_BUTTON, self.on_create_account, self.reg_button)
424 btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
425 btn_sizer.Add(wx.Size(2,2), 1, wx.EXPAND | wx.ALL, 12)
426 btn_sizer.Add(self.reg_button, 0, wx.ALIGN_RIGHT | wx.ALL, 12)
427 button_panel.SetSizer(btn_sizer)
428 self.button_panel = button_panel
432 def on_user_entry(self, event):
433 if event.GetKeyCode() == wx.WXK_RETURN:
434 self.pass_field.SetFocus()
438 def on_pass_entry(self, event):
439 if event.GetKeyCode() == wx.WXK_RETURN:
440 self.conf_field.SetFocus()
444 def on_conf_entry(self, event):
445 if event.GetKeyCode() == wx.WXK_RETURN:
446 self.on_create_account(event)
450 def on_create_account(self, event):
451 user = self.user_field.GetValue()
452 passwd = self.pass_field.GetValue()
453 pconf = self.conf_field.GetValue()
454 subscribe = self.subscribe_box.IsChecked()
455 self.warning_label.SetLabel('Connecting...')
460 self.warning_label.SetLabel('You must enter a password')
461 self.pass_field.SetFocus()
466 self.warning_label.SetLabel("Passwords don't match")
467 self.pass_field.SetValue('')
468 self.conf_field.SetValue('')
469 self.pass_field.SetFocus()
473 backend = self.app.get_backend()
475 #print 'calling create_account', time.asctime()
476 result_code = create_account(backend, user, passwd, subscribe)
478 if result_code == 'account_exists':
479 # try and log into it; if valid, use it anyway
481 #print 'calling get_root_cap (ae)', time.asctime()
482 root_cap = get_root_cap(backend, user, passwd)
483 write_root_cap(root_cap)
485 self.warning_label.SetLabel('That email address is already registered')
486 self.user_field.SetFocus()
489 elif result_code == 'error':
490 self.warning_label.SetLabel('an error occurred')
491 self.user_field.SetFocus()
494 elif result_code == 'ok':
495 #print 'calling get_root_cap (ok)', time.asctime()
496 root_cap = get_root_cap(backend, user, passwd)
497 write_root_cap(root_cap)
499 self.warning_label.SetLabel('an unexpected error occurred ("%s")' % (result_code,))
500 self.user_field.SetFocus()
504 nodeid = get_nodeid()
505 nickname = get_nickname()
506 ret = record_install(backend, user, passwd, nodeid, nickname)
508 wx.MessageBox('Error "%s" recording this system (%s)' % (ret, nodeid), 'Error')
510 configure(backend, user, passwd)
511 maybe_start_services()
512 maybe_write_file('nickname', nickname)
513 maybe_write_file('accountname', user)
515 self.app.open_welcome_page()
518 self.parent.parent.Close()
520 def do_uninstall(server_url):
521 nodeid = get_nodeid()
522 ret = record_uninstall(server_url + BACKEND, nodeid)
525 print 'Error "%s" recording uninstall of this system (%s)' % (ret, nodeid)
527 class Options(usage.Options):
528 synopsis = "Usage: confwiz [options]"
531 ['uninstall', 'u', 'record uninstall'],
534 ['server', 's', DEFAULT_SERVER_URL, 'url of server to contact'],
540 config.parseOptions(argv[1:])
541 except usage.error, e:
543 print "%s: %s" % (sys.argv[0], e)
546 server = config['server']
547 if not server.endswith('/'):
550 if config['uninstall']:
553 app = ConfWizApp(server)
557 if __name__ == '__main__':