def startConnecting(self): # This fires when the DC client connects and wants to be online dcall_discard(self, 'reconnect_dcall') # Only continue if the UDP port is ready if not self.bindUDPPort(): return # Any reason to be online? if not self.reconnectDesired(): return if self.icm or self.osm: # Already in progress; return description. return self.login_text # Get config from DNS def dns_cb(): try: # 2011-08-21: New nodes ignore the value of 'when'. when, ipps = self.state.dns_ipcache except ValueError: pass else: random.shuffle(ipps) for ipp in ipps: ad = Ad().setRawIPPort(ipp) self.state.refreshPeer(ad, 0) self.startInitialContact() self.dcfg.getDynamicConfig(dns_cb)
def doRejoin(self): if self.state != 'invisible': return dcall_discard(self, 'autoRejoin_dcall') self.state = 'ready' # This can trigger an event_DtellaUp() self.main.stateChange_ObserverUp()
def d_MyNick(self, nick): # This is a fake RevConnect that we should terminate. dcall_discard(self, 'init_dcall') if self.state != 'login_1': self.fatalError("$MyNick not expected.") return if not self.main.abort_nick: self.transport.loseConnection() return # Transfer my state to the connection abort handler AbortTransfer_In(self.main.abort_nick, self) self.main.abort_nick = None
def removeDCHandler(self, dch): # DC client has left. if self.pending_dch is dch: self.pending_dch = None return elif self.dch is not dch: return self.dch = None self.abort_nick = None # Announce the DC client's departure. if dch.state == 'ready': self.stateChange_ObserverDown() # If another handler is waiting, let it on. if self.pending_dch: self.pending_dch.attachMeToDtella() self.pending_dch = None return # Maybe forget about reconnecting if not self.reconnectDesired(): dcall_discard(self, 'reconnect_dcall') # Maybe skip the disconnect if self.state.persistent or not (self.icm or self.osm): return # Client left, so shut down in a while when = core.NO_CLIENT_TIMEOUT if self.disconnect_dcall: self.disconnect_dcall.reset(when) return def cb(): self.disconnect_dcall = None self.shutdown(reconnect='no') self.disconnect_dcall = reactor.callLater(when, cb)
def scheduleRequestBlocks(self): # After we receive a sync reply from the bridge, individually # request the full data for each block hash. dcall_discard(self, 'requestBlocks_dcall') if not self.req_blocks: return def cb(timeout): self.requestBlocks_dcall = None # Pick one of the hashes randomly try: bhash = self.req_blocks.peek() except KeyError: return # Build request packet packet = ['bQ'] packet.append(self.main.osm.me.ipp) packet.append(bhash) # Send to bridge ad = Ad().setRawIPPort(self.parent_n.ipp) self.main.ph.sendPacket(''.join(packet), ad.getAddrTuple()) # Too many failures, just give up if timeout > 30.0: self.req_blocks.clear() return # Schedule next request. # This will become immediate if a reply arrives. when = random.uniform(0.9, 1.1) * timeout timeout *= 1.2 self.requestBlocks_dcall = reactor.callLater(when, cb, timeout) self.requestBlocks_dcall = reactor.callLater(0, cb, 1.0)
def connectionLost(self, reason): self.main.removeDCHandler(self) dcall_discard(self, 'init_dcall') dcall_discard(self, 'chatRate_dcall') dcall_discard(self, 'autoRejoin_dcall')
def d_ValidateNick(self, nick): dcall_discard(self, 'init_dcall') if self.state != 'login_1': self.fatalError("$ValidateNick not expected.") return # Next, we expect $GetNickList+$MyINFO self.state = 'login_2' reason = validateNick(nick) if reason: self.pushStatus("Your nick is invalid: %s" % reason) self.pushStatus("Please fix it and reconnect. Goodbye.") self.transport.loseConnection() return self.nick = nick self.pushHello(self.nick)
def attachMeToDtella(self): CHECK(self.main.dch is None) if self.state == 'queued': self.queued_dcall.cancel() self.queued_dcall = None self.pushStatus( "The other client left. Resuming normal connection.") dcall_discard(self, 'queued_dcall') # Add the post-login handlers self.addDispatch('$ConnectToMe', 2, self.d_ConnectToMe) self.addDispatch('$RevConnectToMe', 2, self.d_RevConnectToMe) self.addDispatch('$Search', -2, self.d_Search) self.addDispatch('$To:', -5, self.d_PrivateMsg) self.addDispatch("<%s>" % self.nick, -1, self.d_PublicMsg) # Announce my presence. # If Dtella's online too, this will trigger an event_DtellaUp. self.state = 'ready' self.main.addDCHandler(self)
def addDCHandler(self, dch): CHECK(not self.dch) CHECK(dch.state == 'ready') self.dch = dch # Cancel the disconnect timeout dcall_discard(self, 'disconnect_dcall') # Start connecting, or get status of current connection text = self.startConnecting() if text: # We must already be connecting/online. # Show the last status message. LOG.debug(text) dch.pushStatus(text) # Send a message if there's a newer version self.dcfg.resetReportedVersion() self.dcfg.reportNewVersion() self.stateChange_ObserverUp()
def sendState(self): dcall_discard(self, 'sendState_dcall') def cb(): self.sendState_dcall = None osm = self.main.osm CHECK(osm and osm.syncd) # Decide when to retransmit next when = 60 * 5 self.sendState_dcall = reactor.callLater(when, cb) # Broadcast header packet = osm.mrm.broadcastHeader('BS', osm.me.ipp) # The meat block_hashes, blocks = self.getStateData(packet) # Signature self.signPacket(packet, broadcast=True) # Broadcast status message osm.mrm.newMessage(''.join(packet), tries=8) # Broadcast data blocks # This could potentially be a bottleneck for slow connections for b in blocks: packet = osm.mrm.broadcastHeader('BB', osm.me.ipp) packet.append(self.nextPktNum()) packet.append(struct.pack("!H", len(b))) packet.append(b) osm.mrm.newMessage(''.join(packet), tries=4) # The first time, send state immediately. cb()
def saveState(self): # Save the state file every few minutes def cb(): when = random.uniform(5 * 60, 6 * 60) self.saveState_dcall = reactor.callLater(when, cb) d = {} # Store all state data to dictionary for ls in self.loadsavers: ls.save(self, d) # Write to file try: f = file(self.filename, "wb") keys = d.keys() keys.sort() f.write(struct.pack("!6sI", "DTELLA", len(keys))) for k in keys: v = d[k] f.write(struct.pack("!I", len(k))) f.write(k) f.write(struct.pack("!I", len(v))) f.write(v) f.close() except: twisted.python.log.err() dcall_discard(self, 'saveState_dcall') cb()
def saveState(self): # Save the state file every few minutes def cb(): when = random.uniform(5*60, 6*60) self.saveState_dcall = reactor.callLater(when, cb) d = {} # Store all state data to dictionary for ls in self.loadsavers: ls.save(self, d) # Write to file try: f = file(self.filename, "wb") keys = d.keys() keys.sort() f.write(struct.pack("!6sI", "DTELLA", len(keys))) for k in keys: v = d[k] f.write(struct.pack("!I", len(k))) f.write(k) f.write(struct.pack("!I", len(v))) f.write(v) f.close() except: twisted.python.log.err() dcall_discard(self, 'saveState_dcall') cb()
def dtellaShutdown(self): dcall_discard(self, 'cfgRefresh_dcall') self.cfg_cb = None
def shutdown(self): dcall_discard(self, 'requestBlocks_dcall')
def shutdown(self): dcall_discard(self, 'sendState_dcall') for b in self.cached_blocks.itervalues(): dcall_discard(b, 'expire_dcall')
def connectionLost(self, reason): dcall_discard(self, 'timeout_dcall')