class CSpaceAcceptor(object): def __init__(self, sock, connectionId, reactor, callback): self.stream = TCPStream(sock, reactor) self.stream.setCloseCallback(self._onClose) self.stream.setErrorCallback(self._onError) self.stream.setInputCallback(self._onInput) self.stream.initiateRead(1) self.stream.writeData('ACCEPT %s\r\n' % connectionId) self.response = '' self.op = AsyncOp(callback, self.stream.close) def getOp(self): return self.op def _onClose(self): self.stream.close() self.op.notify(-1, None) def _onError(self, err, errMsg): self._onClose() def _onInput(self, data): self.response += data if self.response.endswith('\n'): if not self.response.startswith('OK'): self._onClose() return self.stream.shutdown() sock = self.stream.getSock() self.op.notify(0, sock)
class CSpaceAcceptor( object ) : def __init__( self, sock, connectionId, reactor, callback ) : self.stream = TCPStream( sock, reactor ) self.stream.setCloseCallback( self._onClose ) self.stream.setErrorCallback( self._onError ) self.stream.setInputCallback( self._onInput ) self.stream.initiateRead( 1 ) self.stream.writeData( 'ACCEPT %s\r\n' % connectionId ) self.response = '' self.op = AsyncOp( callback, self.stream.close ) def getOp( self ) : return self.op def _onClose( self ) : self.stream.close() self.op.notify( -1, None ) def _onError( self, err, errMsg ) : self._onClose() def _onInput( self, data ) : self.response += data if self.response.endswith('\n') : if not self.response.startswith('OK') : self._onClose() return self.stream.shutdown() sock = self.stream.getSock() self.op.notify( 0, sock )
def __init__(self, reactor, callback=None): assert env.isContactAction or env.isIncoming self.reactor = reactor self.op = AsyncOp(callback, self._doCancel) self.state = self.CONNECTING self.connectOp = tcpConnect(('127.0.0.1', env.port), self.reactor, self._onConnect) self.stream = None
def __init__(self, curlHandle, reactor, callback=None): self.reactor = reactor self.cm = pycurl.CurlMulti() self.c = curlHandle self.cm.add_handle(self.c) self.fdsets = ([], [], []) self.timerOp = self.reactor.callLater(0, self._onInitialTimer) self.op = AsyncOp(callback, self._doCancel)
class _Lookup( object ) : def __init__( self, clientObj, destId, startNodes, callback=None ) : assert startNodes self.clientObj = clientObj self.destId = destId self.destNumId = idToNum( destId ) self.seen = {} self.closest = [] self.pending = [] self.calling = {} for nodeAddr in startNodes : self._newNode( nodeAddr ) self._callPending() self.op = AsyncOp( callback, self._doCancel ) def getOp( self ) : return self.op def _doCancel( self ) : for op in self.calling.values() : op.cancel() self.calling.clear() def _newNode( self, nodeAddr ) : if nodeAddr in self.seen : return info = _LookupNodeInfo( nodeAddr, self.destNumId ) self.seen[nodeAddr] = info self._addPending( info ) def _addPending( self, info ) : insort( self.pending, info ) del self.pending[DHT_K*3/2:] def _callPending( self ) : count = DHT_PARALLEL - len(self.calling) nodeInfoList = self.pending[:count] del self.pending[:count] for info in nodeInfoList : self._callNode( info ) def _callNode( self, info ) : assert len(self.calling) < DHT_PARALLEL def onResult( err, payload ) : self._onResult( info, err, payload ) op = self.clientObj.callFindNodes( self.destId, info.nodeAddr, onResult, timeout=3, retries=0, backoff=1 ) self.calling[info.nodeAddr] = op def _onResult( self, info, err, payload ) : del self.calling[info.nodeAddr] if err >= 0 : insort( self.closest, info ) del self.closest[DHT_K:] for nodeAddr in payload : self._newNode( nodeAddr ) self._callPending() if not self.calling : assert len(self.pending) == 0 self.op.notify( [ni.nodeAddr for ni in self.closest] )
def __init__(self, sock, connectionId, reactor, callback): self.stream = TCPStream(sock, reactor) self.stream.setCloseCallback(self._onClose) self.stream.setErrorCallback(self._onError) self.stream.setInputCallback(self._onInput) self.stream.initiateRead(1) self.stream.writeData('ACCEPT %s\r\n' % connectionId) self.response = '' self.op = AsyncOp(callback, self.stream.close)
def __init__(self, sock, remoteUser, remoteService, reactor, callback): self.stream = TCPStream(sock, reactor) self.stream.setCloseCallback(self._onClose) self.stream.setErrorCallback(self._onError) self.stream.setInputCallback(self._onInput) self.stream.initiateRead(1) self.stream.writeData('CONNECT %s %s\r\n' % (remoteUser, remoteService)) self.response = '' self.op = AsyncOp(callback, self.stream.close)
def __init__(self, fileClient, remotePath, localPath, callback=None): self.fileClient = fileClient self.remotePath = remotePath self.localPath = localPath self.out = file(localPath, 'w+b') self.sizeOp = self.fileClient.callGetSize(remotePath, self._onGetSize) self.fileSize = -1 self.op = AsyncOp(callback, self._doCancel) self.blockSize = 16384 self.requestedSize = 0 self.receivedSize = 0 self.fetchOps = {}
class TCPCloser( TCPWriter ) : def __init__( self, sock, pendingWrite, reactor, callback=None ) : TCPWriter.__init__( self, sock, pendingWrite, reactor ) self.op = AsyncOp( callback, self.shutdown ) def getOp( self ) : return self.op def _notify( self ) : self.sock.close() self.op.notify() def onWriteComplete( self ) : self._notify() def onError( self, err, errMsg ) : self._notify() def onTimeout( self ) : self._notify()
def __init__( self, clientObj, destId, startNodes, callback=None ) : assert startNodes self.clientObj = clientObj self.destId = destId self.destNumId = idToNum( destId ) self.seen = {} self.closest = [] self.pending = [] self.calling = {} for nodeAddr in startNodes : self._newNode( nodeAddr ) self._callPending() self.op = AsyncOp( callback, self._doCancel )
def _serviceConnect( sslConn, serviceName, reactor, callback=None ) : def doCancel() : ms.shutdown() sslAbort( sslConn ) def onClose( *args ) : doCancel() op.notify( None ) def onInput( data ) : try : result = decode( data ) assert type(result) is int assert result == 0 except : doCancel() op.notify( None ) return ms.shutdown() op.notify( sslConn ) ms = SSLMessageStream( sslConn, reactor ) ms.setCloseCallback( onClose ) ms.setErrorCallback( onClose ) ms.setInvalidMessageCallback( onClose ) ms.setInputCallback( onInput ) ms.sendMessage( encode(serviceName) ) ms.enableRead( True ) op = AsyncOp( callback, doCancel ) return op
def _initNodeRefresher(self): obj = Dummy() def doWait(): timeout = REFRESH_NODES_INTERVAL obj.op = self.reactor.callLater(timeout, doRefresh) def onLookup(nodes): doWait() def doRefresh(): bucket = choice(self.ktable.table) destNumId = randint(bucket.start, bucket.end - 1) destId = numToId(destNumId) startNodes = self.ktable.getClosestNodes(destNumId) if not startNodes: doWait() return obj.op = self.client.lookup(destId, startNodes, onLookup) def doCancel(): obj.op.cancel() obj.op = self.reactor.callLater(0, doRefresh) op = AsyncOp(None, doCancel) self.otherOps.add(op)
def refreshUser(self, publicKey, callback=None): pubKeyData = publicKey.toDER_PublicKey() entry = self.d.get(pubKeyData) if entry is None: entry = _Entry(publicKey) self.d[pubKeyData] = entry assert entry.state != ES_NOTIFYING if entry.state == ES_DEFAULT: assert entry.lookupOp is None assert entry.notifyOps is None def onLookupUser(location): self._onLookupUser(entry, location) entry.lookupOp = lookupUser( publicKey, self.dhtClient, self.nodeTable, lambda loc: self._onLookupUser(entry, loc)) entry.state = ES_LOOKINGUP entry.notifyOps = set() def doCancel(): entry.notifyOps.remove(op) op = AsyncOp(callback, doCancel) entry.notifyOps.add(op) return op
def acceptIncoming(acceptFlag, sslConn, reactor, callback=None): def doCancel(): stream.close() def onError(*args): doCancel() op.notify(None) def onWriteComplete(): stream.shutdown() op.notify(sslConn) stream = SSLMessageStream(sslConn, reactor) stream.setCloseCallback(onError) stream.setErrorCallback(onError) stream.setInvalidMessageCallback(onError) stream.setInputCallback(onError) stream.setWriteCompleteCallback(onWriteComplete) if acceptFlag: data = 0 else: data = -1 stream.sendMessage(encode(data)) op = AsyncOp(callback, doCancel) return op
def routerAccept( routerAddr, connectionId, reactor, callback=None ) : obj = Dummy() def doCancel() : if obj.op : obj.op.cancel() if obj.rpcConn : obj.rpcConn.close() def onError() : doCancel() op.notify( None ) def onRouterAccept( err, result ) : obj.op = None if err < 0 : onError() return sock = obj.rpcConn.getSock() obj.rpcConn.shutdown() op.notify( sock ) def onTCPConnect( connector ) : obj.op = None if connector.getError() != 0 : onError() return obj.rpcConn = RPCConnection( connector.getSock(), reactor ) obj.rpcConn.setCloseCallback( onError ) obj.op = _doRPCCall( obj.rpcConn, 'Accept', [connectionId], onRouterAccept ) obj.op = tcpConnect( routerAddr, reactor, onTCPConnect ) op = AsyncOp( callback, doCancel ) obj.rpcConn = None return op
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 _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 _sslHandshake(sock, sslContext, reactor, callback=None): def doCancel(): acceptOp.cancel() sslAbort(sslConn) def onSSLAccept(err): if err is not None: sslAbort(sslConn) op.notify(None) return try: peerCert = sslConn.getPeerCertificate() peerKey = peerCert.getPublicKey() peerName = peerCert.getSubject().lookupEntry('commonName') except (SSLError, X509Error): sslAbort(sslConn) op.notify(None) return data = (sslConn, peerKey, peerName) op.notify(data) sslConn = SSLConnection(sslContext, sock) acceptOp = sslAccept(sslConn, reactor, onSSLAccept) op = AsyncOp(callback, doCancel) return op
def _registerKey(self, callback=None): data = 'username:%s' % self.userName digestType = DigestType('SHA1') digest = Digest(digestType).digest(data) signature = self.rsaKey.sign(digest, digestType) form = dict(username=self.userName, public_key=self.rsaKey.toDER_PublicKey(), signature=signature) postData = urllib.urlencode(form) request = HttpRequest(self.reactor) def onResponse(returnCode, data): if returnCode != 200: op.notify(-1) return try: keyId = int(data) op.notify(keyId) except ValueError: op.notify(-1) httpOp = request.post('http://cspace.in/addkey', postData, onResponse) op = AsyncOp(callback, httpOp.cancel) 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
def __init__( self, reactor, callback=None ) : assert env.isContactAction or env.isIncoming self.reactor = reactor self.op = AsyncOp( callback, self._doCancel ) self.state = self.CONNECTING self.connectOp = tcpConnect( ('127.0.0.1',env.port), self.reactor, self._onConnect ) self.stream = None
def __init__( self, curlHandle, reactor, callback=None ) : self.reactor = reactor self.cm = pycurl.CurlMulti() self.c = curlHandle self.cm.add_handle( self.c ) self.fdsets = ( [], [], [] ) self.timerOp = self.reactor.callLater( 0, self._onInitialTimer ) self.op = AsyncOp( callback, self._doCancel )
def lookup( self, destId, startNodes, callback=None ) : def hookCallback( *args, **kwargs ) : print 'done lookup %s' % str(id(lop)) op.notify( *args, **kwargs ) lop = _Lookup( self, destId, startNodes, hookCallback ).getOp() print 'starting lookup... %s' % str(id(lop)) op = AsyncOp( callback, lop.cancel ) return op
def __init__( self, sock, connectionId, reactor, callback ) : self.stream = TCPStream( sock, reactor ) self.stream.setCloseCallback( self._onClose ) self.stream.setErrorCallback( self._onError ) self.stream.setInputCallback( self._onInput ) self.stream.initiateRead( 1 ) self.stream.writeData( 'ACCEPT %s\r\n' % connectionId ) self.response = '' self.op = AsyncOp( callback, self.stream.close )
def __init__( self, sock, remoteUser, remoteService, reactor, callback ) : self.stream = TCPStream( sock, reactor ) self.stream.setCloseCallback( self._onClose ) self.stream.setErrorCallback( self._onError ) self.stream.setInputCallback( self._onInput ) self.stream.initiateRead( 1 ) self.stream.writeData( 'CONNECT %s %s\r\n' % (remoteUser,remoteService) ) self.response = '' self.op = AsyncOp( callback, self.stream.close )
def __init__( self, sslConn, reactor, callback=None, timeout=30 ) : self.sslConn = sslConn self.reactor = reactor self.timeout = timeout self.sock = sslConn.sock self.want = self.WANT_NONE self.timerOp = self.reactor.callLater( 0, self._onAction ) self.timeoutOp = self.reactor.callLater( self.timeout, self._onTimeout ) self.op = AsyncOp( callback, self._close )
def _findLiveNodes(count, nodeTable, dhtClient, reactor, callback=None): obj = Dummy() def doCancel(): for pingOp in obj.pingOps: pingOp.cancel() obj.pingOps.clear() obj.timerOp.cancel() obj.timerOp = None def doNotify(): doCancel() op.notify() def onPing(pingOp, err, payload): obj.pingOps.remove(pingOp) if err >= 0: obj.success += 1 if obj.success >= count: doNotify() elif (not obj.nodes) and (not obj.pingOps): doNotify() def doPing(nodeAddr): pingOp = dhtClient.callPing(nodeAddr, lambda e, p: onPing(pingOp, e, p)) obj.pingOps.add(pingOp) def doPingNodes(count): current = obj.nodes[:count] del obj.nodes[:count] for nodeAddr in current: doPing(nodeAddr) def onTimer(): obj.timerCount += 1 if not obj.pingedSeed: if (not obj.nodes) or (obj.timerCount == \ FIND_LIVE_NODES_TIMEOUT_COUNT ) : for nodeAddr in nodeTable.getSeedNodes(): doPing(nodeAddr) obj.pingedSeed = True return if not obj.nodes: return doPingNodes(10) obj.success = 0 obj.pingOps = set() obj.nodes = nodeTable.getAllNodes() doPingNodes(10) obj.timerCount = 0 obj.pingedSeed = False obj.timerOp = reactor.addTimer(FIND_LIVE_NODES_TIMEOUT, onTimer) op = AsyncOp(callback, doCancel) return op
def callAction( self, name, params, reactor, callback=None ) : payload = self.getActionPayload( name, params ) http = HttpRequest( reactor ) http.addHeader( 'Content-Type: text/xml; charset="utf-8"' ) http.addHeader( 'SOAPACTION: "%s#%s"' % (self.service,name) ) def onResponse( result, data ) : self._onActionResponse( op, name, result, data ) httpOp = http.post( self.controlUrl, payload, onResponse ) op = AsyncOp( callback, httpOp.cancel ) return op
def _findNewNodes(nodeTable, dhtClient, reactor, callback=None): destId = numToId(randint(0, DHT_ID_MAX - 1)) startNodes = nodeTable.getLiveNodes() def onLookup(nodes): op.notify() lookupOp = dhtClient.lookup(destId, startNodes, onLookup) op = AsyncOp(callback, lookupOp.cancel) return op
def initNodeTable(nodeTable, dhtClient, reactor, callback=None): def onFindLive(): print 'done finding live nodes' findNewOp = _findNewNodes(nodeTable, dhtClient, reactor, op.notify) op.setCanceler(findNewOp.cancel) print 'finding live nodes...' findLiveOp = _findLiveNodes(5, nodeTable, dhtClient, reactor, onFindLive) op = AsyncOp(callback, findLiveOp.cancel) return op
def listAllMappings( device, reactor, callback=None ) : mappings = [] def onResponse( mapping ) : if mapping is not None : mappings.append( mapping ) return op.notify( mappings ) listOp = UPnpActions.listMappings( device, reactor, onResponse ) op = AsyncOp( callback, listOp.cancel ) return op
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 delMapping( device, externalPort, protocol, reactor, callback=None ) : assert protocol in ('TCP','UDP') params = { 'NewRemoteHost' : '', 'NewExternalPort' : externalPort, 'NewProtocol' : protocol } def onResponse( result, data ) : op.notify( result == 0 ) actionOp = device.callAction( 'DeletePortMapping', params, reactor, onResponse ) op = AsyncOp( callback, actionOp.cancel ) return op
def getExternalIP( device, reactor, callback=None ) : def onResponse( result, data ) : if result < 0 : op.notify( None ) return externalIP = dict(data).get('NewExternalIPAddress',None) op.notify( externalIP ) actionOp = device.callAction( 'GetExternalIPAddress', {}, reactor, onResponse ) op = AsyncOp( callback, actionOp.cancel ) return op
def __init__(self, fileClient, remotePath, localPath, callback=None): self.fileClient = fileClient self.remotePath = remotePath self.localPath = localPath self.out = file(localPath, "w+b") self.sizeOp = self.fileClient.callGetSize(remotePath, self._onGetSize) self.fileSize = -1 self.op = AsyncOp(callback, self._doCancel) self.blockSize = 16384 self.requestedSize = 0 self.receivedSize = 0 self.fetchOps = {}
def _directConnect( sslContext, remotePublicKey, directLocation, reactor, callback=None ) : def onConnect( connector ) : if connector.getError() != 0 : op.notify( None ) return authOp = _authenticateUser( connector.getSock(), sslContext, remotePublicKey, reactor, op.notify ) op.setCanceler( authOp.cancel ) connectOp = tcpConnect( directLocation.addr, reactor, onConnect ) op = AsyncOp( callback, connectOp.cancel ) return op
def add(self, cname, keyid): # Lookup the contact info and call _doAddContact if self.status != self.ONLINE: return self._error("Can't lookup buddy while not online.") httpOp = HttpRequest(self.reactor).get( # 'http://cspace.in/pubkey/%s' % keyid, "http://identity.datahaven.net/cspacekeys/%s" % keyid, self._onLookupResponse, ) self._addOp = AsyncOp(self._onAddContact, httpOp.cancel) self._addOp.cname = cname return True
def cspaceConnect(cspaceAddr, remoteUser, remoteService, reactor, callback): def onTCPConnect(connector): if connector.getError() != 0: op.notify(-1, None) return connectOp = CSpaceConnector(connector.getSock(), remoteUser, remoteService, reactor, op.notify).getOp() op.setCanceler(connectOp.cancel) tcpOp = tcpConnect(cspaceAddr, reactor, onTCPConnect) op = AsyncOp(callback, tcpOp.cancel) return op
def __init__(self, clientObj, destId, startNodes, callback=None): assert startNodes self.clientObj = clientObj self.destId = destId self.destNumId = idToNum(destId) self.seen = {} self.closest = [] self.pending = [] self.calling = {} for nodeAddr in startNodes: self._newNode(nodeAddr) self._callPending() self.op = AsyncOp(callback, self._doCancel)
def __init__( self, sslConn, initiateCode, reactor, callback=None ) : self.sslConn = sslConn self.reactor = reactor if initiateCode == self.ACCEPT : self.sslFunc = self.sslConn.accept else : assert initiateCode == self.CONNECT self.sslFunc = self.sslConn.connect self.sock = self.sslConn.sock self.sock.setblocking( 0 ) timerOp = self.reactor.callLater( 0, self._doAction ) self.op = AsyncOp( callback, timerOp.cancel ) self.want = self.WANT_NONE
class SSLInitiator( object ) : ACCEPT = 0 CONNECT = 1 WANT_NONE = 0 WANT_READ = 1 WANT_WRITE = 2 def __init__( self, sslConn, initiateCode, reactor, callback=None ) : self.sslConn = sslConn self.reactor = reactor if initiateCode == self.ACCEPT : self.sslFunc = self.sslConn.accept else : assert initiateCode == self.CONNECT self.sslFunc = self.sslConn.connect self.sock = self.sslConn.sock self.sock.setblocking( 0 ) timerOp = self.reactor.callLater( 0, self._doAction ) self.op = AsyncOp( callback, timerOp.cancel ) self.want = self.WANT_NONE def getOp( self ) : return self.op def _removeRead( self ) : self.reactor.removeReadCallback( self.sock.fileno() ) self.want = self.WANT_NONE def _removeWrite( self ) : self.reactor.removeWriteCallback( self.sock.fileno() ) self.want = self.WANT_NONE def _doAction( self ) : try : self.sslFunc() except SSLWantReadError : if self.want != self.WANT_READ : if self.want == self.WANT_WRITE : self._removeWrite() self.reactor.addReadCallback( self.sock.fileno(), self._doAction ) self.want = self.WANT_READ self.op.setCanceler( self._removeRead ) return except SSLWantWriteError : if self.want != self.WANT_WRITE : if self.want == self.WANT_READ : self._removeRead() self.reactor.addWriteCallback( self.sock.fileno(), self._doAction ) self.want = self.WANT_WRITE self.op.setCanceler( self._removeWrite ) return except SSLError, e : if self.want == self.WANT_READ : self._removeRead() elif self.want == self.WANT_WRITE : self._removeWrite() self.op.notify( e ) return except:
def __init__( self, requestId, callback=None, canceler=None ) : AsyncOp.__init__( self, callback, canceler ) self.requestId = requestId
class CurlPerformer( object ) : def __init__( self, curlHandle, reactor, callback=None ) : self.reactor = reactor self.cm = pycurl.CurlMulti() self.c = curlHandle self.cm.add_handle( self.c ) self.fdsets = ( [], [], [] ) self.timerOp = self.reactor.callLater( 0, self._onInitialTimer ) self.op = AsyncOp( callback, self._doCancel ) def getOp( self ) : return self.op def _onInitialTimer( self ) : self.timerOp = self.reactor.addTimer( 1, self._onProcess ) self._onProcess() def _addEvents( self ) : self.fdsets = self.cm.fdset() r = self.reactor for fd in self.fdsets[0] : r.addReadCallback( fd, self._onProcess ) for fd in self.fdsets[1] : r.addWriteCallback( fd, self._onProcess ) for fd in self.fdsets[2] : r.addExceptionCallback( fd, self._onProcess ) def _removeEvents( self ) : r = self.reactor for fd in self.fdsets[0] : r.removeReadCallback( fd ) for fd in self.fdsets[1] : r.removeWriteCallback( fd ) for fd in self.fdsets[2] : r.removeExceptionCallback( fd ) def _doCancel( self ) : self.timerOp.cancel() self.timerOp = None self._removeEvents() self.cm.remove_handle( self.c ) self.cm.close() def _doNotify( self, errCode ) : self.timerOp.cancel() self.timerOp = None self.cm.remove_handle( self.c ) self.cm.close() self.op.notify( errCode ) def _onProcess( self ) : self._removeEvents() while 1 : ret,numHandles = self.cm.perform() if ret != pycurl.E_CALL_MULTI_PERFORM : break numQ, okList, errList = self.cm.info_read() assert numQ == 0 assert len(okList)+len(errList) <= 1 if len(okList) > 0 : self._doNotify( 0 ) elif len(errList) > 0 : c,err,errMsg = errList[0] self._doNotify( err ) else : self._addEvents()
class SSLCloser( object ) : WANT_NONE = 0 WANT_READ = 1 WANT_WRITE = 2 def __init__( self, sslConn, reactor, callback=None, timeout=30 ) : self.sslConn = sslConn self.reactor = reactor self.timeout = timeout self.sock = sslConn.sock self.want = self.WANT_NONE self.timerOp = self.reactor.callLater( 0, self._onAction ) self.timeoutOp = self.reactor.callLater( self.timeout, self._onTimeout ) self.op = AsyncOp( callback, self._close ) def getOp( self ) : return self.op def _close( self ) : if self.timerOp : self.timerOp.cancel() if self.timeoutOp : self.timeoutOp.cancel() if self.want == self.WANT_READ : self._removeRead() elif self.want == self.WANT_WRITE : self._removeWrite() self.sock.close() def _closeNotify( self ) : self._close() self.op.notify() def _removeRead( self ) : assert self.want == self.WANT_READ self.reactor.removeReadCallback( self.sock.fileno() ) self.want = self.WANT_NONE def _removeWrite( self ) : assert self.want == self.WANT_WRITE self.reactor.removeWriteCallback( self.sock.fileno() ) self.want = self.WANT_NONE def _onAction( self ) : self.timerOp = None self._doAction() def _doAction( self ) : try : result = self.sslConn.shutdown() if result == 0 : result = self.sslConn.shutdown() except SSLWantReadError : if self.want != self.WANT_READ : if self.want == self.WANT_WRITE : self._removeWrite() self.reactor.addReadCallback( self.sock.fileno(), self._doAction ) self.want = self.WANT_READ return except SSLWantWriteError : if self.want != self.WANT_WRITE : if self.want == self.WANT_READ : self._removeRead() self.reactor.addWriteCallback( self.sock.fileno(), self._doAction ) self.want = self.WANT_WRITE return except SSLError, e : self._closeNotify() return self._closeNotify()
class CSpaceService: OFFLINE = UserSession.OFFLINE CONNECTING = UserSession.CONNECTING ONLINE = UserSession.ONLINE DISCONNECTING = UserSession.DISCONNECTING UNKNOWN = -1 STATII = { OFFLINE: "Offline", CONNECTING: "Connecting", ONLINE: "Online", DISCONNECTING: "Disconnecting", UNKNOWN: "Unknown", } ########## ## Service initialization ## def __init__(self, seedNodes, settings=None, reactor=None): global logger logger.info("CSpaceService init") if reactor is None: reactor = SelectReactor() if settings is None: settings = localSettings() self.reactor = reactor self.settings = settings self.dispatcher = Eventer() self.seedNodes = seedNodes # Session Manager self.session = UserSession(self.seedNodes, reactor) # Our user profile self.profile = None # Session status State Machine self.sm = self.session.sm # Make sure we handle the edges/state transitions self.sm.insertCallback(self._onStateChange) # Manage actions self.actionManager = ActionManager() # Loads applets in subprocesses self.appletServer = AppletServer(self.session, self.actionManager, self.reactor) # Our contacts' statii self.statusProbe = ContactStatusChecker(self, reactor) # Our current status self.status = self.sm.current() self.statusmsg = self.STATII[self.status] self.exitAfterOffline = False self.reactor.callLater(0, self._onStarted) def _onStarted(self): self.dispatcher.trigger("service.start") def _error(self, msg): logger.error(msg) self.dispatcher.trigger("error", msg) return (False, msg) def run(self): self.reactor.run() try: self.appletServer.bridgeThread.reactor.stop() except AttributeError: pass def svcstatus(self): return self.statusmsg def stop(self): # offline first if self.status != self.OFFLINE: self.exitAfterOffline = True return self.offline() for reactor in (self.appletServer.bridgeThread.reactor, self.reactor): reactor.stop() self.dispatcher.trigger("service.stop") return True ########## ## Go online and offline, change profiles ## def switch(self, profile, password): if self.status is not self.OFFLINE: return self._error("Not currently offline.") st = self.settings logger.debug("Changing profile...") profiles = [userName for userName, keyId, entry in listProfiles()] if not st.getInt("Settings/RememberKey", 0): return self._error("No key found.") if not profile in entries: return self._error("No profile %s found.") profile = loadProfile(profile, password) if not profile: return self._error("Profile couldn't be loaded.") st.setString("Settings/SavedProfile", profile.name) st.setString("Settings/SavedPassword", password) saveLocalSettings() self.dispatcher.trigger("profile.switch", profile) return True def online(self): global logger if self.status is not self.OFFLINE: return self._error("Not currently offline.") st = self.settings logger.debug("Going online...") entries = [entry for userName, keyId, entry in listProfiles()] if not st.getInt("Settings/RememberKey", 0): return self._error("No key found.") entry = st.getString("Settings/SavedProfile") password = st.getString("Settings/SavedPassword") if not (entry and password and (entry in entries)): return self._error("No profile or password.") profile = loadProfile(entry, password) if not profile: return self._error("Profile couldn't be loaded.") self.profile = profile logger.debug("Going online as %s keyID=%s ..." % (profile.name, profile.keyId)) self.session.goOnline(self.profile) self.reconnector = Reconnector(self.profile, self._onReconnectTimer, self.reactor) return True def _onReconnectTimer(self): assert self.reconnector if self.reconnector.timeLeft > 0: return self.profile = self.reconnector.profile self.reconnector.shutdown() if self.session.sm.current() == 0: self.session.goOnline(self.profile) self.reconnector = Reconnector(self.profile, self._onReconnectTimer, self.reactor) self.dispatcher.trigger("profile.reconnecting", self.profile) def offline(self): if self.status is not self.ONLINE: return self._error("Not currently online.") self.session.goOffline() self.profile = None self.reconnector.shutdown() self.reconnector = None return True def _onStateChange(self): """ Respond to connecting/online/disconnecting/offline notices """ self.status = self.sm.current() self.statusmsg = None # No longer online if self.sm.previous() == self.ONLINE: # Stop checking contact statii self.statusProbe.stopProbing() # Clean up any open applets self.appletServer.clearConnections() # Now offline if self.sm.current() == self.OFFLINE: profile = self.profile self.profile = None if self.reconnector: if self.sm.previous() == self.CONNECTING: msg = "Connect failed." else: msg = "Disconnected." self.reconnector.startReconnectTimer(msg) self.statusmsg = msg self.dispatcher.trigger("profile.offline", profile) if self.exitAfterOffline: self.stop() # Now online elif self.sm.current() == self.ONLINE: # Restart checking contact statii self.dispatcher.trigger("profile.online", self.profile) self.statusProbe.startProbing() # Now connecting elif self.sm.current() == self.CONNECTING: self.dispatcher.trigger("profile.connecting", self.profile) elif self.sm.current() == self.DISCONNECTING: self.dispatcher.trigger("profile.disconnecting", self.profile) if not self.statusmsg: self.statusmsg = self.STATII[self.status] logger.info("STATUS: " + self.statusmsg) ########## ## List, add and remove contacts ## def list(self): if self.status != self.ONLINE: return self._error("Not connected.") contactsByName = self.profile.contactNames statii = [] STATII = CSpaceService.STATII for key in contactsByName: contact = contactsByName[key] if hasattr(contact, "status"): buddystatus = (key, contact.status, STATII[contact.status]) else: buddystatus = (key, -1, STATII[-1]) statii.append(buddystatus) statii.sort() return statii def add(self, cname, keyid): # Lookup the contact info and call _doAddContact if self.status != self.ONLINE: return self._error("Can't lookup buddy while not online.") httpOp = HttpRequest(self.reactor).get( # 'http://cspace.in/pubkey/%s' % keyid, "http://identity.datahaven.net/cspacekeys/%s" % keyid, self._onLookupResponse, ) self._addOp = AsyncOp(self._onAddContact, httpOp.cancel) self._addOp.cname = cname return True def addKey(self, cname, key): if cname and not isValidUserName(cname): return self._error("Bad username.") k = RSAKey() try: k.fromPEM_PublicKey(pemPublicKey) except RSAError: return self._error("Bad PEM-encoded key.") contact = Contact(k, cname) self._onAddContact(contact) def _onLookupResponse(self, responseCode, data): if responseCode != 200: self._addOp.notify(None) return inp = StringIO.StringIO(data) name = inp.readline().strip() pemPublicKey = inp.read() if name and not isValidUserName(name): self._addOp.notify(None) return k = RSAKey() try: k.fromPEM_PublicKey(pemPublicKey) except RSAError: self._addOp.notify(None) contact = Contact(k, self._addOp.cname) self._addOp.notify(contact) def _onAddContact(self, contact): del self._addOp if contact == None: # Contact lookup failed. return self.profile.addContact(contact) saveProfileContacts(self.profile) self.dispatcher.trigger("contact.add", contact) def remove(self, cname=None): if self.status != self.ONLINE: return self._error("Can't modify contact list while not online.") # Lookup the contact in profile and remove it contact = self.profile.getContactByName(cname) if contact is None: return self._error("No such contact.") self.profile.removeContact(contact) saveProfileContacts(self.profile) self.dispatcher.trigger("contact.remove", contact) return True ######### ## Perform contact action ## def action(self, buddy, action): if self.status != self.ONLINE: return False actionManager = self.actionManager if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) for actionItem in actionManager.actions: if actionItem[4] == action: logger.info("Running action %s on %s" % (action, buddy)) retVal = actionManager.execAction(action, buddy) self.dispatcher.trigger("contact.action", self.profile.contactNames[buddy], action, retVal) return retVal else: logger.info("Not action %s" % actionItem[4]) return self._error("Bad action name: %s" % action) def probe(self, buddy): if self.status != self.ONLINE: return self._error("Not online.") if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) self.statusProbe.probeContactStatus(buddy) return True def contactinfo(self, buddy): if self.status != self.ONLINE: return self._error("Not online.") if buddy not in self.profile.contactNames: return self._error("Bad buddy name: %s" % buddy) contact = self.profile.contactNames[buddy] contactKey = hexEncode(contact.publicKeyData) if hasattr(contact, "status"): contactStatus = contact.status else: contactStatus = -1 return [contact.name, contactKey, self.STATII[contactStatus]]
def __init__( self, opId, callback=None, canceler=None ) : AsyncOp.__init__( self, callback, canceler ) self.opId = opId
def __init__( self, sock, pendingWrite, reactor, callback=None ) : TCPWriter.__init__( self, sock, pendingWrite, reactor ) self.op = AsyncOp( callback, self.shutdown )
class IMInitializer( object ) : CONNECTING = 0 GETTINGKEY = 1 NOTIFYING = 2 REGISTERING = 3 REGISTERED = 4 CLOSED = 5 def __init__( self, reactor, callback=None ) : assert env.isContactAction or env.isIncoming self.reactor = reactor self.op = AsyncOp( callback, self._doCancel ) self.state = self.CONNECTING self.connectOp = tcpConnect( ('127.0.0.1',env.port), self.reactor, self._onConnect ) self.stream = None def getOp( self ) : return self.op def _doCancel( self ) : if self.connectOp : self.connectOp.cancel() if self.stream : self.stream.close( deferred=True ) self.state = self.CLOSED def _onConnect( self, connector ) : self.connectOp = None if connector.getError() != 0 : self.op.notify( -1, None ) return self.stream = TCPLineStream( connector.getSock(), self.reactor ) self.stream.setCloseCallback( self._onClose ) self.stream.setErrorCallback( self._onError ) self.stream.setInputCallback( self._onInput ) self.stream.enableRead( True ) if env.isContactAction : self.stream.writeData( 'GETPUBKEY %s\r\n' % env.contactName ) else : self.stream.writeData( 'GETINCOMINGPUBKEY %s\r\n' % env.connectionId ) self.state = self.GETTINGKEY def _onClose( self ) : self._doCancel() self.op.notify( -1, None ) def _onError( self, err, errMsg ) : self._onClose() def _onInput( self, line ) : if self.state == self.GETTINGKEY : if not line.startswith('OK') : self._doCancel() self.op.notify( -1, None ) return self.pubKey = line.split()[1] self.state = self.REGISTERING self.listenerName = 'CSpaceIM-%s' % self.pubKey self.stream.writeData( 'REGISTERLISTENER %s\r\n' % self.listenerName ) elif self.state == self.REGISTERING : if not line.startswith('OK') : self.state = self.NOTIFYING if env.isContactAction : msg = 'CONTACTACTION' else : msg = 'INCOMING %s' % env.connectionId self.stream.writeData( 'SENDLISTENER %s %s\r\n' % (self.listenerName,msg) ) return self.state = self.REGISTERED self.stream.setCloseCallback( None ) self.stream.setErrorCallback( None ) self.stream.setInputCallback( None ) self.op.notify( 1, self ) elif self.state == self.NOTIFYING : if not line.startswith('OK') : self._doCancel() self.op.notify( -1, None ) return self._doCancel() self.op.notify( 0, self ) else : assert False
class FileFetcher(object): def __init__(self, fileClient, remotePath, localPath, callback=None): self.fileClient = fileClient self.remotePath = remotePath self.localPath = localPath self.out = file(localPath, "w+b") self.sizeOp = self.fileClient.callGetSize(remotePath, self._onGetSize) self.fileSize = -1 self.op = AsyncOp(callback, self._doCancel) self.blockSize = 16384 self.requestedSize = 0 self.receivedSize = 0 self.fetchOps = {} def getOp(self): return self.op def _doCancel(self): if self.sizeOp: self.sizeOp.cancel() for fop in self.fetchOps.keys(): fop.cancel() self.out.close() def _notifyError(self): self._doCancel() self.op.notify(-1, self.fileSize) def _onGetSize(self, err, size): self.sizeOp = None if err != 0: self._notifyError() return self.fileSize = size self._fetchMore() def _fetchMore(self): while (self.requestedSize < self.fileSize) and (len(self.fetchOps) < 5): self._fetchBlock() if self.receivedSize == self.fileSize: assert len(self.fetchOps) == 0 self.out.close() self.op.notify(self.receivedSize, self.fileSize) return self.op.notify(self.receivedSize, self.fileSize) def _fetchBlock(self): size = min(self.fileSize - self.requestedSize, self.blockSize) assert size > 0 offset = self.requestedSize def onResponse(err, data): del self.fetchOps[rpcOp] if err != 0: self._notifyError() return if len(data) != size: self._notifyError() return try: self.out.write(data) except (IOError, OSError), e: self._notifyError() return self.receivedSize += size self._fetchMore() rpcOp = self.fileClient.callRead(self.remotePath, offset, size, onResponse) self.requestedSize += size self.fetchOps[rpcOp] = 1