Fix LIST handling and message parsing

* Always use simple LIST syntax if it succeeded once after a failed
complex query
* Reject asking for a LIST twice
* Quickly discard any ISUPPORT messages that don't contain things we
need to use
* Detect the server name and drop any messages from the server
This commit is contained in:
Mark Veidemanis 2019-10-20 16:44:33 +01:00
parent f34de8940f
commit b4fa747853
3 changed files with 64 additions and 26 deletions

View File

@ -106,22 +106,26 @@ class IRCBot(IRCClient):
self.realname = alias["realname"]
self.username = alias["nick"]+"/"+relay["net"]
self.password = main.config["Relay"]["Password"]
self.userinfo = None
self.fingerReply = None
self.versionName = None
self.versionNum = None
self.versionEnv = None
self.sourceURL = None
self.userinfo = None #
self.fingerReply = None #
self.versionName = None # don't tell anyone information about us
self.versionNum = None #
self.versionEnv = None #
self.sourceURL = None #
self._getWho = {} # LoopingCall objects -- needed to be able to stop them
self._tempWho = {} # Temporary storage for gathering WHO info
self._tempNames = {} # Temporary storage for gathering NAMES info
self._tempList = ([], []) # Temporary storage for gathering LIST info
self.listOngoing = False
self.listRetried = False
self._tempWho = {} # temporary storage for gathering WHO info
self._tempNames = {} # temporary storage for gathering NAMES info
self._tempList = ([], []) # temporary storage for gathering LIST info
self.listOngoing = False # we are currently receiving a LIST
self.listRetried = False # we asked and got nothing so asked again
self.listAttempted = False # we asked for a list
self.listSimple = False # after asking again we got the list, so
# use the simple syntax from now on
self.chanlimit = 0
self.servername = None
def joinChannels(self, channels):
sleeptime = 0.0
@ -151,11 +155,13 @@ class IRCBot(IRCClient):
if cast[i] == "": # a dictionary that changes length with each iteration
del cast[i]
if "muser" in cast.keys():
if cast["muser"] == self.servername:
return
if "channel" in cast.keys():
if cast["channel"] == "*":
return
if not {"nick", "ident", "host"}.issubset(set(cast.keys())):
cast["nick"], cast["ident"], cast["host"] = parsen(cast["muser"])
# additional checks here to see if it's a server -- irc.example.net
# discard if it is
#if not cast["type"] in ["nick", "kick", "quit", "part", "join"]:
# del cast["muser"]
if set(["nick", "ident", "host", "msg"]).issubset(set(cast)):
if "msg" in cast.keys():
if cast["ident"] == "znc" and cast["host"] == "znc.in":
@ -166,6 +172,7 @@ class IRCBot(IRCClient):
del cast["host"]
del cast["channel"]
if "Disconnected from IRC" in cast["msg"]:
log("ZNC disconnected on %s - %i" (self.net, self.num))
self.connected = False
if not cast["type"] in ["query", "self", "highlight", "znc", "who"]:
if "channel" in cast.keys() and not cast["type"] == "mode": # don't handle modes here
@ -323,10 +330,16 @@ class IRCBot(IRCClient):
newNicklist.append(f)
userinfo.initialNames(self.net, nicklist[0], newNicklist)
def myInfo(self, servername, version, umodes, cmodes):
self.servername = servername
def _list(self, noargs):
d = Deferred()
self._tempList = ([], [])
self._tempList[0].append(d)
if self.listSimple:
self.sendLine("LIST")
return d
if noargs:
self.sendLine("LIST")
else:
@ -334,10 +347,19 @@ class IRCBot(IRCClient):
return d
def list(self, noargs=False):
if not self.listAttempted:
self.listAttempted = True
else:
debug("List request dropped, already asked for LIST - %s - %i" % (self.net, self.num))
return
if not self.listOngoing:
self._list(noargs).addCallback(self.got_list)
else:
debug("List request dropped, already ongoing - %s - %i" % (self.net, self.num))
return
def irc_RPL_LISTSTART(self, prefix, params):
self.listAttempted = False
self.listOngoing = True
def irc_RPL_LIST(self, prefix, params):
@ -359,12 +381,16 @@ class IRCBot(IRCClient):
if noResults:
if not self.listRetried:
self.list(True)
self.listRetried = True
else:
warn("List still empty after retry: %s - %i" % (net, num))
self.listRetried = False
return
else:
if self.listRetried:
self.listRetried = False
debug("List received after retry - defaulting to simple list syntax")
self.listSimple = True
def got_list(self, listinfo):
if len(listinfo) == 0: # probably ngircd not supporting LIST >0
@ -372,19 +398,33 @@ class IRCBot(IRCClient):
chankeep.initialList(self.net, self.num, listinfo, self.chanlimit)
def isupport(self, options):
for i in options:
interested = ("CHANLIMIT", "MAXCHANNELS")
if not any((x for x in options if any(y in x for y in interested))):
return # check if any of interested is in any of options, some networks
chanlimit = None # call isupport() more than once, so discard swiftly anything
for i in options: # we don't care about
if i.startswith("CHANLIMIT"):
if ":" in i:
split = i.split(":")
if len(split) >= 2:
chanlimit = split[1]
break
elif i.startswith("MAXCHANNELS"):
if "=" in i:
split = i.split("=")
if len(split) == 2:
chanlimit = split[1]
break
if chanlimit:
try:
self.chanlimit = int(chanlimit)
return
except TypeError:
warn("Invalid CHANLIMIT: %s" % i)
warn("Invalid chanlimit: %s" % i)
if self.num == 1: # Only one instance should do a list, so
if self.chanlimit:
self.list() # why not this one? :P
else:
debug("Aborting LIST due to bad chanlimit")
self.checkChannels()
#twisted sucks so i have to do this to actually get the user info

View File

@ -27,7 +27,6 @@ def getChanFree(net, new):
name = net+str(i)
chanfree[i] = main.IRCPool[name].chanlimit-len(main.IRCPool[name].channels)
chanlimits.add(main.IRCPool[name].chanlimit)
if not len(chanlimits) == 1:
error("Network %s has servers with different CHANMAX values" % net)
return False
@ -39,7 +38,6 @@ def emptyChanAllocate(net, flist, relay, new):
return
for i in new:
chanfree[0][i] = chanfree[1]
print("chanfree", chanfree)
allocated = {}
toalloc = len(flist)
if toalloc > sum(chanfree[0].values()):

View File

@ -7,7 +7,7 @@ from utils.logging.debug import debug
from utils.parsing import parsen
def getWhoSingle(name, query):
result = main.r.sscan("live.who."+name, 0, query, count=-1)
result = main.r.sscan("live.who."+name, 0, query, count=999999)
if result[1] == []:
return None
return (i.decode() for i in result[1])