Ejemplo n.º 1
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
Ejemplo n.º 2
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
	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
Ejemplo n.º 5
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)
Ejemplo n.º 6
0
    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()