From e8c37ba0a52dd6e5699a86318b1a63189892dde2 Mon Sep 17 00:00:00 2001 From: steering7253 Date: Fri, 12 Jun 2026 19:12:08 -0600 Subject: support ISUPPORT LINELEN --- http2irc.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/http2irc.py b/http2irc.py index 7a38122..15f21a3 100644 --- a/http2irc.py +++ b/http2irc.py @@ -374,6 +374,7 @@ class IRCClientProtocol(asyncio.Protocol): self.whoxReply = [] # List of (nickname, account) tuples from the currently running WHO query self.whoxStartTime = None self.userChannels = collections.defaultdict(set) # List of which channels a user is known to be in; nickname:str -> {channel:str, ...} + self.linelen = 510 @staticmethod def nick_command(nick: str): @@ -402,13 +403,13 @@ class IRCClientProtocol(asyncio.Protocol): # command: b'JOIN' or b'PART'; channels: set[str] channels = [x.encode('utf-8') for x in channels] - if len(command) + sum(1 + len(x) for x in channels) <= 510: # Total length = command + (separator + channel name for each channel, where the separator is a space for the first and then a comma) + if len(command) + sum(1 + len(x) for x in channels) <= self.linelen: # Total length = command + (separator + channel name for each channel, where the separator is a space for the first and then a comma) # Everything fits into one command. self.send(command + b' ' + b','.join(channels)) return # List too long, need to split. - limit = 510 - len(command) + limit = self.linelen - len(command) lengths = [1 + len(x) for x in channels] # separator + channel name chanLengthAcceptable = [l <= limit for l in lengths] if not all(chanLengthAcceptable): @@ -444,8 +445,8 @@ class IRCClientProtocol(asyncio.Protocol): def send(self, data): self.logger.debug(f'Queueing for send: {data!r}') - if len(data) > 510: - raise RuntimeError(f'IRC message too long ({len(data)} > 510): {data!r}') + if len(data) > self.linelen: + raise RuntimeError(f'IRC message too long ({len(data)} > {self.linelen}): {data!r}') self.sendQueue.put_nowait(data) def _direct_send(self, data): @@ -529,12 +530,12 @@ class IRCClientProtocol(asyncio.Protocol): channelB = channel.encode('utf-8') messageB = message.encode('utf-8') usermaskPrefixLength = 1 + self._self_usermask_length() + 1 # :usermask - if usermaskPrefixLength + len(b'PRIVMSG ' + channelB + b' :' + messageB) > 510: + if usermaskPrefixLength + len(b'PRIVMSG ' + channelB + b' :' + messageB) > self.linelen: # Message too long, need to split or truncate. First try to split on spaces, then on codepoints. Ideally, would use graphemes between, but that's too complicated. self.logger.debug(f'Message too long, overlongmode = {overlongmode}') prefix = b'PRIVMSG ' + channelB + b' :' prefixLength = usermaskPrefixLength + len(prefix) # Need to account for the origin prefix included by the ircd when sending to others - maxMessageLength = 510 - prefixLength # maximum length of the message part within each line + maxMessageLength = self.linelen - prefixLength # maximum length of the message part within each line if overlongmode == 'truncate': maxMessageLength -= 3 # Make room for an ellipsis at the end messages = [] @@ -553,7 +554,7 @@ class IRCClientProtocol(asyncio.Protocol): continue # No space found, need to search for a suitable codepoint location. - pMessage = message[:maxMessageLength] # at most 510 codepoints which expand to at least 510 bytes + pMessage = message[:maxMessageLength] # at most linelen codepoints which expand to at least linelen bytes pLengths = [len(x.encode('utf-8')) for x in pMessage] # byte size of each codepoint pRunningLengths = list(itertools.accumulate(pLengths)) # byte size up to each codepoint if pRunningLengths[-1] <= maxMessageLength: # Special case: entire pMessage is short enough @@ -657,6 +658,13 @@ class IRCClientProtocol(asyncio.Protocol): self.logger.error('SASL error, terminating connection') self.transport.close() + elif line.command in (ircstates.numerics.RPL_ENDOFMOTD, ircstates.numerics.ERR_NOMOTD): + if 'LINELEN' in self.server.isupport.raw: + try: + self.linelen = int(self.server.isupport.raw['LINELEN'])-2 + except ValueError as e: + self.logger.error(f'IRC server sent invalid LINELEN, ignoring: {value}') + # NICK errors elif line.command in ('431', ircstates.numerics.ERR_ERRONEUSNICKNAME, ircstates.numerics.ERR_NICKNAMEINUSE, '436'): self.logger.error(f'Failed to set nickname: {message!r}, terminating connection') -- cgit v1.3.1-10-gc9f91