0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2004 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: bitfling.py 4228 2007-05-10 02:59:06Z djpham $ 0009 0010 """This is the BitFling client 0011 0012 It acts as an XML-RPC server over SSH. The UI consists of a tray icon (Windows) 0013 or a small icon (Linux, Mac) that you can click on to get the dialog.""" 0014 0015 # Standard Modules 0016 import sys 0017 import cStringIO 0018 import os 0019 import random 0020 import sha 0021 import thread 0022 import fnmatch 0023 import socket 0024 import threading 0025 import time 0026 from xmlrpclib import Fault, Binary 0027 0028 # wx stuff 0029 import wx 0030 import wx.html 0031 import wx.lib.newevent 0032 import wx.lib.masked.textctrl 0033 import wx.lib.mixins.listctrl 0034 0035 # others 0036 import paramiko 0037 0038 # My stuff 0039 try: 0040 import native.usb as usb 0041 except ImportError: 0042 usb=None 0043 import usbscan 0044 import comscan 0045 import commport 0046 0047 import guihelper 0048 import xmlrpcstuff 0049 import version 0050 0051 ID_CONFIG=wx.NewId() 0052 ID_LOG=wx.NewId() 0053 ID_RESCAN=wx.NewId() 0054 ID_EXIT=wx.NewId() 0055 0056 0057 XmlServerEvent, EVT_XMLSERVER = wx.lib.newevent.NewEvent() 0058 0059 guithreadid=thread.get_ident() 0060 0061 # in theory this should also work for GTK, but in practise it doesn't 0062 if guihelper.IsMSWindows(): parentclass=wx.TaskBarIcon 0063 else: parentclass=wx.Frame 0064 0065 class MyTaskBarIcon(parentclass): 0066 0067 def __init__(self, mw, menu): 0068 self.mw=mw 0069 self.menu=menu 0070 iconfile="bitfling.png" 0071 if parentclass is wx.Frame: 0072 parentclass.__init__(self, None, -1, "BitFling Window", size=(32,32), style=wx.FRAME_TOOL_WINDOW) 0073 self.genericinit(iconfile) 0074 else: 0075 parentclass.__init__(self) 0076 self.windowsinit(iconfile) 0077 0078 self.leftdownpos=0,0 0079 #wx.EVT_MENU(menu, ID_CONFIG, self.OnConfig) 0080 #wx.EVT_MENU(menu, ID_LOG, self.OnLog) 0081 wx.EVT_MENU(menu, ID_EXIT, self.OnExit) 0082 #wx.EVT_MENU(menu, ID_RESCAN, self.OnRescan) 0083 0084 def GoAway(self): 0085 if parentclass is wx.Frame: 0086 self.Close(True) 0087 else: 0088 self.RemoveIcon() 0089 self.Destroy() 0090 0091 def OnConfig(self,_): 0092 print "I would do config at this point" 0093 0094 def OnLog(self,_): 0095 print "I would do log at this point" 0096 0097 def OnHelp(self,_): 0098 print "I would do help at this point" 0099 0100 def OnRescan(self, _): 0101 print "I would do rescan at this point" 0102 0103 def OnExit(self,_): 0104 self.mw.Close(True) 0105 0106 def OnRButtonUp(self, evt=None): 0107 if parentclass is wx.Frame: 0108 self.PopupMenu(self.menu, evt.GetPosition()) 0109 else: 0110 self.PopupMenu(self.menu) 0111 0112 def OnLButtonUp(self, evt=None): 0113 if self.leftdownpos is None: 0114 return # cleared out by motion stuff 0115 if self.mw.IsShown(): 0116 self.mw.Show(False) 0117 else: 0118 self.mw.Show(True) 0119 self.mw.Raise() 0120 0121 def OnLeftDown(self, evt): 0122 if guihelper.IsMSWindows(): 0123 self.leftdownpos=0 0124 else: 0125 self.leftdownpos=evt.GetPosition() 0126 self.motionorigin=self.leftdownpos 0127 0128 def OnMouseMotion(self, evt): 0129 if not evt.Dragging(): 0130 return 0131 if evt.RightIsDown() or evt.MiddleIsDown(): 0132 return 0133 if not evt.LeftIsDown(): 0134 return 0135 self.leftdownpos=None 0136 x,y=evt.GetPosition() 0137 xdelta=x-self.motionorigin[0] 0138 ydelta=y-self.motionorigin[1] 0139 screenx,screeny=self.GetPositionTuple() 0140 self.MoveXY(screenx+xdelta, screeny+ydelta) 0141 0142 def windowsinit(self, iconfile): 0143 bitmap=wx.Bitmap(guihelper.getresourcefile(iconfile), wx.BITMAP_TYPE_PNG) 0144 icon=wx.EmptyIcon() 0145 icon.CopyFromBitmap(bitmap) 0146 self.SetIcon(icon, "BitFling") 0147 wx.EVT_TASKBAR_RIGHT_UP(self, self.OnRButtonUp) 0148 wx.EVT_TASKBAR_LEFT_UP(self, self.OnLButtonUp) 0149 #wx.EVT_TASKBAR_MOVE(self, self.OnMouseMotion) 0150 wx.EVT_TASKBAR_LEFT_DOWN(self, self.OnLeftDown) 0151 0152 def genericinit(self, iconfile): 0153 self.SetCursor(wx.StockCursor(wx.CURSOR_HAND)) 0154 bitmap=wx.Bitmap(guihelper.getresourcefile(iconfile), wx.BITMAP_TYPE_PNG) 0155 bit=wx.StaticBitmap(self, -1, bitmap) 0156 self.Show(True) 0157 wx.EVT_RIGHT_UP(bit, self.OnRButtonUp) 0158 wx.EVT_LEFT_UP(bit, self.OnLButtonUp) 0159 wx.EVT_MOTION(bit, self.OnMouseMotion) 0160 wx.EVT_LEFT_DOWN(bit, self.OnLeftDown) 0161 self.bit=bit 0162 0163 class ConfigPanel(wx.Panel, wx.lib.mixins.listctrl.ColumnSorterMixin): 0164 0165 def __init__(self, mw, parent, id=-1): 0166 wx.Panel.__init__(self, parent, id) 0167 self.mw=mw 0168 vbs=wx.BoxSizer(wx.VERTICAL) 0169 0170 # General 0171 bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "General"), wx.HORIZONTAL) 0172 bs.Add(wx.StaticText(self, -1, "Fingerprint"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0173 self.fingerprint=wx.TextCtrl(self, -1, "a", style=wx.TE_READONLY, size=(300,-1)) 0174 bs.Add(self.fingerprint, 0, wx.EXPAND|wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0175 bs.Add(wx.StaticText(self, -1, ""), 0, wx.ALL, 5) # spacer 0176 bs.Add(wx.StaticText(self, -1, "Port"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0177 self.porttext=wx.StaticText(self, -1, "<No Port>") 0178 bs.Add(self.porttext, 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0179 vbs.Add(bs, 0, wx.EXPAND|wx.ALL, 5) 0180 0181 # authorization 0182 bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Authorization"), wx.VERTICAL) 0183 hbs=wx.BoxSizer(wx.HORIZONTAL) 0184 butadd=wx.Button(self, wx.NewId(), "Add ...") 0185 hbs.Add(butadd, 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0186 hbs.Add(wx.StaticText(self, -1, ""), 0, wx.ALL, 5) # spacer 0187 self.butedit=wx.Button(self, wx.NewId(), "Edit ...") 0188 self.butedit.Enable(False) 0189 hbs.Add(self.butedit, 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0190 hbs.Add(wx.StaticText(self, -1, ""), 0, wx.ALL, 5) # spacer 0191 self.butdelete=wx.Button(self, wx.NewId(), "Delete") 0192 self.butdelete.Enable(False) 0193 hbs.Add(self.butdelete, 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5) 0194 bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5) 0195 0196 wx.EVT_BUTTON(self, butadd.GetId(), self.OnAddAuth) 0197 wx.EVT_BUTTON(self, self.butedit.GetId(), self.OnEditAuth) 0198 wx.EVT_BUTTON(self, self.butdelete.GetId(), self.OnDeleteAuth) 0199 0200 # and the authorization listview 0201 self.authlist=wx.ListCtrl(self, wx.NewId(), style=wx.LC_REPORT|wx.LC_SINGLE_SEL) 0202 self.authlist.InsertColumn(0, "User") 0203 self.authlist.InsertColumn(1, "Allowed Addresses") 0204 self.authlist.InsertColumn(2, "Expires") 0205 self.authlist.SetColumnWidth(0, 300) 0206 self.authlist.SetColumnWidth(1, 300) 0207 self.authlist.SetColumnWidth(2, 100) 0208 bs.Add(self.authlist, 1, wx.EXPAND|wx.ALL, 5) 0209 0210 vbs.Add(bs, 1, wx.EXPAND|wx.ALL, 5) 0211 self.itemDataMap={} 0212 wx.lib.mixins.listctrl.ColumnSorterMixin.__init__(self,3) 0213 0214 wx.EVT_LIST_ITEM_ACTIVATED(self.authlist, self.authlist.GetId(), self.OnEditAuth) 0215 wx.EVT_LIST_ITEM_SELECTED(self.authlist, self.authlist.GetId(), self.OnAuthListItemFondled) 0216 wx.EVT_LIST_ITEM_DESELECTED(self.authlist, self.authlist.GetId(), self.OnAuthListItemFondled) 0217 wx.EVT_LIST_ITEM_FOCUSED(self.authlist, self.authlist.GetId(), self.OnAuthListItemFondled) 0218 0219 # devices 0220 bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Devices"), wx.VERTICAL) 0221 buttoggle=wx.Button(self, wx.NewId(), "Toggle Allowed") 0222 bs.Add(buttoggle, 0, wx.ALL, 5) 0223 self.devicelist=wx.ListCtrl(self, wx.NewId(), style=wx.LC_REPORT|wx.LC_SINGLE_SEL) 0224 self.devicelist.InsertColumn(0, "Allowed") 0225 self.devicelist.InsertColumn(1, "Name") 0226 self.devicelist.InsertColumn(2, "Available") 0227 self.devicelist.InsertColumn(3, "Description") 0228 self.devicelist.SetColumnWidth(0, 100) 0229 self.devicelist.SetColumnWidth(1, 300) 0230 self.devicelist.SetColumnWidth(2, 100) 0231 self.devicelist.SetColumnWidth(3, 300) 0232 bs.Add(self.devicelist, 1, wx.EXPAND|wx.ALL, 5) 0233 0234 vbs.Add(bs, 1, wx.EXPAND|wx.ALL, 5) 0235 0236 self.setupauthorization() 0237 self.SortListItems() 0238 0239 self.SetSizer(vbs) 0240 self.SetAutoLayout(True) 0241 0242 def _updateauthitemmap(self, itemnum): 0243 pos=-1 0244 if itemnum in self.itemDataMap: 0245 # find item by looking for ItemData, and set pos 0246 # to corresponding pos in list 0247 for i in range(self.authlist.GetItemCount()): 0248 if self.authlist.GetItemData(i)==itemnum: 0249 pos=i 0250 break 0251 assert pos!=-1 0252 # clear the is connection allowed cache 0253 self.mw.icacache={} 0254 v=self.mw.authinfo[itemnum] 0255 username=v[0] 0256 expires=v[2] 0257 addresses=" ".join(v[3]) 0258 if pos<0: 0259 pos=self.authlist.GetItemCount() 0260 self.authlist.InsertStringItem(pos, username) 0261 else: 0262 self.authlist.SetStringItem(pos, 0, username) 0263 self.authlist.SetStringItem(pos, 2, `expires`) 0264 self.authlist.SetStringItem(pos, 1, addresses) 0265 self.authlist.SetItemData(pos, itemnum) 0266 self.itemDataMap[itemnum]=(username, addresses, expires) 0267 0268 def GetListCtrl(self): 0269 "Used by the ColumnSorter mixin" 0270 return self.authlist 0271 0272 def setupauthorization(self): 0273 dict={} 0274 items=[] 0275 for i in range(1000): 0276 if self.mw.config.HasEntry("user-"+`i`): 0277 username,password,expires,addresses=self.mw.config.Read("user-"+`i`).split(":") 0278 expires=int(expires) 0279 addresses=addresses.split() 0280 dict[i]=username,password,expires,addresses 0281 items.append(i) 0282 self.mw.authinfo=dict 0283 for i in items: 0284 self._updateauthitemmap(i) 0285 0286 0287 def OnAddAuth(self,_): 0288 dlg=AuthItemDialog(self, "Add Entry") 0289 if dlg.ShowModal()==wx.ID_OK: 0290 username,password,expires,addresses=dlg.GetValue() 0291 for i in range(1000): 0292 if i not in self.mw.authinfo: 0293 self.mw.config.Write("user-"+`i`, "%s:%s:%d:%s" % (username, password, expires, " ".join(addresses))) 0294 self.mw.config.Flush() 0295 self.mw.authinfo[i]=username,password,expires,addresses 0296 self._updateauthitemmap(i) 0297 self.SortListItems() 0298 break 0299 dlg.Destroy() 0300 0301 def OnDeleteAuth(self, _): 0302 item=self._getselectedlistitem(self.authlist) 0303 key=self.authlist.GetItemData(item) 0304 del self.mw.authinfo[key] 0305 self.authlist.DeleteItem(item) 0306 self.mw.config.DeleteEntry("user-"+`key`) 0307 self.mw.config.Flush() 0308 0309 def _getselectedlistitem(self, listctrl): 0310 "Finds the selected item in a listctrl since the wx methods don't actually work" 0311 i=-1 0312 while True: 0313 nexti=listctrl.GetNextItem(i, state=wx.LIST_STATE_SELECTED) 0314 if nexti<0: 0315 break 0316 i=nexti 0317 return i 0318 return None 0319 0320 def OnAuthListItemFondled(self, _): 0321 "Called whenever list items are selected, unselectected or similar fondling" 0322 selitem=self._getselectedlistitem(self.authlist) 0323 self.butedit.Enable(selitem is not None) 0324 self.butdelete.Enable(selitem is not None) 0325 0326 def OnEditAuth(self, _): 0327 "Called to edit the currently selected entry" 0328 item=self._getselectedlistitem(self.authlist) 0329 key=self.authlist.GetItemData(item) 0330 username,password,expires,addresses=self.mw.authinfo[key] 0331 dlg=AuthItemDialog(self, "Edit Entry", username=username, password=password, expires=expires, addresses=addresses) 0332 if dlg.ShowModal()==wx.ID_OK: 0333 username,password,expires,addresses=dlg.GetValue() 0334 self.mw.authinfo[key]=username,password,expires,addresses 0335 self._updateauthitemmap(key) 0336 dlg.Destroy() 0337 0338 class AuthItemDialog(wx.Dialog): 0339 0340 _password_sentinel="\x01\x02\x03\x04\x05\x06\x07\x08" # magic value used to detect if user has changed the field 0341 0342 def __init__(self, parent, title, username="New User", password="", expires=0, addresses=[]): 0343 wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) 0344 0345 p=self 0346 gs=wx.FlexGridSizer(4, 2, 5, 5) 0347 gs.AddGrowableCol(1) 0348 gs.AddGrowableRow(3) 0349 0350 gs.Add(wx.StaticText(p, -1, "Username/Email")) 0351 self.username=wx.TextCtrl(self, -1, username) 0352 gs.Add(self.username,0, wx.EXPAND) 0353 gs.Add(wx.StaticText(p, -1, "Password")) 0354 self.password=wx.TextCtrl(self, -1, "", style=wx.TE_PASSWORD) 0355 self.origpassword=password 0356 if len(password): self.password.SetValue(self._password_sentinel) 0357 gs.Add(self.password, 0, wx.EXPAND) 0358 gs.Add(wx.StaticText(p, -1, "Expires")) 0359 self.expires=wx.lib.masked.textctrl.TextCtrl(p, -1, "", autoformat="EUDATETIMEYYYYMMDD.HHMM") 0360 gs.Add(self.expires) 0361 gs.Add(wx.StaticText(p, -1, "Allowed Addresses")) 0362 self.addresses=wx.TextCtrl(self, -1, "\n".join(addresses), style=wx.TE_MULTILINE) 0363 gs.Add(self.addresses, 1, wx.EXPAND) 0364 0365 0366 vbs=wx.BoxSizer(wx.VERTICAL) 0367 vbs.Add(gs,1, wx.EXPAND|wx.ALL, 5) 0368 vbs.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5) 0369 vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTER|wx.ALL, 5) 0370 0371 self.SetSizer(vbs) 0372 vbs.Fit(self) 0373 0374 0375 def GenPassword(self, string): 0376 # random salt 0377 salt="".join([chr(random.randint(0,127)) for x in range(8)]) 0378 saltstr="".join(["%02x" % (ord(x),) for x in salt]) 0379 # we use a sha of the salt followed by the string 0380 val=sha.new(salt+string) 0381 # return generated password as $ seperated hex strings 0382 return "$".join([saltstr, val.hexdigest()]) 0383 0384 0385 def GetValue(self): 0386 # ::TODO:: ensure no colons in username or addresses 0387 # figure out password 0388 if self.password.GetValue()!=self._password_sentinel: 0389 password=self.GenPassword(self.password.GetValue()) 0390 else: 0391 password=self.origpassword 0392 return [self.username.GetValue(), password, 0, self.addresses.GetValue().split()] 0393 0394 class MainWindow(wx.Frame): 0395 0396 def __init__(self, parent, id, title): 0397 self.taskwin=None # set later 0398 wx.Frame.__init__(self, parent, id, title, style=wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.CAPTION) 0399 0400 sys.excepthook=self.excepthook 0401 0402 self.authinfo={} # updated by config panel 0403 self.icacache={} # used by IsConnectionAllowed 0404 0405 # Establish config stuff 0406 cfgstr='bitfling' 0407 if guihelper.IsMSWindows(): 0408 cfgstr="BitFling" # nicely capitalized on Windows 0409 self.config=wx.Config(cfgstr, style=wx.CONFIG_USE_LOCAL_FILE) 0410 # self.config.SetRecordDefaults(True) 0411 # for help to save prefs 0412 wx.GetApp().SetAppName(cfgstr) 0413 wx.GetApp().SetVendorName(cfgstr) 0414 0415 self.setuphelp() 0416 0417 wx.EVT_CLOSE(self, self.CloseRequested) 0418 0419 panel=wx.Panel(self, -1) 0420 0421 bs=wx.BoxSizer(wx.VERTICAL) 0422 0423 self.nb=wx.Notebook(panel, -1) 0424 bs.Add(self.nb, 1, wx.EXPAND|wx.ALL, 5) 0425 bs.Add(wx.StaticLine(panel, -1), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5) 0426 0427 gs=wx.GridSizer(1,4, 5, 5) 0428 0429 for name in ("Rescan", "Hide", "Help", "Exit" ): 0430 but=wx.Button(panel, wx.NewId(), name) 0431 setattr(self, name.lower(), but) 0432 gs.Add(but) 0433 bs.Add(gs,0,wx.ALIGN_CENTRE|wx.ALL, 5) 0434 0435 panel.SetSizer(bs) 0436 panel.SetAutoLayout(True) 0437 0438 # the notebook pages 0439 self.configpanel=ConfigPanel(self, self.nb) 0440 self.nb.AddPage(self.configpanel, "Configuration") 0441 self.lw=guihelper.LogWindow(self.nb) 0442 self.nb.AddPage(self.lw, "Log") 0443 0444 wx.EVT_BUTTON(self, self.hide.GetId(), self.OnHideButton) 0445 wx.EVT_BUTTON(self, self.help.GetId(), self.OnHelpButton) 0446 wx.EVT_BUTTON(self, self.exit.GetId(), self.OnExitButton) 0447 0448 EVT_XMLSERVER(self, self.OnXmlServerEvent) 0449 0450 self.xmlrpcserver=None 0451 wx.CallAfter(self.StartIfICan) 0452 0453 def setuphelp(self): 0454 """Does all the nonsense to get help working""" 0455 import wx.html 0456 # Add the Zip filesystem 0457 wx.FileSystem_AddHandler(wx.ZipFSHandler()) 0458 # Get the help working 0459 self.helpcontroller=wx.html.HtmlHelpController() 0460 self.helpcontroller.AddBook(guihelper.getresourcefile("bitpim.htb")) 0461 self.helpcontroller.UseConfig(self.config, "help") 0462 0463 # now context help 0464 # (currently borken) 0465 # self.helpprovider=wx.HelpControllerHelpProvider(self.helpcontroller) 0466 # wx.HelpProvider_Set(provider) 0467 0468 def IsConnectionAllowed(self, peeraddr, username=None, password=None): 0469 """Verifies if a connection is allowed 0470 0471 If username and password are supplied (as should be the case if calling this method 0472 before executing some code) then they are checked as being from a valid address 0473 as well. 0474 0475 If username and password are not supplied then this method checks if any 0476 of the authentication rules allow a connection from the peeraddr. This form 0477 is used immediately after calling accept() on a socket, but before doing 0478 anything else.""" 0479 # Note that this method is not called in the main thread, and any variables could be 0480 # updated underneath us. Be threadsafe and only use atomic methods on shared data! 0481 0482 v=(peeraddr[0], username, password) 0483 if username is not None and password is None: 0484 self.Log("%s: No password supplied for user %s" % (peeraddr, `username`)) 0485 assert False, "No password supplied" 0486 return False # not allowed to have None as password 0487 print "ica of "+`v` 0488 val=self.icacache.get(v, None) 0489 if val is not None: 0490 allowed, expires = val 0491 if allowed: 0492 if self._has_expired(expires): 0493 msg="Connection from %s no longer allowed due to expiry" % (peeraddr[0],) 0494 if username is not None: 0495 msg+=". Username "+`username` 0496 self.Log(msg) 0497 return False 0498 return True 0499 return False 0500 0501 ret_allowed=False 0502 ret_expiry=0 # an expiry of zero is infinite, so this will be overridden by any specific expiries 0503 for uname, pwd, expires, addresses in self.authinfo.values(): # values() is threadsafe 0504 # do any of the addresses match? 0505 if not self._does_address_match(peeraddr[0], addresses): 0506 continue 0507 # check username/password if username supplied 0508 if username is not None: 0509 if username!=uname: 0510 continue 0511 # check password 0512 if not self._verify_password(password, pwd): 0513 self.Log("Wrong password supplied for user %s from %s" % (`username`, peeraddr[0])) 0514 continue 0515 # remember expiry value (largest value) 0516 ret_expiry=max(ret_expiry, expires) 0517 ret_allowed=True 0518 0519 if not ret_allowed: 0520 if username is not None: 0521 self.Log("No valid credentials for user %s from %s" % (username, peeraddr[0])) 0522 else: 0523 self.Log("No defined users for address "+`peeraddr`) 0524 0525 # recurse so that correct log messages about expiry get generated 0526 self.icacache[v]=ret_allowed, ret_expiry 0527 return self.IsConnectionAllowed(peeraddr, username, password) 0528 0529 def _verify_password(self, password, pwd): 0530 """Returns True if password matches pwd 0531 @param password: password supplied by user 0532 @param pwd: password as we store it (salt $ hash)""" 0533 salt,hash=pwd.split("$", 1) 0534 # turn salt back into binary 0535 x="" 0536 for i in range(0, len(salt), 2): 0537 x+=chr(int(salt[i:i+2], 16)) 0538 salt=x 0539 # we do this silly string stuff to avoid issues with encoding - it only works for iso 8859-1 0540 str=[] 0541 str.extend([ord(x) for x in salt]) 0542 str.extend([ord(x) for x in password]) 0543 val=sha.new("".join([chr(x) for x in str])) 0544 print password, pwd, val.hexdigest(), val.hexdigest()==hash 0545 return val.hexdigest()==hash 0546 0547 def _does_address_match(self, peeraddr, addresses): 0548 """Returns if the peeraddr matches any of the supplied addresses""" 0549 # note this function can be called from any thread. do not access any data 0550 for addr in addresses: 0551 # the easy case 0552 if peeraddr==addr: return True 0553 # is addr a glob pattern? 0554 if '*' in addr or '?' in addr or '[' in addr: 0555 if fnmatch.fnmatch(peeraddr, addr): 0556 return True 0557 # ::TODO:: addr/bits style checking - see Python cookbook 10.5 for code 0558 # ok, do dns lookup on it 0559 ips=[] 0560 try: 0561 ips=socket.getaddrinfo(addr, None) 0562 except: 0563 pass 0564 for _, _, _, _, ip in ips: 0565 if peeraddr==ip[0]: 0566 return True 0567 return False 0568 0569 def _has_expired(self, expires): 0570 if expires==0: 0571 return False 0572 if time.time()>expires: 0573 return True 0574 return False 0575 0576 def CloseRequested(self, evt): 0577 if evt.CanVeto(): 0578 self.Show(False) 0579 evt.Veto() 0580 return 0581 self.taskwin.GoAway() 0582 evt.Skip() 0583 sys.excepthook=sys.__excepthook__ 0584 0585 def OnXmlServerEvent(self, msg): 0586 if msg.cmd=="log": 0587 self.Log(msg.data) 0588 elif msg.cmd=="logexception": 0589 self.LogException(msg.data) 0590 else: 0591 assert False, "bad message "+`msg` 0592 pass 0593 0594 0595 def OnExitButton(self, _): 0596 self.Close(True) 0597 0598 def OnHideButton(self, _): 0599 self.Show(False) 0600 0601 def OnHelpButton(self, _): 0602 import helpids 0603 self.helpcontroller.Display(helpids.ID_BITFLING) 0604 0605 def Log(self, text): 0606 if thread.get_ident()!=guithreadid: 0607 wx.PostEvent(self, XmlServerEvent(cmd="log", data=text)) 0608 else: 0609 self.lw.log(text) 0610 0611 def LogException(self, exc): 0612 if thread.get_ident()!=guithreadid: 0613 # need to send it to guithread 0614 wx.PostEvent(self, XmlServerEvent(cmd="log", data="Exception in thread "+threading.currentThread().getName())) 0615 wx.PostEvent(self, XmlServerEvent(cmd="logexception", data=exc)) 0616 else: 0617 self.lw.logexception(exc) 0618 0619 def excepthook(self, *args): 0620 """Replacement exception handler that sends stuff to our log window""" 0621 self.LogException(args) 0622 0623 def GetCertificateFilename(self): 0624 """Return certificate filename 0625 0626 By default $HOME (or My Documents) / .bitfling.key 0627 but can be overridden with "certificatefile" config key""" 0628 0629 if guihelper.IsMSWindows(): # we want subdir of my documents on windows 0630 # nice and painful 0631 from win32com.shell import shell, shellcon 0632 path=shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0) 0633 path=os.path.join(path, ".bitfling.key") 0634 else: 0635 path=os.path.expanduser("~/.bitfling.key") 0636 return self.config.Read("certificatefile", path) 0637 0638 0639 def StartIfICan(self): 0640 certfile=self.GetCertificateFilename() 0641 if not os.path.isfile(certfile): 0642 wx.BeginBusyCursor(wx.StockCursor(wx.CURSOR_ARROWWAIT)) 0643 bi=wx.BusyInfo("Creating BitFling host certificate and keys") 0644 try: 0645 generate_certificate(certfile) 0646 if not os.path.isfile(certfile): 0647 # ::TODO:: report that we failed 0648 certfile=None 0649 finally: 0650 del bi 0651 wx.EndBusyCursor() 0652 port=self.config.ReadInt("port", 12652) 0653 if port<1 or port>65535: port=None 0654 host=self.config.Read("bindaddress", "") 0655 if certfile is None or port is None: 0656 return 0657 self.Log("Starting on port "+`port`) 0658 key=paramiko.DSSKey.from_private_key_file(certfile) 0659 fp=paramiko.util.hexify(key.get_fingerprint()) 0660 self.configpanel.fingerprint.SetValue(fp) 0661 self.configpanel.porttext.SetLabel(`port`) 0662 self.configpanel.GetSizer().Layout() 0663 self.xmlrpcserver=BitFlingService(self, host, port, key) 0664 self.xmlrpcserver.setDaemon(True) 0665 self.xmlrpcserver.start() 0666 0667 0668 def generate_certificate(outfile): 0669 key=paramiko.DSSKey.generate() 0670 key.write_private_key_file(outfile, None) 0671 0672 0673 0674 class XMLRPCService(xmlrpcstuff.Server): 0675 0676 def __init__(self, mainwin, host, port, servercert): 0677 self.mainwin=mainwin 0678 xmlrpcstuff.Server.__init__(self, host, port, servercert) 0679 0680 def OnLog(self, msg): 0681 wx.PostEvent(self.mainwin, XmlServerEvent(cmd="log", data=msg)) 0682 0683 def OnLogException(self, exc): 0684 wx.PostEvent(self.mainwin, XmlServerEvent(cmd="logexception", data=exc)) 0685 0686 def OnNewAccept(self, clientaddr): 0687 return self.mainwin.IsConnectionAllowed(clientaddr) 0688 0689 def OnNewUser(self, clientaddr, username, password): 0690 return self.mainwin.IsConnectionAllowed(clientaddr, username, password) 0691 0692 def OnMethodDispatch(self, method, params, username, clientaddr): 0693 method="exp_"+method 0694 if not hasattr(self, method): 0695 raise Fault(3, "No such method") 0696 0697 context={ 'username': username, 'clientaddr': clientaddr } 0698 0699 return getattr(self, method)(*params, **{'context': context}) 0700 0701 0702 class BitFlingService(XMLRPCService): 0703 0704 def __init__(self, mainwin, host, port, servercert): 0705 XMLRPCService.__init__(self, mainwin, host, port, servercert) 0706 self.handles={} 0707 0708 def stashhandle(self, context, comm): 0709 for i in range(10000): 0710 if i not in self.handles: 0711 self.handles[i]=[context, comm] 0712 return i 0713 0714 def gethandle(self, context, num): 0715 # ::TODO:: check context has access 0716 return self.handles[num][1] 0717 0718 # Only methods begining with exp are exported to XML-RPC 0719 0720 def exp_scan(self, context): 0721 if usb is None: 0722 return comscan.comscan() 0723 else: 0724 return usbscan.usbscan()+comscan.comscan() 0725 0726 def exp_getversion(self, context): 0727 return version.description 0728 0729 def exp_deviceopen(self, port, baud, timeout, hardwareflow, softwareflow, context): 0730 # ::TODO:: None is pointer to log object 0731 return self.stashhandle(context, commport.CommConnection(None, port, baud, timeout, 0732 hardwareflow, softwareflow)) 0733 0734 def exp_deviceclose(self, handle, context): 0735 comm=self.gethandle(context, handle) 0736 comm.close() 0737 del self.handles[handle] 0738 return True 0739 0740 def exp_devicesetbaudrate(self, handle, rate, context): 0741 return self.gethandle(context, handle).setbaudrate(rate) 0742 0743 def exp_devicesetdtr(self, handle, dtr, context): 0744 return self.gethandle(context, handle).setdtr(dtr) 0745 0746 def exp_devicesetrts(self, handle, rts, context): 0747 return self.gethandle(context, handle).setrts(rts) 0748 0749 def exp_devicewrite(self, handle, data, context): 0750 self.gethandle(context, handle).write(data.data) 0751 return len(data.data) 0752 0753 def exp_devicesendatcommand(self, handle, atcommand, ignoreerror, context): 0754 "Special handling for empty lists and exceptions" 0755 try: 0756 res=self.gethandle(context, handle).sendatcommand(atcommand.data, ignoreerror=ignoreerror) 0757 if len(res)==0: 0758 res=1 0759 except: 0760 res=0 0761 return res 0762 0763 def exp_devicereaduntil(self, handle, char, numfailures, context): 0764 return Binary(self.gethandle(context, handle).readuntil(char.data, numfailures=numfailures)) 0765 0766 def exp_deviceread(self, handle, numchars, context): 0767 return Binary(self.gethandle(context, handle).read(numchars)) 0768 0769 def exp_devicereadsome(self, handle, numchars, context): 0770 if numchars==-1: 0771 numchars=None 0772 return Binary(self.gethandle(context, handle).readsome(log=True,numchars=numchars)) 0773 0774 def exp_devicewritethenreaduntil(self, handle, data, char, numfailures, context): 0775 return Binary(self.gethandle(context, handle).writethenreaduntil(data.data, False, char.data, False, False, numfailures)) 0776 0777 def run(args): 0778 theApp=wx.PySimpleApp() 0779 0780 menu=wx.Menu() 0781 #menu.Append(ID_CONFIG, "Configuration") 0782 #menu.Append(ID_LOG, "Log") 0783 #menu.Append(ID_RESCAN, "Rescan devices") 0784 menu.Append(ID_EXIT, "Exit") 0785 0786 mw=MainWindow(None, -1, "BitFling") 0787 taskwin=MyTaskBarIcon(mw, menu) 0788 mw.taskwin=taskwin 0789 theApp.MainLoop() 0790 0791 if __name__ == '__main__': 0792 run() 0793
Generated by PyXR 0.9.4