def execute(self, server, data): connectTime = data["connecttime"] nickTime = data["nicktime"] newUser = RemoteUser(self.ircd, data["ip"], data["uuid"], data["host"]) newUser.changeHost(data["hosttype"], data["displayhost"], True) newUser.changeIdent(data["ident"], server) newUser.changeGecos(data["gecos"], True) newUser.connectedSince = connectTime newUser.nickSince = nickTime newUser.idleSince = now() if data["nick"] in self.ircd.userNicks: # Handle nick collisions otherUser = self.ircd.users[self.ircd.userNicks[data["nick"]]] if otherUser.localOnly: changeOK = self.ircd.runActionUntilValue("localnickcollision", otherUser, newUser, server, users=[otherUser, newUser]) if changeOK is None: return None sameUser = ("{}@{}".format(otherUser.ident, otherUser.ip) == "{}@{}".format(newUser.ident, newUser.ip)) if sameUser and newUser.nickSince < otherUser.nickSince: # If the user@ip is the same, the newer nickname should win newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.nickSince < newUser.nickSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.nickSince < otherUser.nickSince: # Otherwise, the older nickname should win otherUser.changeNick(otherUser.uuid, server) elif otherUser.nickSince < newUser.nickSince: newUser.changeNick(newUser.uuid, server) else: # If the nickname times are the same, fall back on connection times, with the same hierarchy as before if sameUser and newUser.connectedSince < otherUser.connectedSince: newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.connectedSince < newUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.connectedSince < otherUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif otherUser.connectedSince < newUser.connectedSince: newUser.changeNick(newUser.uuid, server) else: # As a final fallback, change both nicknames otherUser.changeNick(otherUser.uuid, server) newUser.changeNick(newUser.uuid, server) if newUser.nick is None: # wasn't set by above logic newUser.changeNick(data["nick"], server) modeList = [] for mode, param in data["modes"].iteritems(): modeType = self.ircd.userModeTypes[mode] if modeType == ModeType.List: for paramData in param: modeList.append((True, mode, paramData)) else: modeList.append((True, mode, param)) newUser.setModes(modeList, server.serverID) newUser.register("connection", True) newUser.register("USER", True) newUser.register("NICK", True) connectTimestamp = str(timestamp(connectTime)) nickTimestamp = str(timestamp(nickTime)) modeString = newUser.modeString(None) self.ircd.broadcastToServers(server, "UID", newUser.uuid, connectTimestamp, newUser.nick, newUser.realHost, newUser.host(), newUser.currentHostType(), newUser.ident, newUser.ip, nickTimestamp, modeString, newUser.gecos, prefix=self.ircd.serverID) return True
def broadcastUID(self, user): self.ircd.broadcastToServers(None, "UID", user.uuid, str(timestamp(user.connectedSince)), user.nick, user.realHost, user.host(), user.currentHostType(), user.ident, user.ip, str(timestamp(user.nickSince)), user.modeString(None), user.gecos, prefix=self.ircd.serverID)
def broadcastNickChange(self, user, oldNick, fromServer): nickTS = str(timestamp(user.nickSince)) self.ircd.broadcastToServers(fromServer, "NICK", nickTS, user.nick, prefix=user.uuid)
def getPeriodData(self): """Returns (period as integer, time to end of period)""" nowTS = timestamp(now()) interval = self.ircd.config["rate_interval"] period = int(nowTS / interval) timeToEnd = (period + 1) * interval - nowTS return period, timeToEnd
def addLine(self, mask, createdTime, durationSeconds, setter, reason, fromServer=None): if not self.lineType: return False self.expireLines() normalMask = self.normalizeMask(mask) lines = self.ircd.storage["xlines"][self.lineType] for lineData in lines: lineMask = self.normalizeMask(lineData["mask"]) if normalMask == lineMask: return False lines.append({ "mask": mask, "created": createdTime, "duration": durationSeconds, "setter": setter, "reason": reason }) if self.propagateToServers: self.ircd.broadcastToServers(fromServer, "ADDLINE", self.lineType, mask, setter, str(timestamp(createdTime)), str(durationSeconds), reason, prefix=self.ircd.serverID) return True
def addUserToWhowas(self, user, reason): if not user.isRegistered(): # user never registered a nick, so no whowas entry to add return lowerNick = ircLower(user.nick) allWhowas = self.ircd.storage["whowas"] if lowerNick in allWhowas: whowasEntries = allWhowas[lowerNick] else: whowasEntries = [] serverName = self.ircd.name if user.uuid[:3] != self.ircd.serverID: serverName = self.ircd.servers[user.uuid[:3]].name whowasEntries.append({ "nick": user.nick, "ident": user.ident, "host": user.host(), "gecos": user.gecos, "server": serverName, "when": timestamp(now()) }) whowasEntries = self.removeOldEntries(whowasEntries) if whowasEntries: allWhowas[lowerNick] = whowasEntries elif lowerNick in allWhowas: del allWhowas[lowerNick]
def showListParams(self, user, channel): if user not in channel.users or "b" not in channel.modes: user.sendMessage(irc.RPL_ENDOFBANLIST, channel.name, "End of channel ban list") return for paramData in channel.modes["b"]: user.sendMessage(irc.RPL_BANLIST, channel.name, paramData[0], paramData[1], str(timestamp(paramData[2]))) user.sendMessage(irc.RPL_ENDOFBANLIST, channel.name, "End of channel ban list")
def generateInfo(self): if not self.lineType: return None self.expireLines() lineInfo = {} for lineData in self.ircd.storage["xlines"][self.lineType]: lineInfo[lineData["mask"]] = "{} {} {} :{}".format(timestamp(lineData["created"]), lineData["duration"], lineData["setter"], lineData["reason"]) return lineInfo
def removeOldEntries(self, whowasEntries): expireDuration = durationToSeconds(self.ircd.config.get("whowas_duration", "1d")) maxCount = self.ircd.config.get("whowas_max_entries", 10) while whowasEntries and len(whowasEntries) > maxCount: whowasEntries.pop(0) expireTime = timestamp(now()) - expireDuration while whowasEntries and whowasEntries[0]["when"] < expireTime: whowasEntries.pop(0) return whowasEntries
def generateInfo(self): if not self.lineType: return None self.expireLines() lineInfo = {} for lineData in self.ircd.storage["xlines"][self.lineType]: lineInfo[lineData["mask"]] = "{} {} {} :{}".format( timestamp(lineData["created"]), lineData["duration"], lineData["setter"], lineData["reason"]) return lineInfo
def removeOldEntries(self, whowasEntries): expireDuration = durationToSeconds( self.ircd.config.get("whowas_duration", "1d")) maxCount = self.ircd.config.get("whowas_max_entries", 10) while whowasEntries and len(whowasEntries) > maxCount: whowasEntries.pop(0) expireTime = timestamp(now()) - expireDuration while whowasEntries and whowasEntries[0]["when"] < expireTime: whowasEntries.pop(0) return whowasEntries
def execute(self, server, data): if "lostsource" in data or "losttarget" in data: return True source = data["source"] target = data["target"] targTS = data["timestamp"] if target in self.ircd.channels: channel = self.ircd.channels[target] if targTS > timestamp(channel.existedSince): return True if targTS < timestamp(channel.existedSince): # We need to remove all of the channel's modes modeUnsetList = [] for mode, param, in channel.modes.iteritems(): if self.ircd.channelModeTypes[mode] == ModeType.List: for paramData in param: modeUnsetList.append((False, mode, paramData[0])) else: modeUnsetList.append((False, mode, param)) for user, userData in channel.users.iteritems(): for status in userData["status"]: modeUnsetList.append((False, mode, user.uuid)) if modeUnsetList: channel.setModes(modeUnsetList, source) # We'll need to transform the user parameters of status modes before we're done here channel.setModes(data["modes"], source) return True user = self.ircd.users[target] if targTS > timestamp(user.connectedSince): return True if targTS < timestamp(user.connectedSince): modeUnsetList = [] for mode, param in user.modes.iteritems(): if self.ircd.userModeTypes[mode] == ModeType.List: for paramData in param: modeUnsetList.append((False, mode, paramData[0])) else: modeUnsetList.append((False, mode, param)) if modeUnsetList: user.setModes(modeUnsetList, source) user.setModes(data["modes"], source) return True
def sendChannelModesToServers(self, channel, source, sourceName, modes): modeOuts = self.getOutputModes(modes, True) if source[:3] == self.ircd.serverID: fromServer = None else: fromServer = self.ircd.servers[source[:3]] while fromServer.nextClosest != self.ircd.serverID: fromServer = self.ircd.servers[fromServer.nextClosest] for modeOut in modeOuts: modeStr = modeOut[0] params = modeOut[1:] self.ircd.broadcastToServers(fromServer, "MODE", channel.name, str(timestamp(channel.existedSince)), modeStr, *params, prefix=source)
def burstLines(self, server): if not self.lineType: return self.expireLines() if self.propagateToServers: for lineData in self.ircd.storage["xlines"][self.lineType]: server.sendMessage("ADDLINE", self.lineType, lineData["mask"], lineData["setter"], str(timestamp(lineData["created"])), str(lineData["duration"]), lineData["reason"], prefix=self.ircd.serverID)
def sendUserModesToServers(self, user, source, sourceName, modes): if not user.isRegistered(): return # If the user isn't registered yet, it's a remote user for whom we just received modes modeOuts = self.getOutputModes(modes, False) if source[:3] == self.ircd.serverID: fromServer = None else: fromServer = self.ircd.servers[source[:3]] while fromServer.nextClosest != self.ircd.serverID: fromServer = self.ircd.servers[fromServer.nextClosest] for modeOut in modeOuts: modeStr = modeOut[0] params = modeOut[1:] self.ircd.broadcastToServers(fromServer, "MODE", user.uuid, str(timestamp(user.connectedSince)), modeStr, *params, prefix=source)
def execute(self, user, data): if "modes" not in data: if "channel" in data: channel = data["channel"] user.sendMessage(irc.RPL_CHANNELMODEIS, channel.name, *(channel.modeString(user).split(" "))) user.sendMessage(irc.RPL_CREATIONTIME, channel.name, str(timestamp(channel.existedSince))) return True user.sendMessage(irc.RPL_UMODEIS, user.modeString(user)) return True if "channel" in data: channel = data["channel"] channel.setModesByUser(user, data["modes"], data["params"]) return True user.setModesByUser(user, data["modes"], data["params"]) return True
def onTopic(self, channel, setter, oldTopic): userSource = setter in self.ircd.users if userSource: sourceUser = self.ircd.users[setter] conditionalTags = {} self.ircd.runActionStandard("sendingusertags", sourceUser, conditionalTags) for user in channel.users.iterkeys(): if user.uuid[:3] == self.ircd.serverID: tags = {} if userSource: tags = user.filterConditionalTags(conditionalTags) user.sendMessage("TOPIC", channel.topic, to=channel.name, prefix=channel.topicSetter, tags=tags) sourceServer = None if userSource and setter[:3] == self.ircd.serverID: if sourceUser not in channel.users: tags = sourceUser.filterConditionalTags(conditionalTags) sourceUser.sendMessage("TOPIC", channel.topic, to=channel.name, prefix=channel.topicSetter, tags=tags) elif setter != self.ircd.serverID: sourceServer = self.ircd.servers[setter[:3]] while sourceServer.nextClosest != self.ircd.serverID: sourceServer = self.ircd.servers[sourceServer.nextClosest] self.ircd.broadcastToServers(sourceServer, "TOPIC", channel.name, str(timestamp(channel.existedSince)), str(timestamp(channel.topicTime)), channel.topic, prefix=setter)
def addLine(self, mask, createdTime, durationSeconds, setter, reason, fromServer = None): if not self.lineType: return False self.expireLines() normalMask = self.normalizeMask(mask) lines = self.ircd.storage["xlines"][self.lineType] for lineData in lines: lineMask = self.normalizeMask(lineData["mask"]) if normalMask == lineMask: return False lines.append({ "mask": mask, "created": createdTime, "duration": durationSeconds, "setter": setter, "reason": reason }) if self.propagateToServers: self.ircd.broadcastToServers(fromServer, "ADDLINE", self.lineType, mask, setter, str(timestamp(createdTime)), str(durationSeconds), reason, prefix=self.ircd.serverID) return True
def sendChannelTopic(self, channel, user): if not channel.topic: user.sendMessage(irc.RPL_NOTOPIC, channel.name, "No topic is set") else: user.sendMessage(irc.RPL_TOPIC, channel.name, channel.topic) user.sendMessage(irc.RPL_TOPICWHOTIME, channel.name, channel.topicSetter, str(timestamp(channel.topicTime)))
def propagateUserMetadata(self, user, key, oldValue, value, visibility, setByUser, fromServer): self.propagateMetadata(user.uuid, str(timestamp(user.connectedSince)), key, value, visibility, setByUser, fromServer)
def propagateChannelMetadata(self, channel, key, oldValue, value, visibility, setByUser, fromServer): self.propagateMetadata(channel.name, str(timestamp(channel.existedSince)), key, value, visibility, setByUser, fromServer)
def execute(self, user, data): for targetUser in data["targetusers"]: user.sendMessage(irc.RPL_WHOISUSER, targetUser.nick, targetUser.ident, targetUser.host(), "*", targetUser.gecos) if self.ircd.runActionUntilValue( "userhasoperpermission", user, "whois-host", users=[user]) or user == targetUser: user.sendMessage( irc.RPL_WHOISHOST, targetUser.nick, "is connecting from {}@{} {}".format( targetUser.ident, targetUser.realHost, targetUser.ip)) chanList = [] for channel in targetUser.channels: if self.ircd.runActionUntilValue("showchannel-whois", channel, user, targetUser, users=[user, targetUser], channels=[channel ]) is not False: chanList.append("{}{}".format( self.ircd.runActionUntilValue("channelstatuses", channel, targetUser, user, users=[targetUser, user], channels=[channel]), channel.name)) if chanList: user.sendMessage(irc.RPL_WHOISCHANNELS, targetUser.nick, " ".join(chanList)) if targetUser.uuid[:3] == self.ircd.serverID: serverName = self.ircd.name serverDescription = self.ircd.config["server_description"] else: server = self.ircd.servers[targetUser.uuid[:3]] serverName = server.name serverDescription = server.description user.sendMessage(irc.RPL_WHOISSERVER, targetUser.nick, serverName, serverDescription) if self.ircd.runActionUntilValue("userhasoperpermission", targetUser, "", users=[targetUser]): user.sendMessage(irc.RPL_WHOISOPERATOR, targetUser.nick, "is an IRC operator") if targetUser.secureConnection: user.sendMessage(irc.RPL_WHOISSECURE, targetUser.nick, "is using a secure connection") self.ircd.runActionStandard("extrawhois", user, targetUser) if targetUser.uuid[: 3] == self.ircd.serverID: # Idle time will only be accurate for local users signonTS = timestamp(user.connectedSince) idleTime = int((now() - user.idleSince).total_seconds()) user.sendMessage(irc.RPL_WHOISIDLE, targetUser.nick, str(idleTime), str(signonTS), "seconds idle, signon time") user.sendMessage(irc.RPL_ENDOFWHOIS, targetUser.nick, "End of /WHOIS list") return True
def execute(self, server, data): connectTime = data["connecttime"] nickTime = data["nicktime"] newUser = RemoteUser(self.ircd, data["ip"], data["uuid"], data["host"]) newUser.changeHost(data["hosttype"], data["displayhost"], True) newUser.changeIdent(data["ident"], server) newUser.changeGecos(data["gecos"], True) newUser.connectedSince = connectTime newUser.nickSince = nickTime newUser.idleSince = now() if data["nick"] in self.ircd.userNicks: # Handle nick collisions otherUser = self.ircd.users[self.ircd.userNicks[data["nick"]]] if otherUser.localOnly: changeOK = self.ircd.runActionUntilValue( "localnickcollision", otherUser, newUser, server, users=[otherUser, newUser]) if changeOK is None: return None sameUser = ("{}@{}".format(otherUser.ident, otherUser.ip) == "{}@{}".format( newUser.ident, newUser.ip)) if sameUser and newUser.nickSince < otherUser.nickSince: # If the user@ip is the same, the newer nickname should win newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.nickSince < newUser.nickSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.nickSince < otherUser.nickSince: # Otherwise, the older nickname should win otherUser.changeNick(otherUser.uuid, server) elif otherUser.nickSince < newUser.nickSince: newUser.changeNick(newUser.uuid, server) else: # If the nickname times are the same, fall back on connection times, with the same hierarchy as before if sameUser and newUser.connectedSince < otherUser.connectedSince: newUser.changeNick(newUser.uuid, server) elif sameUser and otherUser.connectedSince < newUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif newUser.connectedSince < otherUser.connectedSince: otherUser.changeNick(otherUser.uuid, server) elif otherUser.connectedSince < newUser.connectedSince: newUser.changeNick(newUser.uuid, server) else: # As a final fallback, change both nicknames otherUser.changeNick(otherUser.uuid, server) newUser.changeNick(newUser.uuid, server) if newUser.nick is None: # wasn't set by above logic newUser.changeNick(data["nick"], server) modeList = [] for mode, param in data["modes"].iteritems(): modeType = self.ircd.userModeTypes[mode] if modeType == ModeType.List: for paramData in param: modeList.append((True, mode, paramData)) else: modeList.append((True, mode, param)) newUser.setModes(modeList, server.serverID) newUser.register("connection", True) newUser.register("USER", True) newUser.register("NICK", True) connectTimestamp = str(timestamp(connectTime)) nickTimestamp = str(timestamp(nickTime)) modeString = newUser.modeString(None) self.ircd.broadcastToServers(server, "UID", newUser.uuid, connectTimestamp, newUser.nick, newUser.realHost, newUser.host(), newUser.currentHostType(), newUser.ident, newUser.ip, nickTimestamp, modeString, newUser.gecos, prefix=self.ircd.serverID) return True
def startBurst(self, server): server.bursted = False serversByHopcount = [] serversBurstingTo = [] for remoteServer in self.ircd.servers.itervalues(): if remoteServer == server: continue hopCount = 1 servTrace = remoteServer if server == servTrace: serversBurstingTo.append(remoteServer.serverID) continue # Don't count this server burstingRemote = False while servTrace.nextClosest != self.ircd.serverID: servTrace = self.ircd.servers[servTrace.nextClosest] if server == servTrace: burstingRemote = True break hopCount += 1 if burstingRemote: serversBurstingTo.append(remoteServer.serverID) continue while len(serversByHopcount) < hopCount: serversByHopcount.append([]) serversByHopcount[hopCount - 1].append(remoteServer) for hopCount in range(1, len(serversByHopcount) + 1): strHopCount = str(hopCount) for remoteServer in serversByHopcount[hopCount - 1]: server.sendMessage("SERVER", remoteServer.name, remoteServer.serverID, strHopCount, remoteServer.nextClosest, remoteServer.description, prefix=self.ircd.serverID) for user in self.ircd.users.itervalues(): if user.localOnly: continue if not user.isRegistered(): continue if user.uuid[:3] in serversBurstingTo: # The remote server apparently already finished its burst (or at least enough that we know this), so we need to not send it those again. continue signonTimestamp = str(timestamp(user.connectedSince)) nickTimestamp = str(timestamp(user.nickSince)) modes = [] params = [] listModes = {} for mode, param in user.modes.iteritems(): if self.ircd.userModeTypes[mode] == ModeType.List: listModes[mode] = param else: modes.append(mode) if param is not None: params.append(param) modeStr = "+{} {}".format("".join(modes), " ".join(params)) if params else "+{}".format("".join(modes)) server.sendMessage("UID", user.uuid, signonTimestamp, user.nick, user.realHost, user.host(), user.currentHostType(), user.ident, user.ip, nickTimestamp, modeStr, user.gecos, prefix=self.ircd.serverID) sentListModes = False for mode, paramList in listModes.iteritems(): for param, setter, time in paramList: server.sendMessage("LISTMODE", user.uuid, signonTimestamp, mode, param, setter, str(timestamp(time)), prefix=self.ircd.serverID) sentListModes = True if sentListModes: server.sendMessage("ENDLISTMODE", user.uuid, prefix=self.ircd.serverID) for key, value, visibility, setByUser in user.metadataList(): server.sendMessage("METADATA", user.uuid, signonTimestamp, key, visibility, "1" if setByUser else "0", value, prefix=self.ircd.serverID) for channel in self.ircd.channels.itervalues(): channelTimestamp = str(timestamp(channel.existedSince)) users = [] for user, data in channel.users.iteritems(): if user.localOnly: continue if user.uuid[:3] in serversBurstingTo: # The remote server already knows about these users continue ranks = data["status"] users.append("{},{}".format(ranks, user.uuid)) if not users: continue # Let's not sync this channel since it won't sync properly modes = [] params = [] listModes = {} for mode, param in channel.modes.iteritems(): if self.ircd.channelModeTypes[mode] == ModeType.List: listModes[mode] = param else: modes.append(mode) if param is not None: params.append(param) modeStr = "+{} {}".format("".join(modes), " ".join(params)) if params else "+{}".format("".join(modes)) fjoinParams = [channel.name, channelTimestamp] + modeStr.split(" ") + [" ".join(users)] server.sendMessage("FJOIN", *fjoinParams, prefix=self.ircd.serverID) sentListModes = False for mode, params in listModes.iteritems(): for param, setter, time in params: server.sendMessage("LISTMODE", channel.name, channelTimestamp, mode, param, setter, str(timestamp(time)), prefix=self.ircd.serverID) sentListModes = True if sentListModes: server.sendMessage("ENDLISTMODE", channel.name, prefix=self.ircd.serverID) if channel.topic: server.sendMessage("TOPIC", channel.name, channelTimestamp, str(timestamp(channel.topicTime)), channel.topic, prefix=self.ircd.serverID) for key, value, visibility, setByUser in channel.metadataList(): server.sendMessage("METADATA", channel.name, channelTimestamp, key, visibility, "1" if setByUser else "0", value, prefix=self.ircd.serverID)
def showListParams(self, user, target): if "s" in target.modes: for mask in target.modes["s"]: target.sendMessage(irc.RPL_LISTMODE, "s", mask[0], mask[1], str(timestamp(mask[2]))) target.sendMessage(irc.RPL_ENDOFLISTMODE, "End of server notice type list")