def poll(self): try: if self.status == 0: if len(self.sources) == 0: self.status = 3 self.lock.acquire() for listener in self.listeners: listener.onHublist(self._hubs.values()) self.lock.release() return 1 addr = self.sources[0] del self.sources[0] self.sock = AsyncSocket() self.sock.connect(addr[0]) request = "GET %s HTTP/1.0\r\n" % addr[1] request += "Host: %s\r\n\r\n" % addr[0][0] self.sock.send(request) self.sock.poll() self.timestamp = time.time() self.setStatus(1) elif self.status == 1: if time.time() - self.timestamp > DCHublistRetriever.LIST_TIMEOUT: self.setStatus(2) self.sock.poll() if not self.sock.isAlive(): self.parse(self.sock.recv()) self.setStatus(2) else: if self.sock != None: self.sock.close() if self.sock.poll(): self.sock = None if self.status == 2: self.setStatus(0) else: return 0 else: return 1 return 0 except error: self.setStatus(2) return 0 except DNSError: self.setStatus(2) return 0
def parseCmd(self, cmd): if cmd[0] == '<': #Chat message self.appendLogRow(self.LOG_CHAT, cmd) return 2 elif cmd[0] != "$": self.appendLogRow(self.LOG_ERROR, "Unknown command: " + cmd) return 2 cmd = cmd[1:] try: pos = cmd.index(' ') cmdname = cmd[:pos] except ValueError: if cmd == "HubIsFull": self.appendLogRow(self.LOG_ERROR, "Hub is full; disconnected.") return 0 else: #Discard silently return 0 if cmdname == "Hello": user = DCUser() user.nick = cmd[pos+1:] self.addUser(user) elif cmdname == "MyINFO": try: if cmd[pos+1:pos+6] != "$ALL ": return 2 cmd = cmd[pos+6:] #Nick pos = cmd.index(" ") nick = cmd[:pos] cmd = cmd[pos+1:] if nick == self.userNick: self.logged = 1 return 2 try: user = self._users[nick] except KeyError: user = DCUser() user.nick = nick self.addUser(user, 0) #Description pos = cmd.index("$ $") if pos != 0: user.description = cmd[:pos] else: user.description = None cmd = cmd[pos+3:] #Split remaining fields fields = cmd.split("$") if len(fields) != 4: raise ValueError if len(fields[3]) > 0: raise ValueError #Connection fields[0] = fields[0][:-1] #get rid of speed class byte if fields[0] == "56Kbps": user.connection = DCUser.CONN_56K elif fields[0] == "Satellite": user.connection = DCUser.CONN_SATELLITE elif fields[0] == "DSL": user.connection = DCUser.CONN_DSL elif fields[0] == "Cable": user.connection = DCUser.CONN_CABLE elif fields[0] == "LAN(T1)": user.connection = DCUser.CONN_T1 elif fields[0] == "LAN(T3)": user.connection = DCUser.CONN_T3 #Email if len(fields[1]) != 0: user.email = fields[1] else: user.email = None #Share user.share = long(fields[2]) self._size += user.share #Notify listeners of changes self.lock.acquire() for listener in self.listeners: listener.onUserInfo(self, user) self.lock.release() except ValueError: pass elif cmdname == "Search": cmd = cmd[7:] try: p1, p2 = cmd.index(':'), cmd.index(' ') addr = cmd[:p1] if addr == "Hub": activeSearch = 0 addr = cmd[p1+1:p2] else: activeSearch = 1 addr = (addr, int(cmd[p1+1:p2])) params = cmd[p2+1:].split('?') if len(params) != 5: raise ValueError if params[0] == 'F': limit = DCFileList.LIMIT_NONE size = 0 else: if params[1] == 'F': limit = DCFileList.LIMIT_ATLEAST else: limit = DCFileList.LIMIT_ATMOST size = long(params[2]) worker = DCWorker.getWorker() slots = [worker.getSettings().slots - worker.getUsedSlots(), worker.getSettings().slots] hubAddr = self.sock.getpeername() #Safety check if hubAddr == None: return 0 for i in worker.getLocalLists(): for r in i.search(params[4].replace('$', ' '), limit, size): data = '$SR %s %s\5%d %d/%d\5%s (%s:%d)' % (self.userNick, r.getPath(), r.size, slots[0], slots[1], self._name, hubAddr[0], hubAddr[1]) if activeSearch: worker.getSearchWorker().sendResult(addr, data + '|') else: data += '\5%s|' % addr self.sock.send(data) except ValueError: pass elif cmdname == 'SR': DCWorker.getWorker().getSearchWorker().passiveResult(cmd[pos+1:]) elif cmdname == "Quit": self.removeUser(cmd[pos+1:]) elif cmdname == "To:": try: idx = cmd.index('$') - 1 nick = cmd[cmd[:idx].rindex(' ')+1:idx] self.appendLogRow(self.LOG_PRIVCHAT, '<%s> %s' % (nick, cmd[idx+2:])) except ValueError: pass elif cmdname == "ConnectToMe": addr = cmd[cmd.rindex(' ')+1:].split(':') try: addr = (addr[0], int(addr[1])) except ValueError: pass sock = AsyncSocket() sock.connect(addr) DCWorker.getWorker().addJob(DCXfer(sock)) elif cmdname == 'RevConnectToMe': if not DCWorker.getWorker().getSettings().active: return 2 self.requireConnection(cmd[cmd.index(' ')+1:cmd.rindex(' ')]) elif cmdname == "LogedIn": user = DCUser() user.op = 1 user.nick = cmd[pos+1:] self.addUser(user) elif cmdname == "NickList": nicks = cmd[pos+1:].split("$$") for nick in nicks: user = DCUser() user.nick = nick self.addUser(user) elif cmdname == "OpList": nicks = cmd[pos+1:].split("$$") for nick in nicks: user = DCUser() user.op = 1 user.nick = nick self.addUser(user) elif cmdname == "ForceMove": newAddr = cmd[pos+1:] if len(newAddr) == 0: return 0 addr = self.parseAddress(newAddr) if addr == None: return 0 self.appendLogRow(self.LOG_STATUS, "Redirected to \"" + newAddr + "\".") self._addr = addr return 1 elif cmdname == "Lock": key = solveChallenge(cmd[pos+1:]) if len(key) == 0: self.appendLogRow(self.LOG_ERROR, "Error while generating authkey.") return 0 self.sendCmd("Key", key) self.sendCmd("ValidateNick", self.userNick) self.sendCmd("Version", "1.3") self.sendCmd("MyINFO", self.buildInfo()) self.sendCmd("GetNickList") elif cmdname == "HubName": self._name = cmd[pos+1:] self.lock.acquire() for i in self.listeners: i.onHubInfo(self) self.lock.release() elif cmdname == "GetPass": self.appendLogRow(self.LOG_ERROR, "Hub requires password. Function not yet implemented.") return 0 elif cmdname == 'ValidateDenide': self.appendLogRow(self.LOG_ERROR, "Authentication error; disconnected.") return 0 #else: # self.appendLogRow(self.LOG_ERROR, "Unknown command: " + cmd) return 2
class DCHub(object, Job, EventGenerator): STATUS_CONNECTING = 1 STATUS_CONNECTED = 2 STATUS_CLOSED = 3 LOG_STATUS = 1 LOG_CHAT = 2 LOG_PRIVCHAT = 3 LOG_ERROR = 4 USER_NORMAL = 1 USER_FILESERVER = 4 USER_SPEEDUSER = 8 USER_AWAY_FLAG = 2 MAX_LOG_ROWS = 2000 RECONNECTION_DELAY = 30 def __init__(self, addr): EventGenerator.__init__(self) self._addr = None self._status = 0 self.logged = 0 self.userNick = DCWorker.getWorker().user.nick self.userStatus = DCHub.USER_NORMAL self.delay = None self._users = {} self._log = [] self.opLock = Lock() self._addr = self.parseAddress(addr) self.reset() def reset(self): if self._addr != None: self._name = self._addr[0] else: self._name = 'unknown' self.sock = None self._size = long(0) self.cmdQueue = [] self.buf = '' def disconnect(self): self.stop() def startSearch(self, search, active): self.opLock.acquire() worker = DCWorker.getWorker() cmd = '$Search ' if active: cmd += '%s:%d ' % (worker.getLocalAddress(), worker.getSettings().port) else: cmd += 'Hub:%s ' % self.userNick cmd += 'F?F?0?1?' for i in search.getPattern().split(' '): cmd += i + "$" self.cmdQueue.append(cmd[:-1] + '|') self.opLock.release() def requireConnection(self, nick): self.opLock.acquire() worker = DCWorker.getWorker() if worker.getSettings().active: self.cmdQueue.append('$ConnectToMe %s %s:%d|' % (nick, worker.getLocalAddress(), worker.getSettings().port)) else: self.cmdQueue.append('$RevConnectToMe %s %s|' % (self.userNick, nick)) self.opLock.release() def updateInfo(self): info = self.buildInfo() self.appendLogRow(self.LOG_STATUS, 'Updating user info...') self.opLock.acquire() self.cmdQueue.append('$MyINFO %s' % info) self.opLock.release() def setAway(self, away): if away: if self.userStatus & DCHub.USER_AWAY_FLAG: return else: txt = 'on' self.userStatus += DCHub.USER_AWAY_FLAG else: if self.userStatus & DCHub.USER_AWAY_FLAG: txt = 'off' self.userStatus -= DCHub.USER_AWAY_FLAG else: return info = self.buildInfo() self.appendLogRow(self.LOG_STATUS, 'Away flag: %s.' % txt) self.opLock.acquire() self.cmdQueue.append('$MyINFO %s' % info) self.opLock.release() def sendMsg(self, msg, nick=None): if self._status != 2: self.appendLogRow(self.LOG_ERROR, 'You must be connected to send messages.') return self.opLock.acquire() if nick == None: self.cmdQueue.append('<%s> %s|' % (self.userNick, msg.replace('|', '_'))) else: self.cmdQueue.append('$To: %s From: %s $<%s> %s|' % (nick, self.userNick, self.userNick, msg.replace('|', '_'))) self.opLock.release() def poll(self): try: if self._status == 0: if self.delay != None: curtime = time.time() self.delay[0] -= curtime - self.delay[1] if self.delay[0] <= 0: self.delay = None else: self.delay[1] = curtime return 0 self.appendLogRow(self.LOG_STATUS, "Connecting to " + self._addr[0] + ":" + str(self._addr[1]) + "...") self.sock = AsyncSocket() self.sock.connect(self._addr) self.setStatus(1) elif self._status == 1: self.sock.poll() if self.sock.isConnected(): self.appendLogRow(self.LOG_STATUS, "Connection established.") self.setStatus(2) elif self._status == 2: self.opLock.acquire() if self.logged: for cmd in self.cmdQueue: self.sock.send(cmd) self.cmdQueue = [] self.opLock.release() finished = self.sock.poll() self.buf += self.sock.recv() if len(self.buf) > 0: cmds = self.buf.split('|') if self.buf[-1] != "|": self.buf = cmds[-1] cmds = cmds[:-1] else: self.buf = "" for i in cmds: if len(i) > 0: res = self.parseCmd(i) if res == 0: self.setStatus(6) return 0 elif res == 1: self.setStatus(3) return 0 if finished: self.appendLogRow(self.LOG_ERROR, "Connection closed.") self.checkReconnection() elif self._status == 3: self.sock.close() if self.sock.poll(): self.removeAllUsers() self.opLock.acquire() self.reset() if self._status == 3: self._status = 0 self.opLock.release() self.lock.acquire() for listener in self.listeners: listener.onHubInfo(self) self.lock.release() elif self._status == 4: self.sendCmd('Quit', self.userNick) self.sock.poll() self.setStatus(5) elif self._status == 5: if self.sock.getBytesToSend() > 0: self.sock.poll() else: self.setStatus(6) elif self._status == 6: if self.sock != None: self.sock.close() if not self.sock.poll(): return 0 self.opLock.acquire() self.reset() self._status = 7 self.opLock.release() self.removeAllUsers() self.lock.acquire() for listener in self.listeners: listener.onHubDisconnection(self) self.lock.release() return 1 else: return 1 except DNSError: self.appendLogRow(self.LOG_ERROR, 'Connection closed because of DNS error.') self.setStatus(6) except error: self.appendLogRow(self.LOG_ERROR, 'Connection closed because of socket error.') self.checkReconnection() return 0 def stop(self): self.opLock.acquire() log = None if self._status < 4: self._status = (6, 6, 4, 6)[self._status] log = (self.LOG_STATUS, 'Connection aborted by user.\n') self._log.append(log) self.opLock.release() if log: self.lock.acquire() for listener in self.listeners: listener.onLogUpdate(self, log) self.lock.release() def getAddress(self): return self._addr def getName(self): return self._name def getUserNum(self): return len(self._users) def getUsers(self): self.opLock.acquire() users = [] for user in self._users: users.append(self._users[user]) self.opLock.release() return users def getUserByNick(self, nick): self.opLock.acquire() try: user = self._users[nick] except KeyError: user = None self.opLock.release() return user def getSize(self): return self._size def getLogRows(self): return self._log def getStatus(self): return (self.STATUS_CONNECTING, self.STATUS_CONNECTING, self.STATUS_CONNECTED, self.STATUS_CONNECTING, self.STATUS_CLOSED, self.STATUS_CLOSED, self.STATUS_CLOSED, self.STATUS_CLOSED)[self._status] def isAlive(self): return (1,1,1,1,0,0,0,0)[self._status] #=================# # Private members # #=================# def parseAddress(self, addr): if addr.__class__ == str: pos = addr.find(':') if pos != -1: try: return (addr[:pos], int(addr[pos+1:])) except ValueError: self.appendLogRow(self.LOG_ERROR, "Invalid address: \"" + addr + "\".") self.setStatus(6) return None else: return (addr, 411) else: return addr def checkReconnection(self): if self._status == 2 and self.logged and self.getUserNum() > 0: self.delay = [self.RECONNECTION_DELAY, time.time()] self.setStatus(3) self.appendLogRow(self.LOG_STATUS, "Reconnecting in %d seconds..." % self.delay[0]) else: self.setStatus(6) def setStatus(self, status): self.opLock.acquire() if status > self._status: self._status = status self.opLock.release() def buildInfo(self): user = DCWorker.getWorker().getUser() info = "$ALL " + self.userNick + " " if user.description != None: info += user.description info += "$ $" if user.connection == DCUser.CONN_56K: info += "56Kbps" elif user.connection == DCUser.CONN_SATELLITE: info += "Satellite" elif user.connection == DCUser.CONN_DSL: info += "DSL" elif user.connection == DCUser.CONN_CABLE: info += "Cable" elif user.connection == DCUser.CONN_T1: info += "LAN(T1)" elif user.connection == DCUser.CONN_T3: info += "LAN(T3)" info += chr(self.userStatus) + "$" if user.email != None: info += user.email info += "$" if user.share == -1: info += "0" else: info += str(user.share) info += "$" return info def appendLogRow(self, type, text): self.opLock.acquire() row = (type, text + '\n') self._log.append(row) if len(self._log) > DCHub.MAX_LOG_ROWS: del self._log[0] self.opLock.release() self.lock.acquire() for listener in self.listeners: listener.onLogUpdate(self, row) self.lock.release() def addUser(self, user, askinfo = 1): if len(user.nick) == 0 or user.nick == self.userNick: return if self._users.has_key(user.nick): if user.op: self._users[user.nick].op = 1 return user.hub = self if askinfo and user.connection == DCUser.CONN_NONE: self.sendCmd("GetINFO", user.nick, self.userNick) self.opLock.acquire() self._users[user.nick] = user self.opLock.release() self.lock.acquire() for listener in self.listeners: listener.onNewUser(self, user) self.lock.release() def removeUser(self, nick): self.opLock.acquire() try: user = self._users[nick] except KeyError: self.opLock.release() return user.hub = None if user.share > 0: self._size -= user.share del self._users[nick] self.opLock.release() self.lock.acquire() for listener in self.listeners: listener.onUserDisconnection(self, user) self.lock.release() def removeAllUsers(self): self.opLock.acquire() users = self._users self._users = {} self._size = 0 self.opLock.release() self.lock.acquire() for listener in self.listeners: for nick in users: listener.onUserDisconnection(self, users[nick]) self.lock.release() def parseCmd(self, cmd): if cmd[0] == '<': #Chat message self.appendLogRow(self.LOG_CHAT, cmd) return 2 elif cmd[0] != "$": self.appendLogRow(self.LOG_ERROR, "Unknown command: " + cmd) return 2 cmd = cmd[1:] try: pos = cmd.index(' ') cmdname = cmd[:pos] except ValueError: if cmd == "HubIsFull": self.appendLogRow(self.LOG_ERROR, "Hub is full; disconnected.") return 0 else: #Discard silently return 0 if cmdname == "Hello": user = DCUser() user.nick = cmd[pos+1:] self.addUser(user) elif cmdname == "MyINFO": try: if cmd[pos+1:pos+6] != "$ALL ": return 2 cmd = cmd[pos+6:] #Nick pos = cmd.index(" ") nick = cmd[:pos] cmd = cmd[pos+1:] if nick == self.userNick: self.logged = 1 return 2 try: user = self._users[nick] except KeyError: user = DCUser() user.nick = nick self.addUser(user, 0) #Description pos = cmd.index("$ $") if pos != 0: user.description = cmd[:pos] else: user.description = None cmd = cmd[pos+3:] #Split remaining fields fields = cmd.split("$") if len(fields) != 4: raise ValueError if len(fields[3]) > 0: raise ValueError #Connection fields[0] = fields[0][:-1] #get rid of speed class byte if fields[0] == "56Kbps": user.connection = DCUser.CONN_56K elif fields[0] == "Satellite": user.connection = DCUser.CONN_SATELLITE elif fields[0] == "DSL": user.connection = DCUser.CONN_DSL elif fields[0] == "Cable": user.connection = DCUser.CONN_CABLE elif fields[0] == "LAN(T1)": user.connection = DCUser.CONN_T1 elif fields[0] == "LAN(T3)": user.connection = DCUser.CONN_T3 #Email if len(fields[1]) != 0: user.email = fields[1] else: user.email = None #Share user.share = long(fields[2]) self._size += user.share #Notify listeners of changes self.lock.acquire() for listener in self.listeners: listener.onUserInfo(self, user) self.lock.release() except ValueError: pass elif cmdname == "Search": cmd = cmd[7:] try: p1, p2 = cmd.index(':'), cmd.index(' ') addr = cmd[:p1] if addr == "Hub": activeSearch = 0 addr = cmd[p1+1:p2] else: activeSearch = 1 addr = (addr, int(cmd[p1+1:p2])) params = cmd[p2+1:].split('?') if len(params) != 5: raise ValueError if params[0] == 'F': limit = DCFileList.LIMIT_NONE size = 0 else: if params[1] == 'F': limit = DCFileList.LIMIT_ATLEAST else: limit = DCFileList.LIMIT_ATMOST size = long(params[2]) worker = DCWorker.getWorker() slots = [worker.getSettings().slots - worker.getUsedSlots(), worker.getSettings().slots] hubAddr = self.sock.getpeername() #Safety check if hubAddr == None: return 0 for i in worker.getLocalLists(): for r in i.search(params[4].replace('$', ' '), limit, size): data = '$SR %s %s\5%d %d/%d\5%s (%s:%d)' % (self.userNick, r.getPath(), r.size, slots[0], slots[1], self._name, hubAddr[0], hubAddr[1]) if activeSearch: worker.getSearchWorker().sendResult(addr, data + '|') else: data += '\5%s|' % addr self.sock.send(data) except ValueError: pass elif cmdname == 'SR': DCWorker.getWorker().getSearchWorker().passiveResult(cmd[pos+1:]) elif cmdname == "Quit": self.removeUser(cmd[pos+1:]) elif cmdname == "To:": try: idx = cmd.index('$') - 1 nick = cmd[cmd[:idx].rindex(' ')+1:idx] self.appendLogRow(self.LOG_PRIVCHAT, '<%s> %s' % (nick, cmd[idx+2:])) except ValueError: pass elif cmdname == "ConnectToMe": addr = cmd[cmd.rindex(' ')+1:].split(':') try: addr = (addr[0], int(addr[1])) except ValueError: pass sock = AsyncSocket() sock.connect(addr) DCWorker.getWorker().addJob(DCXfer(sock)) elif cmdname == 'RevConnectToMe': if not DCWorker.getWorker().getSettings().active: return 2 self.requireConnection(cmd[cmd.index(' ')+1:cmd.rindex(' ')]) elif cmdname == "LogedIn": user = DCUser() user.op = 1 user.nick = cmd[pos+1:] self.addUser(user) elif cmdname == "NickList": nicks = cmd[pos+1:].split("$$") for nick in nicks: user = DCUser() user.nick = nick self.addUser(user) elif cmdname == "OpList": nicks = cmd[pos+1:].split("$$") for nick in nicks: user = DCUser() user.op = 1 user.nick = nick self.addUser(user) elif cmdname == "ForceMove": newAddr = cmd[pos+1:] if len(newAddr) == 0: return 0 addr = self.parseAddress(newAddr) if addr == None: return 0 self.appendLogRow(self.LOG_STATUS, "Redirected to \"" + newAddr + "\".") self._addr = addr return 1 elif cmdname == "Lock": key = solveChallenge(cmd[pos+1:]) if len(key) == 0: self.appendLogRow(self.LOG_ERROR, "Error while generating authkey.") return 0 self.sendCmd("Key", key) self.sendCmd("ValidateNick", self.userNick) self.sendCmd("Version", "1.3") self.sendCmd("MyINFO", self.buildInfo()) self.sendCmd("GetNickList") elif cmdname == "HubName": self._name = cmd[pos+1:] self.lock.acquire() for i in self.listeners: i.onHubInfo(self) self.lock.release() elif cmdname == "GetPass": self.appendLogRow(self.LOG_ERROR, "Hub requires password. Function not yet implemented.") return 0 elif cmdname == 'ValidateDenide': self.appendLogRow(self.LOG_ERROR, "Authentication error; disconnected.") return 0 #else: # self.appendLogRow(self.LOG_ERROR, "Unknown command: " + cmd) return 2 def sendCmd(self, cmd, *args): cmdstr = "$" + cmd for i in args: cmdstr += " " + i cmdstr += "|" self.sock.send(cmdstr) address = property(getAddress, None, None, None) name = property(getName, None, None, None) userNum = property(getUserNum, None, None, None) users = property(getUsers, None, None, None) size = property(getSize, None, None, None) logRows = property(getLogRows, None, None, None) status = property(getStatus, None, None, None)
def poll(self): try: if self._status == 0: if self.delay != None: curtime = time.time() self.delay[0] -= curtime - self.delay[1] if self.delay[0] <= 0: self.delay = None else: self.delay[1] = curtime return 0 self.appendLogRow(self.LOG_STATUS, "Connecting to " + self._addr[0] + ":" + str(self._addr[1]) + "...") self.sock = AsyncSocket() self.sock.connect(self._addr) self.setStatus(1) elif self._status == 1: self.sock.poll() if self.sock.isConnected(): self.appendLogRow(self.LOG_STATUS, "Connection established.") self.setStatus(2) elif self._status == 2: self.opLock.acquire() if self.logged: for cmd in self.cmdQueue: self.sock.send(cmd) self.cmdQueue = [] self.opLock.release() finished = self.sock.poll() self.buf += self.sock.recv() if len(self.buf) > 0: cmds = self.buf.split('|') if self.buf[-1] != "|": self.buf = cmds[-1] cmds = cmds[:-1] else: self.buf = "" for i in cmds: if len(i) > 0: res = self.parseCmd(i) if res == 0: self.setStatus(6) return 0 elif res == 1: self.setStatus(3) return 0 if finished: self.appendLogRow(self.LOG_ERROR, "Connection closed.") self.checkReconnection() elif self._status == 3: self.sock.close() if self.sock.poll(): self.removeAllUsers() self.opLock.acquire() self.reset() if self._status == 3: self._status = 0 self.opLock.release() self.lock.acquire() for listener in self.listeners: listener.onHubInfo(self) self.lock.release() elif self._status == 4: self.sendCmd('Quit', self.userNick) self.sock.poll() self.setStatus(5) elif self._status == 5: if self.sock.getBytesToSend() > 0: self.sock.poll() else: self.setStatus(6) elif self._status == 6: if self.sock != None: self.sock.close() if not self.sock.poll(): return 0 self.opLock.acquire() self.reset() self._status = 7 self.opLock.release() self.removeAllUsers() self.lock.acquire() for listener in self.listeners: listener.onHubDisconnection(self) self.lock.release() return 1 else: return 1 except DNSError: self.appendLogRow(self.LOG_ERROR, 'Connection closed because of DNS error.') self.setStatus(6) except error: self.appendLogRow(self.LOG_ERROR, 'Connection closed because of socket error.') self.checkReconnection() return 0
class DCHublistRetriever(object, Job, EventGenerator): LIST_TIMEOUT = 15 def __init__(self, sources=None): EventGenerator.__init__(self) if sources == None: sources = DCWorker.getWorker().getSettings().hublists self.sources = [] for addr in sources: i = urlparse(addr) if i[0] != "http": raise IllegalProtocolError() pos = i[1].rfind(":") if pos != -1: try: host = (i[1][:pos], int(i[1][pos + 1])) except ValueError: raise IllegalPortError() else: host = (i[1], 80) self.sources.append((host, i[2])) self.status = 0 self._hubs = {} self.sock = None self.timestamp = 0 def poll(self): try: if self.status == 0: if len(self.sources) == 0: self.status = 3 self.lock.acquire() for listener in self.listeners: listener.onHublist(self._hubs.values()) self.lock.release() return 1 addr = self.sources[0] del self.sources[0] self.sock = AsyncSocket() self.sock.connect(addr[0]) request = "GET %s HTTP/1.0\r\n" % addr[1] request += "Host: %s\r\n\r\n" % addr[0][0] self.sock.send(request) self.sock.poll() self.timestamp = time.time() self.setStatus(1) elif self.status == 1: if time.time() - self.timestamp > DCHublistRetriever.LIST_TIMEOUT: self.setStatus(2) self.sock.poll() if not self.sock.isAlive(): self.parse(self.sock.recv()) self.setStatus(2) else: if self.sock != None: self.sock.close() if self.sock.poll(): self.sock = None if self.status == 2: self.setStatus(0) else: return 0 else: return 1 return 0 except error: self.setStatus(2) return 0 except DNSError: self.setStatus(2) return 0 def stop(self): self.lock.acquire() self.status = 3 self.lock.release() def getHubs(self): return self._hubs.values() ################### # Private methods # ################### def setStatus(self, code): self.lock.acquire() if self.status < 3: self.status = code self.lock.release() def parse(self, data): i, datalen = 0, len(data) start, col = 0, 0 hub = ["", "", "", 0] # Skip HTTP header while i < datalen: if data[i : i + 4] == "\r\n\r\n": break i += 1 if i >= datalen: return 0 i += 4 start = i while i < datalen: if data[i] == "|": hub[col] = data[start:i] col += 1 if col > 3: try: hub[3] = int(hub[3]) self._hubs[hub[1]] = hub except ValueError: pass hub = ["", "", "", 0] i += 6 col = 0 start = i + 1 i += 1 return 1 hubs = property(getHubs, None, None, None)
def __init__(self, configfile=None, backupcfg=False): self.configfile = configfile if configfile else 'bot.cfg' self.backupcfg = backupcfg if backupcfg and os.path.exists(self.configfile): backupcfgname = self.configfile + '.' + datetime.datetime.now( ).strftime('%Y%m%d%H%M%S') + '.bak' shutil.copyfile(self.configfile, backupcfgname) self.config = AdvConfigParser() self.config.read(self.configfile) self.host = self.config.get('SERVER', 'HOST', '') self.port = self.config.geti('SERVER', 'PORT', '6667') self.channels = set( self.config.get('SERVER', 'CHANNELS', "").split(';') if self. config.get('SERVER', 'CHANNELS', "").strip() else []) self.floodLimit = self.config.getf( 'SERVER', 'FLOODLIMIT', "0.75", 'Min delay between two line sending.') self.servpass = self.config.get('SERVER', 'PASSWORD', "", 'Server password') self.use_ssl = self.config.getb( 'SERVER', 'USESSL', 'False', 'Whether to use SSL for the IRC connection.') self.nickserv = self.config.get( 'NICKSERV', 'NICKSERV', "NickServ", 'Nick of the nick server used for auth purposes') self.nickAuth = self.config.get( 'NICKSERV', 'NICKAUTH', "PRIVMSG {nickserv} :acc {nick}", 'Command to use to determine the ACC level of a user') self.authRegex = self.config.get('NICKSERV', 'AUTHREGEX', "(?P<nick>.+) ACC (?P<level>[0-9])", 'Regexp to parse the ACC answer') self.nsmarker = self.config.get( 'NICKSERV', 'NSMARKER', "This nickname is registered", 'Regexp to parse in the nickserv msg when the nick need to be identified' ) self.nsidentify = self.config.get( 'NICKSERV', 'NSIDENTIFY', "You are now identified for", 'Regexp to parse in the nickserv msg when identify was successful') self.nsreply = self.config.get( 'NICKSERV', 'NSREPLY', "PRIVMSG {nickserv} :identify {nspass}", 'Reply to an identify request') self.nick = self.config.get('BOT', 'NICK', "PyBot") self.nspass = self.config.get('BOT', 'NICKSERV_PASS', '') self.cmdChar = self.config.get('BOT', 'CMDCHAR', "*") self.moreCount = self.config.geti( 'BOT', 'MORECMDCOUNT', '10', 'The number of queued messages to send each time a user executes the more command.' ) self.moreCountDcc = self.config.geti( 'BOT', 'MORECMDCOUNTDCC', '100', 'The number of queued messages to send each time a user executes the more command in a DCC session.' ) self.moreRate = self.config.geti( 'BOT', 'MORERATELIMIT', '15', 'The number of seconds that must pass before a user can execute the more command again.' ) self.autoInvite = self.config.getb('BOT', 'AUTOACCEPT', "true", 'Automatically accept invites?') self.autoJoin = self.config.getb( 'BOT', 'AUTOJOIN', "true", 'Automatically join channels in the chan list?') self.lognormal = self.config.get('BOT', 'LOGNORMAL', "botlog.log") self.lognormalmaxbytes = self.config.geti( 'BOT', 'LOGNORMALMAXBYTES', str(1024 * 1024 * 5), "The log will be rotated when its size reaches this many bytes.") self.logerrors = self.config.get('BOT', 'LOGERRORS', "errors.log") self.logerrorsmaxbytes = self.config.geti( 'BOT', 'LOGERRORSMAXBYTES', str(1024 * 1024), "The log will be rotated when its size reaches this many bytes.") self.help_url = self.config.get('BOT', 'HELP_URL', '') self.primary_channels = set( self.config.get( 'BOT', 'PRIMARY_CHANNELS', '', 'Important bot messages will be sent to Ops in these channels.' ).split(';') if self.config.get('BOT', 'PRIMARY_CHANNELS', ""). strip() else []) self.allowunregistered = self.config.getb( 'AUTH', 'ALLOWUNREGISTERED', "true", 'Can users without a registered nick emit commands?') self.authtimeout = self.config.geti( 'AUTH', 'TIMEOUT', "60", 'User authentication refresh delay in seconds. User auth will be considered valid for this period.' ) self.dccActive = self.config.getb('DCC', 'ACTIVE', "true") self.dccAllowAnon = self.config.getb( 'DCC', 'ALLOWANON', "false", 'Can users connect via DCC if the user is not properly IP identified?' ) self.monitorevents = self.config.getb( 'EVENTMONITOR', 'MONITOREVENTS', "true", "Should we periodically check the last event time to see if the connection was severed? NOTE: it is the responsiblity of the bot implementation to handle reconnection if desired. This check will shutdown the bot without setting isTerminating." ) self.monitorperiod = self.config.geti( 'EVENTMONITOR', 'MONITORPERIOD', "60", "The number of seconds between event monitoring checks.") self.monitortimeout = self.config.geti( 'EVENTMONITOR', 'MONITORTIMEOUT', "240", "The minimum number of seconds that must pass without an event before we consider the connection dead." ) self.logger = Logger.getLogger("%s-%s-%s" % (__name__, self.nick, self.host), lognormal=self.lognormal, logerror=self.logerrors, lognormalmaxsize=self.lognormalmaxbytes, logerrormaxsize=self.logerrorsmaxbytes) # We collect the list of groups (<group> = <authorised commands separated by ;>) self.groups = {} for option in self.config.options('GROUPS'): self.groups[option] = json.loads(self.config.get('GROUPS', option)) self.groups[option]['commands'] = set( self.groups[option]['commands']) # We collect the list of users (<user> = <groups authorised separated by ;>) self.authUsers = {} for option in self.config.options('USERS'): self.authUsers[option] = set( self.config.get('USERS', option).lower().split(';') if self. config.get('USERS', option).strip() else []) self.banList = {} for option in self.config.options('BANLIST'): self.banList[option] = set( self.config.get('BANLIST', option).lower().split(';') if self. config.get('BANLIST', option).strip() else []) self.logger.info("Users : %s" % self.authUsers) self.logger.info("Groups : %s" % self.groups) self.txtcmds = {} self.updateConfig() self.users = {} self.usersInfo = {} self.isIdentified = False #Turn to true when nick/ident commands are sent self.isReady = False #Turn to true after RPL_ENDOFMOTD. Every join/nick etc commands should be sent once this is True. self.isTerminating = False #This is set to true by the stop command to bypass restarting self.isRestarting = False self.isRunning = False self.socket = AsyncSocket.AsyncSocket(self, self.host, self.port, self.floodLimit) self.dccSocket = DCCSocket.DCCSocket(self) self.readOnly = False self.cmdHandler = CmdHandler(self) self.registerCommand('dcc', self.requestDCC, ['any'], 0, 0, "", "Requests a DCC connection to the bot.") self.registerCommand( 'more', self.sendMore, ['any'], 0, 1, "[clear]", 'Gets the next %d queued command results. Commands that can queue results will tell you so.' % self.moreCount, allowpub=True) self.registerCommand('useradd', self.useradd, ['admin'], 2, 2, "<user> <group>", "Adds user to group.") self.registerCommand('userrm', self.userrm, ['admin'], 2, 2, "<user> <group>", "Removes user from group.") self.registerCommand('userget', self.userget, ['admin'], 1, 1, "<user>", "Returns list of groups for this user.") self.registerCommand('userall', self.userall, ['admin'], 0, 0, "", "Returns a list of groups and users.") self.registerCommand('groupadd', self.groupadd, ['admin'], 2, 2, "<group> <cmd>", "Adds command to group.") self.registerCommand('grouprm', self.grouprm, ['admin'], 2, 2, "<group> <cmd>", "Remove command from group.") self.registerCommand('groupget', self.groupget, ['admin'], 0, 0, "", "Returns a list of groups and commands.") self.registerCommand('groupmeta', self.groupmeta, ['admin'], 3, 999, "<group> <key> <value>", "Add a value to a group key") self.registerCommand( 'banadd', self.banadd, ['admin'], 2, 2, "<user|host> <cmd>", "Bans the user from using the specified command.") self.registerCommand('banrm', self.banrm, ['admin'], 2, 2, "<user|host> <cmd>", "Remove a ban on a user.") self.registerCommand('banget', self.banget, ['admin'], 1, 1, "<user|host>", "Returns the ban list for the given user.") self.registerCommand('banall', self.banall, ['admin'], 0, 0, "", "Returns a complete dump of the ban table.") self.registerCommand('sendraw', self.sendRawCmd, ['admin'], 0, 999, "<irccmd>", "Send a raw IRC cmd.") self.registerCommand('shutdown', self.killSelf, ['admin'], 0, 0, "", "Kills the bot.") self.registerCommand('restart', self.restart, ['admin'], 0, 0, '', 'Restarts the bot.') self.registerCommand('fpull', self.fpull, ['admin'], 0, 0, '', 'Pulls and updates the bot code.') self.registerCommand( 'readonly', self.setReadOnly, ['admin'], 1, 1, '[true|false]', 'Sets Read-Only Mode for commands that are able to set data.') self.registerCommand( 'help', self.helpcmd, ['any'], 0, 1, "[<command>|*]", "Lists available commands or help about a specific command.", allowpub=True) # We collect the list of simple text commands) for option in self.config.options('TEXTCOMMANDS'): self.addtxtcmd(option, json.loads(self.config.get('TEXTCOMMANDS', option))) if 'about' not in self.txtcmds.keys(): self.addtxtcmd('about', {}) self.updateConfig()