class UPnpMapper(object): DEFAULT = 0 INITIALIZING = 1 READY = 2 CLEANINGUP = 3 CLOSED = 4 def __init__(self, reactor): self.reactor = reactor self.sm = StateMachine(self.DEFAULT) self.device = None self.externalIP = None self.cleaningupCount = 0 self.sm.appendCallback(self._onCleanup, dest=self.CLEANINGUP, single=True) self._initialize() def shutdown(self, callback=None): if self.sm.current() in (self.CLEANINGUP, self.CLOSED): return None if self.sm.current() == self.READY: self.sm.change(self.CLEANINGUP) else: self.sm.change(self.CLOSED) if self.sm.current() == self.CLOSED: return None def onClosed(): op.notify() def doCancel(): self.sm.removeCallback(callbackId) callbackId = self.sm.appendCallback(onClosed, dest=self.CLOSED, single=True) op = AsyncOp(callback, doCancel) return op def _onCleanup(self): if self.cleaningupCount == 0: self.sm.change(self.CLOSED) def _initialize(self): class Dummy: pass obj = Dummy() def onError(): self.sm.removeCallback(callbackId) self.sm.change(self.CLOSED) def onDiscover(device): if device is None: onError() return self.device = device obj.op = UPnpActions.getExternalIP(device, self.reactor, onExternalIP) def onExternalIP(externalIP): if externalIP is None: onError() return self.externalIP = externalIP self.sm.removeCallback(callbackId) self.sm.change(self.READY) self.sm.change(self.INITIALIZING) obj.op = UPnpActions.findDevice(self.reactor, onDiscover) callbackId = self.sm.insertCallback(lambda: obj.op.cancel(), src=self.INITIALIZING, single=True) def addMapping(self, localIP, localPort, callback=None): class Dummy: pass obj = Dummy() if self.sm.current() not in (self.INITIALIZING, self.READY): timerOp = self.reactor.callLater(0, lambda: obj.op.notify(None)) obj.op = AsyncOp(callback, timerOp.cancel) return obj.op def doReady(): obj.attempt = 0 doAttempt() def doAttempt(): if obj.attempt == 3: obj.op.notify(None) return obj.attempt += 1 obj.externalPort = randint(10000, 20000) desc = 'CSpace_t%d' % int(time()) obj.addOp = UPnpActions.addMapping(self.device, obj.externalPort, 'TCP', localPort, localIP, desc, self.reactor, onAdd) obj.callbackId = self.sm.insertCallback(onAbort, src=self.READY, single=True) obj.op.setCanceler(onCancel) def onCancel(): obj.addOp.cancel() self.sm.removeCallback(obj.callbackId) def onAbort(): obj.addOp.cancel() obj.op.notify(None) def onAdd(result): self.sm.removeCallback(obj.callbackId) if not result: doAttempt() return mapping = (self.externalIP, obj.externalPort) obj.op.notify(mapping) self.sm.insertCallback(onCleanup, dest=self.CLEANINGUP, single=True) def onCleanup(): self.cleaningupCount += 1 UPnpActions.delMapping(self.device, obj.externalPort, 'TCP', self.reactor, onDelMapping) def onDelMapping(result): self.cleaningupCount -= 1 self._onCleanup() if self.sm.current() == self.INITIALIZING: def checkReady(): if self.sm.current() == self.READY: obj.op.setCanceler(None) doReady() return obj.op.notify(None) obj.callbackId = self.sm.insertCallback(checkReady, src=self.INITIALIZING, single=True) obj.op = AsyncOp(callback, lambda: self.sm.removeCallback(obj.callbackId)) return obj.op obj.op = AsyncOp(callback, None) doReady() return obj.op
class UserSession( object ) : (OFFLINE,CONNECTING,ONLINE,DISCONNECTING) = range(4) def __init__( self, seedNodes, reactor ) : self.seedNodes = seedNodes self.reactor = reactor self.sm = StateMachine( self.OFFLINE ) self.services = {} self.nodeTable = None self.nodeTableRefresher = None self.rpcSocket = None self.dhtClient = None self.locationCache = None self.profile = None self.listener = None self.permissions = None #self.nodeRunner = None def getProfile( self ) : return self.profile def getPermissions( self ) : return self.permissions def isOnline( self ) : return self.sm.current() == self.ONLINE def registerService( self, serviceName, serviceCallback ) : assert isValidServiceName(serviceName) if serviceName in self.services : return False self.services[serviceName] = serviceCallback return True def unregisterService( self, serviceName ) : if self.services.pop(serviceName,None) is None : return False return True def goOnline( self, profile ) : assert self.sm.current() == self.OFFLINE obj = Dummy() def doCleanup() : if obj.initNodeTableOp is not None : obj.initNodeTableOp.cancel() if obj.nodeTableRefresher is not None : obj.nodeTableRefresher.close() if obj.listener is not None : obj.listener.close() obj.rpcSocket.close() def onOffline() : #self.nodeRunner.close() #self.nodeRunner = None self.listener.close() self.listener = None self.permissions = None self.locationCache.close() self.locationCache = None self.nodeTableRefresher.close() self.nodeTableRefresher = None _saveNodeCache( self.nodeTable ) self.nodeTable = None self.dhtClient = None self.rpcSocket.close() self.rpcSocket = None self.profile = None def onListenerStart( result ) : if not result : self.sm.removeCallback( obj.callbackId ) doCleanup() self.sm.change( self.OFFLINE ) return obj.listener.setCloseCallback( self._onListenerClose ) obj.listener.setIncomingCallback( self._onIncoming ) self.profile = profile self.rpcSocket = obj.rpcSocket self.dhtClient = obj.dhtClient self.nodeTable = obj.nodeTable self.nodeTableRefresher = obj.nodeTableRefresher self.locationCache = LocationCache( self.dhtClient, self.nodeTable, self.reactor ) self.listener = obj.listener self.permissions = Permissions( profile, self.services.keys() ) if self.permissions.isModified() : self.permissions.savePermissions() #self.nodeRunner = NodeRunner( self.nodeTable, self.reactor ) self.sm.removeCallback( obj.callbackId ) self.sm.appendCallback( onOffline, dest=self.OFFLINE, single=True ) self.sm.change( self.ONLINE ) def onNodeTableInit() : obj.initNodeTableOp = None obj.nodeTableRefresher = NodeTableRefresher( obj.nodeTable, obj.dhtClient, self.reactor ) updateLevelStore = UpdateLevelStore( profile ) obj.listener = UserListener( profile.name, profile.keyId, profile.rsaKey, updateLevelStore, obj.dhtClient, obj.nodeTable, self.reactor ) obj.listener.start( onListenerStart ) self.sm.change( self.CONNECTING ) obj.callbackId = self.sm.insertCallback( doCleanup, src=self.CONNECTING, single=True ) obj.nodeTable = NodeTable( self.seedNodes ) #_loadNodeCache( obj.nodeTable ) udpSock = socket( AF_INET, SOCK_DGRAM ) udpSock.bind( ('',0) ) obj.rpcSocket = RPCSocket( udpSock, self.reactor ) obj.dhtClient = DHTClient( obj.rpcSocket, nodeTracker=obj.nodeTable ) obj.initNodeTableOp = initNodeTable( obj.nodeTable, obj.dhtClient, self.reactor, onNodeTableInit ) obj.nodeTableRefresher = None obj.listener = None def _onListenerClose( self ) : self.sm.change( self.OFFLINE ) def _rejectIncoming( self, sslConn, callback=None ) : def onReject( sslConn ) : if sslConn is None : op.notify( False ) return sslAbort( sslConn ) op.notify( True ) rejectOp = acceptIncoming( False, sslConn, self.reactor, onReject ) op = AsyncOp( callback, rejectOp.cancel ) return op def _acceptIncoming( self, sslConn, serviceName, peerKey, contactName, peerKeyID, incomingName, callback=None ) : def onAccept( sslConn ) : if sslConn is None : op.notify( False ) return serviceCallback = self.services.get( serviceName ) if serviceCallback is None : sslAbort( sslConn ) op.notify( False ) return serviceCallback( sslConn, peerKey, contactName, peerKeyID, incomingName ) op.notify( True ) acceptOp = acceptIncoming( True, sslConn, self.reactor, onAccept ) op = AsyncOp( callback, acceptOp.cancel ) return op # def _promptIncoming( self, sslConn, serviceName, peerKey, # contactName, incomingName, callback=None ) : # def doCancel() : # promptOp.cancel() # sslAbort( sslConn ) # def onPromptResult( promptResult ) : # if not promptResult : # rejectOp = self._rejectIncoming( sslConn, op.notify ) # op.setCanceler( rejectOp.cancel ) # else : # acceptOp = self._acceptIncoming( sslConn, serviceName, # peerKey, contactName, incomingName, op.notify ) # op.setCanceler( acceptOp.cancel ) # promptName = contactName # if not promptName : # promptName = '(Unknown %s)' % incomingName # promptOp = IncomingPromptWindow( promptName, serviceName, # self.reactor, onPromptResult ).getOp() # op = AsyncOp( callback, doCancel ) # return op def _onIncoming( self, sslConn, peerKey, incomingName, peerKeyID, serviceName ) : assert self.sm.current() == self.ONLINE if not isValidUserName(incomingName) : sslAbort( sslConn ) return if not isValidServiceName(serviceName) : sslAbort( sslConn ) return contact = self.profile.getContactByPublicKey( peerKey ) if contact is None : contactName = peerKeyID else : contactName = contact.name if serviceName not in self.services : action = ACCESS_DENY else : action = self.permissions.execute( contactName, serviceName ) def onActionDone( result ) : self.sm.removeCallback( callbackId ) if action == ACCESS_DENY : actionOp = self._rejectIncoming( sslConn, onActionDone ) else: # elif action == ACCESS_ALLOW : actionOp = self._acceptIncoming( sslConn, serviceName, peerKey, contactName, peerKeyID, incomingName, onActionDone ) # else : # assert action == ACCESS_PROMPT # actionOp = self._promptIncoming( sslConn, serviceName, # peerKey, contactName, incomingName, onActionDone ) callbackId = self.sm.insertCallback( actionOp.cancel, src=self.ONLINE, single=True ) def goOffline( self ) : assert self.sm.current() == self.ONLINE def onStop( result ) : self.sm.change( self.OFFLINE ) self.sm.change( self.DISCONNECTING ) self.listener.stop( onStop ) def shutdown( self ) : self.sm.change( self.OFFLINE ) def probeUserOnline( self, publicKey, callback=None ) : assert self.sm.current() == self.ONLINE def onStateChange() : lookupOp.cancel() op.notify( False ) def doCancel() : lookupOp.cancel() self.sm.removeCallback( callbackId ) def onLookup( location ) : self.sm.removeCallback( callbackId ) if location is None : op.notify( False ) return if (not location.directLocations) and (not location.routedLocations) : op.notify( False ) return op.notify( True ) lookupOp = self.locationCache.refreshUser( publicKey, onLookup ) callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE, single=True ) op = AsyncOp( callback, doCancel ) return op def connectTo( self, publicKey, serviceName, callback=None ) : assert self.sm.current() == self.ONLINE def onStateChange() : connectOp.cancel() op.notify( -1, None ) def doCancel() : connectOp.cancel() self.sm.removeCallback( callbackId ) def onConnect( sslConn ) : self.sm.removeCallback( callbackId ) if sslConn is None : op.notify( -1, None ) return op.notify( 0, sslConn ) connectOp = userServiceConnect( self.profile.name, self.profile.keyId, self.profile.rsaKey, self.listener.getPublicIP(), publicKey, serviceName, self.locationCache, self.reactor, onConnect ) callbackId = self.sm.insertCallback( onStateChange, src=self.ONLINE, single=True ) op = AsyncOp( callback, doCancel ) return op
class UserSession(object): (OFFLINE, CONNECTING, ONLINE, DISCONNECTING) = range(4) def __init__(self, seedNodes, reactor): self.seedNodes = seedNodes self.reactor = reactor self.sm = StateMachine(self.OFFLINE) self.services = {} self.nodeTable = None self.nodeTableRefresher = None self.rpcSocket = None self.dhtClient = None self.locationCache = None self.profile = None self.listener = None self.permissions = None #self.nodeRunner = None def getProfile(self): return self.profile def getPermissions(self): return self.permissions def isOnline(self): return self.sm.current() == self.ONLINE def registerService(self, serviceName, serviceCallback): assert isValidServiceName(serviceName) if serviceName in self.services: return False self.services[serviceName] = serviceCallback return True def unregisterService(self, serviceName): if self.services.pop(serviceName, None) is None: return False return True def goOnline(self, profile): assert self.sm.current() == self.OFFLINE obj = Dummy() def doCleanup(): if obj.initNodeTableOp is not None: obj.initNodeTableOp.cancel() if obj.nodeTableRefresher is not None: obj.nodeTableRefresher.close() if obj.listener is not None: obj.listener.close() obj.rpcSocket.close() def onOffline(): #self.nodeRunner.close() #self.nodeRunner = None self.listener.close() self.listener = None self.permissions = None self.locationCache.close() self.locationCache = None self.nodeTableRefresher.close() self.nodeTableRefresher = None _saveNodeCache(self.nodeTable) self.nodeTable = None self.dhtClient = None self.rpcSocket.close() self.rpcSocket = None self.profile = None def onListenerStart(result): if not result: self.sm.removeCallback(obj.callbackId) doCleanup() self.sm.change(self.OFFLINE) return obj.listener.setCloseCallback(self._onListenerClose) obj.listener.setIncomingCallback(self._onIncoming) self.profile = profile self.rpcSocket = obj.rpcSocket self.dhtClient = obj.dhtClient self.nodeTable = obj.nodeTable self.nodeTableRefresher = obj.nodeTableRefresher self.locationCache = LocationCache(self.dhtClient, self.nodeTable, self.reactor) self.listener = obj.listener self.permissions = Permissions(profile, self.services.keys()) if self.permissions.isModified(): self.permissions.savePermissions() #self.nodeRunner = NodeRunner( self.nodeTable, self.reactor ) self.sm.removeCallback(obj.callbackId) self.sm.appendCallback(onOffline, dest=self.OFFLINE, single=True) self.sm.change(self.ONLINE) def onNodeTableInit(): obj.initNodeTableOp = None obj.nodeTableRefresher = NodeTableRefresher( obj.nodeTable, obj.dhtClient, self.reactor) updateLevelStore = UpdateLevelStore(profile) obj.listener = UserListener(profile.name, profile.rsaKey, updateLevelStore, obj.dhtClient, obj.nodeTable, self.reactor) obj.listener.start(onListenerStart) self.sm.change(self.CONNECTING) obj.callbackId = self.sm.insertCallback(doCleanup, src=self.CONNECTING, single=True) obj.nodeTable = NodeTable(self.seedNodes) #_loadNodeCache( obj.nodeTable ) udpSock = socket(AF_INET, SOCK_DGRAM) udpSock.bind(('', 0)) obj.rpcSocket = RPCSocket(udpSock, self.reactor) obj.dhtClient = DHTClient(obj.rpcSocket, nodeTracker=obj.nodeTable) obj.initNodeTableOp = initNodeTable(obj.nodeTable, obj.dhtClient, self.reactor, onNodeTableInit) obj.nodeTableRefresher = None obj.listener = None def _onListenerClose(self): self.sm.change(self.OFFLINE) def _rejectIncoming(self, sslConn, callback=None): def onReject(sslConn): if sslConn is None: op.notify(False) return sslAbort(sslConn) op.notify(True) rejectOp = acceptIncoming(False, sslConn, self.reactor, onReject) op = AsyncOp(callback, rejectOp.cancel) return op def _acceptIncoming(self, sslConn, serviceName, peerKey, contactName, incomingName, callback=None): def onAccept(sslConn): if sslConn is None: op.notify(False) return serviceCallback = self.services.get(serviceName) if serviceCallback is None: sslAbort(sslConn) op.notify(False) return serviceCallback(sslConn, peerKey, contactName, incomingName) op.notify(True) acceptOp = acceptIncoming(True, sslConn, self.reactor, onAccept) op = AsyncOp(callback, acceptOp.cancel) return op def _promptIncoming(self, sslConn, serviceName, peerKey, contactName, incomingName, callback=None): def doCancel(): promptOp.cancel() sslAbort(sslConn) def onPromptResult(promptResult): if not promptResult: rejectOp = self._rejectIncoming(sslConn, op.notify) op.setCanceler(rejectOp.cancel) else: acceptOp = self._acceptIncoming(sslConn, serviceName, peerKey, contactName, incomingName, op.notify) op.setCanceler(acceptOp.cancel) promptName = contactName if not promptName: promptName = '(Unknown %s)' % incomingName promptOp = IncomingPromptWindow(promptName, serviceName, self.reactor, onPromptResult).getOp() op = AsyncOp(callback, doCancel) return op def _onIncoming(self, sslConn, peerKey, incomingName, serviceName): assert self.sm.current() == self.ONLINE if not isValidUserName(incomingName): sslAbort(sslConn) return if not isValidServiceName(serviceName): sslAbort(sslConn) return contact = self.profile.getContactByPublicKey(peerKey) if contact is None: contactName = '' else: contactName = contact.name if serviceName not in self.services: action = ACCESS_DENY else: action = self.permissions.execute(contactName, serviceName) def onActionDone(result): self.sm.removeCallback(callbackId) if action == ACCESS_DENY: actionOp = self._rejectIncoming(sslConn, onActionDone) elif action == ACCESS_ALLOW: actionOp = self._acceptIncoming(sslConn, serviceName, peerKey, contactName, incomingName, onActionDone) else: assert action == ACCESS_PROMPT actionOp = self._promptIncoming(sslConn, serviceName, peerKey, contactName, incomingName, onActionDone) callbackId = self.sm.insertCallback(actionOp.cancel, src=self.ONLINE, single=True) def goOffline(self): assert self.sm.current() == self.ONLINE def onStop(result): self.sm.change(self.OFFLINE) self.sm.change(self.DISCONNECTING) self.listener.stop(onStop) def shutdown(self): self.sm.change(self.OFFLINE) def probeUserOnline(self, publicKey, callback=None): assert self.sm.current() == self.ONLINE def onStateChange(): lookupOp.cancel() op.notify(False) def doCancel(): lookupOp.cancel() self.sm.removeCallback(callbackId) def onLookup(location): self.sm.removeCallback(callbackId) if location is None: op.notify(False) return if (not location.directLocations) and ( not location.routedLocations): op.notify(False) return op.notify(True) lookupOp = self.locationCache.refreshUser(publicKey, onLookup) callbackId = self.sm.insertCallback(onStateChange, src=self.ONLINE, single=True) op = AsyncOp(callback, doCancel) return op def connectTo(self, publicKey, serviceName, callback=None): assert self.sm.current() == self.ONLINE def onStateChange(): connectOp.cancel() op.notify(-1, None) def doCancel(): connectOp.cancel() self.sm.removeCallback(callbackId) def onConnect(sslConn): self.sm.removeCallback(callbackId) if sslConn is None: op.notify(-1, None) return op.notify(0, sslConn) connectOp = userServiceConnect(self.profile.name, self.profile.rsaKey, self.listener.getPublicIP(), publicKey, serviceName, self.locationCache, self.reactor, onConnect) callbackId = self.sm.insertCallback(onStateChange, src=self.ONLINE, single=True) op = AsyncOp(callback, doCancel) return op
class UPnpMapper(object): DEFAULT = 0 INITIALIZING = 1 READY = 2 CLEANINGUP = 3 CLOSED = 4 def __init__(self, reactor): self.reactor = reactor self.sm = StateMachine(self.DEFAULT) self.device = None self.externalIP = None self.cleaningupCount = 0 self.sm.appendCallback(self._onCleanup, dest=self.CLEANINGUP, single=True) self._initialize() def shutdown(self, callback=None): if self.sm.current() in (self.CLEANINGUP, self.CLOSED): return None if self.sm.current() == self.READY: self.sm.change(self.CLEANINGUP) else: self.sm.change(self.CLOSED) if self.sm.current() == self.CLOSED: return None def onClosed(): op.notify() def doCancel(): self.sm.removeCallback(callbackId) callbackId = self.sm.appendCallback(onClosed, dest=self.CLOSED, single=True) op = AsyncOp(callback, doCancel) return op def _onCleanup(self): if self.cleaningupCount == 0: self.sm.change(self.CLOSED) def _initialize(self): class Dummy: pass obj = Dummy() def onError(): self.sm.removeCallback(callbackId) self.sm.change(self.CLOSED) def onDiscover(device): if device is None: onError() return self.device = device obj.op = UPnpActions.getExternalIP(device, self.reactor, onExternalIP) def onExternalIP(externalIP): if externalIP is None: onError() return self.externalIP = externalIP self.sm.removeCallback(callbackId) self.sm.change(self.READY) self.sm.change(self.INITIALIZING) obj.op = UPnpActions.findDevice(self.reactor, onDiscover) callbackId = self.sm.insertCallback(lambda: obj.op.cancel(), src=self.INITIALIZING, single=True) def addMapping(self, localIP, localPort, callback=None): class Dummy: pass obj = Dummy() if self.sm.current() not in (self.INITIALIZING, self.READY): timerOp = self.reactor.callLater(0, lambda: obj.op.notify(None)) obj.op = AsyncOp(callback, timerOp.cancel) return obj.op def doReady(): obj.attempt = 0 doAttempt() def doAttempt(): if obj.attempt == 3: obj.op.notify(None) return obj.attempt += 1 obj.externalPort = randint(10000, 20000) desc = "CSpace_t%d" % int(time()) obj.addOp = UPnpActions.addMapping( self.device, obj.externalPort, "TCP", localPort, localIP, desc, self.reactor, onAdd ) obj.callbackId = self.sm.insertCallback(onAbort, src=self.READY, single=True) obj.op.setCanceler(onCancel) def onCancel(): obj.addOp.cancel() self.sm.removeCallback(obj.callbackId) def onAbort(): obj.addOp.cancel() obj.op.notify(None) def onAdd(result): self.sm.removeCallback(obj.callbackId) if not result: doAttempt() return mapping = (self.externalIP, obj.externalPort) obj.op.notify(mapping) self.sm.insertCallback(onCleanup, dest=self.CLEANINGUP, single=True) def onCleanup(): self.cleaningupCount += 1 UPnpActions.delMapping(self.device, obj.externalPort, "TCP", self.reactor, onDelMapping) def onDelMapping(result): self.cleaningupCount -= 1 self._onCleanup() if self.sm.current() == self.INITIALIZING: def checkReady(): if self.sm.current() == self.READY: obj.op.setCanceler(None) doReady() return obj.op.notify(None) obj.callbackId = self.sm.insertCallback(checkReady, src=self.INITIALIZING, single=True) obj.op = AsyncOp(callback, lambda: self.sm.removeCallback(obj.callbackId)) return obj.op obj.op = AsyncOp(callback, None) doReady() return obj.op