mirror of
https://github.com/jlu5/PyLink.git
synced 2024-12-24 11:42:51 +01:00
classes, protocols: convert self.irc usage to self
This commit is contained in:
parent
eef7a73ce9
commit
7814914a05
46
classes.py
46
classes.py
@ -421,18 +421,18 @@ class PyLinkNetworkCore(utils.DeprecatedAttributesObject, utils.CamelCaseToSnake
|
|||||||
|
|
||||||
def remove_client(self, numeric):
|
def remove_client(self, numeric):
|
||||||
"""Internal function to remove a client from our internal state."""
|
"""Internal function to remove a client from our internal state."""
|
||||||
for c, v in self.irc.channels.copy().items():
|
for c, v in self.channels.copy().items():
|
||||||
v.removeuser(numeric)
|
v.removeuser(numeric)
|
||||||
# Clear empty non-permanent channels.
|
# Clear empty non-permanent channels.
|
||||||
if not (self.irc.channels[c].users or ((self.irc.cmodes.get('permanent'), None) in self.irc.channels[c].modes)):
|
if not (self.channels[c].users or ((self.cmodes.get('permanent'), None) in self.channels[c].modes)):
|
||||||
del self.irc.channels[c]
|
del self.channels[c]
|
||||||
assert numeric not in v.users, "IrcChannel's removeuser() is broken!"
|
assert numeric not in v.users, "IrcChannel's removeuser() is broken!"
|
||||||
|
|
||||||
sid = self.irc.get_server(numeric)
|
sid = self.get_server(numeric)
|
||||||
log.debug('Removing client %s from self.irc.users', numeric)
|
log.debug('Removing client %s from self.users', numeric)
|
||||||
del self.irc.users[numeric]
|
del self.users[numeric]
|
||||||
log.debug('Removing client %s from self.irc.servers[%s].users', numeric, sid)
|
log.debug('Removing client %s from self.servers[%s].users', numeric, sid)
|
||||||
self.irc.servers[sid].users.discard(numeric)
|
self.servers[sid].users.discard(numeric)
|
||||||
|
|
||||||
|
|
||||||
class PyLinkNetworkCoreWithUtils(PyLinkNetworkCore):
|
class PyLinkNetworkCoreWithUtils(PyLinkNetworkCore):
|
||||||
@ -1065,46 +1065,46 @@ class PyLinkNetworkCoreWithUtils(PyLinkNetworkCore):
|
|||||||
modes = []
|
modes = []
|
||||||
|
|
||||||
def _clear():
|
def _clear():
|
||||||
log.debug("(%s) Clearing local modes from channel %s due to TS change", self.irc.name,
|
log.debug("(%s) Clearing local modes from channel %s due to TS change", self.name,
|
||||||
channel)
|
channel)
|
||||||
self.irc.channels[channel].modes.clear()
|
self.channels[channel].modes.clear()
|
||||||
for p in self.irc.channels[channel].prefixmodes.values():
|
for p in self.channels[channel].prefixmodes.values():
|
||||||
for user in p.copy():
|
for user in p.copy():
|
||||||
if not self.irc.is_internal_client(user):
|
if not self.is_internal_client(user):
|
||||||
p.discard(user)
|
p.discard(user)
|
||||||
|
|
||||||
def _apply():
|
def _apply():
|
||||||
if modes:
|
if modes:
|
||||||
log.debug("(%s) Applying modes on channel %s (TS ok)", self.irc.name,
|
log.debug("(%s) Applying modes on channel %s (TS ok)", self.name,
|
||||||
channel)
|
channel)
|
||||||
self.irc.apply_modes(channel, modes)
|
self.apply_modes(channel, modes)
|
||||||
|
|
||||||
# Use a lock so only one thread can change a channel's TS at once: this prevents race
|
# Use a lock so only one thread can change a channel's TS at once: this prevents race
|
||||||
# conditions from desyncing the channel list.
|
# conditions from desyncing the channel list.
|
||||||
with self.ts_lock:
|
with self.ts_lock:
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
assert type(our_ts) == int, "Wrong type for our_ts (expected int, got %s)" % type(our_ts)
|
assert type(our_ts) == int, "Wrong type for our_ts (expected int, got %s)" % type(our_ts)
|
||||||
assert type(their_ts) == int, "Wrong type for their_ts (expected int, got %s)" % type(their_ts)
|
assert type(their_ts) == int, "Wrong type for their_ts (expected int, got %s)" % type(their_ts)
|
||||||
|
|
||||||
# Check if we're the mode sender based on the UID / SID given.
|
# Check if we're the mode sender based on the UID / SID given.
|
||||||
our_mode = self.irc.is_internal_client(sender) or self.irc.is_internal_server(sender)
|
our_mode = self.is_internal_client(sender) or self.is_internal_server(sender)
|
||||||
|
|
||||||
log.debug("(%s/%s) our_ts: %s; their_ts: %s; is the mode origin us? %s", self.irc.name,
|
log.debug("(%s/%s) our_ts: %s; their_ts: %s; is the mode origin us? %s", self.name,
|
||||||
channel, our_ts, their_ts, our_mode)
|
channel, our_ts, their_ts, our_mode)
|
||||||
|
|
||||||
if their_ts == our_ts:
|
if their_ts == our_ts:
|
||||||
log.debug("(%s/%s) remote TS of %s is equal to our %s; mode query %s",
|
log.debug("(%s/%s) remote TS of %s is equal to our %s; mode query %s",
|
||||||
self.irc.name, channel, their_ts, our_ts, modes)
|
self.name, channel, their_ts, our_ts, modes)
|
||||||
# Their TS is equal to ours. Merge modes.
|
# Their TS is equal to ours. Merge modes.
|
||||||
_apply()
|
_apply()
|
||||||
|
|
||||||
elif (their_ts < our_ts):
|
elif (their_ts < our_ts):
|
||||||
if their_ts < 750000:
|
if their_ts < 750000:
|
||||||
log.warning('(%s) Possible desync? Not setting bogus TS %s on channel %s', self.irc.name, their_ts, channel)
|
log.warning('(%s) Possible desync? Not setting bogus TS %s on channel %s', self.name, their_ts, channel)
|
||||||
else:
|
else:
|
||||||
log.debug('(%s) Resetting channel TS of %s from %s to %s (remote has lower TS)',
|
log.debug('(%s) Resetting channel TS of %s from %s to %s (remote has lower TS)',
|
||||||
self.irc.name, channel, our_ts, their_ts)
|
self.name, channel, our_ts, their_ts)
|
||||||
self.irc.channels[channel].ts = their_ts
|
self.channels[channel].ts = their_ts
|
||||||
|
|
||||||
# Remote TS was lower and we're receiving modes. Clear the modelist and apply theirs.
|
# Remote TS was lower and we're receiving modes. Clear the modelist and apply theirs.
|
||||||
|
|
||||||
@ -1115,7 +1115,7 @@ class PyLinkNetworkCoreWithUtils(PyLinkNetworkCore):
|
|||||||
def _get_SID(self, sname):
|
def _get_SID(self, sname):
|
||||||
"""Returns the SID of a server with the given name, if present."""
|
"""Returns the SID of a server with the given name, if present."""
|
||||||
name = sname.lower()
|
name = sname.lower()
|
||||||
for k, v in self.irc.servers.items():
|
for k, v in self.servers.items():
|
||||||
if v.name.lower() == name:
|
if v.name.lower() == name:
|
||||||
return k
|
return k
|
||||||
else:
|
else:
|
||||||
@ -1125,7 +1125,7 @@ class PyLinkNetworkCoreWithUtils(PyLinkNetworkCore):
|
|||||||
def _get_UID(self, target):
|
def _get_UID(self, target):
|
||||||
"""Converts a nick argument to its matching UID. This differs from irc.nick_to_uid()
|
"""Converts a nick argument to its matching UID. This differs from irc.nick_to_uid()
|
||||||
in that it returns the original text instead of None, if no matching nick is found."""
|
in that it returns the original text instead of None, if no matching nick is found."""
|
||||||
target = self.irc.nick_to_uid(target) or target
|
target = self.nick_to_uid(target) or target
|
||||||
return target
|
return target
|
||||||
_getUid = _get_UID
|
_getUid = _get_UID
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
Returns the real nick for the given PUID.
|
Returns the real nick for the given PUID.
|
||||||
"""
|
"""
|
||||||
if uid in self.irc.users:
|
if uid in self.users:
|
||||||
nick = self.irc.users[uid].nick
|
nick = self.users[uid].nick
|
||||||
log.debug('(%s) Mangling target PUID %s to nick %s', self.irc.name, uid, nick)
|
log.debug('(%s) Mangling target PUID %s to nick %s', self.name, uid, nick)
|
||||||
return nick
|
return nick
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
self.sidgen = utils.PUIDGenerator('PSID')
|
self.sidgen = utils.PUIDGenerator('PSID')
|
||||||
|
|
||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
f = lambda text: self.irc.send(text, queue=False)
|
f = lambda text: self.send(text, queue=False)
|
||||||
|
|
||||||
# Enumerate our own server
|
# Enumerate our own server
|
||||||
self.irc.sid = self.sidgen.next_sid()
|
self.sid = self.sidgen.next_sid()
|
||||||
|
|
||||||
# Clear states from last connect
|
# Clear states from last connect
|
||||||
self.who_received.clear()
|
self.who_received.clear()
|
||||||
@ -71,7 +71,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
self.ircv3_caps.clear()
|
self.ircv3_caps.clear()
|
||||||
self.ircv3_caps_available.clear()
|
self.ircv3_caps_available.clear()
|
||||||
|
|
||||||
sendpass = self.irc.serverdata.get("sendpass")
|
sendpass = self.serverdata.get("sendpass")
|
||||||
if sendpass:
|
if sendpass:
|
||||||
f('PASS %s' % sendpass)
|
f('PASS %s' % sendpass)
|
||||||
|
|
||||||
@ -81,17 +81,17 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# never replied to).
|
# never replied to).
|
||||||
def capEnd():
|
def capEnd():
|
||||||
log.info('(%s) Skipping SASL due to timeout; are the IRCd and services configured '
|
log.info('(%s) Skipping SASL due to timeout; are the IRCd and services configured '
|
||||||
'properly?', self.irc.name)
|
'properly?', self.name)
|
||||||
self.capEnd()
|
self.capEnd()
|
||||||
self._cap_timer = threading.Timer(self.irc.serverdata.get('sasl_timeout') or 15, capEnd)
|
self._cap_timer = threading.Timer(self.serverdata.get('sasl_timeout') or 15, capEnd)
|
||||||
self._cap_timer.start()
|
self._cap_timer.start()
|
||||||
|
|
||||||
# This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS.
|
# This is a really gross hack to get the defined NICK/IDENT/HOST/GECOS.
|
||||||
# But this connection stuff is done before any of the spawnClient stuff in
|
# But this connection stuff is done before any of the spawnClient stuff in
|
||||||
# services_support fires.
|
# services_support fires.
|
||||||
self.conf_nick = self.irc.serverdata.get('pylink_nick') or conf.conf["bot"].get("nick", "PyLink")
|
self.conf_nick = self.serverdata.get('pylink_nick') or conf.conf["bot"].get("nick", "PyLink")
|
||||||
f('NICK %s' % (self.conf_nick))
|
f('NICK %s' % (self.conf_nick))
|
||||||
ident = self.irc.serverdata.get('pylink_ident') or conf.conf["bot"].get("ident", "pylink")
|
ident = self.serverdata.get('pylink_ident') or conf.conf["bot"].get("ident", "pylink")
|
||||||
f('USER %s 8 * :%s' % (ident, # TODO: per net realnames or hostnames aren't implemented yet.
|
f('USER %s 8 * :%s' % (ident, # TODO: per net realnames or hostnames aren't implemented yet.
|
||||||
conf.conf["bot"].get("realname", "PyLink Clientbot")))
|
conf.conf["bot"].get("realname", "PyLink Clientbot")))
|
||||||
|
|
||||||
@ -103,17 +103,17 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
STUB: Pretends to spawn a new client with a subset of the given options.
|
STUB: Pretends to spawn a new client with a subset of the given options.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
uid = self.uidgen.next_uid(prefix=nick)
|
uid = self.uidgen.next_uid(prefix=nick)
|
||||||
|
|
||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
|
|
||||||
log.debug('(%s) spawnClient stub called, saving nick %s as PUID %s', self.irc.name, nick, uid)
|
log.debug('(%s) spawnClient stub called, saving nick %s as PUID %s', self.name, nick, uid)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
manipulatable=manipulatable, realhost=realhost, ip=ip)
|
manipulatable=manipulatable, realhost=realhost, ip=ip)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
|
|
||||||
return u
|
return u
|
||||||
|
|
||||||
@ -123,85 +123,85 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
sid = self.sidgen.next_sid(prefix=name)
|
sid = self.sidgen.next_sid(prefix=name)
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=internal)
|
self.servers[sid] = IrcServer(uplink, name, internal=internal)
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def away(self, source, text):
|
def away(self, source, text):
|
||||||
"""STUB: sets away messages for clients internally."""
|
"""STUB: sets away messages for clients internally."""
|
||||||
log.debug('(%s) away: target is %s, internal client? %s', self.irc.name, source, self.irc.isInternalClient(source))
|
log.debug('(%s) away: target is %s, internal client? %s', self.name, source, self.isInternalClient(source))
|
||||||
|
|
||||||
if self.irc.users[source].away != text:
|
if self.users[source].away != text:
|
||||||
if not self.irc.isInternalClient(source):
|
if not self.isInternalClient(source):
|
||||||
log.debug('(%s) away: sending AWAY hook from %s with text %r', self.irc.name, source, text)
|
log.debug('(%s) away: sending AWAY hook from %s with text %r', self.name, source, text)
|
||||||
self.irc.callHooks([source, 'AWAY', {'text': text}])
|
self.callHooks([source, 'AWAY', {'text': text}])
|
||||||
|
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
def invite(self, client, target, channel):
|
def invite(self, client, target, channel):
|
||||||
"""Invites a user to a channel."""
|
"""Invites a user to a channel."""
|
||||||
self.irc.send('INVITE %s %s' % (self.irc.getFriendlyName(target), channel))
|
self.send('INVITE %s %s' % (self.getFriendlyName(target), channel))
|
||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""STUB: Joins a user to a channel."""
|
"""STUB: Joins a user to a channel."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
|
|
||||||
# Only joins for the main PyLink client are actually forwarded. Others are ignored.
|
# Only joins for the main PyLink client are actually forwarded. Others are ignored.
|
||||||
# Note: we do not automatically add our main client to the channel state, as we
|
# Note: we do not automatically add our main client to the channel state, as we
|
||||||
# rely on the /NAMES reply to sync it up properly.
|
# rely on the /NAMES reply to sync it up properly.
|
||||||
if self.irc.pseudoclient and client == self.irc.pseudoclient.uid:
|
if self.pseudoclient and client == self.pseudoclient.uid:
|
||||||
self.irc.send('JOIN %s' % channel)
|
self.send('JOIN %s' % channel)
|
||||||
# Send /names and /who requests right after
|
# Send /names and /who requests right after
|
||||||
self.irc.send('MODE %s' % channel)
|
self.send('MODE %s' % channel)
|
||||||
self.irc.send('NAMES %s' % channel)
|
self.send('NAMES %s' % channel)
|
||||||
self.irc.send('WHO %s' % channel)
|
self.send('WHO %s' % channel)
|
||||||
else:
|
else:
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
log.debug('(%s) join: faking JOIN of client %s/%s to %s', self.irc.name, client,
|
log.debug('(%s) join: faking JOIN of client %s/%s to %s', self.name, client,
|
||||||
self.irc.getFriendlyName(client), channel)
|
self.getFriendlyName(client), channel)
|
||||||
self.irc.callHooks([client, 'CLIENTBOT_JOIN', {'channel': channel}])
|
self.callHooks([client, 'CLIENTBOT_JOIN', {'channel': channel}])
|
||||||
|
|
||||||
def kick(self, source, channel, target, reason=''):
|
def kick(self, source, channel, target, reason=''):
|
||||||
"""Sends channel kicks."""
|
"""Sends channel kicks."""
|
||||||
|
|
||||||
log.debug('(%s) kick: checking if target %s (nick: %s) is an internal client? %s',
|
log.debug('(%s) kick: checking if target %s (nick: %s) is an internal client? %s',
|
||||||
self.irc.name, target, self.irc.getFriendlyName(target),
|
self.name, target, self.getFriendlyName(target),
|
||||||
self.irc.isInternalClient(target))
|
self.isInternalClient(target))
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
# Target was one of our virtual clients. Just remove them from the state.
|
# Target was one of our virtual clients. Just remove them from the state.
|
||||||
self.handle_part(target, 'KICK', [channel, reason])
|
self.handle_part(target, 'KICK', [channel, reason])
|
||||||
|
|
||||||
# Send a KICK hook for message formatting.
|
# Send a KICK hook for message formatting.
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_KICK', {'channel': channel, 'target': target, 'text': reason}])
|
self.callHooks([source, 'CLIENTBOT_KICK', {'channel': channel, 'target': target, 'text': reason}])
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.send('KICK %s %s :<%s> %s' % (channel, self._expandPUID(target),
|
self.send('KICK %s %s :<%s> %s' % (channel, self._expandPUID(target),
|
||||||
self.irc.getFriendlyName(source), reason))
|
self.getFriendlyName(source), reason))
|
||||||
|
|
||||||
# Don't update our state here: wait for the IRCd to send an acknowledgement instead.
|
# Don't update our state here: wait for the IRCd to send an acknowledgement instead.
|
||||||
# There is essentially a 3 second wait to do this, as we send NAMES with a delay
|
# There is essentially a 3 second wait to do this, as we send NAMES with a delay
|
||||||
# to resync any users lost due to kicks being blocked, etc.
|
# to resync any users lost due to kicks being blocked, etc.
|
||||||
if (channel not in self.kick_queue) or (not self.kick_queue[channel][1].is_alive()):
|
if (channel not in self.kick_queue) or (not self.kick_queue[channel][1].is_alive()):
|
||||||
# However, only do this if there isn't a NAMES request scheduled already.
|
# However, only do this if there isn't a NAMES request scheduled already.
|
||||||
t = threading.Timer(3, lambda: self.irc.send('NAMES %s' % channel))
|
t = threading.Timer(3, lambda: self.send('NAMES %s' % channel))
|
||||||
log.debug('(%s) kick: setting NAMES timer for %s on %s', self.irc.name, target, channel)
|
log.debug('(%s) kick: setting NAMES timer for %s on %s', self.name, target, channel)
|
||||||
|
|
||||||
# Store the channel, target UID, and timer object in the internal kick queue.
|
# Store the channel, target UID, and timer object in the internal kick queue.
|
||||||
self.kick_queue[channel] = ({target}, t)
|
self.kick_queue[channel] = ({target}, t)
|
||||||
t.start()
|
t.start()
|
||||||
else:
|
else:
|
||||||
log.debug('(%s) kick: adding %s to kick queue for channel %s', self.irc.name, target, channel)
|
log.debug('(%s) kick: adding %s to kick queue for channel %s', self.name, target, channel)
|
||||||
self.kick_queue[channel][0].add(target)
|
self.kick_queue[channel][0].add(target)
|
||||||
|
|
||||||
def message(self, source, target, text, notice=False):
|
def message(self, source, target, text, notice=False):
|
||||||
"""Sends messages to the target."""
|
"""Sends messages to the target."""
|
||||||
command = 'NOTICE' if notice else 'PRIVMSG'
|
command = 'NOTICE' if notice else 'PRIVMSG'
|
||||||
|
|
||||||
if self.irc.pseudoclient and self.irc.pseudoclient.uid == source:
|
if self.pseudoclient and self.pseudoclient.uid == source:
|
||||||
self.irc.send('%s %s :%s' % (command, self._expandPUID(target), text))
|
self.send('%s %s :%s' % (command, self._expandPUID(target), text))
|
||||||
else:
|
else:
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_MESSAGE', {'target': target, 'is_notice': notice, 'text': text}])
|
self.callHooks([source, 'CLIENTBOT_MESSAGE', {'target': target, 'is_notice': notice, 'text': text}])
|
||||||
|
|
||||||
def mode(self, source, channel, modes, ts=None):
|
def mode(self, source, channel, modes, ts=None):
|
||||||
"""Sends channel MODE changes."""
|
"""Sends channel MODE changes."""
|
||||||
@ -211,35 +211,35 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# things that were never banned. This prevents the bot from getting caught in a loop
|
# things that were never banned. This prevents the bot from getting caught in a loop
|
||||||
# with IRCd MODE acknowledgements.
|
# with IRCd MODE acknowledgements.
|
||||||
# FIXME: More related safety checks should be added for this.
|
# FIXME: More related safety checks should be added for this.
|
||||||
log.debug('(%s) mode: re-parsing modes %s', self.irc.name, modes)
|
log.debug('(%s) mode: re-parsing modes %s', self.name, modes)
|
||||||
joined_modes = self.irc.joinModes(modes)
|
joined_modes = self.joinModes(modes)
|
||||||
for modepair in self.irc.parseModes(channel, joined_modes):
|
for modepair in self.parseModes(channel, joined_modes):
|
||||||
log.debug('(%s) mode: checking if %s a prefix mode: %s', self.irc.name, modepair, self.irc.prefixmodes)
|
log.debug('(%s) mode: checking if %s a prefix mode: %s', self.name, modepair, self.prefixmodes)
|
||||||
if modepair[0][-1] in self.irc.prefixmodes:
|
if modepair[0][-1] in self.prefixmodes:
|
||||||
if self.irc.isInternalClient(modepair[1]):
|
if self.isInternalClient(modepair[1]):
|
||||||
# Ignore prefix modes for virtual internal clients.
|
# Ignore prefix modes for virtual internal clients.
|
||||||
log.debug('(%s) mode: skipping virtual client prefixmode change %s', self.irc.name, modepair)
|
log.debug('(%s) mode: skipping virtual client prefixmode change %s', self.name, modepair)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
# For other clients, change the mode argument to nick instead of PUID.
|
# For other clients, change the mode argument to nick instead of PUID.
|
||||||
nick = self.irc.getFriendlyName(modepair[1])
|
nick = self.getFriendlyName(modepair[1])
|
||||||
log.debug('(%s) mode: coersing mode %s argument to %s', self.irc.name, modepair, nick)
|
log.debug('(%s) mode: coersing mode %s argument to %s', self.name, modepair, nick)
|
||||||
modepair = (modepair[0], nick)
|
modepair = (modepair[0], nick)
|
||||||
extmodes.append(modepair)
|
extmodes.append(modepair)
|
||||||
|
|
||||||
log.debug('(%s) mode: filtered modes for %s: %s', self.irc.name, channel, extmodes)
|
log.debug('(%s) mode: filtered modes for %s: %s', self.name, channel, extmodes)
|
||||||
if extmodes:
|
if extmodes:
|
||||||
self.irc.send('MODE %s %s' % (channel, self.irc.joinModes(extmodes)))
|
self.send('MODE %s %s' % (channel, self.joinModes(extmodes)))
|
||||||
# Don't update the state here: the IRCd sill respond with a MODE reply if successful.
|
# Don't update the state here: the IRCd sill respond with a MODE reply if successful.
|
||||||
|
|
||||||
def nick(self, source, newnick):
|
def nick(self, source, newnick):
|
||||||
"""STUB: Sends NICK changes."""
|
"""STUB: Sends NICK changes."""
|
||||||
if self.irc.pseudoclient and source == self.irc.pseudoclient.uid:
|
if self.pseudoclient and source == self.pseudoclient.uid:
|
||||||
self.irc.send('NICK :%s' % newnick)
|
self.send('NICK :%s' % newnick)
|
||||||
# No state update here: the IRCd will respond with a NICK acknowledgement if the change succeeds.
|
# No state update here: the IRCd will respond with a NICK acknowledgement if the change succeeds.
|
||||||
else:
|
else:
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_NICK', {'newnick': newnick}])
|
self.callHooks([source, 'CLIENTBOT_NICK', {'newnick': newnick}])
|
||||||
self.irc.users[source].nick = newnick
|
self.users[source].nick = newnick
|
||||||
|
|
||||||
def notice(self, source, target, text):
|
def notice(self, source, target, text):
|
||||||
"""Sends notices to the target."""
|
"""Sends notices to the target."""
|
||||||
@ -250,29 +250,29 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
Sends PING to the uplink.
|
Sends PING to the uplink.
|
||||||
"""
|
"""
|
||||||
if self.irc.uplink:
|
if self.uplink:
|
||||||
self.irc.send('PING %s' % self.irc.getFriendlyName(self.irc.uplink))
|
self.send('PING %s' % self.getFriendlyName(self.uplink))
|
||||||
|
|
||||||
# Poll WHO periodically to figure out any ident/host/away status changes.
|
# Poll WHO periodically to figure out any ident/host/away status changes.
|
||||||
for channel in self.irc.pseudoclient.channels:
|
for channel in self.pseudoclient.channels:
|
||||||
self.irc.send('WHO %s' % channel)
|
self.send('WHO %s' % channel)
|
||||||
|
|
||||||
def part(self, source, channel, reason=''):
|
def part(self, source, channel, reason=''):
|
||||||
"""STUB: Parts a user from a channel."""
|
"""STUB: Parts a user from a channel."""
|
||||||
self.irc.channels[channel].removeuser(source)
|
self.channels[channel].removeuser(source)
|
||||||
self.irc.users[source].channels.discard(channel)
|
self.users[source].channels.discard(channel)
|
||||||
|
|
||||||
# Only parts for the main PyLink client are actually forwarded. Others are ignored.
|
# Only parts for the main PyLink client are actually forwarded. Others are ignored.
|
||||||
if self.irc.pseudoclient and source == self.irc.pseudoclient.uid:
|
if self.pseudoclient and source == self.pseudoclient.uid:
|
||||||
self.irc.send('PART %s :%s' % (channel, reason))
|
self.send('PART %s :%s' % (channel, reason))
|
||||||
else:
|
else:
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_PART', {'channel': channel, 'text': reason}])
|
self.callHooks([source, 'CLIENTBOT_PART', {'channel': channel, 'text': reason}])
|
||||||
|
|
||||||
def quit(self, source, reason):
|
def quit(self, source, reason):
|
||||||
"""STUB: Quits a client."""
|
"""STUB: Quits a client."""
|
||||||
userdata = self.irc.users[source]
|
userdata = self.users[source]
|
||||||
self.removeClient(source)
|
self.removeClient(source)
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_QUIT', {'text': reason, 'userdata': userdata}])
|
self.callHooks([source, 'CLIENTBOT_QUIT', {'text': reason, 'userdata': userdata}])
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""STUB: bursts joins from a server."""
|
"""STUB: bursts joins from a server."""
|
||||||
@ -280,16 +280,16 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# given. modes and TS are currently ignored.
|
# given. modes and TS are currently ignored.
|
||||||
puids = {u[-1] for u in users}
|
puids = {u[-1] for u in users}
|
||||||
for user in puids:
|
for user in puids:
|
||||||
if self.irc.pseudoclient and self.irc.pseudoclient.uid == user:
|
if self.pseudoclient and self.pseudoclient.uid == user:
|
||||||
# If the SJOIN affects our main client, forward it as a regular JOIN.
|
# If the SJOIN affects our main client, forward it as a regular JOIN.
|
||||||
self.join(user, channel)
|
self.join(user, channel)
|
||||||
else:
|
else:
|
||||||
# Otherwise, track the state for our virtual clients.
|
# Otherwise, track the state for our virtual clients.
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
self.irc.channels[channel].users |= puids
|
self.channels[channel].users |= puids
|
||||||
nicks = {self.irc.getFriendlyName(u) for u in puids}
|
nicks = {self.getFriendlyName(u) for u in puids}
|
||||||
self.irc.callHooks([server, 'CLIENTBOT_SJOIN', {'channel': channel, 'nicks': nicks}])
|
self.callHooks([server, 'CLIENTBOT_SJOIN', {'channel': channel, 'nicks': nicks}])
|
||||||
|
|
||||||
def squit(self, source, target, text):
|
def squit(self, source, target, text):
|
||||||
"""STUB: SQUITs a server."""
|
"""STUB: SQUITs a server."""
|
||||||
@ -298,7 +298,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
squit_data = self._squit(source, 'CLIENTBOT_VIRTUAL_SQUIT', [target, text])
|
squit_data = self._squit(source, 'CLIENTBOT_VIRTUAL_SQUIT', [target, text])
|
||||||
|
|
||||||
if squit_data.get('nicks'):
|
if squit_data.get('nicks'):
|
||||||
self.irc.callHooks([source, 'CLIENTBOT_SQUIT', squit_data])
|
self.callHooks([source, 'CLIENTBOT_SQUIT', squit_data])
|
||||||
|
|
||||||
def _stub(self, *args):
|
def _stub(self, *args):
|
||||||
"""Stub outgoing command function (does nothing)."""
|
"""Stub outgoing command function (does nothing)."""
|
||||||
@ -307,28 +307,28 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def updateClient(self, target, field, text):
|
||||||
"""Updates the known ident, host, or realname of a client."""
|
"""Updates the known ident, host, or realname of a client."""
|
||||||
if target not in self.irc.users:
|
if target not in self.users:
|
||||||
log.warning("(%s) Unknown target %s for updateClient()", self.irc.name, target)
|
log.warning("(%s) Unknown target %s for updateClient()", self.name, target)
|
||||||
return
|
return
|
||||||
|
|
||||||
u = self.irc.users[target]
|
u = self.users[target]
|
||||||
|
|
||||||
if field == 'IDENT' and u.ident != text:
|
if field == 'IDENT' and u.ident != text:
|
||||||
u.ident = text
|
u.ident = text
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
# We're updating the host of an external client in our state, so send the appropriate
|
# We're updating the host of an external client in our state, so send the appropriate
|
||||||
# hook payloads.
|
# hook payloads.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGIDENT',
|
self.callHooks([self.sid, 'CHGIDENT',
|
||||||
{'target': target, 'newident': text}])
|
{'target': target, 'newident': text}])
|
||||||
elif field == 'HOST' and u.host != text:
|
elif field == 'HOST' and u.host != text:
|
||||||
u.host = text
|
u.host = text
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.callHooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
elif field in ('REALNAME', 'GECOS') and u.realname != text:
|
elif field in ('REALNAME', 'GECOS') and u.realname != text:
|
||||||
u.realname = text
|
u.realname = text
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGNAME',
|
self.callHooks([self.sid, 'CHGNAME',
|
||||||
{'target': target, 'newgecos': text}])
|
{'target': target, 'newgecos': text}])
|
||||||
else:
|
else:
|
||||||
return # Nothing changed
|
return # Nothing changed
|
||||||
@ -340,19 +340,19 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
Limited (internal) nick collision checking is done here to prevent Clientbot users from
|
Limited (internal) nick collision checking is done here to prevent Clientbot users from
|
||||||
being confused with virtual clients, and vice versa."""
|
being confused with virtual clients, and vice versa."""
|
||||||
self._validateNick(nick)
|
self._validateNick(nick)
|
||||||
idsource = self.irc.nickToUid(nick)
|
idsource = self.nickToUid(nick)
|
||||||
is_internal = self.irc.isInternalClient(idsource)
|
is_internal = self.isInternalClient(idsource)
|
||||||
|
|
||||||
# If this sender isn't known or it is one of our virtual clients, spawn a new one.
|
# If this sender isn't known or it is one of our virtual clients, spawn a new one.
|
||||||
# This also takes care of any nick collisions caused by new, Clientbot users
|
# This also takes care of any nick collisions caused by new, Clientbot users
|
||||||
# taking the same nick as one of our virtual clients, and will force the virtual client to lose.
|
# taking the same nick as one of our virtual clients, and will force the virtual client to lose.
|
||||||
if (not idsource) or (is_internal and self.irc.pseudoclient and idsource != self.irc.pseudoclient.uid):
|
if (not idsource) or (is_internal and self.pseudoclient and idsource != self.pseudoclient.uid):
|
||||||
if idsource:
|
if idsource:
|
||||||
log.debug('(%s) Nick-colliding virtual client %s/%s', self.irc.name, idsource, nick)
|
log.debug('(%s) Nick-colliding virtual client %s/%s', self.name, idsource, nick)
|
||||||
self.irc.callHooks([self.irc.sid, 'CLIENTBOT_NICKCOLLIDE', {'target': idsource, 'parse_as': 'SAVE'}])
|
self.callHooks([self.sid, 'CLIENTBOT_NICKCOLLIDE', {'target': idsource, 'parse_as': 'SAVE'}])
|
||||||
|
|
||||||
idsource = self.spawnClient(nick, ident or 'unknown', host or 'unknown',
|
idsource = self.spawnClient(nick, ident or 'unknown', host or 'unknown',
|
||||||
server=self.irc.uplink, realname=FALLBACK_REALNAME).uid
|
server=self.uplink, realname=FALLBACK_REALNAME).uid
|
||||||
|
|
||||||
return idsource
|
return idsource
|
||||||
|
|
||||||
@ -373,7 +373,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
tagdata[idx] = tag
|
tagdata[idx] = tag
|
||||||
|
|
||||||
results = self.parseCapabilities(tagdata, fallback=None)
|
results = self.parseCapabilities(tagdata, fallback=None)
|
||||||
log.debug('(%s) parsed message tags %s', self.irc.name, results)
|
log.debug('(%s) parsed message tags %s', self.name, results)
|
||||||
return results
|
return results
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -395,7 +395,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
# Raw command without an explicit sender; assume it's being sent by our uplink.
|
# Raw command without an explicit sender; assume it's being sent by our uplink.
|
||||||
args = self.parseArgs(data)
|
args = self.parseArgs(data)
|
||||||
idsource = sender = self.irc.uplink
|
idsource = sender = self.uplink
|
||||||
command = args[0]
|
command = args[0]
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
else:
|
else:
|
||||||
@ -405,7 +405,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
if ('!' not in sender) and '.' in sender:
|
if ('!' not in sender) and '.' in sender:
|
||||||
# Sender is a server name. XXX: make this check more foolproof
|
# Sender is a server name. XXX: make this check more foolproof
|
||||||
idsource = self._get_SID(sender)
|
idsource = self._get_SID(sender)
|
||||||
if idsource not in self.irc.servers:
|
if idsource not in self.servers:
|
||||||
idsource = self.spawnServer(sender, internal=False)
|
idsource = self.spawnServer(sender, internal=False)
|
||||||
else:
|
else:
|
||||||
# Sender is a either a nick or a nick!user@host prefix. Split it into its relevant parts.
|
# Sender is a either a nick or a nick!user@host prefix. Split it into its relevant parts.
|
||||||
@ -430,8 +430,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
Abort SASL login by sending CAP END.
|
Abort SASL login by sending CAP END.
|
||||||
"""
|
"""
|
||||||
self.irc.send('CAP END')
|
self.send('CAP END')
|
||||||
log.debug("(%s) Stopping CAP END timer.", self.irc.name)
|
log.debug("(%s) Stopping CAP END timer.", self.name)
|
||||||
self._cap_timer.cancel()
|
self._cap_timer.cancel()
|
||||||
|
|
||||||
def saslAuth(self):
|
def saslAuth(self):
|
||||||
@ -440,43 +440,43 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
is enabled and correctly configured, and False otherwise.
|
is enabled and correctly configured, and False otherwise.
|
||||||
"""
|
"""
|
||||||
if 'sasl' not in self.ircv3_caps:
|
if 'sasl' not in self.ircv3_caps:
|
||||||
log.info("(%s) Skipping SASL auth since the IRCd doesn't support it.", self.irc.name)
|
log.info("(%s) Skipping SASL auth since the IRCd doesn't support it.", self.name)
|
||||||
return
|
return
|
||||||
|
|
||||||
sasl_mech = self.irc.serverdata.get('sasl_mechanism')
|
sasl_mech = self.serverdata.get('sasl_mechanism')
|
||||||
if sasl_mech:
|
if sasl_mech:
|
||||||
sasl_mech = sasl_mech.upper()
|
sasl_mech = sasl_mech.upper()
|
||||||
sasl_user = self.irc.serverdata.get('sasl_username')
|
sasl_user = self.serverdata.get('sasl_username')
|
||||||
sasl_pass = self.irc.serverdata.get('sasl_password')
|
sasl_pass = self.serverdata.get('sasl_password')
|
||||||
ssl_cert = self.irc.serverdata.get('ssl_certfile')
|
ssl_cert = self.serverdata.get('ssl_certfile')
|
||||||
ssl_key = self.irc.serverdata.get('ssl_keyfile')
|
ssl_key = self.serverdata.get('ssl_keyfile')
|
||||||
ssl = self.irc.serverdata.get('ssl')
|
ssl = self.serverdata.get('ssl')
|
||||||
|
|
||||||
if sasl_mech == 'PLAIN':
|
if sasl_mech == 'PLAIN':
|
||||||
if not (sasl_user and sasl_pass):
|
if not (sasl_user and sasl_pass):
|
||||||
log.warning("(%s) Not attempting PLAIN authentication; sasl_username and/or "
|
log.warning("(%s) Not attempting PLAIN authentication; sasl_username and/or "
|
||||||
"sasl_password aren't correctly set.", self.irc.name)
|
"sasl_password aren't correctly set.", self.name)
|
||||||
return False
|
return False
|
||||||
elif sasl_mech == 'EXTERNAL':
|
elif sasl_mech == 'EXTERNAL':
|
||||||
if not ssl:
|
if not ssl:
|
||||||
log.warning("(%s) Not attempting EXTERNAL authentication; SASL external requires "
|
log.warning("(%s) Not attempting EXTERNAL authentication; SASL external requires "
|
||||||
"SSL, but it isn't enabled.", self.irc.name)
|
"SSL, but it isn't enabled.", self.name)
|
||||||
return False
|
return False
|
||||||
elif not (ssl_cert and ssl_key):
|
elif not (ssl_cert and ssl_key):
|
||||||
log.warning("(%s) Not attempting EXTERNAL authentication; ssl_certfile and/or "
|
log.warning("(%s) Not attempting EXTERNAL authentication; ssl_certfile and/or "
|
||||||
"ssl_keyfile aren't correctly set.", self.irc.name)
|
"ssl_keyfile aren't correctly set.", self.name)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
log.warning('(%s) Unsupported SASL mechanism %s; aborting SASL.', self.irc.name, sasl_mech)
|
log.warning('(%s) Unsupported SASL mechanism %s; aborting SASL.', self.name, sasl_mech)
|
||||||
return False
|
return False
|
||||||
self.irc.send('AUTHENTICATE %s' % sasl_mech, queue=False)
|
self.send('AUTHENTICATE %s' % sasl_mech, queue=False)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sendAuthChunk(self, data):
|
def sendAuthChunk(self, data):
|
||||||
"""Send Base64 encoded SASL authentication chunks."""
|
"""Send Base64 encoded SASL authentication chunks."""
|
||||||
enc_data = base64.b64encode(data).decode()
|
enc_data = base64.b64encode(data).decode()
|
||||||
self.irc.send('AUTHENTICATE %s' % enc_data, queue=False)
|
self.send('AUTHENTICATE %s' % enc_data, queue=False)
|
||||||
|
|
||||||
def handle_authenticate(self, source, command, args):
|
def handle_authenticate(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
@ -488,21 +488,21 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
if not args:
|
if not args:
|
||||||
return
|
return
|
||||||
if args[0] == '+':
|
if args[0] == '+':
|
||||||
sasl_mech = self.irc.serverdata['sasl_mechanism'].upper()
|
sasl_mech = self.serverdata['sasl_mechanism'].upper()
|
||||||
if sasl_mech == 'PLAIN':
|
if sasl_mech == 'PLAIN':
|
||||||
sasl_user = self.irc.serverdata['sasl_username']
|
sasl_user = self.serverdata['sasl_username']
|
||||||
sasl_pass = self.irc.serverdata['sasl_password']
|
sasl_pass = self.serverdata['sasl_password']
|
||||||
authstring = '%s\0%s\0%s' % (sasl_user, sasl_user, sasl_pass)
|
authstring = '%s\0%s\0%s' % (sasl_user, sasl_user, sasl_pass)
|
||||||
self.sendAuthChunk(authstring.encode('utf-8'))
|
self.sendAuthChunk(authstring.encode('utf-8'))
|
||||||
elif sasl_mech == 'EXTERNAL':
|
elif sasl_mech == 'EXTERNAL':
|
||||||
self.irc.send('AUTHENTICATE +')
|
self.send('AUTHENTICATE +')
|
||||||
|
|
||||||
def handle_904(self, source, command, args):
|
def handle_904(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
Handles SASL authentication status reports.
|
Handles SASL authentication status reports.
|
||||||
"""
|
"""
|
||||||
logfunc = log.info if command == '903' else log.warning
|
logfunc = log.info if command == '903' else log.warning
|
||||||
logfunc('(%s) %s', self.irc.name, args[-1])
|
logfunc('(%s) %s', self.name, args[-1])
|
||||||
if not self.has_eob:
|
if not self.has_eob:
|
||||||
self.capEnd()
|
self.capEnd()
|
||||||
handle_903 = handle_902 = handle_905 = handle_906 = handle_907 = handle_904
|
handle_903 = handle_902 = handle_905 = handle_906 = handle_907 = handle_904
|
||||||
@ -513,9 +513,9 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# And by the ones we don't already have.
|
# And by the ones we don't already have.
|
||||||
caps_wanted = available_caps - self.ircv3_caps
|
caps_wanted = available_caps - self.ircv3_caps
|
||||||
|
|
||||||
log.debug('(%s) Requesting IRCv3 capabilities %s (available: %s)', self.irc.name, caps_wanted, available_caps)
|
log.debug('(%s) Requesting IRCv3 capabilities %s (available: %s)', self.name, caps_wanted, available_caps)
|
||||||
if caps_wanted:
|
if caps_wanted:
|
||||||
self.irc.send('CAP REQ :%s' % ' '.join(caps_wanted), queue=False)
|
self.send('CAP REQ :%s' % ' '.join(caps_wanted), queue=False)
|
||||||
|
|
||||||
def handle_cap(self, source, command, args):
|
def handle_cap(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
@ -527,7 +527,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# Server: CAP * LS * :multi-prefix extended-join account-notify batch invite-notify tls
|
# Server: CAP * LS * :multi-prefix extended-join account-notify batch invite-notify tls
|
||||||
# Server: CAP * LS * :cap-notify server-time example.org/dummy-cap=dummyvalue example.org/second-dummy-cap
|
# Server: CAP * LS * :cap-notify server-time example.org/dummy-cap=dummyvalue example.org/second-dummy-cap
|
||||||
# Server: CAP * LS :userhost-in-names sasl=EXTERNAL,DH-AES,DH-BLOWFISH,ECDSA-NIST256P-CHALLENGE,PLAIN
|
# Server: CAP * LS :userhost-in-names sasl=EXTERNAL,DH-AES,DH-BLOWFISH,ECDSA-NIST256P-CHALLENGE,PLAIN
|
||||||
log.debug('(%s) Got new capabilities %s', self.irc.name, args[-1])
|
log.debug('(%s) Got new capabilities %s', self.name, args[-1])
|
||||||
self.ircv3_caps_available.update(self.parseCapabilities(args[-1], None))
|
self.ircv3_caps_available.update(self.parseCapabilities(args[-1], None))
|
||||||
if args[2] != '*':
|
if args[2] != '*':
|
||||||
self.requestNewCaps()
|
self.requestNewCaps()
|
||||||
@ -535,7 +535,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
elif subcmd == 'ACK':
|
elif subcmd == 'ACK':
|
||||||
# Server: CAP * ACK :multi-prefix sasl
|
# Server: CAP * ACK :multi-prefix sasl
|
||||||
newcaps = set(args[-1].split())
|
newcaps = set(args[-1].split())
|
||||||
log.debug('(%s) Received ACK for IRCv3 capabilities %s', self.irc.name, newcaps)
|
log.debug('(%s) Received ACK for IRCv3 capabilities %s', self.name, newcaps)
|
||||||
self.ircv3_caps |= newcaps
|
self.ircv3_caps |= newcaps
|
||||||
|
|
||||||
# Only send CAP END immediately if SASL is disabled. Otherwise, wait for the 90x responses
|
# Only send CAP END immediately if SASL is disabled. Otherwise, wait for the 90x responses
|
||||||
@ -545,7 +545,7 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
self.capEnd()
|
self.capEnd()
|
||||||
elif subcmd == 'NAK':
|
elif subcmd == 'NAK':
|
||||||
log.warning('(%s) Got NAK for IRCv3 capabilities %s, even though they were supposedly available',
|
log.warning('(%s) Got NAK for IRCv3 capabilities %s, even though they were supposedly available',
|
||||||
self.irc.name, args[-1])
|
self.name, args[-1])
|
||||||
if not self.has_eob:
|
if not self.has_eob:
|
||||||
self.capEnd()
|
self.capEnd()
|
||||||
elif subcmd == 'NEW':
|
elif subcmd == 'NEW':
|
||||||
@ -553,19 +553,19 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# :irc.example.com CAP tester NEW :away-notify extended-join
|
# :irc.example.com CAP tester NEW :away-notify extended-join
|
||||||
# Note: CAP NEW allows capabilities with values (e.g. sasl=mech1,mech2), while CAP DEL
|
# Note: CAP NEW allows capabilities with values (e.g. sasl=mech1,mech2), while CAP DEL
|
||||||
# does not.
|
# does not.
|
||||||
log.debug('(%s) Got new capabilities %s', self.irc.name, args[-1])
|
log.debug('(%s) Got new capabilities %s', self.name, args[-1])
|
||||||
newcaps = self.parseCapabilities(args[-1], None)
|
newcaps = self.parseCapabilities(args[-1], None)
|
||||||
self.ircv3_caps_available.update(newcaps)
|
self.ircv3_caps_available.update(newcaps)
|
||||||
self.requestNewCaps()
|
self.requestNewCaps()
|
||||||
|
|
||||||
# Attempt SASL auth routines when sasl is added/removed, if doing so is enabled.
|
# Attempt SASL auth routines when sasl is added/removed, if doing so is enabled.
|
||||||
if 'sasl' in newcaps and self.irc.serverdata.get('sasl_reauth'):
|
if 'sasl' in newcaps and self.serverdata.get('sasl_reauth'):
|
||||||
log.debug('(%s) Attempting SASL reauth due to CAP NEW', self.irc.name)
|
log.debug('(%s) Attempting SASL reauth due to CAP NEW', self.name)
|
||||||
self.saslAuth()
|
self.saslAuth()
|
||||||
|
|
||||||
elif subcmd == 'DEL':
|
elif subcmd == 'DEL':
|
||||||
# :irc.example.com CAP modernclient DEL :userhost-in-names multi-prefix away-notify
|
# :irc.example.com CAP modernclient DEL :userhost-in-names multi-prefix away-notify
|
||||||
log.debug('(%s) Removing capabilities %s', self.irc.name, args[-1])
|
log.debug('(%s) Removing capabilities %s', self.name, args[-1])
|
||||||
for cap in args[-1].split():
|
for cap in args[-1].split():
|
||||||
# Remove the capabilities from the list available, and return None (ignore) if any fail
|
# Remove the capabilities from the list available, and return None (ignore) if any fail
|
||||||
self.ircv3_caps_available.pop(cap, None)
|
self.ircv3_caps_available.pop(cap, None)
|
||||||
@ -576,41 +576,41 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
Handles 001 / RPL_WELCOME.
|
Handles 001 / RPL_WELCOME.
|
||||||
"""
|
"""
|
||||||
# enumerate our uplink
|
# enumerate our uplink
|
||||||
self.irc.uplink = source
|
self.uplink = source
|
||||||
|
|
||||||
def handle_005(self, source, command, args):
|
def handle_005(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
Handles 005 / RPL_ISUPPORT.
|
Handles 005 / RPL_ISUPPORT.
|
||||||
"""
|
"""
|
||||||
self.caps.update(self.parseCapabilities(args[1:-1]))
|
self.caps.update(self.parseCapabilities(args[1:-1]))
|
||||||
log.debug('(%s) handle_005: self.caps is %s', self.irc.name, self.caps)
|
log.debug('(%s) handle_005: self.caps is %s', self.name, self.caps)
|
||||||
|
|
||||||
if 'CHANMODES' in self.caps:
|
if 'CHANMODES' in self.caps:
|
||||||
self.irc.cmodes['*A'], self.irc.cmodes['*B'], self.irc.cmodes['*C'], self.irc.cmodes['*D'] = \
|
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = \
|
||||||
self.caps['CHANMODES'].split(',')
|
self.caps['CHANMODES'].split(',')
|
||||||
log.debug('(%s) handle_005: cmodes: %s', self.irc.name, self.irc.cmodes)
|
log.debug('(%s) handle_005: cmodes: %s', self.name, self.cmodes)
|
||||||
|
|
||||||
if 'USERMODES' in self.caps:
|
if 'USERMODES' in self.caps:
|
||||||
self.irc.umodes['*A'], self.irc.umodes['*B'], self.irc.umodes['*C'], self.irc.umodes['*D'] = \
|
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] = \
|
||||||
self.caps['USERMODES'].split(',')
|
self.caps['USERMODES'].split(',')
|
||||||
log.debug('(%s) handle_005: umodes: %s', self.irc.name, self.irc.umodes)
|
log.debug('(%s) handle_005: umodes: %s', self.name, self.umodes)
|
||||||
|
|
||||||
self.casemapping = self.caps.get('CASEMAPPING', self.casemapping)
|
self.casemapping = self.caps.get('CASEMAPPING', self.casemapping)
|
||||||
log.debug('(%s) handle_005: casemapping set to %s', self.irc.name, self.casemapping)
|
log.debug('(%s) handle_005: casemapping set to %s', self.name, self.casemapping)
|
||||||
|
|
||||||
if 'PREFIX' in self.caps:
|
if 'PREFIX' in self.caps:
|
||||||
self.irc.prefixmodes = prefixmodes = self.parsePrefixes(self.caps['PREFIX'])
|
self.prefixmodes = prefixmodes = self.parsePrefixes(self.caps['PREFIX'])
|
||||||
log.debug('(%s) handle_005: prefix modes set to %s', self.irc.name, self.irc.prefixmodes)
|
log.debug('(%s) handle_005: prefix modes set to %s', self.name, self.prefixmodes)
|
||||||
|
|
||||||
# Autodetect common prefix mode names.
|
# Autodetect common prefix mode names.
|
||||||
for char, modename in COMMON_PREFIXMODES:
|
for char, modename in COMMON_PREFIXMODES:
|
||||||
# Don't overwrite existing named mode definitions.
|
# Don't overwrite existing named mode definitions.
|
||||||
if char in self.irc.prefixmodes and modename not in self.irc.cmodes:
|
if char in self.prefixmodes and modename not in self.cmodes:
|
||||||
self.irc.cmodes[modename] = char
|
self.cmodes[modename] = char
|
||||||
log.debug('(%s) handle_005: autodetecting mode %s (%s) as %s', self.irc.name,
|
log.debug('(%s) handle_005: autodetecting mode %s (%s) as %s', self.name,
|
||||||
char, self.irc.prefixmodes[char], modename)
|
char, self.prefixmodes[char], modename)
|
||||||
|
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
def handle_376(self, source, command, args):
|
def handle_376(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
@ -618,8 +618,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Run autoperform commands.
|
# Run autoperform commands.
|
||||||
for line in self.irc.serverdata.get("autoperform", []):
|
for line in self.serverdata.get("autoperform", []):
|
||||||
self.irc.send(line)
|
self.send(line)
|
||||||
|
|
||||||
# Virtual endburst hook.
|
# Virtual endburst hook.
|
||||||
if not self.has_eob:
|
if not self.has_eob:
|
||||||
@ -634,14 +634,14 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# <- :charybdis.midnight.vpn 353 ice = #test :ice @GL
|
# <- :charybdis.midnight.vpn 353 ice = #test :ice @GL
|
||||||
|
|
||||||
# Mark "@"-type channels as secret automatically, per RFC2812.
|
# Mark "@"-type channels as secret automatically, per RFC2812.
|
||||||
channel = self.irc.toLower(args[2])
|
channel = self.toLower(args[2])
|
||||||
if args[1] == '@':
|
if args[1] == '@':
|
||||||
self.irc.applyModes(channel, [('+s', None)])
|
self.applyModes(channel, [('+s', None)])
|
||||||
|
|
||||||
names = set()
|
names = set()
|
||||||
modes = set()
|
modes = set()
|
||||||
prefix_to_mode = {v:k for k, v in self.irc.prefixmodes.items()}
|
prefix_to_mode = {v:k for k, v in self.prefixmodes.items()}
|
||||||
prefixes = ''.join(self.irc.prefixmodes.values())
|
prefixes = ''.join(self.prefixmodes.values())
|
||||||
|
|
||||||
for name in args[-1].split():
|
for name in args[-1].split():
|
||||||
nick = name.lstrip(prefixes)
|
nick = name.lstrip(prefixes)
|
||||||
@ -653,41 +653,41 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
|
|
||||||
# Queue these virtual users to be joined if they're not already in the channel,
|
# Queue these virtual users to be joined if they're not already in the channel,
|
||||||
# or we're waiting for a kick acknowledgment for them.
|
# or we're waiting for a kick acknowledgment for them.
|
||||||
if (idsource not in self.irc.channels[channel].users) or (idsource in \
|
if (idsource not in self.channels[channel].users) or (idsource in \
|
||||||
self.kick_queue.get(channel, ([],))[0]):
|
self.kick_queue.get(channel, ([],))[0]):
|
||||||
names.add(idsource)
|
names.add(idsource)
|
||||||
self.irc.users[idsource].channels.add(channel)
|
self.users[idsource].channels.add(channel)
|
||||||
|
|
||||||
# Process prefix modes
|
# Process prefix modes
|
||||||
for char in name:
|
for char in name:
|
||||||
if char in self.irc.prefixmodes.values():
|
if char in self.prefixmodes.values():
|
||||||
modes.add(('+' + prefix_to_mode[char], idsource))
|
modes.add(('+' + prefix_to_mode[char], idsource))
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
# Statekeeping: make sure the channel's user list is updated!
|
# Statekeeping: make sure the channel's user list is updated!
|
||||||
self.irc.channels[channel].users |= names
|
self.channels[channel].users |= names
|
||||||
self.irc.applyModes(channel, modes)
|
self.applyModes(channel, modes)
|
||||||
|
|
||||||
log.debug('(%s) handle_353: adding users %s to %s', self.irc.name, names, channel)
|
log.debug('(%s) handle_353: adding users %s to %s', self.name, names, channel)
|
||||||
log.debug('(%s) handle_353: adding modes %s to %s', self.irc.name, modes, channel)
|
log.debug('(%s) handle_353: adding modes %s to %s', self.name, modes, channel)
|
||||||
|
|
||||||
# Unless /WHO has already been received for the given channel, we generally send the hook
|
# Unless /WHO has already been received for the given channel, we generally send the hook
|
||||||
# for JOIN after /who data is received, to enumerate the ident, host, and real names of
|
# for JOIN after /who data is received, to enumerate the ident, host, and real names of
|
||||||
# users.
|
# users.
|
||||||
if names and hasattr(self.irc.channels[channel], 'who_received'):
|
if names and hasattr(self.channels[channel], 'who_received'):
|
||||||
# /WHO *HAS* already been received. Send JOIN hooks here because we use this to keep
|
# /WHO *HAS* already been received. Send JOIN hooks here because we use this to keep
|
||||||
# track of any failed KICK attempts sent by the relay bot.
|
# track of any failed KICK attempts sent by the relay bot.
|
||||||
log.debug('(%s) handle_353: sending JOIN hook because /WHO was already received for %s',
|
log.debug('(%s) handle_353: sending JOIN hook because /WHO was already received for %s',
|
||||||
self.irc.name, channel)
|
self.name, channel)
|
||||||
return {'channel': channel, 'users': names, 'modes': self.irc.channels[channel].modes,
|
return {'channel': channel, 'users': names, 'modes': self.channels[channel].modes,
|
||||||
'parse_as': "JOIN"}
|
'parse_as': "JOIN"}
|
||||||
|
|
||||||
def _validateNick(self, nick):
|
def _validateNick(self, nick):
|
||||||
"""
|
"""
|
||||||
Checks to make sure a nick doesn't clash with a PUID.
|
Checks to make sure a nick doesn't clash with a PUID.
|
||||||
"""
|
"""
|
||||||
if nick in self.irc.users or nick in self.irc.servers:
|
if nick in self.users or nick in self.servers:
|
||||||
raise ProtocolError("Got bad nick %s from IRC which clashes with a PUID. Is someone trying to spoof users?" % nick)
|
raise ProtocolError("Got bad nick %s from IRC which clashes with a PUID. Is someone trying to spoof users?" % nick)
|
||||||
|
|
||||||
def handle_352(self, source, command, args):
|
def handle_352(self, source, command, args):
|
||||||
@ -705,10 +705,10 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
realname = args[-1].split(' ', 1)[-1]
|
realname = args[-1].split(' ', 1)[-1]
|
||||||
|
|
||||||
self._validateNick(nick)
|
self._validateNick(nick)
|
||||||
uid = self.irc.nickToUid(nick)
|
uid = self.nickToUid(nick)
|
||||||
|
|
||||||
if uid is None:
|
if uid is None:
|
||||||
log.debug("(%s) Ignoring extraneous /WHO info for %s", self.irc.name, nick)
|
log.debug("(%s) Ignoring extraneous /WHO info for %s", self.name, nick)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.updateClient(uid, 'IDENT', ident)
|
self.updateClient(uid, 'IDENT', ident)
|
||||||
@ -720,27 +720,27 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# G means away is set (we'll have to fake a message because it's not given)
|
# G means away is set (we'll have to fake a message because it's not given)
|
||||||
# * means IRCop.
|
# * means IRCop.
|
||||||
# The rest are prefix modes. Multiple can be given by the IRCd if multiple are set
|
# The rest are prefix modes. Multiple can be given by the IRCd if multiple are set
|
||||||
log.debug('(%s) handle_352: status string on user %s: %s', self.irc.name, nick, status)
|
log.debug('(%s) handle_352: status string on user %s: %s', self.name, nick, status)
|
||||||
if status[0] == 'G':
|
if status[0] == 'G':
|
||||||
log.debug('(%s) handle_352: calling away() with argument', self.irc.name)
|
log.debug('(%s) handle_352: calling away() with argument', self.name)
|
||||||
self.away(uid, 'Away')
|
self.away(uid, 'Away')
|
||||||
elif status[0] == 'H':
|
elif status[0] == 'H':
|
||||||
log.debug('(%s) handle_352: calling away() without argument', self.irc.name)
|
log.debug('(%s) handle_352: calling away() without argument', self.name)
|
||||||
self.away(uid, '') # Unmark away status
|
self.away(uid, '') # Unmark away status
|
||||||
else:
|
else:
|
||||||
log.warning('(%s) handle_352: got wrong string %s for away status', self.irc.name, status[0])
|
log.warning('(%s) handle_352: got wrong string %s for away status', self.name, status[0])
|
||||||
|
|
||||||
if self.irc.serverdata.get('track_oper_statuses'):
|
if self.serverdata.get('track_oper_statuses'):
|
||||||
if '*' in status: # Track IRCop status
|
if '*' in status: # Track IRCop status
|
||||||
if not self.irc.isOper(uid, allowAuthed=False):
|
if not self.isOper(uid, allowAuthed=False):
|
||||||
# Don't send duplicate oper ups if the target is already oper.
|
# Don't send duplicate oper ups if the target is already oper.
|
||||||
self.irc.applyModes(uid, [('+o', None)])
|
self.applyModes(uid, [('+o', None)])
|
||||||
self.irc.callHooks([uid, 'MODE', {'target': uid, 'modes': {('+o', None)}}])
|
self.callHooks([uid, 'MODE', {'target': uid, 'modes': {('+o', None)}}])
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
elif self.irc.isOper(uid, allowAuthed=False) and not self.irc.isInternalClient(uid):
|
elif self.isOper(uid, allowAuthed=False) and not self.isInternalClient(uid):
|
||||||
# Track deopers
|
# Track deopers
|
||||||
self.irc.applyModes(uid, [('-o', None)])
|
self.applyModes(uid, [('-o', None)])
|
||||||
self.irc.callHooks([uid, 'MODE', {'target': uid, 'modes': {('-o', None)}}])
|
self.callHooks([uid, 'MODE', {'target': uid, 'modes': {('-o', None)}}])
|
||||||
|
|
||||||
self.who_received.add(uid)
|
self.who_received.add(uid)
|
||||||
|
|
||||||
@ -753,8 +753,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
users = self.who_received.copy()
|
users = self.who_received.copy()
|
||||||
self.who_received.clear()
|
self.who_received.clear()
|
||||||
|
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
c = self.irc.channels[channel]
|
c = self.channels[channel]
|
||||||
c.who_received = True
|
c.who_received = True
|
||||||
|
|
||||||
modes = set(c.modes)
|
modes = set(c.modes)
|
||||||
@ -762,13 +762,13 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# Fill in prefix modes of everyone when doing mock SJOIN.
|
# Fill in prefix modes of everyone when doing mock SJOIN.
|
||||||
try:
|
try:
|
||||||
for mode in c.getPrefixModes(user):
|
for mode in c.getPrefixModes(user):
|
||||||
modechar = self.irc.cmodes.get(mode)
|
modechar = self.cmodes.get(mode)
|
||||||
log.debug('(%s) handle_315: adding mode %s +%s %s', self.irc.name, mode, modechar, user)
|
log.debug('(%s) handle_315: adding mode %s +%s %s', self.name, mode, modechar, user)
|
||||||
if modechar:
|
if modechar:
|
||||||
modes.add((modechar, user))
|
modes.add((modechar, user))
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
log.debug("(%s) Ignoring KeyError (%s) from WHO response; it's probably someone we "
|
log.debug("(%s) Ignoring KeyError (%s) from WHO response; it's probably someone we "
|
||||||
"don't share any channels with", self.irc.name, e)
|
"don't share any channels with", self.name, e)
|
||||||
|
|
||||||
return {'channel': channel, 'users': users, 'modes': modes,
|
return {'channel': channel, 'users': users, 'modes': modes,
|
||||||
'parse_as': "JOIN"}
|
'parse_as': "JOIN"}
|
||||||
@ -779,8 +779,8 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# irc.pseudoclient doesn't exist as an attribute until we get run the ENDBURST stuff
|
# irc.pseudoclient doesn't exist as an attribute until we get run the ENDBURST stuff
|
||||||
# in service_support (this is mapped to 005 here).
|
# in service_support (this is mapped to 005 here).
|
||||||
self.conf_nick += '_'
|
self.conf_nick += '_'
|
||||||
self.irc.serverdata['pylink_nick'] = self.conf_nick
|
self.serverdata['pylink_nick'] = self.conf_nick
|
||||||
self.irc.send('NICK %s' % self.conf_nick)
|
self.send('NICK %s' % self.conf_nick)
|
||||||
handle_432 = handle_437 = handle_433
|
handle_432 = handle_437 = handle_433
|
||||||
|
|
||||||
def handle_join(self, source, command, args):
|
def handle_join(self, source, command, args):
|
||||||
@ -788,18 +788,18 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
Handles incoming JOINs.
|
Handles incoming JOINs.
|
||||||
"""
|
"""
|
||||||
# <- :GL|!~GL@127.0.0.1 JOIN #whatever
|
# <- :GL|!~GL@127.0.0.1 JOIN #whatever
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
self.join(source, channel)
|
self.join(source, channel)
|
||||||
|
|
||||||
return {'channel': channel, 'users': [source], 'modes': self.irc.channels[channel].modes}
|
return {'channel': channel, 'users': [source], 'modes': self.channels[channel].modes}
|
||||||
|
|
||||||
def handle_kick(self, source, command, args):
|
def handle_kick(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
Handles incoming KICKs.
|
Handles incoming KICKs.
|
||||||
"""
|
"""
|
||||||
# <- :GL!~gl@127.0.0.1 KICK #whatever GL| :xd
|
# <- :GL!~gl@127.0.0.1 KICK #whatever GL| :xd
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
target = self.irc.nickToUid(args[1])
|
target = self.nickToUid(args[1])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reason = args[2]
|
reason = args[2]
|
||||||
@ -808,29 +808,29 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
|
|
||||||
if channel in self.kick_queue:
|
if channel in self.kick_queue:
|
||||||
# Remove this client from the kick queue if present there.
|
# Remove this client from the kick queue if present there.
|
||||||
log.debug('(%s) kick: removing %s from kick queue for channel %s', self.irc.name, target, channel)
|
log.debug('(%s) kick: removing %s from kick queue for channel %s', self.name, target, channel)
|
||||||
self.kick_queue[channel][0].discard(target)
|
self.kick_queue[channel][0].discard(target)
|
||||||
|
|
||||||
if not self.kick_queue[channel][0]:
|
if not self.kick_queue[channel][0]:
|
||||||
log.debug('(%s) kick: cancelling kick timer for channel %s (all kicks accounted for)', self.irc.name, channel)
|
log.debug('(%s) kick: cancelling kick timer for channel %s (all kicks accounted for)', self.name, channel)
|
||||||
# There aren't any kicks that failed to be acknowledged. We can remove the timer now
|
# There aren't any kicks that failed to be acknowledged. We can remove the timer now
|
||||||
self.kick_queue[channel][1].cancel()
|
self.kick_queue[channel][1].cancel()
|
||||||
del self.kick_queue[channel]
|
del self.kick_queue[channel]
|
||||||
|
|
||||||
# Statekeeping: remove the target from the channel they were previously in.
|
# Statekeeping: remove the target from the channel they were previously in.
|
||||||
self.irc.channels[channel].removeuser(target)
|
self.channels[channel].removeuser(target)
|
||||||
try:
|
try:
|
||||||
self.irc.users[target].channels.remove(channel)
|
self.users[target].channels.remove(channel)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(source)) and not self.irc.isInternalServer(source):
|
if (not self.isInternalClient(source)) and not self.isInternalServer(source):
|
||||||
# Don't repeat hooks if we're the kicker.
|
# Don't repeat hooks if we're the kicker.
|
||||||
self.irc.callHooks([source, 'KICK', {'channel': channel, 'target': target, 'text': reason}])
|
self.callHooks([source, 'KICK', {'channel': channel, 'target': target, 'text': reason}])
|
||||||
|
|
||||||
# Delete channels that we were kicked from, for better state keeping.
|
# Delete channels that we were kicked from, for better state keeping.
|
||||||
if self.irc.pseudoclient and target == self.irc.pseudoclient.uid:
|
if self.pseudoclient and target == self.pseudoclient.uid:
|
||||||
del self.irc.channels[channel]
|
del self.channels[channel]
|
||||||
|
|
||||||
def handle_mode(self, source, command, args):
|
def handle_mode(self, source, command, args):
|
||||||
"""Handles MODE changes."""
|
"""Handles MODE changes."""
|
||||||
@ -838,23 +838,23 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# <- :ice MODE ice :+Zi
|
# <- :ice MODE ice :+Zi
|
||||||
target = args[0]
|
target = args[0]
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
target = self.irc.toLower(target)
|
target = self.toLower(target)
|
||||||
oldobj = self.irc.channels[target].deepcopy()
|
oldobj = self.channels[target].deepcopy()
|
||||||
else:
|
else:
|
||||||
target = self.irc.nickToUid(target)
|
target = self.nickToUid(target)
|
||||||
oldobj = None
|
oldobj = None
|
||||||
modes = args[1:]
|
modes = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modes)
|
changedmodes = self.parseModes(target, modes)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.applyModes(target, changedmodes)
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
log.debug('(%s) Suppressing MODE change hook for internal client %s', self.irc.name, target)
|
log.debug('(%s) Suppressing MODE change hook for internal client %s', self.name, target)
|
||||||
return
|
return
|
||||||
if changedmodes:
|
if changedmodes:
|
||||||
# Prevent infinite loops: don't send MODE hooks if the sender is US.
|
# Prevent infinite loops: don't send MODE hooks if the sender is US.
|
||||||
# Note: this is not the only check in Clientbot to prevent mode loops: if our nick
|
# Note: this is not the only check in Clientbot to prevent mode loops: if our nick
|
||||||
# somehow gets desynced, this may not catch everything it's supposed to.
|
# somehow gets desynced, this may not catch everything it's supposed to.
|
||||||
if (self.irc.pseudoclient and source != self.irc.pseudoclient.uid) or not self.irc.pseudoclient:
|
if (self.pseudoclient and source != self.pseudoclient.uid) or not self.pseudoclient:
|
||||||
return {'target': target, 'modes': changedmodes, 'channeldata': oldobj}
|
return {'target': target, 'modes': changedmodes, 'channeldata': oldobj}
|
||||||
|
|
||||||
def handle_324(self, source, command, args):
|
def handle_324(self, source, command, args):
|
||||||
@ -862,36 +862,36 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# -> MODE #test
|
# -> MODE #test
|
||||||
# <- :midnight.vpn 324 GL #test +nt
|
# <- :midnight.vpn 324 GL #test +nt
|
||||||
# <- :midnight.vpn 329 GL #test 1491773459
|
# <- :midnight.vpn 329 GL #test 1491773459
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
log.debug('(%s) Got RPL_CHANNELMODEIS (324) modes %s for %s', self.irc.name, modes, channel)
|
log.debug('(%s) Got RPL_CHANNELMODEIS (324) modes %s for %s', self.name, modes, channel)
|
||||||
changedmodes = self.irc.parseModes(channel, modes)
|
changedmodes = self.parseModes(channel, modes)
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.applyModes(channel, changedmodes)
|
||||||
|
|
||||||
def handle_329(self, source, command, args):
|
def handle_329(self, source, command, args):
|
||||||
"""Handles TS announcements via RPL_CREATIONTIME."""
|
"""Handles TS announcements via RPL_CREATIONTIME."""
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
ts = int(args[2])
|
ts = int(args[2])
|
||||||
self.irc.channels[channel].ts = ts
|
self.channels[channel].ts = ts
|
||||||
|
|
||||||
def handle_nick(self, source, command, args):
|
def handle_nick(self, source, command, args):
|
||||||
"""Handles NICK changes."""
|
"""Handles NICK changes."""
|
||||||
# <- :GL|!~GL@127.0.0.1 NICK :GL_
|
# <- :GL|!~GL@127.0.0.1 NICK :GL_
|
||||||
|
|
||||||
if not self.irc.pseudoclient:
|
if not self.pseudoclient:
|
||||||
# We haven't properly logged on yet, so any initial NICK should be treated as a forced
|
# We haven't properly logged on yet, so any initial NICK should be treated as a forced
|
||||||
# nick change for US. For example, this clause is used to handle forced nick changes
|
# nick change for US. For example, this clause is used to handle forced nick changes
|
||||||
# sent by ZNC, when the login nick and the actual IRC nick of the bouncer differ.
|
# sent by ZNC, when the login nick and the actual IRC nick of the bouncer differ.
|
||||||
|
|
||||||
# HACK: change the nick config entry so services_support knows what our main
|
# HACK: change the nick config entry so services_support knows what our main
|
||||||
# pseudoclient is called.
|
# pseudoclient is called.
|
||||||
oldnick = self.irc.serverdata['pylink_nick']
|
oldnick = self.serverdata['pylink_nick']
|
||||||
self.irc.serverdata['pylink_nick'] = self.conf_nick = args[0]
|
self.serverdata['pylink_nick'] = self.conf_nick = args[0]
|
||||||
log.debug('(%s) Pre-auth FNC: Forcing configured nick to %s from %s', self.irc.name, args[0], oldnick)
|
log.debug('(%s) Pre-auth FNC: Forcing configured nick to %s from %s', self.name, args[0], oldnick)
|
||||||
return
|
return
|
||||||
|
|
||||||
oldnick = self.irc.users[source].nick
|
oldnick = self.users[source].nick
|
||||||
self.irc.users[source].nick = args[0]
|
self.users[source].nick = args[0]
|
||||||
|
|
||||||
return {'newnick': args[0], 'oldnick': oldnick}
|
return {'newnick': args[0], 'oldnick': oldnick}
|
||||||
|
|
||||||
@ -900,35 +900,35 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
Handles incoming PARTs.
|
Handles incoming PARTs.
|
||||||
"""
|
"""
|
||||||
# <- :GL|!~GL@127.0.0.1 PART #whatever
|
# <- :GL|!~GL@127.0.0.1 PART #whatever
|
||||||
channels = list(map(self.irc.toLower, args[0].split(',')))
|
channels = list(map(self.toLower, args[0].split(',')))
|
||||||
try:
|
try:
|
||||||
reason = args[1]
|
reason = args[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = ''
|
reason = ''
|
||||||
|
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
self.irc.channels[channel].removeuser(source)
|
self.channels[channel].removeuser(source)
|
||||||
self.irc.users[source].channels -= set(channels)
|
self.users[source].channels -= set(channels)
|
||||||
|
|
||||||
self.irc.callHooks([source, 'PART', {'channels': channels, 'text': reason}])
|
self.callHooks([source, 'PART', {'channels': channels, 'text': reason}])
|
||||||
|
|
||||||
# Clear channels that are empty, or that we're parting.
|
# Clear channels that are empty, or that we're parting.
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
if (self.irc.pseudoclient and source == self.irc.pseudoclient.uid) or not self.irc.channels[channel].users:
|
if (self.pseudoclient and source == self.pseudoclient.uid) or not self.channels[channel].users:
|
||||||
del self.irc.channels[channel]
|
del self.channels[channel]
|
||||||
|
|
||||||
def handle_ping(self, source, command, args):
|
def handle_ping(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
Handles incoming PING requests.
|
Handles incoming PING requests.
|
||||||
"""
|
"""
|
||||||
self.irc.send('PONG :%s' % args[0], queue=False)
|
self.send('PONG :%s' % args[0], queue=False)
|
||||||
|
|
||||||
def handle_pong(self, source, command, args):
|
def handle_pong(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
Handles incoming PONG.
|
Handles incoming PONG.
|
||||||
"""
|
"""
|
||||||
if source == self.irc.uplink:
|
if source == self.uplink:
|
||||||
self.irc.lastping = time.time()
|
self.lastping = time.time()
|
||||||
|
|
||||||
def handle_privmsg(self, source, command, args):
|
def handle_privmsg(self, source, command, args):
|
||||||
"""Handles incoming PRIVMSG/NOTICE."""
|
"""Handles incoming PRIVMSG/NOTICE."""
|
||||||
@ -936,22 +936,22 @@ class ClientbotWrapperProtocol(IRCCommonProtocol):
|
|||||||
# <- :sender NOTICE somenick :afasfsa
|
# <- :sender NOTICE somenick :afasfsa
|
||||||
target = args[0]
|
target = args[0]
|
||||||
|
|
||||||
if self.irc.isInternalClient(source) or self.irc.isInternalServer(source):
|
if self.isInternalClient(source) or self.isInternalServer(source):
|
||||||
log.warning('(%s) Received %s to %s being routed the wrong way!', self.irc.name, command, target)
|
log.warning('(%s) Received %s to %s being routed the wrong way!', self.name, command, target)
|
||||||
return
|
return
|
||||||
|
|
||||||
# We use lowercase channels internally.
|
# We use lowercase channels internally.
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
target = self.irc.toLower(target)
|
target = self.toLower(target)
|
||||||
else:
|
else:
|
||||||
target = self.irc.nickToUid(target)
|
target = self.nickToUid(target)
|
||||||
if target:
|
if target:
|
||||||
return {'target': target, 'text': args[1]}
|
return {'target': target, 'text': args[1]}
|
||||||
handle_notice = handle_privmsg
|
handle_notice = handle_privmsg
|
||||||
|
|
||||||
def handle_quit(self, source, command, args):
|
def handle_quit(self, source, command, args):
|
||||||
"""Handles incoming QUITs."""
|
"""Handles incoming QUITs."""
|
||||||
if self.irc.pseudoclient and source == self.irc.pseudoclient.uid:
|
if self.pseudoclient and source == self.pseudoclient.uid:
|
||||||
# Someone faked a quit from us? We should abort.
|
# Someone faked a quit from us? We should abort.
|
||||||
raise ProtocolError("Received QUIT from uplink (%s)" % args[0])
|
raise ProtocolError("Received QUIT from uplink (%s)" % args[0])
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ class HybridProtocol(TS6Protocol):
|
|||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
||||||
cmodes = {
|
cmodes = {
|
||||||
@ -37,7 +37,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
'*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
|
'*A': 'beI', '*B': 'k', '*C': 'l', '*D': 'cimnprstCMORS'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.irc.cmodes = cmodes
|
self.cmodes = cmodes
|
||||||
|
|
||||||
umodes = {
|
umodes = {
|
||||||
'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'locops': 'l',
|
'oper': 'o', 'invisible': 'i', 'wallops': 'w', 'locops': 'l',
|
||||||
@ -52,13 +52,13 @@ class HybridProtocol(TS6Protocol):
|
|||||||
'*A': '', '*B': '', '*C': '', '*D': 'DFGHRSWabcdefgijklnopqrsuwxy'
|
'*A': '', '*B': '', '*C': '', '*D': 'DFGHRSWabcdefgijklnopqrsuwxy'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.irc.umodes = umodes
|
self.umodes = umodes
|
||||||
|
|
||||||
# halfops is mandatory on Hybrid
|
# halfops is mandatory on Hybrid
|
||||||
self.irc.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
||||||
f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))
|
f('PASS %s TS 6 %s' % (self.serverdata["sendpass"], self.sid))
|
||||||
|
|
||||||
# We request the following capabilities (for hybrid):
|
# We request the following capabilities (for hybrid):
|
||||||
|
|
||||||
@ -79,11 +79,11 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# EOB: Supports EOB (end of burst) command
|
# EOB: Supports EOB (end of burst) command
|
||||||
f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')
|
f('CAPAB :TBURST DLN KNOCK UNDLN UNKLN KLN ENCAP IE EX HOPS CHW SVS CLUSTER EOB QS')
|
||||||
|
|
||||||
f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
|
f('SERVER %s 0 :%s' % (self.serverdata["hostname"],
|
||||||
self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
# send endburst now
|
# send endburst now
|
||||||
self.irc.send(':%s EOB' % (self.irc.sid,))
|
self.send(':%s EOB' % (self.sid,))
|
||||||
|
|
||||||
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
def spawnClient(self, nick, ident='null', host='null', realhost=None, modes=set(),
|
||||||
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None,
|
||||||
@ -95,8 +95,8 @@ class HybridProtocol(TS6Protocol):
|
|||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -104,11 +104,11 @@ class HybridProtocol(TS6Protocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
self._send_with_prefix(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
self._send_with_prefix(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
"* :{realname}".format(ts=ts, host=host,
|
"* :{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
@ -125,28 +125,28 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# parv[4] = optional argument (services account, vhost)
|
# parv[4] = optional argument (services account, vhost)
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
|
|
||||||
ts = self.irc.users[target].ts
|
ts = self.users[target].ts
|
||||||
|
|
||||||
if field == 'HOST':
|
if field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
# On Hybrid, it appears that host changing is actually just forcing umode
|
# On Hybrid, it appears that host changing is actually just forcing umode
|
||||||
# "+x <hostname>" on the target. -GLolol
|
# "+x <hostname>" on the target. -GLolol
|
||||||
self._send_with_prefix(self.irc.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
|
self._send_with_prefix(self.sid, 'SVSMODE %s %s +x %s' % (target, ts, text))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)
|
raise NotImplementedError("Changing field %r of a client is unsupported by this protocol." % field)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topicBurst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.isInternalServer(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
|
|
||||||
ts = self.irc.channels[target].ts
|
ts = self.channels[target].ts
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
|
self._send_with_prefix(numeric, 'TBURST %s %s %s %s :%s' % (ts, target, int(time.time()), servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
# command handlers
|
# command handlers
|
||||||
|
|
||||||
@ -154,13 +154,13 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# We only get a list of keywords here. Hybrid obviously assumes that
|
# We only get a list of keywords here. Hybrid obviously assumes that
|
||||||
# we know what modes it supports (indeed, this is a standard list).
|
# we know what modes it supports (indeed, this is a standard list).
|
||||||
# <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
|
# <- CAPAB :UNDLN UNKLN KLN TBURST KNOCK ENCAP DLN IE EX HOPS CHW SVS CLUSTER EOB QS
|
||||||
self.irc.caps = caps = args[0].split()
|
self.caps = caps = args[0].split()
|
||||||
for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
|
for required_cap in ('EX', 'IE', 'SVS', 'EOB', 'HOPS', 'QS', 'TBURST', 'SVS'):
|
||||||
if required_cap not in caps:
|
if required_cap not in caps:
|
||||||
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
||||||
|
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
def handle_uid(self, numeric, command, args):
|
def handle_uid(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -174,39 +174,39 @@ class HybridProtocol(TS6Protocol):
|
|||||||
if account == '*':
|
if account == '*':
|
||||||
account = None
|
account = None
|
||||||
log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
|
log.debug('(%s) handle_uid: got args nick=%s ts=%s uid=%s ident=%s '
|
||||||
'host=%s realname=%s ip=%s', self.irc.name, nick, ts, uid,
|
'host=%s realname=%s ip=%s', self.name, nick, ts, uid,
|
||||||
ident, host, realname, ip)
|
ident, host, realname, ip)
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, host, ip)
|
self.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, host, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [modes])
|
parsedmodes = self.parseModes(uid, [modes])
|
||||||
log.debug('(%s) handle_uid: Applying modes %s for %s', self.irc.name, parsedmodes, uid)
|
log.debug('(%s) handle_uid: Applying modes %s for %s', self.name, parsedmodes, uid)
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.applyModes(uid, parsedmodes)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Call the OPERED UP hook if +o is being added to the mode list.
|
# Call the OPERED UP hook if +o is being added to the mode list.
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC_Operator'}])
|
||||||
|
|
||||||
# Set the account name if present
|
# Set the account name if present
|
||||||
if account:
|
if account:
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realname': realname, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_tburst(self, numeric, command, args):
|
def handle_tburst(self, numeric, command, args):
|
||||||
"""Handles incoming topic burst (TBURST) commands."""
|
"""Handles incoming topic burst (TBURST) commands."""
|
||||||
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
# <- :0UY TBURST 1459308205 #testchan 1459309379 dan!~d@localhost :sdf
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
setter = args[3]
|
setter = args[3]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_eob(self, numeric, command, args):
|
def handle_eob(self, numeric, command, args):
|
||||||
log.debug('(%s) end of burst received', self.irc.name)
|
log.debug('(%s) end of burst received', self.name)
|
||||||
if not self.has_eob: # Only call ENDBURST hooks if we haven't already.
|
if not self.has_eob: # Only call ENDBURST hooks if we haven't already.
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
target = args[0]
|
target = args[0]
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
parsedmodes = self.irc.parseModes(target, modes)
|
parsedmodes = self.parseModes(target, modes)
|
||||||
|
|
||||||
for modepair in parsedmodes:
|
for modepair in parsedmodes:
|
||||||
if modepair[0] == '+d':
|
if modepair[0] == '+d':
|
||||||
@ -241,7 +241,7 @@ class HybridProtocol(TS6Protocol):
|
|||||||
|
|
||||||
# Send the login hook, and remove this mode from the mode
|
# Send the login hook, and remove this mode from the mode
|
||||||
# list, as it shouldn't be parsed literally.
|
# list, as it shouldn't be parsed literally.
|
||||||
self.irc.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
parsedmodes.remove(modepair)
|
parsedmodes.remove(modepair)
|
||||||
|
|
||||||
elif modepair[0] == '+x':
|
elif modepair[0] == '+x':
|
||||||
@ -250,16 +250,16 @@ class HybridProtocol(TS6Protocol):
|
|||||||
# to some.host, for example.
|
# to some.host, for example.
|
||||||
host = args[-1]
|
host = args[-1]
|
||||||
|
|
||||||
self.irc.users[target].host = host
|
self.users[target].host = host
|
||||||
|
|
||||||
# Propagate the hostmask change as a hook.
|
# Propagate the hostmask change as a hook.
|
||||||
self.irc.callHooks([numeric, 'CHGHOST',
|
self.callHooks([numeric, 'CHGHOST',
|
||||||
{'target': target, 'newhost': host}])
|
{'target': target, 'newhost': host}])
|
||||||
|
|
||||||
parsedmodes.remove(modepair)
|
parsedmodes.remove(modepair)
|
||||||
|
|
||||||
if parsedmodes:
|
if parsedmodes:
|
||||||
self.irc.applyModes(target, parsedmodes)
|
self.applyModes(target, parsedmodes)
|
||||||
|
|
||||||
return {'target': target, 'modes': parsedmodes}
|
return {'target': target, 'modes': parsedmodes}
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
|
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -52,12 +52,12 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
|
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
self._send_with_prefix(server, "UID {uid} {ts} {nick} {realhost} {host} {ident} {ip}"
|
self._send_with_prefix(server, "UID {uid} {ts} {nick} {realhost} {host} {ident} {ip}"
|
||||||
" {ts} {modes} + :{realname}".format(ts=ts, host=host,
|
" {ts} {modes} + :{realname}".format(ts=ts, host=host,
|
||||||
@ -73,20 +73,20 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# InspIRCd doesn't distinguish between burst joins and regular joins,
|
# InspIRCd doesn't distinguish between burst joins and regular joins,
|
||||||
# so what we're actually doing here is sending FJOIN from the server,
|
# so what we're actually doing here is sending FJOIN from the server,
|
||||||
# on behalf of the clients that are joining.
|
# on behalf of the clients that are joining.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
|
|
||||||
server = self.irc.getServer(client)
|
server = self.getServer(client)
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
# Strip out list-modes, they shouldn't be ever sent in FJOIN.
|
||||||
modes = [m for m in self.irc.channels[channel].modes if m[0] not in self.irc.cmodes['*A']]
|
modes = [m for m in self.channels[channel].modes if m[0] not in self.cmodes['*A']]
|
||||||
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
|
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :,{uid}".format(
|
||||||
ts=self.irc.channels[channel].ts, uid=client, channel=channel,
|
ts=self.channels[channel].ts, uid=client, channel=channel,
|
||||||
modes=self.irc.joinModes(modes)))
|
modes=self.joinModes(modes)))
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -97,29 +97,29 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
log.debug('(%s) sjoin: got %r for users', self.irc.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
|
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Strip out list-modes, they shouldn't ever be sent in FJOIN (protocol rules).
|
# Strip out list-modes, they shouldn't ever be sent in FJOIN (protocol rules).
|
||||||
modes = modes or self.irc.channels[channel].modes
|
modes = modes or self.channels[channel].modes
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
|
|
||||||
banmodes = []
|
banmodes = []
|
||||||
regularmodes = []
|
regularmodes = []
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
modechar = mode[0][-1]
|
modechar = mode[0][-1]
|
||||||
if modechar in self.irc.cmodes['*A']:
|
if modechar in self.cmodes['*A']:
|
||||||
# Track bans separately (they are sent as a normal FMODE instead of in FJOIN.
|
# Track bans separately (they are sent as a normal FMODE instead of in FJOIN.
|
||||||
# However, don't reset bans that have already been set.
|
# However, don't reset bans that have already been set.
|
||||||
if (modechar, mode[1]) not in self.irc.channels[channel].modes:
|
if (modechar, mode[1]) not in self.channels[channel].modes:
|
||||||
banmodes.append(mode)
|
banmodes.append(mode)
|
||||||
else:
|
else:
|
||||||
regularmodes.append(mode)
|
regularmodes.append(mode)
|
||||||
@ -137,21 +137,21 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
for m in prefixes:
|
for m in prefixes:
|
||||||
changedmodes.add(('+%s' % m, user))
|
changedmodes.add(('+%s' % m, user))
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
|
|
||||||
namelist = ' '.join(namelist)
|
namelist = ' '.join(namelist)
|
||||||
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :{users}".format(
|
self._send_with_prefix(server, "FJOIN {channel} {ts} {modes} :{users}".format(
|
||||||
ts=ts, users=namelist, channel=channel,
|
ts=ts, users=namelist, channel=channel,
|
||||||
modes=self.irc.joinModes(modes)))
|
modes=self.joinModes(modes)))
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
if banmodes:
|
if banmodes:
|
||||||
# Burst ban modes if there are any.
|
# Burst ban modes if there are any.
|
||||||
# <- :1ML FMODE #test 1461201525 +bb *!*@bad.user *!*@rly.bad.user
|
# <- :1ML FMODE #test 1461201525 +bb *!*@bad.user *!*@rly.bad.user
|
||||||
self._send_with_prefix(server, "FMODE {channel} {ts} {modes} ".format(
|
self._send_with_prefix(server, "FMODE {channel} {ts} {modes} ".format(
|
||||||
ts=ts, channel=channel, modes=self.irc.joinModes(banmodes)))
|
ts=ts, channel=channel, modes=self.joinModes(banmodes)))
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
@ -163,19 +163,19 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
recognize ANY non-burst oper ups.
|
recognize ANY non-burst oper ups.
|
||||||
|
|
||||||
Plugins don't have to call this function themselves, but they can
|
Plugins don't have to call this function themselves, but they can
|
||||||
set the opertype attribute of an IrcUser object (in self.irc.users),
|
set the opertype attribute of an IrcUser object (in self.users),
|
||||||
and the change will be reflected here."""
|
and the change will be reflected here."""
|
||||||
userobj = self.irc.users[target]
|
userobj = self.users[target]
|
||||||
try:
|
try:
|
||||||
otype = opertype or userobj.opertype or 'IRC Operator'
|
otype = opertype or userobj.opertype or 'IRC Operator'
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
log.debug('(%s) opertype field for %s (%s) isn\'t filled yet!',
|
log.debug('(%s) opertype field for %s (%s) isn\'t filled yet!',
|
||||||
self.irc.name, target, userobj.nick)
|
self.name, target, userobj.nick)
|
||||||
# whatever, this is non-standard anyways.
|
# whatever, this is non-standard anyways.
|
||||||
otype = 'IRC Operator'
|
otype = 'IRC Operator'
|
||||||
assert otype, "Tried to send an empty OPERTYPE!"
|
assert otype, "Tried to send an empty OPERTYPE!"
|
||||||
log.debug('(%s) Sending OPERTYPE from %s to oper them up.',
|
log.debug('(%s) Sending OPERTYPE from %s to oper them up.',
|
||||||
self.irc.name, target)
|
self.name, target)
|
||||||
userobj.opertype = otype
|
userobj.opertype = otype
|
||||||
|
|
||||||
# InspIRCd 2.x uses _ in OPERTYPE to denote spaces, while InspIRCd 3.x does not. This is not
|
# InspIRCd 2.x uses _ in OPERTYPE to denote spaces, while InspIRCd 3.x does not. This is not
|
||||||
@ -194,64 +194,64 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# -> :9PYAAAAAA FMODE #pylink 1433653951 +os 9PYAAAAAA
|
# -> :9PYAAAAAA FMODE #pylink 1433653951 +os 9PYAAAAAA
|
||||||
# -> :9PYAAAAAA MODE 9PYAAAAAA -i+w
|
# -> :9PYAAAAAA MODE 9PYAAAAAA -i+w
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
log.debug('(%s) inspircd._send_with_prefixModes: received %r for mode list', self.irc.name, modes)
|
log.debug('(%s) inspircd._send_with_prefixModes: received %r for mode list', self.name, modes)
|
||||||
if ('+o', None) in modes and not utils.isChannel(target):
|
if ('+o', None) in modes and not utils.isChannel(target):
|
||||||
# https://github.com/inspircd/inspircd/blob/master/src/modules/m_spanningtree/opertype.cpp#L26-L28
|
# https://github.com/inspircd/inspircd/blob/master/src/modules/m_spanningtree/opertype.cpp#L26-L28
|
||||||
# Servers need a special command to set umode +o on people.
|
# Servers need a special command to set umode +o on people.
|
||||||
self._operUp(target)
|
self._operUp(target)
|
||||||
self.irc.applyModes(target, modes)
|
self.applyModes(target, modes)
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.joinModes(modes)
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.toLower(target)].ts
|
||||||
self._send_with_prefix(numeric, 'FMODE %s %s %s' % (target, ts, joinedmodes))
|
self._send_with_prefix(numeric, 'FMODE %s %s %s' % (target, ts, joinedmodes))
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
||||||
|
|
||||||
def kill(self, numeric, target, reason):
|
def kill(self, numeric, target, reason):
|
||||||
"""Sends a kill from a PyLink client/server."""
|
"""Sends a kill from a PyLink client/server."""
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# InspIRCd will show the raw kill message sent from our server as the quit message.
|
# InspIRCd will show the raw kill message sent from our server as the quit message.
|
||||||
# So, make the kill look actually like a kill instead of someone quitting with
|
# So, make the kill look actually like a kill instead of someone quitting with
|
||||||
# an arbitrary message.
|
# an arbitrary message.
|
||||||
if numeric in self.irc.servers:
|
if numeric in self.servers:
|
||||||
sourcenick = self.irc.servers[numeric].name
|
sourcenick = self.servers[numeric].name
|
||||||
else:
|
else:
|
||||||
sourcenick = self.irc.users[numeric].nick
|
sourcenick = self.users[numeric].nick
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'KILL %s :Killed (%s (%s))' % (target, sourcenick, reason))
|
self._send_with_prefix(numeric, 'KILL %s :Killed (%s (%s))' % (target, sourcenick, reason))
|
||||||
|
|
||||||
# We only need to call removeClient here if the target is one of our
|
# We only need to call removeClient here if the target is one of our
|
||||||
# clients, since any remote servers will send a QUIT from
|
# clients, since any remote servers will send a QUIT from
|
||||||
# their target if the command succeeds.
|
# their target if the command succeeds.
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
self.removeClient(target)
|
self.removeClient(target)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topicBurst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.isInternalServer(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
ts = int(time.time())
|
ts = int(time.time())
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
self._send_with_prefix(numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
|
self._send_with_prefix(numeric, 'FTOPIC %s %s %s :%s' % (target, ts, servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
||||||
|
|
||||||
def knock(self, numeric, target, text):
|
def knock(self, numeric, target, text):
|
||||||
"""Sends a KNOCK from a PyLink client."""
|
"""Sends a KNOCK from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(numeric, 'ENCAP * KNOCK %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'ENCAP * KNOCK %s :%s' % (target, text))
|
||||||
|
|
||||||
@ -263,56 +263,56 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
"unsupported by this protocol." % field)
|
"unsupported by this protocol." % field)
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
# It is one of our clients, use FIDENT/HOST/NAME.
|
# It is one of our clients, use FIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send_with_prefix(target, 'FIDENT %s' % text)
|
self._send_with_prefix(target, 'FIDENT %s' % text)
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send_with_prefix(target, 'FHOST %s' % text)
|
self._send_with_prefix(target, 'FHOST %s' % text)
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send_with_prefix(target, 'FNAME :%s' % text)
|
self._send_with_prefix(target, 'FNAME :%s' % text)
|
||||||
else:
|
else:
|
||||||
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
if 'm_chgident.so' not in self.modsupport:
|
if 'm_chgident.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change ident of %s to %r: load m_chgident.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change ident of %s to %r: load m_chgident.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGIDENT %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGIDENT %s %s' % (target, text))
|
||||||
|
|
||||||
# Send hook payloads for other plugins to listen to.
|
# Send hook payloads for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGIDENT',
|
self.callHooks([self.sid, 'CHGIDENT',
|
||||||
{'target': target, 'newident': text}])
|
{'target': target, 'newident': text}])
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
if 'm_chghost.so' not in self.modsupport:
|
if 'm_chghost.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change host of %s to %r: load m_chghost.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change host of %s to %r: load m_chghost.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGHOST %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s %s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.callHooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
|
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
if 'm_chgname.so' not in self.modsupport:
|
if 'm_chgname.so' not in self.modsupport:
|
||||||
log.warning('(%s) Failed to change real name of %s to %r: load m_chgname.so!', self.irc.name, target, text)
|
log.warning('(%s) Failed to change real name of %s to %r: load m_chgname.so!', self.name, target, text)
|
||||||
return
|
return
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGNAME %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGNAME %s :%s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGNAME',
|
self.callHooks([self.sid, 'CHGNAME',
|
||||||
{'target': target, 'newgecos': text}])
|
{'target': target, 'newgecos': text}])
|
||||||
|
|
||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
target = target or self.irc.uplink
|
target = target or self.uplink
|
||||||
if not (target is None or source is None):
|
if not (target is None or source is None):
|
||||||
self._send_with_prefix(source, 'PING %s %s' % (source, target))
|
self._send_with_prefix(source, 'PING %s %s' % (source, target))
|
||||||
|
|
||||||
@ -327,7 +327,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
|
# :<sid> NUM <numeric source sid> <target uuid> <3 digit number> <params>
|
||||||
# Take this into consideration if we ever target InspIRCd 2.2, even though m_spanningtree
|
# Take this into consideration if we ever target InspIRCd 2.2, even though m_spanningtree
|
||||||
# does provide backwards compatibility for commands like this. -GLolol
|
# does provide backwards compatibility for commands like this. -GLolol
|
||||||
self._send_with_prefix(self.irc.sid, 'PUSH %s ::%s %s %s %s' % (target, source, numeric, target, text))
|
self._send_with_prefix(self.sid, 'PUSH %s ::%s %s %s %s' % (target, source, numeric, target, text))
|
||||||
|
|
||||||
def away(self, source, text):
|
def away(self, source, text):
|
||||||
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
||||||
@ -336,7 +336,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
self._send_with_prefix(source, 'AWAY %s :%s' % (int(time.time()), text))
|
self._send_with_prefix(source, 'AWAY %s :%s' % (int(time.time()), text))
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(source, 'AWAY')
|
self._send_with_prefix(source, 'AWAY')
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
||||||
"""
|
"""
|
||||||
@ -351,30 +351,30 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
and prevent connections from filling up the snomasks too much.
|
and prevent connections from filling up the snomasks too much.
|
||||||
"""
|
"""
|
||||||
# -> :0AL SERVER test.server * 1 0AM :some silly pseudoserver
|
# -> :0AL SERVER test.server * 1 0AM :some silly pseudoserver
|
||||||
uplink = uplink or self.irc.sid
|
uplink = uplink or self.sid
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
# "desc" defaults to the configured server description.
|
# "desc" defaults to the configured server description.
|
||||||
desc = desc or self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = desc or self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
if sid is None: # No sid given; generate one!
|
if sid is None: # No sid given; generate one!
|
||||||
sid = self.sidgen.next_sid()
|
sid = self.sidgen.next_sid()
|
||||||
assert len(sid) == 3, "Incorrect SID length"
|
assert len(sid) == 3, "Incorrect SID length"
|
||||||
if sid in self.irc.servers:
|
if sid in self.servers:
|
||||||
raise ValueError('A server with SID %r already exists!' % sid)
|
raise ValueError('A server with SID %r already exists!' % sid)
|
||||||
for server in self.irc.servers.values():
|
for server in self.servers.values():
|
||||||
if name == server.name:
|
if name == server.name:
|
||||||
raise ValueError('A server named %r already exists!' % name)
|
raise ValueError('A server named %r already exists!' % name)
|
||||||
if not self.irc.isInternalServer(uplink):
|
if not self.isInternalServer(uplink):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
||||||
if not utils.isServerName(name):
|
if not utils.isServerName(name):
|
||||||
raise ValueError('Invalid server name %r' % name)
|
raise ValueError('Invalid server name %r' % name)
|
||||||
self._send_with_prefix(uplink, 'SERVER %s * 1 %s :%s' % (name, sid, desc))
|
self._send_with_prefix(uplink, 'SERVER %s * 1 %s :%s' % (name, sid, desc))
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
self.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
||||||
|
|
||||||
def endburstf():
|
def endburstf():
|
||||||
# Delay ENDBURST by X seconds if requested.
|
# Delay ENDBURST by X seconds if requested.
|
||||||
if self.irc.aborted.wait(endburst_delay):
|
if self.aborted.wait(endburst_delay):
|
||||||
# We managed to catch the abort flag before sending ENDBURST, so break
|
# We managed to catch the abort flag before sending ENDBURST, so break
|
||||||
log.debug('(%s) stopping endburstf() for %s as aborted was set', self.irc.name, sid)
|
log.debug('(%s) stopping endburstf() for %s as aborted was set', self.name, sid)
|
||||||
return
|
return
|
||||||
self._send_with_prefix(sid, 'ENDBURST')
|
self._send_with_prefix(sid, 'ENDBURST')
|
||||||
|
|
||||||
@ -394,25 +394,25 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
|
|
||||||
# Track the modules supported by the uplink.
|
# Track the modules supported by the uplink.
|
||||||
self.modsupport = []
|
self.modsupport = []
|
||||||
|
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
f('CAPAB START %s' % self.proto_ver)
|
f('CAPAB START %s' % self.proto_ver)
|
||||||
f('CAPAB CAPABILITIES :PROTOCOL=%s' % self.proto_ver)
|
f('CAPAB CAPABILITIES :PROTOCOL=%s' % self.proto_ver)
|
||||||
f('CAPAB END')
|
f('CAPAB END')
|
||||||
|
|
||||||
host = self.irc.serverdata["hostname"]
|
host = self.serverdata["hostname"]
|
||||||
f('SERVER {host} {Pass} 0 {sid} :{sdesc}'.format(host=host,
|
f('SERVER {host} {Pass} 0 {sid} :{sdesc}'.format(host=host,
|
||||||
Pass=self.irc.serverdata["sendpass"], sid=self.irc.sid,
|
Pass=self.serverdata["sendpass"], sid=self.sid,
|
||||||
sdesc=self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
sdesc=self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
self._send_with_prefix(self.irc.sid, 'BURST %s' % ts)
|
self._send_with_prefix(self.sid, 'BURST %s' % ts)
|
||||||
# InspIRCd sends VERSION data on link, instead of whenever requested by a client.
|
# InspIRCd sends VERSION data on link, instead of whenever requested by a client.
|
||||||
self._send_with_prefix(self.irc.sid, 'VERSION :%s' % self.irc.version())
|
self._send_with_prefix(self.sid, 'VERSION :%s' % self.version())
|
||||||
self._send_with_prefix(self.irc.sid, 'ENDBURST')
|
self._send_with_prefix(self.sid, 'ENDBURST')
|
||||||
|
|
||||||
def handle_capab(self, source, command, args):
|
def handle_capab(self, source, command, args):
|
||||||
"""
|
"""
|
||||||
@ -453,7 +453,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
name = 'owner'
|
name = 'owner'
|
||||||
|
|
||||||
# We don't care about mode prefixes; just the mode char.
|
# We don't care about mode prefixes; just the mode char.
|
||||||
self.irc.cmodes[name] = char[-1]
|
self.cmodes[name] = char[-1]
|
||||||
|
|
||||||
|
|
||||||
elif args[0] == 'USERMODES':
|
elif args[0] == 'USERMODES':
|
||||||
@ -467,7 +467,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
name, char = modepair.split('=')
|
name, char = modepair.split('=')
|
||||||
# Strip u_ prefixes to be consistent with other protocols.
|
# Strip u_ prefixes to be consistent with other protocols.
|
||||||
name = name.lstrip('u_')
|
name = name.lstrip('u_')
|
||||||
self.irc.umodes[name] = char
|
self.umodes[name] = char
|
||||||
|
|
||||||
elif args[0] == 'CAPABILITIES':
|
elif args[0] == 'CAPABILITIES':
|
||||||
# <- CAPAB CAPABILITIES :NICKMAX=21 CHANMAX=64 MAXMODES=20
|
# <- CAPAB CAPABILITIES :NICKMAX=21 CHANMAX=64 MAXMODES=20
|
||||||
@ -478,7 +478,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# First, turn the arguments into a dict
|
# First, turn the arguments into a dict
|
||||||
caps = self.parseCapabilities(args[-1])
|
caps = self.parseCapabilities(args[-1])
|
||||||
log.debug("(%s) capabilities list: %s", self.irc.name, caps)
|
log.debug("(%s) capabilities list: %s", self.name, caps)
|
||||||
|
|
||||||
# Check the protocol version
|
# Check the protocol version
|
||||||
self.remote_proto_ver = protocol_version = int(caps['PROTOCOL'])
|
self.remote_proto_ver = protocol_version = int(caps['PROTOCOL'])
|
||||||
@ -491,30 +491,30 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
elif protocol_version > self.max_proto_ver:
|
elif protocol_version > self.max_proto_ver:
|
||||||
log.warning("(%s) PyLink support for InspIRCd 2.2+ is experimental, "
|
log.warning("(%s) PyLink support for InspIRCd 2.2+ is experimental, "
|
||||||
"and should not be relied upon for anything important.",
|
"and should not be relied upon for anything important.",
|
||||||
self.irc.name)
|
self.name)
|
||||||
|
|
||||||
# Store the max nick and channel lengths
|
# Store the max nick and channel lengths
|
||||||
self.irc.maxnicklen = int(caps['NICKMAX'])
|
self.maxnicklen = int(caps['NICKMAX'])
|
||||||
self.irc.maxchanlen = int(caps['CHANMAX'])
|
self.maxchanlen = int(caps['CHANMAX'])
|
||||||
|
|
||||||
# Modes are divided into A, B, C, and D classes
|
# Modes are divided into A, B, C, and D classes
|
||||||
# See http://www.irc.org/tech_docs/005.html
|
# See http://www.irc.org/tech_docs/005.html
|
||||||
|
|
||||||
# FIXME: Find a neater way to assign/store this.
|
# FIXME: Find a neater way to assign/store this.
|
||||||
self.irc.cmodes['*A'], self.irc.cmodes['*B'], self.irc.cmodes['*C'], self.irc.cmodes['*D'] \
|
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] \
|
||||||
= caps['CHANMODES'].split(',')
|
= caps['CHANMODES'].split(',')
|
||||||
self.irc.umodes['*A'], self.irc.umodes['*B'], self.irc.umodes['*C'], self.irc.umodes['*D'] \
|
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] \
|
||||||
= caps['USERMODES'].split(',')
|
= caps['USERMODES'].split(',')
|
||||||
|
|
||||||
# Separate the prefixes field (e.g. "(Yqaohv)!~&@%+") into a
|
# Separate the prefixes field (e.g. "(Yqaohv)!~&@%+") into a
|
||||||
# dict mapping mode characters to mode prefixes.
|
# dict mapping mode characters to mode prefixes.
|
||||||
self.irc.prefixmodes = self.parsePrefixes(caps['PREFIX'])
|
self.prefixmodes = self.parsePrefixes(caps['PREFIX'])
|
||||||
log.debug('(%s) self.irc.prefixmodes set to %r', self.irc.name,
|
log.debug('(%s) self.prefixmodes set to %r', self.name,
|
||||||
self.irc.prefixmodes)
|
self.prefixmodes)
|
||||||
|
|
||||||
# Finally, set the irc.connected (protocol negotiation complete)
|
# Finally, set the irc.connected (protocol negotiation complete)
|
||||||
# state to True.
|
# state to True.
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
elif args[0] == 'MODSUPPORT':
|
elif args[0] == 'MODSUPPORT':
|
||||||
# <- CAPAB MODSUPPORT :m_alltime.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_fullversion.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_opmoderated.so m_sajoin.so m_sanick.so m_sapart.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
|
# <- CAPAB MODSUPPORT :m_alltime.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_fullversion.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_opmoderated.so m_sajoin.so m_sanick.so m_sapart.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
|
||||||
self.modsupport = args[-1].split()
|
self.modsupport = args[-1].split()
|
||||||
@ -523,19 +523,19 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
"""Handles incoming PING commands, so we don't time out."""
|
"""Handles incoming PING commands, so we don't time out."""
|
||||||
# <- :70M PING 70M 0AL
|
# <- :70M PING 70M 0AL
|
||||||
# -> :0AL PONG 0AL 70M
|
# -> :0AL PONG 0AL 70M
|
||||||
if self.irc.isInternalServer(args[1]):
|
if self.isInternalServer(args[1]):
|
||||||
self._send_with_prefix(args[1], 'PONG %s %s' % (args[1], source), queue=False)
|
self._send_with_prefix(args[1], 'PONG %s %s' % (args[1], source), queue=False)
|
||||||
|
|
||||||
def handle_fjoin(self, servernumeric, command, args):
|
def handle_fjoin(self, servernumeric, command, args):
|
||||||
"""Handles incoming FJOIN commands (InspIRCd equivalent of JOIN/SJOIN)."""
|
"""Handles incoming FJOIN commands (InspIRCd equivalent of JOIN/SJOIN)."""
|
||||||
# :70M FJOIN #chat 1423790411 +AFPfjnt 6:5 7:5 9:5 :o,1SRAABIT4 v,1IOAAF53R <...>
|
# :70M FJOIN #chat 1423790411 +AFPfjnt 6:5 7:5 9:5 :o,1SRAABIT4 v,1IOAAF53R <...>
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
# InspIRCd sends each channel's users in the form of 'modeprefix(es),UID'
|
# InspIRCd sends each channel's users in the form of 'modeprefix(es),UID'
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
modestring = args[2:-1] or args[2]
|
modestring = args[2:-1] or args[2]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parseModes(channel, modestring)
|
||||||
namelist = []
|
namelist = []
|
||||||
|
|
||||||
# Keep track of other modes that are added due to prefix modes being joined too.
|
# Keep track of other modes that are added due to prefix modes being joined too.
|
||||||
@ -545,18 +545,18 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
modeprefix, user = user.split(',', 1)
|
modeprefix, user = user.split(',', 1)
|
||||||
|
|
||||||
# Don't crash when we get an invalid UID.
|
# Don't crash when we get an invalid UID.
|
||||||
if user not in self.irc.users:
|
if user not in self.users:
|
||||||
log.debug('(%s) handle_fjoin: tried to introduce user %s not in our user list, ignoring...',
|
log.debug('(%s) handle_fjoin: tried to introduce user %s not in our user list, ignoring...',
|
||||||
self.irc.name, user)
|
self.name, user)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only save mode changes if the remote has lower TS than us.
|
# Only save mode changes if the remote has lower TS than us.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in modeprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in modeprefix}
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
# Statekeeping with timestamps. Note: some service packages (Anope 1.8) send a trailing
|
# Statekeeping with timestamps. Note: some service packages (Anope 1.8) send a trailing
|
||||||
# 'd' after the timestamp, which we should strip out to prevent int() from erroring.
|
# 'd' after the timestamp, which we should strip out to prevent int() from erroring.
|
||||||
@ -565,7 +565,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK
|
# <- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK
|
||||||
their_ts = int(''.join(char for char in args[1] if char.isdigit()))
|
their_ts = int(''.join(char for char in args[1] if char.isdigit()))
|
||||||
|
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
||||||
@ -577,35 +577,35 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
uid, ts, nick, realhost, host, ident, ip = args[0:7]
|
uid, ts, nick, realhost, host, ident, ip = args[0:7]
|
||||||
self.check_nick_collision(nick)
|
self.check_nick_collision(nick)
|
||||||
realname = args[-1]
|
realname = args[-1]
|
||||||
self.irc.users[uid] = userobj = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = userobj = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [args[8], args[9]])
|
parsedmodes = self.parseModes(uid, [args[8], args[9]])
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.applyModes(uid, parsedmodes)
|
||||||
|
|
||||||
if (self.irc.umodes.get('servprotect'), None) in userobj.modes:
|
if (self.umodes.get('servprotect'), None) in userobj.modes:
|
||||||
# Services are usually given a "Network Service" WHOIS, so
|
# Services are usually given a "Network Service" WHOIS, so
|
||||||
# set that as the opertype.
|
# set that as the opertype.
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'Network Service'}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': 'Network Service'}])
|
||||||
|
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
"""Handles incoming SERVER commands (introduction of servers)."""
|
"""Handles incoming SERVER commands (introduction of servers)."""
|
||||||
|
|
||||||
# Initial SERVER command on connect.
|
# Initial SERVER command on connect.
|
||||||
if self.irc.uplink is None:
|
if self.uplink is None:
|
||||||
# <- SERVER whatever.net abcdefgh 0 10X :some server description
|
# <- SERVER whatever.net abcdefgh 0 10X :some server description
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
numeric = args[3]
|
numeric = args[3]
|
||||||
|
|
||||||
if args[1] != self.irc.serverdata['recvpass']:
|
if args[1] != self.serverdata['recvpass']:
|
||||||
# Check if recvpass is correct
|
# Check if recvpass is correct
|
||||||
raise ProtocolError('Error: recvpass from uplink server %s does not match configuration!' % servername)
|
raise ProtocolError('Error: recvpass from uplink server %s does not match configuration!' % servername)
|
||||||
|
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[numeric] = IrcServer(None, servername, desc=sdesc)
|
self.servers[numeric] = IrcServer(None, servername, desc=sdesc)
|
||||||
self.irc.uplink = numeric
|
self.uplink = numeric
|
||||||
return
|
return
|
||||||
|
|
||||||
# Other server introductions.
|
# Other server introductions.
|
||||||
@ -613,18 +613,18 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sid = args[3]
|
sid = args[3]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
||||||
|
|
||||||
return {'name': servername, 'sid': args[3], 'text': sdesc}
|
return {'name': servername, 'sid': args[3], 'text': sdesc}
|
||||||
|
|
||||||
def handle_fmode(self, numeric, command, args):
|
def handle_fmode(self, numeric, command, args):
|
||||||
"""Handles the FMODE command, used for channel mode changes."""
|
"""Handles the FMODE command, used for channel mode changes."""
|
||||||
# <- :70MAAAAAA FMODE #chat 1433653462 +hhT 70MAAAAAA 70MAAAAAD
|
# <- :70MAAAAAA FMODE #chat 1433653462 +hhT 70MAAAAAA 70MAAAAAD
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
changedmodes = self.irc.parseModes(channel, modes)
|
changedmodes = self.parseModes(channel, modes)
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.applyModes(channel, changedmodes)
|
||||||
ts = int(args[1])
|
ts = int(args[1])
|
||||||
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
||||||
'channeldata': oldobj}
|
'channeldata': oldobj}
|
||||||
@ -636,8 +636,8 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
||||||
target = args[0]
|
target = args[0]
|
||||||
modestrings = args[1:]
|
modestrings = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modestrings)
|
changedmodes = self.parseModes(target, modestrings)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.applyModes(target, changedmodes)
|
||||||
return {'target': target, 'modes': changedmodes}
|
return {'target': target, 'modes': changedmodes}
|
||||||
|
|
||||||
def handle_idle(self, numeric, command, args):
|
def handle_idle(self, numeric, command, args):
|
||||||
@ -662,12 +662,12 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
def handle_ftopic(self, numeric, command, args):
|
def handle_ftopic(self, numeric, command, args):
|
||||||
"""Handles incoming FTOPIC (sets topic on burst)."""
|
"""Handles incoming FTOPIC (sets topic on burst)."""
|
||||||
# <- :70M FTOPIC #channel 1434510754 GLo|o|!GLolol@escape.the.dreamland.ca :Some channel topic
|
# <- :70M FTOPIC #channel 1434510754 GLo|o|!GLolol@escape.the.dreamland.ca :Some channel topic
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
setter = args[2]
|
setter = args[2]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
# SVSTOPIC is used by InspIRCd module m_topiclock - its arguments are the same as FTOPIC
|
# SVSTOPIC is used by InspIRCd module m_topiclock - its arguments are the same as FTOPIC
|
||||||
@ -677,14 +677,14 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
"""Handles incoming INVITEs."""
|
"""Handles incoming INVITEs."""
|
||||||
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 0
|
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 0
|
||||||
target = args[0]
|
target = args[0]
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
# We don't actually need to process this; just send the hook so plugins can use it
|
# We don't actually need to process this; just send the hook so plugins can use it
|
||||||
return {'target': target, 'channel': channel}
|
return {'target': target, 'channel': channel}
|
||||||
|
|
||||||
def handle_knock(self, numeric, command, args):
|
def handle_knock(self, numeric, command, args):
|
||||||
"""Handles channel KNOCKs."""
|
"""Handles channel KNOCKs."""
|
||||||
# <- :70MAAAAAA ENCAP * KNOCK #blah :abcdefg
|
# <- :70MAAAAAA ENCAP * KNOCK #blah :abcdefg
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
text = args[1]
|
text = args[1]
|
||||||
return {'channel': channel, 'text': text}
|
return {'channel': channel, 'text': text}
|
||||||
|
|
||||||
@ -701,29 +701,29 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Set umode +o on the target.
|
# Set umode +o on the target.
|
||||||
omode = [('+o', None)]
|
omode = [('+o', None)]
|
||||||
self.irc.applyModes(target, omode)
|
self.applyModes(target, omode)
|
||||||
|
|
||||||
# Call the CLIENT_OPERED hook that protocols use. The MODE hook
|
# Call the CLIENT_OPERED hook that protocols use. The MODE hook
|
||||||
# payload is returned below.
|
# payload is returned below.
|
||||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': opertype}])
|
self.callHooks([target, 'CLIENT_OPERED', {'text': opertype}])
|
||||||
return {'target': target, 'modes': omode}
|
return {'target': target, 'modes': omode}
|
||||||
|
|
||||||
def handle_fident(self, numeric, command, args):
|
def handle_fident(self, numeric, command, args):
|
||||||
"""Handles FIDENT, used for denoting ident changes."""
|
"""Handles FIDENT, used for denoting ident changes."""
|
||||||
# <- :70MAAAAAB FIDENT test
|
# <- :70MAAAAAB FIDENT test
|
||||||
self.irc.users[numeric].ident = newident = args[0]
|
self.users[numeric].ident = newident = args[0]
|
||||||
return {'target': numeric, 'newident': newident}
|
return {'target': numeric, 'newident': newident}
|
||||||
|
|
||||||
def handle_fhost(self, numeric, command, args):
|
def handle_fhost(self, numeric, command, args):
|
||||||
"""Handles FHOST, used for denoting hostname changes."""
|
"""Handles FHOST, used for denoting hostname changes."""
|
||||||
# <- :70MAAAAAB FHOST some.host
|
# <- :70MAAAAAB FHOST some.host
|
||||||
self.irc.users[numeric].host = newhost = args[0]
|
self.users[numeric].host = newhost = args[0]
|
||||||
return {'target': numeric, 'newhost': newhost}
|
return {'target': numeric, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_fname(self, numeric, command, args):
|
def handle_fname(self, numeric, command, args):
|
||||||
"""Handles FNAME, used for denoting real name/gecos changes."""
|
"""Handles FNAME, used for denoting real name/gecos changes."""
|
||||||
# <- :70MAAAAAB FNAME :afdsafasf
|
# <- :70MAAAAAB FNAME :afdsafasf
|
||||||
self.irc.users[numeric].realname = newgecos = args[0]
|
self.users[numeric].realname = newgecos = args[0]
|
||||||
return {'target': numeric, 'newgecos': newgecos}
|
return {'target': numeric, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_endburst(self, numeric, command, args):
|
def handle_endburst(self, numeric, command, args):
|
||||||
@ -735,10 +735,10 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# <- :1MLAAAAIG AWAY 1439371390 :Auto-away
|
# <- :1MLAAAAIG AWAY 1439371390 :Auto-away
|
||||||
try:
|
try:
|
||||||
ts = args[0]
|
ts = args[0]
|
||||||
self.irc.users[numeric].away = text = args[1]
|
self.users[numeric].away = text = args[1]
|
||||||
return {'text': text, 'ts': ts}
|
return {'text': text, 'ts': ts}
|
||||||
except IndexError: # User is unsetting away status
|
except IndexError: # User is unsetting away status
|
||||||
self.irc.users[numeric].away = ''
|
self.users[numeric].away = ''
|
||||||
return {'text': ''}
|
return {'text': ''}
|
||||||
|
|
||||||
def handle_rsquit(self, numeric, command, args):
|
def handle_rsquit(self, numeric, command, args):
|
||||||
@ -760,15 +760,15 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# SQUIT, in order to be consistent with other IRCds which make SQUITs
|
# SQUIT, in order to be consistent with other IRCds which make SQUITs
|
||||||
# implicit.
|
# implicit.
|
||||||
target = self._get_SID(args[0])
|
target = self._get_SID(args[0])
|
||||||
if self.irc.isInternalServer(target):
|
if self.isInternalServer(target):
|
||||||
# The target has to be one of our servers in order to work...
|
# The target has to be one of our servers in order to work...
|
||||||
uplink = self.irc.servers[target].uplink
|
uplink = self.servers[target].uplink
|
||||||
reason = 'Requested by %s' % self.irc.getHostmask(numeric)
|
reason = 'Requested by %s' % self.getHostmask(numeric)
|
||||||
self._send_with_prefix(uplink, 'SQUIT %s :%s' % (target, reason))
|
self._send_with_prefix(uplink, 'SQUIT %s :%s' % (target, reason))
|
||||||
return self.handle_squit(numeric, 'SQUIT', [target, reason])
|
return self.handle_squit(numeric, 'SQUIT', [target, reason])
|
||||||
else:
|
else:
|
||||||
log.debug("(%s) Got RSQUIT for '%s', which is either invalid or not "
|
log.debug("(%s) Got RSQUIT for '%s', which is either invalid or not "
|
||||||
"a server of ours!", self.irc.name, args[0])
|
"a server of ours!", self.name, args[0])
|
||||||
|
|
||||||
def handle_metadata(self, numeric, command, args):
|
def handle_metadata(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -777,12 +777,12 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
"""
|
"""
|
||||||
uid = args[0]
|
uid = args[0]
|
||||||
|
|
||||||
if args[1] == 'accountname' and uid in self.irc.users:
|
if args[1] == 'accountname' and uid in self.users:
|
||||||
# <- :00A METADATA 1MLAAAJET accountname :
|
# <- :00A METADATA 1MLAAAJET accountname :
|
||||||
# <- :00A METADATA 1MLAAAJET accountname :tester
|
# <- :00A METADATA 1MLAAAJET accountname :tester
|
||||||
# Sets the services login name of the client.
|
# Sets the services login name of the client.
|
||||||
|
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': args[-1]}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': args[-1]}])
|
||||||
|
|
||||||
def handle_version(self, numeric, command, args):
|
def handle_version(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -797,7 +797,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# removed from our user list.
|
# removed from our user list.
|
||||||
# If not, we have to assume that KILL = QUIT and remove them
|
# If not, we have to assume that KILL = QUIT and remove them
|
||||||
# ourselves.
|
# ourselves.
|
||||||
data = self.irc.users.get(killed)
|
data = self.users.get(killed)
|
||||||
if data:
|
if data:
|
||||||
self.removeClient(killed)
|
self.removeClient(killed)
|
||||||
return {'target': killed, 'text': args[1], 'userdata': data}
|
return {'target': killed, 'text': args[1], 'userdata': data}
|
||||||
@ -808,20 +808,20 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
# ENCAP -> SAKICK args: ['#test', '0ALAAAAAB', 'test']
|
# ENCAP -> SAKICK args: ['#test', '0ALAAAAAB', 'test']
|
||||||
|
|
||||||
target = args[1]
|
target = args[1]
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
try:
|
try:
|
||||||
reason = args[2]
|
reason = args[2]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# Kick reason is optional, strange...
|
# Kick reason is optional, strange...
|
||||||
reason = self.irc.getFriendlyName(source)
|
reason = self.getFriendlyName(source)
|
||||||
|
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
log.warning("(%s) Got SAKICK for client that not one of ours: %s", self.irc.name, target)
|
log.warning("(%s) Got SAKICK for client that not one of ours: %s", self.name, target)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
# Like RSQUIT, SAKICK requires that the receiving server acknowledge that a kick has
|
# Like RSQUIT, SAKICK requires that the receiving server acknowledge that a kick has
|
||||||
# happened. This comes from the server hosting the target client.
|
# happened. This comes from the server hosting the target client.
|
||||||
server = self.irc.getServer(target)
|
server = self.getServer(target)
|
||||||
|
|
||||||
self.kick(server, channel, target, reason)
|
self.kick(server, channel, target, reason)
|
||||||
return {'channel': channel, 'target': target, 'text': reason}
|
return {'channel': channel, 'target': target, 'text': reason}
|
||||||
@ -833,6 +833,6 @@ class InspIRCdProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
||||||
timestring = '%s (%s)' % (time.strftime('%Y-%m-%d %H:%M:%S'), int(time.time()))
|
timestring = '%s (%s)' % (time.strftime('%Y-%m-%d %H:%M:%S'), int(time.time()))
|
||||||
self._send_with_prefix(self.irc.sid, 'NOTICE %s :System time is %s on %s' % (source, timestring, self.irc.hostname()))
|
self._send_with_prefix(self.sid, 'NOTICE %s :System time is %s on %s' % (source, timestring, self.hostname()))
|
||||||
|
|
||||||
Class = InspIRCdProtocol
|
Class = InspIRCdProtocol
|
||||||
|
@ -14,10 +14,10 @@ class IRCCommonProtocol(IRCNetwork):
|
|||||||
def validate_server_conf(self):
|
def validate_server_conf(self):
|
||||||
"""Validates that the server block given contains the required keys."""
|
"""Validates that the server block given contains the required keys."""
|
||||||
for k in self.conf_keys:
|
for k in self.conf_keys:
|
||||||
assert k in self.irc.serverdata, "Missing option %r in server block for network %s." % (k, self.irc.name)
|
assert k in self.serverdata, "Missing option %r in server block for network %s." % (k, self.name)
|
||||||
|
|
||||||
port = self.irc.serverdata['port']
|
port = self.serverdata['port']
|
||||||
assert type(port) == int and 0 < port < 65535, "Invalid port %r for network %s" % (port, self.irc.name)
|
assert type(port) == int and 0 < port < 65535, "Invalid port %r for network %s" % (port, self.name)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parseArgs(args):
|
def parseArgs(args):
|
||||||
@ -56,20 +56,20 @@ class IRCCommonProtocol(IRCNetwork):
|
|||||||
# Normally we'd only need to check for our SID as the SQUIT target, but Nefarious
|
# Normally we'd only need to check for our SID as the SQUIT target, but Nefarious
|
||||||
# actually uses the uplink server as the SQUIT target.
|
# actually uses the uplink server as the SQUIT target.
|
||||||
# <- ABAAE SQ nefarious.midnight.vpn 0 :test
|
# <- ABAAE SQ nefarious.midnight.vpn 0 :test
|
||||||
if split_server in (self.irc.sid, self.irc.uplink):
|
if split_server in (self.sid, self.uplink):
|
||||||
raise ProtocolError('SQUIT received: (reason: %s)' % args[-1])
|
raise ProtocolError('SQUIT received: (reason: %s)' % args[-1])
|
||||||
|
|
||||||
affected_users = []
|
affected_users = []
|
||||||
affected_nicks = defaultdict(list)
|
affected_nicks = defaultdict(list)
|
||||||
log.debug('(%s) Splitting server %s (reason: %s)', self.irc.name, split_server, args[-1])
|
log.debug('(%s) Splitting server %s (reason: %s)', self.name, split_server, args[-1])
|
||||||
|
|
||||||
if split_server not in self.irc.servers:
|
if split_server not in self.servers:
|
||||||
log.warning("(%s) Tried to split a server (%s) that didn't exist!", self.irc.name, split_server)
|
log.warning("(%s) Tried to split a server (%s) that didn't exist!", self.name, split_server)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Prevent RuntimeError: dictionary changed size during iteration
|
# Prevent RuntimeError: dictionary changed size during iteration
|
||||||
old_servers = self.irc.servers.copy()
|
old_servers = self.servers.copy()
|
||||||
old_channels = self.irc.channels.copy()
|
old_channels = self.channels.copy()
|
||||||
|
|
||||||
# Cycle through our list of servers. If any server's uplink is the one that is being SQUIT,
|
# Cycle through our list of servers. If any server's uplink is the one that is being SQUIT,
|
||||||
# remove them and all their users too.
|
# remove them and all their users too.
|
||||||
@ -81,9 +81,9 @@ class IRCCommonProtocol(IRCNetwork):
|
|||||||
"PyLink: Automatically splitting leaf servers of %s" % sid])
|
"PyLink: Automatically splitting leaf servers of %s" % sid])
|
||||||
affected_users += args['users']
|
affected_users += args['users']
|
||||||
|
|
||||||
for user in self.irc.servers[split_server].users.copy():
|
for user in self.servers[split_server].users.copy():
|
||||||
affected_users.append(user)
|
affected_users.append(user)
|
||||||
nick = self.irc.users[user].nick
|
nick = self.users[user].nick
|
||||||
|
|
||||||
# Nicks affected is channel specific for SQUIT:. This makes Clientbot's SQUIT relaying
|
# Nicks affected is channel specific for SQUIT:. This makes Clientbot's SQUIT relaying
|
||||||
# much easier to implement.
|
# much easier to implement.
|
||||||
@ -94,12 +94,12 @@ class IRCCommonProtocol(IRCNetwork):
|
|||||||
log.debug('Removing client %s (%s)', user, nick)
|
log.debug('Removing client %s (%s)', user, nick)
|
||||||
self.removeClient(user)
|
self.removeClient(user)
|
||||||
|
|
||||||
serverdata = self.irc.servers[split_server]
|
serverdata = self.servers[split_server]
|
||||||
sname = serverdata.name
|
sname = serverdata.name
|
||||||
uplink = serverdata.uplink
|
uplink = serverdata.uplink
|
||||||
|
|
||||||
del self.irc.servers[split_server]
|
del self.servers[split_server]
|
||||||
log.debug('(%s) Netsplit affected users: %s', self.irc.name, affected_users)
|
log.debug('(%s) Netsplit affected users: %s', self.name, affected_users)
|
||||||
|
|
||||||
return {'target': split_server, 'users': affected_users, 'name': sname,
|
return {'target': split_server, 'users': affected_users, 'name': sname,
|
||||||
'uplink': uplink, 'nicks': affected_nicks, 'serverdata': serverdata,
|
'uplink': uplink, 'nicks': affected_nicks, 'serverdata': serverdata,
|
||||||
@ -168,37 +168,37 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
sender_sid = self._get_SID(sender)
|
sender_sid = self._get_SID(sender)
|
||||||
sender_uid = self._get_UID(sender)
|
sender_uid = self._get_UID(sender)
|
||||||
|
|
||||||
if sender_sid in self.irc.servers:
|
if sender_sid in self.servers:
|
||||||
# Sender is a server (converting from name to SID gave a valid result).
|
# Sender is a server (converting from name to SID gave a valid result).
|
||||||
sender = sender_sid
|
sender = sender_sid
|
||||||
elif sender_uid in self.irc.users:
|
elif sender_uid in self.users:
|
||||||
# Sender is a user (converting from name to UID gave a valid result).
|
# Sender is a user (converting from name to UID gave a valid result).
|
||||||
sender = sender_uid
|
sender = sender_uid
|
||||||
else:
|
else:
|
||||||
# No sender prefix; treat as coming from uplink IRCd.
|
# No sender prefix; treat as coming from uplink IRCd.
|
||||||
sender = self.irc.uplink
|
sender = self.uplink
|
||||||
args.insert(0, sender)
|
args.insert(0, sender)
|
||||||
|
|
||||||
if self.irc.isInternalClient(sender) or self.irc.isInternalServer(sender):
|
if self.isInternalClient(sender) or self.isInternalServer(sender):
|
||||||
log.warning("(%s) Received command %s being routed the wrong way!", self.irc.name, command)
|
log.warning("(%s) Received command %s being routed the wrong way!", self.name, command)
|
||||||
return
|
return
|
||||||
|
|
||||||
raw_command = args[1].upper()
|
raw_command = args[1].upper()
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
|
|
||||||
log.debug('(%s) Found message sender as %s', self.irc.name, sender)
|
log.debug('(%s) Found message sender as %s', self.name, sender)
|
||||||
|
|
||||||
# For P10, convert the command token into a regular command, if present.
|
# For P10, convert the command token into a regular command, if present.
|
||||||
command = self.COMMAND_TOKENS.get(raw_command, raw_command)
|
command = self.COMMAND_TOKENS.get(raw_command, raw_command)
|
||||||
if command != raw_command:
|
if command != raw_command:
|
||||||
log.debug('(%s) Translating token %s to command %s', self.irc.name, raw_command, command)
|
log.debug('(%s) Translating token %s to command %s', self.name, raw_command, command)
|
||||||
|
|
||||||
if command == 'ENCAP':
|
if command == 'ENCAP':
|
||||||
# Special case for TS6 encapsulated commands (ENCAP), in forms like this:
|
# Special case for TS6 encapsulated commands (ENCAP), in forms like this:
|
||||||
# <- :00A ENCAP * SU 42XAAAAAC :GLolol
|
# <- :00A ENCAP * SU 42XAAAAAC :GLolol
|
||||||
command = args[1]
|
command = args[1]
|
||||||
args = args[2:]
|
args = args[2:]
|
||||||
log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.irc.name, command, args)
|
log.debug("(%s) Rewriting incoming ENCAP to command %s (args: %s)", self.name, command, args)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
func = getattr(self, 'handle_'+command.lower())
|
func = getattr(self, 'handle_'+command.lower())
|
||||||
@ -232,8 +232,8 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
# Note: don't mess with the case of the channel prefix, or ~#channel
|
# Note: don't mess with the case of the channel prefix, or ~#channel
|
||||||
# messages will break on RFC1459 casemapping networks (it becomes ^#channel
|
# messages will break on RFC1459 casemapping networks (it becomes ^#channel
|
||||||
# instead).
|
# instead).
|
||||||
target = '#'.join((split_channel[0], self.irc.toLower(split_channel[1])))
|
target = '#'.join((split_channel[0], self.toLower(split_channel[1])))
|
||||||
log.debug('(%s) Normalizing channel target %s to %s', self.irc.name, args[0], target)
|
log.debug('(%s) Normalizing channel target %s to %s', self.name, args[0], target)
|
||||||
|
|
||||||
return {'target': target, 'text': args[1]}
|
return {'target': target, 'text': args[1]}
|
||||||
|
|
||||||
@ -243,13 +243,13 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
"""
|
"""
|
||||||
Nick collision checker.
|
Nick collision checker.
|
||||||
"""
|
"""
|
||||||
uid = self.irc.nickToUid(nick)
|
uid = self.nickToUid(nick)
|
||||||
# If there is a nick collision, we simply alert plugins. Relay will purposely try to
|
# If there is a nick collision, we simply alert plugins. Relay will purposely try to
|
||||||
# lose fights and tag nicks instead, while other plugins can choose how to handle this.
|
# lose fights and tag nicks instead, while other plugins can choose how to handle this.
|
||||||
if uid:
|
if uid:
|
||||||
log.info('(%s) Nick collision on %s/%s, forwarding this to plugins', self.irc.name,
|
log.info('(%s) Nick collision on %s/%s, forwarding this to plugins', self.name,
|
||||||
uid, nick)
|
uid, nick)
|
||||||
self.irc.callHooks([self.irc.sid, 'SAVE', {'target': uid}])
|
self.callHooks([self.sid, 'SAVE', {'target': uid}])
|
||||||
|
|
||||||
def handle_kill(self, source, command, args):
|
def handle_kill(self, source, command, args):
|
||||||
"""Handles incoming KILLs."""
|
"""Handles incoming KILLs."""
|
||||||
@ -259,7 +259,7 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
# removed from our user list.
|
# removed from our user list.
|
||||||
# If not, we have to assume that KILL = QUIT and remove them
|
# If not, we have to assume that KILL = QUIT and remove them
|
||||||
# ourselves.
|
# ourselves.
|
||||||
data = self.irc.users.get(killed)
|
data = self.users.get(killed)
|
||||||
if data:
|
if data:
|
||||||
self.removeClient(killed)
|
self.removeClient(killed)
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the nick or server name of the caller.
|
# Get the nick or server name of the caller.
|
||||||
killer = self.irc.getFriendlyName(source)
|
killer = self.getFriendlyName(source)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# Killer was... neither? We must have aliens or something. Fallback
|
# Killer was... neither? We must have aliens or something. Fallback
|
||||||
# to the given "UID".
|
# to the given "UID".
|
||||||
@ -295,9 +295,9 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
# <- ABAAA A :blah
|
# <- ABAAA A :blah
|
||||||
# <- ABAAA A
|
# <- ABAAA A
|
||||||
try:
|
try:
|
||||||
self.irc.users[numeric].away = text = args[0]
|
self.users[numeric].away = text = args[0]
|
||||||
except IndexError: # User is unsetting away status
|
except IndexError: # User is unsetting away status
|
||||||
self.irc.users[numeric].away = text = ''
|
self.users[numeric].away = text = ''
|
||||||
return {'text': text}
|
return {'text': text}
|
||||||
|
|
||||||
def handle_version(self, numeric, command, args):
|
def handle_version(self, numeric, command, args):
|
||||||
@ -333,5 +333,5 @@ class IRCS2SProtocol(IRCCommonProtocol):
|
|||||||
|
|
||||||
def handle_pong(self, source, command, args):
|
def handle_pong(self, source, command, args):
|
||||||
"""Handles incoming PONG commands."""
|
"""Handles incoming PONG commands."""
|
||||||
if source == self.irc.uplink:
|
if source == self.uplink:
|
||||||
self.irc.lastping = time.time()
|
self.lastping = time.time()
|
||||||
|
@ -11,6 +11,6 @@ class NefariousProtocol(P10Protocol):
|
|||||||
log.warning("(%s) protocols/nefarious.py has been renamed to protocols/p10.py, which "
|
log.warning("(%s) protocols/nefarious.py has been renamed to protocols/p10.py, which "
|
||||||
"now also supports other IRCu variants. Please update your configuration, "
|
"now also supports other IRCu variants. Please update your configuration, "
|
||||||
"as this migration stub will be removed in a future version.",
|
"as this migration stub will be removed in a future version.",
|
||||||
self.irc.name)
|
self.name)
|
||||||
|
|
||||||
Class = NefariousProtocol
|
Class = NefariousProtocol
|
||||||
|
362
protocols/p10.py
362
protocols/p10.py
@ -165,7 +165,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
self.protocol_caps |= {'slash-in-hosts', 'underscore-in-hosts'}
|
self.protocol_caps |= {'slash-in-hosts', 'underscore-in-hosts'}
|
||||||
|
|
||||||
def _send_with_prefix(self, source, text, **kwargs):
|
def _send_with_prefix(self, source, text, **kwargs):
|
||||||
self.irc.send("%s %s" % (source, text), **kwargs)
|
self.send("%s %s" % (source, text), **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def access_sort(key):
|
def access_sort(key):
|
||||||
@ -261,8 +261,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# -2 <numeric>
|
# -2 <numeric>
|
||||||
# -1 <fullname>
|
# -1 <fullname>
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
# Create an UIDGenerator instance for every SID, so that each gets
|
# Create an UIDGenerator instance for every SID, so that each gets
|
||||||
@ -273,16 +273,16 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
|
|
||||||
# Initialize an IrcUser instance
|
# Initialize an IrcUser instance
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable,
|
realhost=realhost, ip=ip, manipulatable=manipulatable,
|
||||||
opertype=opertype)
|
opertype=opertype)
|
||||||
|
|
||||||
# Fill in modes and add it to our users index
|
# Fill in modes and add it to our users index
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
# Encode IPs when sending
|
# Encode IPs when sending
|
||||||
if ip_address(ip).version == 4:
|
if ip_address(ip).version == 4:
|
||||||
@ -301,62 +301,62 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
def away(self, source, text):
|
def away(self, source, text):
|
||||||
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
"""Sends an AWAY message from a PyLink client. <text> can be an empty string
|
||||||
to unset AWAY status."""
|
to unset AWAY status."""
|
||||||
if not self.irc.isInternalClient(source):
|
if not self.isInternalClient(source):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
if text:
|
if text:
|
||||||
self._send_with_prefix(source, 'A :%s' % text)
|
self._send_with_prefix(source, 'A :%s' % text)
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(source, 'A')
|
self._send_with_prefix(source, 'A')
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends INVITEs from a PyLink client."""
|
"""Sends INVITEs from a PyLink client."""
|
||||||
# Note: we have to send a nick as the target, not a UID.
|
# Note: we have to send a nick as the target, not a UID.
|
||||||
# <- ABAAA I PyLink-devel #services 1460948992
|
# <- ABAAA I PyLink-devel #services 1460948992
|
||||||
|
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
nick = self.irc.users[target].nick
|
nick = self.users[target].nick
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'I %s %s %s' % (nick, channel, self.irc.channels[channel].ts))
|
self._send_with_prefix(numeric, 'I %s %s %s' % (nick, channel, self.channels[channel].ts))
|
||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""Joins a PyLink client to a channel."""
|
"""Joins a PyLink client to a channel."""
|
||||||
# <- ABAAB J #test3 1460744371
|
# <- ABAAB J #test3 1460744371
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
ts = self.irc.channels[channel].ts
|
ts = self.channels[channel].ts
|
||||||
|
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.isInternalClient(client):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
if not self.irc.channels[channel].users:
|
if not self.channels[channel].users:
|
||||||
# Empty channels should be created with the CREATE command.
|
# Empty channels should be created with the CREATE command.
|
||||||
self._send_with_prefix(client, "C {channel} {ts}".format(ts=ts, channel=channel))
|
self._send_with_prefix(client, "C {channel} {ts}".format(ts=ts, channel=channel))
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(client, "J {channel} {ts}".format(ts=ts, channel=channel))
|
self._send_with_prefix(client, "J {channel} {ts}".format(ts=ts, channel=channel))
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def kick(self, numeric, channel, target, reason=None):
|
def kick(self, numeric, channel, target, reason=None):
|
||||||
"""Sends kicks from a PyLink client/server."""
|
"""Sends kicks from a PyLink client/server."""
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
if not reason:
|
if not reason:
|
||||||
reason = 'No reason given'
|
reason = 'No reason given'
|
||||||
|
|
||||||
cobj = self.irc.channels[channel]
|
cobj = self.channels[channel]
|
||||||
# HACK: prevent kick bounces by sending our kick through the server if
|
# HACK: prevent kick bounces by sending our kick through the server if
|
||||||
# the sender isn't op.
|
# the sender isn't op.
|
||||||
if numeric not in self.irc.servers and (not cobj.isOp(numeric)) and (not cobj.isHalfop(numeric)):
|
if numeric not in self.servers and (not cobj.isOp(numeric)) and (not cobj.isHalfop(numeric)):
|
||||||
reason = '(%s) %s' % (self.irc.getFriendlyName(numeric), reason)
|
reason = '(%s) %s' % (self.getFriendlyName(numeric), reason)
|
||||||
numeric = self.irc.getServer(numeric)
|
numeric = self.getServer(numeric)
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'K %s %s :%s' % (channel, target, reason))
|
self._send_with_prefix(numeric, 'K %s %s :%s' % (channel, target, reason))
|
||||||
|
|
||||||
@ -369,8 +369,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
"""Sends a kill from a PyLink client/server."""
|
"""Sends a kill from a PyLink client/server."""
|
||||||
# <- ABAAA D AyAAA :nefarious.midnight.vpn!GL (test)
|
# <- ABAAA D AyAAA :nefarious.midnight.vpn!GL (test)
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'D %s :Killed (%s)' % (target, reason))
|
self._send_with_prefix(numeric, 'D %s :Killed (%s)' % (target, reason))
|
||||||
@ -381,7 +381,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def message(self, numeric, target, text):
|
def message(self, numeric, target, text):
|
||||||
"""Sends a PRIVMSG from a PyLink client."""
|
"""Sends a PRIVMSG from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'P %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'P %s :%s' % (target, text))
|
||||||
@ -391,8 +391,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# <- ABAAA M GL -w
|
# <- ABAAA M GL -w
|
||||||
# <- ABAAA M #test +v ABAAB 1460747615
|
# <- ABAAA M #test +v ABAAB 1460747615
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
modes = list(modes)
|
modes = list(modes)
|
||||||
@ -404,13 +404,13 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
is_cmode = utils.isChannel(target)
|
is_cmode = utils.isChannel(target)
|
||||||
if is_cmode:
|
if is_cmode:
|
||||||
# Channel mode changes have a trailing TS. User mode changes do not.
|
# Channel mode changes have a trailing TS. User mode changes do not.
|
||||||
cobj = self.irc.channels[self.irc.toLower(target)]
|
cobj = self.channels[self.toLower(target)]
|
||||||
ts = ts or cobj.ts
|
ts = ts or cobj.ts
|
||||||
|
|
||||||
# HACK: prevent mode bounces by sending our mode through the server if
|
# HACK: prevent mode bounces by sending our mode through the server if
|
||||||
# the sender isn't op.
|
# the sender isn't op.
|
||||||
if numeric not in self.irc.servers and (not cobj.isOp(numeric)) and (not cobj.isHalfop(numeric)):
|
if numeric not in self.servers and (not cobj.isOp(numeric)) and (not cobj.isHalfop(numeric)):
|
||||||
numeric = self.irc.getServer(numeric)
|
numeric = self.getServer(numeric)
|
||||||
|
|
||||||
# Wrap modes: start with max bufsize and subtract the lengths of the source, target,
|
# Wrap modes: start with max bufsize and subtract the lengths of the source, target,
|
||||||
# mode command, and whitespace.
|
# mode command, and whitespace.
|
||||||
@ -418,16 +418,16 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
real_target = target
|
real_target = target
|
||||||
else:
|
else:
|
||||||
assert target in self.irc.users, "Unknown mode target %s" % target
|
assert target in self.users, "Unknown mode target %s" % target
|
||||||
# P10 uses nicks in user MODE targets, NOT UIDs. ~GL
|
# P10 uses nicks in user MODE targets, NOT UIDs. ~GL
|
||||||
real_target = self.irc.users[target].nick
|
real_target = self.users[target].nick
|
||||||
|
|
||||||
self.irc.applyModes(target, modes)
|
self.applyModes(target, modes)
|
||||||
|
|
||||||
while modes[:12]:
|
while modes[:12]:
|
||||||
joinedmodes = self.irc.joinModes([m for m in modes[:12]])
|
joinedmodes = self.joinModes([m for m in modes[:12]])
|
||||||
if is_cmode:
|
if is_cmode:
|
||||||
for wrapped_modes in self.irc.wrapModes(modes[:12], bufsize):
|
for wrapped_modes in self.wrapModes(modes[:12], bufsize):
|
||||||
self._send_with_prefix(numeric, 'M %s %s %s' % (real_target, wrapped_modes, ts))
|
self._send_with_prefix(numeric, 'M %s %s %s' % (real_target, wrapped_modes, ts))
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(numeric, 'M %s %s' % (real_target, joinedmodes))
|
self._send_with_prefix(numeric, 'M %s %s' % (real_target, joinedmodes))
|
||||||
@ -436,14 +436,14 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
def nick(self, numeric, newnick):
|
def nick(self, numeric, newnick):
|
||||||
"""Changes the nick of a PyLink client."""
|
"""Changes the nick of a PyLink client."""
|
||||||
# <- ABAAA N GL_ 1460753763
|
# <- ABAAA N GL_ 1460753763
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'N %s %s' % (newnick, int(time.time())))
|
self._send_with_prefix(numeric, 'N %s %s' % (newnick, int(time.time())))
|
||||||
self.irc.users[numeric].nick = newnick
|
self.users[numeric].nick = newnick
|
||||||
|
|
||||||
# Update the NICK TS.
|
# Update the NICK TS.
|
||||||
self.irc.users[numeric].ts = int(time.time())
|
self.users[numeric].ts = int(time.time())
|
||||||
|
|
||||||
def numeric(self, source, numeric, target, text):
|
def numeric(self, source, numeric, target, text):
|
||||||
"""Sends raw numerics from a server to a remote client. This is used for WHOIS
|
"""Sends raw numerics from a server to a remote client. This is used for WHOIS
|
||||||
@ -453,17 +453,17 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def notice(self, numeric, target, text):
|
def notice(self, numeric, target, text):
|
||||||
"""Sends a NOTICE from a PyLink client or server."""
|
"""Sends a NOTICE from a PyLink client or server."""
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'O %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'O %s :%s' % (target, text))
|
||||||
|
|
||||||
def part(self, client, channel, reason=None):
|
def part(self, client, channel, reason=None):
|
||||||
"""Sends a part from a PyLink client."""
|
"""Sends a part from a PyLink client."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
|
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.isInternalClient(client):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
msg = "L %s" % channel
|
msg = "L %s" % channel
|
||||||
@ -475,7 +475,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
if source is None:
|
if source is None:
|
||||||
return
|
return
|
||||||
if target is not None:
|
if target is not None:
|
||||||
@ -485,7 +485,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def quit(self, numeric, reason):
|
def quit(self, numeric, reason):
|
||||||
"""Quits a PyLink client."""
|
"""Quits a PyLink client."""
|
||||||
if self.irc.isInternalClient(numeric):
|
if self.isInternalClient(numeric):
|
||||||
self._send_with_prefix(numeric, "Q :%s" % reason)
|
self._send_with_prefix(numeric, "Q :%s" % reason)
|
||||||
self.removeClient(numeric)
|
self.removeClient(numeric)
|
||||||
else:
|
else:
|
||||||
@ -500,14 +500,14 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
# <- AB B #test 1460742014 +tnl 10 ABAAB,ABAAA:o :%*!*@other.bad.host ~ *!*@bad.host
|
# <- AB B #test 1460742014 +tnl 10 ABAAB,ABAAA:o :%*!*@other.bad.host ~ *!*@bad.host
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
|
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
log.debug('(%s) sjoin: got %r for users', self.irc.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
@ -515,8 +515,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# <- AB B #test 1460742014 +tnl 10 ABAAB,ABAAA:o :%*!*@other.bad.host *!*@bad.host
|
# <- AB B #test 1460742014 +tnl 10 ABAAB,ABAAA:o :%*!*@other.bad.host *!*@bad.host
|
||||||
# <- AB B #test2 1460743539 +l 10 ABAAA:vo :%*!*@bad.host
|
# <- AB B #test2 1460743539 +l 10 ABAAA:vo :%*!*@bad.host
|
||||||
# <- AB B #test 1460747615 ABAAA:o :% ~ *!*@test.host
|
# <- AB B #test 1460747615 ABAAA:o :% ~ *!*@test.host
|
||||||
modes = modes or self.irc.channels[channel].modes
|
modes = modes or self.channels[channel].modes
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
|
|
||||||
bans = []
|
bans = []
|
||||||
@ -525,8 +525,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
for mode in modes:
|
for mode in modes:
|
||||||
modechar = mode[0][-1]
|
modechar = mode[0][-1]
|
||||||
# Store bans and exempts in separate lists for processing, but don't reset bans that have already been set.
|
# Store bans and exempts in separate lists for processing, but don't reset bans that have already been set.
|
||||||
if modechar in self.irc.cmodes['*A']:
|
if modechar in self.cmodes['*A']:
|
||||||
if (modechar, mode[1]) not in self.irc.channels[channel].modes:
|
if (modechar, mode[1]) not in self.channels[channel].modes:
|
||||||
if modechar == 'b':
|
if modechar == 'b':
|
||||||
bans.append(mode[1])
|
bans.append(mode[1])
|
||||||
elif modechar == 'e':
|
elif modechar == 'e':
|
||||||
@ -534,7 +534,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
else:
|
else:
|
||||||
regularmodes.append(mode)
|
regularmodes.append(mode)
|
||||||
|
|
||||||
log.debug('(%s) sjoin: bans: %s, exempts: %s, other modes: %s', self.irc.name, bans, exempts, regularmodes)
|
log.debug('(%s) sjoin: bans: %s, exempts: %s, other modes: %s', self.name, bans, exempts, regularmodes)
|
||||||
|
|
||||||
changedmodes = set(modes)
|
changedmodes = set(modes)
|
||||||
changedusers = []
|
changedusers = []
|
||||||
@ -546,7 +546,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
msgprefix = '{sid} B {channel} {ts} '.format(sid=server, channel=channel, ts=ts)
|
msgprefix = '{sid} B {channel} {ts} '.format(sid=server, channel=channel, ts=ts)
|
||||||
if regularmodes:
|
if regularmodes:
|
||||||
msgprefix += '%s ' % self.irc.joinModes(regularmodes)
|
msgprefix += '%s ' % self.joinModes(regularmodes)
|
||||||
|
|
||||||
last_prefixes = ''
|
last_prefixes = ''
|
||||||
for userpair in users:
|
for userpair in users:
|
||||||
@ -557,7 +557,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# Keep track of all the users and modes that are added. namelist is used
|
# Keep track of all the users and modes that are added. namelist is used
|
||||||
# to track what we actually send to the IRCd.
|
# to track what we actually send to the IRCd.
|
||||||
changedusers.append(user)
|
changedusers.append(user)
|
||||||
log.debug('(%s) sjoin: adding %s:%s to namelist', self.irc.name, user, prefixes)
|
log.debug('(%s) sjoin: adding %s:%s to namelist', self.name, user, prefixes)
|
||||||
|
|
||||||
if prefixes and prefixes != last_prefixes:
|
if prefixes and prefixes != last_prefixes:
|
||||||
namelist.append('%s:%s' % (user, prefixes))
|
namelist.append('%s:%s' % (user, prefixes))
|
||||||
@ -569,10 +569,10 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
changedmodes.add(('+%s' % prefix, user))
|
changedmodes.add(('+%s' % prefix, user))
|
||||||
|
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
else:
|
else:
|
||||||
if namelist:
|
if namelist:
|
||||||
log.debug('(%s) sjoin: got %r for namelist', self.irc.name, namelist)
|
log.debug('(%s) sjoin: got %r for namelist', self.name, namelist)
|
||||||
|
|
||||||
# Flip the (prefixmodes, user) pairs in users, and save it as a dict for easy lookup
|
# Flip the (prefixmodes, user) pairs in users, and save it as a dict for easy lookup
|
||||||
# later of what modes each target user should have.
|
# later of what modes each target user should have.
|
||||||
@ -581,14 +581,14 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# Wrap all users and send them to prevent cutoff. Subtract 4 off the maximum
|
# Wrap all users and send them to prevent cutoff. Subtract 4 off the maximum
|
||||||
# buf size to account for user prefix data that may be re-added (e.g. ":ohv")
|
# buf size to account for user prefix data that may be re-added (e.g. ":ohv")
|
||||||
for linenum, wrapped_msg in \
|
for linenum, wrapped_msg in \
|
||||||
enumerate(utils.wrapArguments(msgprefix, namelist, S2S_BUFSIZE-1-len(self.irc.prefixmodes),
|
enumerate(utils.wrapArguments(msgprefix, namelist, S2S_BUFSIZE-1-len(self.prefixmodes),
|
||||||
separator=',')):
|
separator=',')):
|
||||||
if linenum: # Implies "if linenum > 0"
|
if linenum: # Implies "if linenum > 0"
|
||||||
# XXX: Ugh, this postprocessing sucks, but we have to make sure that mode prefixes are accounted
|
# XXX: Ugh, this postprocessing sucks, but we have to make sure that mode prefixes are accounted
|
||||||
# for in the burst.
|
# for in the burst.
|
||||||
wrapped_args = self.parseArgs(wrapped_msg.split(" "))
|
wrapped_args = self.parseArgs(wrapped_msg.split(" "))
|
||||||
wrapped_namelist = wrapped_args[-1].split(',')
|
wrapped_namelist = wrapped_args[-1].split(',')
|
||||||
log.debug('(%s) sjoin: wrapped args: %s (post-wrap fixing)', self.irc.name,
|
log.debug('(%s) sjoin: wrapped args: %s (post-wrap fixing)', self.name,
|
||||||
wrapped_args)
|
wrapped_args)
|
||||||
|
|
||||||
# If the first UID was supposed to have a prefix mode attached, re-add it here
|
# If the first UID was supposed to have a prefix mode attached, re-add it here
|
||||||
@ -596,20 +596,20 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# XXX: I'm not sure why the prefix list has to be reversed for it to match the
|
# XXX: I'm not sure why the prefix list has to be reversed for it to match the
|
||||||
# original string...
|
# original string...
|
||||||
first_prefix = names_dict.get(first_uid, '')[::-1]
|
first_prefix = names_dict.get(first_uid, '')[::-1]
|
||||||
log.debug('(%s) sjoin: prefixes for first user %s: %s (post-wrap fixing)', self.irc.name,
|
log.debug('(%s) sjoin: prefixes for first user %s: %s (post-wrap fixing)', self.name,
|
||||||
first_uid, first_prefix)
|
first_uid, first_prefix)
|
||||||
|
|
||||||
if (':' not in first_uid) and first_prefix:
|
if (':' not in first_uid) and first_prefix:
|
||||||
log.debug('(%s) sjoin: re-adding prefix %s to user %s (post-wrap fixing)', self.irc.name,
|
log.debug('(%s) sjoin: re-adding prefix %s to user %s (post-wrap fixing)', self.name,
|
||||||
first_uid, first_prefix)
|
first_uid, first_prefix)
|
||||||
wrapped_namelist[0] += ':%s' % prefixes
|
wrapped_namelist[0] += ':%s' % prefixes
|
||||||
wrapped_msg = ' '.join(wrapped_args[:-1])
|
wrapped_msg = ' '.join(wrapped_args[:-1])
|
||||||
wrapped_msg += ' '
|
wrapped_msg += ' '
|
||||||
wrapped_msg += ','.join(wrapped_namelist)
|
wrapped_msg += ','.join(wrapped_namelist)
|
||||||
|
|
||||||
self.irc.send(wrapped_msg)
|
self.send(wrapped_msg)
|
||||||
|
|
||||||
self.irc.channels[channel].users.update(changedusers)
|
self.channels[channel].users.update(changedusers)
|
||||||
|
|
||||||
# Technically we can send bans together with the above user introductions, but
|
# Technically we can send bans together with the above user introductions, but
|
||||||
# it's easier to line wrap them separately.
|
# it's easier to line wrap them separately.
|
||||||
@ -617,12 +617,12 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
msgprefix += ':%' # Ban string starts with a % if there is anything
|
msgprefix += ':%' # Ban string starts with a % if there is anything
|
||||||
if bans:
|
if bans:
|
||||||
for wrapped_msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE):
|
for wrapped_msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE):
|
||||||
self.irc.send(wrapped_msg)
|
self.send(wrapped_msg)
|
||||||
if exempts:
|
if exempts:
|
||||||
# Now add exempts, which are separated from the ban list by a single argument "~".
|
# Now add exempts, which are separated from the ban list by a single argument "~".
|
||||||
msgprefix += ' ~ '
|
msgprefix += ' ~ '
|
||||||
for wrapped_msg in utils.wrapArguments(msgprefix, exempts, S2S_BUFSIZE):
|
for wrapped_msg in utils.wrapArguments(msgprefix, exempts, S2S_BUFSIZE):
|
||||||
self.irc.send(wrapped_msg)
|
self.send(wrapped_msg)
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
@ -637,37 +637,37 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
option will be ignored if given.
|
option will be ignored if given.
|
||||||
"""
|
"""
|
||||||
# <- SERVER nefarious.midnight.vpn 1 1460673022 1460673239 J10 ABP]] +h6 :Nefarious2 test server
|
# <- SERVER nefarious.midnight.vpn 1 1460673022 1460673239 J10 ABP]] +h6 :Nefarious2 test server
|
||||||
uplink = uplink or self.irc.sid
|
uplink = uplink or self.sid
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
desc = desc or self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = desc or self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
|
|
||||||
if sid is None: # No sid given; generate one!
|
if sid is None: # No sid given; generate one!
|
||||||
sid = self.sidgen.next_sid()
|
sid = self.sidgen.next_sid()
|
||||||
|
|
||||||
assert len(sid) == 2, "Incorrect SID length"
|
assert len(sid) == 2, "Incorrect SID length"
|
||||||
if sid in self.irc.servers:
|
if sid in self.servers:
|
||||||
raise ValueError('A server with SID %r already exists!' % sid)
|
raise ValueError('A server with SID %r already exists!' % sid)
|
||||||
|
|
||||||
for server in self.irc.servers.values():
|
for server in self.servers.values():
|
||||||
if name == server.name:
|
if name == server.name:
|
||||||
raise ValueError('A server named %r already exists!' % name)
|
raise ValueError('A server named %r already exists!' % name)
|
||||||
|
|
||||||
if not self.irc.isInternalServer(uplink):
|
if not self.isInternalServer(uplink):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
||||||
if not utils.isServerName(name):
|
if not utils.isServerName(name):
|
||||||
raise ValueError('Invalid server name %r' % name)
|
raise ValueError('Invalid server name %r' % name)
|
||||||
|
|
||||||
self._send_with_prefix(uplink, 'SERVER %s 1 %s %s P10 %s]]] +h6 :%s' % \
|
self._send_with_prefix(uplink, 'SERVER %s 1 %s %s P10 %s]]] +h6 :%s' % \
|
||||||
(name, self.irc.start_ts, int(time.time()), sid, desc))
|
(name, self.start_ts, int(time.time()), sid, desc))
|
||||||
|
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
self.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def squit(self, source, target, text='No reason given'):
|
def squit(self, source, target, text='No reason given'):
|
||||||
"""SQUITs a PyLink server."""
|
"""SQUITs a PyLink server."""
|
||||||
# <- ABAAE SQ nefarious.midnight.vpn 0 :test
|
# <- ABAAE SQ nefarious.midnight.vpn 0 :test
|
||||||
|
|
||||||
targetname = self.irc.servers[target].name
|
targetname = self.servers[target].name
|
||||||
|
|
||||||
self._send_with_prefix(source, 'SQ %s 0 :%s' % (targetname, text))
|
self._send_with_prefix(source, 'SQ %s 0 :%s' % (targetname, text))
|
||||||
self.handle_squit(source, 'SQUIT', [target, text])
|
self.handle_squit(source, 'SQUIT', [target, text])
|
||||||
@ -677,41 +677,41 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# <- ABAAA T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
# <- ABAAA T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
||||||
# First timestamp is channel creation time, second is current time,
|
# First timestamp is channel creation time, second is current time,
|
||||||
|
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
sendername = self.irc.getHostmask(numeric)
|
sendername = self.getHostmask(numeric)
|
||||||
|
|
||||||
creationts = self.irc.channels[target].ts
|
creationts = self.channels[target].ts
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'T %s %s %s %s :%s' % (target, sendername, creationts,
|
self._send_with_prefix(numeric, 'T %s %s %s %s :%s' % (target, sendername, creationts,
|
||||||
int(time.time()), text))
|
int(time.time()), text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topicBurst(self, numeric, target, text):
|
||||||
"""Sends a TOPIC change from a PyLink server."""
|
"""Sends a TOPIC change from a PyLink server."""
|
||||||
# <- AB T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
# <- AB T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
||||||
|
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.isInternalServer(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
|
|
||||||
sendername = self.irc.servers[numeric].name
|
sendername = self.servers[numeric].name
|
||||||
|
|
||||||
creationts = self.irc.channels[target].ts
|
creationts = self.channels[target].ts
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'T %s %s %s %s :%s' % (target, sendername, creationts,
|
self._send_with_prefix(numeric, 'T %s %s %s %s :%s' % (target, sendername, creationts,
|
||||||
int(time.time()), text))
|
int(time.time()), text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def updateClient(self, target, field, text):
|
||||||
"""Updates the ident or host of any connected client."""
|
"""Updates the ident or host of any connected client."""
|
||||||
uobj = self.irc.users[target]
|
uobj = self.users[target]
|
||||||
|
|
||||||
ircd = self.irc.serverdata.get('p10_ircd', 'nefarious').lower()
|
ircd = self.serverdata.get('p10_ircd', 'nefarious').lower()
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
# Host changing via SETHOST is only supported on nefarious and snircd.
|
# Host changing via SETHOST is only supported on nefarious and snircd.
|
||||||
if ircd not in ('nefarious', 'snircd'):
|
if ircd not in ('nefarious', 'snircd'):
|
||||||
raise NotImplementedError("Host changing for internal clients (via SETHOST) is only "
|
raise NotImplementedError("Host changing for internal clients (via SETHOST) is only "
|
||||||
@ -737,12 +737,12 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
"only available on nefarious, and we're using p10_ircd=%r" % ircd)
|
"only available on nefarious, and we're using p10_ircd=%r" % ircd)
|
||||||
|
|
||||||
# Use FAKE (FA) for external clients.
|
# Use FAKE (FA) for external clients.
|
||||||
self._send_with_prefix(self.irc.sid, 'FA %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'FA %s %s' % (target, text))
|
||||||
|
|
||||||
# Save the host change as a user mode (this is what P10 does on bursts),
|
# Save the host change as a user mode (this is what P10 does on bursts),
|
||||||
# so further host checks work.
|
# so further host checks work.
|
||||||
self.irc.applyModes(target, [('+f', text)])
|
self.applyModes(target, [('+f', text)])
|
||||||
self.mode(self.irc.sid, target, [('+x', None)])
|
self.mode(self.sid, target, [('+x', None)])
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
"unsupported by this protocol." % field)
|
"unsupported by this protocol." % field)
|
||||||
@ -756,9 +756,9 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
|
|
||||||
self.irc.send("PASS :%s" % self.irc.serverdata["sendpass"])
|
self.send("PASS :%s" % self.serverdata["sendpass"])
|
||||||
|
|
||||||
# {7S} *** SERVER
|
# {7S} *** SERVER
|
||||||
|
|
||||||
@ -771,15 +771,15 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# 7 <flags> <-- Mark ourselves as a service with IPv6 support (+s & +6) -GLolol
|
# 7 <flags> <-- Mark ourselves as a service with IPv6 support (+s & +6) -GLolol
|
||||||
# -1 <description of new server>
|
# -1 <description of new server>
|
||||||
|
|
||||||
name = self.irc.serverdata["hostname"]
|
name = self.serverdata["hostname"]
|
||||||
|
|
||||||
# Encode our SID using P10 Base64.
|
# Encode our SID using P10 Base64.
|
||||||
self.irc.sid = sid = p10b64encode(self.irc.serverdata["sid"])
|
self.sid = sid = p10b64encode(self.serverdata["sid"])
|
||||||
|
|
||||||
desc = self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
|
|
||||||
# Enumerate modes, from https://github.com/evilnet/nefarious2/blob/master/doc/modes.txt
|
# Enumerate modes, from https://github.com/evilnet/nefarious2/blob/master/doc/modes.txt
|
||||||
p10_ircd = self.irc.serverdata.get('p10_ircd', 'nefarious').lower()
|
p10_ircd = self.serverdata.get('p10_ircd', 'nefarious').lower()
|
||||||
if p10_ircd == 'nefarious':
|
if p10_ircd == 'nefarious':
|
||||||
cmodes = {'delayjoin': 'D', 'registered': 'R', 'key': 'k', 'banexception': 'e',
|
cmodes = {'delayjoin': 'D', 'registered': 'R', 'key': 'k', 'banexception': 'e',
|
||||||
'redirect': 'L', 'oplevel_apass': 'A', 'oplevel_upass': 'U',
|
'redirect': 'L', 'oplevel_apass': 'A', 'oplevel_upass': 'U',
|
||||||
@ -787,7 +787,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
'permanent': 'z', 'hidequits': 'Q', 'noctcp': 'C', 'noamsg': 'T', 'blockcolor': 'c',
|
'permanent': 'z', 'hidequits': 'Q', 'noctcp': 'C', 'noamsg': 'T', 'blockcolor': 'c',
|
||||||
'stripcolor': 'S', 'had_delayjoin': 'd', 'regonly': 'r',
|
'stripcolor': 'S', 'had_delayjoin': 'd', 'regonly': 'r',
|
||||||
'*A': 'be', '*B': 'AUk', '*C': 'Ll', '*D': 'psmtinrDRaOMNzQCTcSd'}
|
'*A': 'be', '*B': 'AUk', '*C': 'Ll', '*D': 'psmtinrDRaOMNzQCTcSd'}
|
||||||
self.irc.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x', 'privdeaf': 'D',
|
self.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x', 'privdeaf': 'D',
|
||||||
'hidechans': 'n', 'deaf_commonchan': 'q', 'bot': 'B', 'deaf': 'd',
|
'hidechans': 'n', 'deaf_commonchan': 'q', 'bot': 'B', 'deaf': 'd',
|
||||||
'hideoper': 'H', 'hideidle': 'I', 'regdeaf': 'R', 'showwhois': 'W',
|
'hideoper': 'H', 'hideidle': 'I', 'regdeaf': 'R', 'showwhois': 'W',
|
||||||
'admin': 'a', 'override': 'X', 'noforward': 'L', 'ssl': 'z',
|
'admin': 'a', 'override': 'X', 'noforward': 'L', 'ssl': 'z',
|
||||||
@ -802,7 +802,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
'*A': 'b', '*B': 'AUk', '*C': 'l', '*D': 'imnpstrDducCMNT'}
|
'*A': 'b', '*B': 'AUk', '*C': 'l', '*D': 'imnpstrDducCMNT'}
|
||||||
# From https://www.quakenet.org/help/general/what-user-modes-are-available-on-quakenet
|
# From https://www.quakenet.org/help/general/what-user-modes-are-available-on-quakenet
|
||||||
# plus my own testing.
|
# plus my own testing.
|
||||||
self.irc.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x',
|
self.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x',
|
||||||
'hidechans': 'n', 'deaf': 'd', 'hideidle': 'I', 'regdeaf': 'R',
|
'hidechans': 'n', 'deaf': 'd', 'hideidle': 'I', 'regdeaf': 'R',
|
||||||
'override': 'X', 'registered': 'r', 'cloak_sethost': 'h', 'locop': 'O',
|
'override': 'X', 'registered': 'r', 'cloak_sethost': 'h', 'locop': 'O',
|
||||||
'*A': '', '*B': '', '*C': 'h', '*D': 'imnpstrkgxndIRXO'})
|
'*A': '', '*B': '', '*C': 'h', '*D': 'imnpstrkgxndIRXO'})
|
||||||
@ -811,18 +811,18 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
cmodes = {'oplevel_apass': 'A', 'oplevel_upass': 'U', 'delayjoin': 'D', 'regonly': 'r',
|
cmodes = {'oplevel_apass': 'A', 'oplevel_upass': 'U', 'delayjoin': 'D', 'regonly': 'r',
|
||||||
'had_delayjoin': 'd', 'blockcolor': 'c', 'noctcp': 'C', 'registered': 'R',
|
'had_delayjoin': 'd', 'blockcolor': 'c', 'noctcp': 'C', 'registered': 'R',
|
||||||
'*A': 'b', '*B': 'AUk', '*C': 'l', '*D': 'imnpstrDdRcC'}
|
'*A': 'b', '*B': 'AUk', '*C': 'l', '*D': 'imnpstrDdRcC'}
|
||||||
self.irc.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x',
|
self.umodes.update({'servprotect': 'k', 'sno_debug': 'g', 'cloak': 'x',
|
||||||
'deaf': 'd', 'registered': 'r', 'locop': 'O',
|
'deaf': 'd', 'registered': 'r', 'locop': 'O',
|
||||||
'*A': '', '*B': '', '*C': '', '*D': 'imnpstrkgxdO'})
|
'*A': '', '*B': '', '*C': '', '*D': 'imnpstrkgxdO'})
|
||||||
|
|
||||||
if self.irc.serverdata.get('use_halfop'):
|
if self.serverdata.get('use_halfop'):
|
||||||
cmodes['halfop'] = 'h'
|
cmodes['halfop'] = 'h'
|
||||||
self.irc.prefixmodes['h'] = '%'
|
self.prefixmodes['h'] = '%'
|
||||||
self.irc.cmodes.update(cmodes)
|
self.cmodes.update(cmodes)
|
||||||
|
|
||||||
self.irc.send('SERVER %s 1 %s %s J10 %s]]] +s6 :%s' % (name, ts, ts, sid, desc))
|
self.send('SERVER %s 1 %s %s J10 %s]]] +s6 :%s' % (name, ts, ts, sid, desc))
|
||||||
self._send_with_prefix(sid, "EB")
|
self._send_with_prefix(sid, "EB")
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
def handle_server(self, source, command, args):
|
def handle_server(self, source, command, args):
|
||||||
"""Handles incoming server introductions."""
|
"""Handles incoming server introductions."""
|
||||||
@ -830,11 +830,11 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sid = args[5][:2]
|
sid = args[5][:2]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(source, servername, desc=sdesc)
|
self.servers[sid] = IrcServer(source, servername, desc=sdesc)
|
||||||
|
|
||||||
if self.irc.uplink is None:
|
if self.uplink is None:
|
||||||
# If we haven't already found our uplink, this is probably it.
|
# If we haven't already found our uplink, this is probably it.
|
||||||
self.irc.uplink = sid
|
self.uplink = sid
|
||||||
|
|
||||||
return {'name': servername, 'sid': sid, 'text': sdesc}
|
return {'name': servername, 'sid': sid, 'text': sdesc}
|
||||||
|
|
||||||
@ -853,11 +853,11 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
realname = args[-1]
|
realname = args[-1]
|
||||||
|
|
||||||
log.debug('(%s) handle_nick got args: nick=%s ts=%s uid=%s ident=%s '
|
log.debug('(%s) handle_nick got args: nick=%s ts=%s uid=%s ident=%s '
|
||||||
'host=%s realname=%s realhost=%s ip=%s', self.irc.name, nick, ts, uid,
|
'host=%s realname=%s realhost=%s ip=%s', self.name, nick, ts, uid,
|
||||||
ident, host, realname, realhost, ip)
|
ident, host, realname, realhost, ip)
|
||||||
|
|
||||||
uobj = self.irc.users[uid] = IrcUser(nick, ts, uid, source, ident, host, realname, realhost, ip)
|
uobj = self.users[uid] = IrcUser(nick, ts, uid, source, ident, host, realname, realhost, ip)
|
||||||
self.irc.servers[source].users.add(uid)
|
self.servers[source].users.add(uid)
|
||||||
|
|
||||||
# https://github.com/evilnet/nefarious2/blob/master/doc/p10.txt#L708
|
# https://github.com/evilnet/nefarious2/blob/master/doc/p10.txt#L708
|
||||||
# Mode list is optional, and can be detected if the 6th argument starts with a +.
|
# Mode list is optional, and can be detected if the 6th argument starts with a +.
|
||||||
@ -865,18 +865,18 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# parameters attached.
|
# parameters attached.
|
||||||
if args[5].startswith('+'):
|
if args[5].startswith('+'):
|
||||||
modes = args[5:-3]
|
modes = args[5:-3]
|
||||||
parsedmodes = self.irc.parseModes(uid, modes)
|
parsedmodes = self.parseModes(uid, modes)
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.applyModes(uid, parsedmodes)
|
||||||
|
|
||||||
for modepair in parsedmodes:
|
for modepair in parsedmodes:
|
||||||
if modepair[0][-1] == 'r':
|
if modepair[0][-1] == 'r':
|
||||||
# Parse account registrations, sent as usermode "+r accountname:TS"
|
# Parse account registrations, sent as usermode "+r accountname:TS"
|
||||||
accountname = modepair[1].split(':', 1)[0]
|
accountname = modepair[1].split(':', 1)[0]
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
# Call the OPERED UP hook if +o is being added to the mode list.
|
# Call the OPERED UP hook if +o is being added to the mode list.
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
self.check_cloak_change(uid)
|
self.check_cloak_change(uid)
|
||||||
|
|
||||||
@ -884,21 +884,21 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
# <- ABAAA N GL_ 1460753763
|
# <- ABAAA N GL_ 1460753763
|
||||||
oldnick = self.irc.users[source].nick
|
oldnick = self.users[source].nick
|
||||||
newnick = self.irc.users[source].nick = args[0]
|
newnick = self.users[source].nick = args[0]
|
||||||
|
|
||||||
self.irc.users[source].ts = ts = int(args[1])
|
self.users[source].ts = ts = int(args[1])
|
||||||
|
|
||||||
# Update the nick TS.
|
# Update the nick TS.
|
||||||
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
||||||
|
|
||||||
def check_cloak_change(self, uid):
|
def check_cloak_change(self, uid):
|
||||||
"""Checks for cloak changes (ident and host) on the given UID."""
|
"""Checks for cloak changes (ident and host) on the given UID."""
|
||||||
uobj = self.irc.users[uid]
|
uobj = self.users[uid]
|
||||||
ident = uobj.ident
|
ident = uobj.ident
|
||||||
|
|
||||||
modes = dict(uobj.modes)
|
modes = dict(uobj.modes)
|
||||||
log.debug('(%s) check_cloak_change: modes of %s are %s', self.irc.name, uid, modes)
|
log.debug('(%s) check_cloak_change: modes of %s are %s', self.name, uid, modes)
|
||||||
|
|
||||||
if 'x' not in modes: # +x isn't set, so cloaking is disabled.
|
if 'x' not in modes: # +x isn't set, so cloaking is disabled.
|
||||||
newhost = uobj.realhost
|
newhost = uobj.realhost
|
||||||
@ -912,7 +912,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# +f represents another way of setting vHosts, via a command called FAKE.
|
# +f represents another way of setting vHosts, via a command called FAKE.
|
||||||
# Atheme uses this for vHosts, afaik.
|
# Atheme uses this for vHosts, afaik.
|
||||||
newhost = modes['f']
|
newhost = modes['f']
|
||||||
elif uobj.services_account and self.irc.serverdata.get('use_account_cloaks'):
|
elif uobj.services_account and self.serverdata.get('use_account_cloaks'):
|
||||||
# The user is registered. However, if account cloaks are enabled, we have to figure
|
# The user is registered. However, if account cloaks are enabled, we have to figure
|
||||||
# out their new cloaked host. There can be oper cloaks and user cloaks, each with
|
# out their new cloaked host. There can be oper cloaks and user cloaks, each with
|
||||||
# a different suffix. Account cloaks take the format of <accountname>.<suffix>.
|
# a different suffix. Account cloaks take the format of <accountname>.<suffix>.
|
||||||
@ -920,24 +920,24 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# someone opered and logged in as "person2" might get cloak "person.opers.somenet.org"
|
# someone opered and logged in as "person2" might get cloak "person.opers.somenet.org"
|
||||||
# This is a lot of extra configuration on the services' side, but there's nothing else
|
# This is a lot of extra configuration on the services' side, but there's nothing else
|
||||||
# we can do about it.
|
# we can do about it.
|
||||||
if self.irc.serverdata.get('use_oper_account_cloaks') and 'o' in modes:
|
if self.serverdata.get('use_oper_account_cloaks') and 'o' in modes:
|
||||||
try:
|
try:
|
||||||
# These errors should be fatal.
|
# These errors should be fatal.
|
||||||
suffix = self.irc.serverdata['oper_cloak_suffix']
|
suffix = self.serverdata['oper_cloak_suffix']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ProtocolError("(%s) use_oper_account_cloaks was enabled, but "
|
raise ProtocolError("(%s) use_oper_account_cloaks was enabled, but "
|
||||||
"oper_cloak_suffix was not defined!" % self.irc.name)
|
"oper_cloak_suffix was not defined!" % self.name)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
suffix = self.irc.serverdata['cloak_suffix']
|
suffix = self.serverdata['cloak_suffix']
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ProtocolError("(%s) use_account_cloaks was enabled, but "
|
raise ProtocolError("(%s) use_account_cloaks was enabled, but "
|
||||||
"cloak_suffix was not defined!" % self.irc.name)
|
"cloak_suffix was not defined!" % self.name)
|
||||||
|
|
||||||
accountname = uobj.services_account
|
accountname = uobj.services_account
|
||||||
newhost = "%s.%s" % (accountname, suffix)
|
newhost = "%s.%s" % (accountname, suffix)
|
||||||
|
|
||||||
elif 'C' in modes and self.irc.serverdata.get('use_account_cloaks'):
|
elif 'C' in modes and self.serverdata.get('use_account_cloaks'):
|
||||||
# +C propagates hashed IP cloaks, similar to UnrealIRCd. (thank god we don't
|
# +C propagates hashed IP cloaks, similar to UnrealIRCd. (thank god we don't
|
||||||
# need to generate these ourselves)
|
# need to generate these ourselves)
|
||||||
newhost = modes['C']
|
newhost = modes['C']
|
||||||
@ -947,9 +947,9 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
# Propagate a hostname update to plugins, but only if the changed host is different.
|
# Propagate a hostname update to plugins, but only if the changed host is different.
|
||||||
if newhost != uobj.host:
|
if newhost != uobj.host:
|
||||||
self.irc.callHooks([uid, 'CHGHOST', {'target': uid, 'newhost': newhost}])
|
self.callHooks([uid, 'CHGHOST', {'target': uid, 'newhost': newhost}])
|
||||||
if ident != uobj.ident:
|
if ident != uobj.ident:
|
||||||
self.irc.callHooks([uid, 'CHGIDENT', {'target': uid, 'newident': ident}])
|
self.callHooks([uid, 'CHGIDENT', {'target': uid, 'newident': ident}])
|
||||||
uobj.host = newhost
|
uobj.host = newhost
|
||||||
uobj.ident = ident
|
uobj.ident = ident
|
||||||
|
|
||||||
@ -972,15 +972,15 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
currtime = time.time()
|
currtime = time.time()
|
||||||
timediff = int(time.time() - float(orig_pingtime))
|
timediff = int(time.time() - float(orig_pingtime))
|
||||||
|
|
||||||
if self.irc.isInternalServer(sid):
|
if self.isInternalServer(sid):
|
||||||
# Only respond if the target server is ours. No forwarding is needed because
|
# Only respond if the target server is ours. No forwarding is needed because
|
||||||
# no IRCds can ever connect behind us...
|
# no IRCds can ever connect behind us...
|
||||||
self._send_with_prefix(self.irc.sid, 'Z %s %s %s %s' % (target, orig_pingtime, timediff, currtime), queue=False)
|
self._send_with_prefix(self.sid, 'Z %s %s %s %s' % (target, orig_pingtime, timediff, currtime), queue=False)
|
||||||
|
|
||||||
def handle_pass(self, source, command, args):
|
def handle_pass(self, source, command, args):
|
||||||
"""Handles authentication with our uplink."""
|
"""Handles authentication with our uplink."""
|
||||||
# <- PASS :testpass
|
# <- PASS :testpass
|
||||||
if args[0] != self.irc.serverdata['recvpass']:
|
if args[0] != self.serverdata['recvpass']:
|
||||||
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
||||||
|
|
||||||
def handle_burst(self, source, command, args):
|
def handle_burst(self, source, command, args):
|
||||||
@ -1002,8 +1002,8 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# No useful data was sent, ignore.
|
# No useful data was sent, ignore.
|
||||||
return
|
return
|
||||||
|
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
|
|
||||||
bans = []
|
bans = []
|
||||||
if args[-1].startswith('%'):
|
if args[-1].startswith('%'):
|
||||||
@ -1030,7 +1030,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# If no modes are given, this will simply be empty.
|
# If no modes are given, this will simply be empty.
|
||||||
modestring = args[2:-1]
|
modestring = args[2:-1]
|
||||||
if modestring:
|
if modestring:
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parseModes(channel, modestring)
|
||||||
else:
|
else:
|
||||||
parsedmodes = []
|
parsedmodes = []
|
||||||
|
|
||||||
@ -1039,7 +1039,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
namelist = []
|
namelist = []
|
||||||
prefixes = ''
|
prefixes = ''
|
||||||
userlist = args[-1].split(',')
|
userlist = args[-1].split(',')
|
||||||
log.debug('(%s) handle_burst: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_burst: got userlist %r for %r', self.name, userlist, channel)
|
||||||
|
|
||||||
if args[-1] != args[1]: # Make sure the user list is the right argument (not the TS).
|
if args[-1] != args[1]: # Make sure the user list is the right argument (not the TS).
|
||||||
for userpair in userlist:
|
for userpair in userlist:
|
||||||
@ -1052,26 +1052,26 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
user, prefixes = userpair.split(':')
|
user, prefixes = userpair.split(':')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
user = userpair
|
user = userpair
|
||||||
log.debug('(%s) handle_burst: got mode prefixes %r for user %r', self.irc.name, prefixes, user)
|
log.debug('(%s) handle_burst: got mode prefixes %r for user %r', self.name, prefixes, user)
|
||||||
|
|
||||||
# Don't crash when we get an invalid UID.
|
# Don't crash when we get an invalid UID.
|
||||||
if user not in self.irc.users:
|
if user not in self.users:
|
||||||
log.warning('(%s) handle_burst: tried to introduce user %s not in our user list, ignoring...',
|
log.warning('(%s) handle_burst: tried to introduce user %s not in our user list, ignoring...',
|
||||||
self.irc.name, user)
|
self.name, user)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
|
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only save mode changes if the remote has lower TS than us.
|
# Only save mode changes if the remote has lower TS than us.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in prefixes}
|
changedmodes |= {('+%s' % mode, user) for mode in prefixes}
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
# Statekeeping with timestamps
|
# Statekeeping with timestamps
|
||||||
their_ts = int(args[1])
|
their_ts = int(args[1])
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
self.updateTS(source, channel, their_ts, changedmodes)
|
self.updateTS(source, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
||||||
@ -1090,33 +1090,33 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
if args[0] == '0' and command == 'JOIN':
|
if args[0] == '0' and command == 'JOIN':
|
||||||
# /join 0; part the user from all channels
|
# /join 0; part the user from all channels
|
||||||
oldchans = self.irc.users[source].channels.copy()
|
oldchans = self.users[source].channels.copy()
|
||||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||||
self.irc.name, source, oldchans)
|
self.name, source, oldchans)
|
||||||
|
|
||||||
for channel in oldchans:
|
for channel in oldchans:
|
||||||
self.irc.channels[channel].users.discard(source)
|
self.channels[channel].users.discard(source)
|
||||||
self.irc.users[source].channels.discard(channel)
|
self.users[source].channels.discard(channel)
|
||||||
|
|
||||||
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||||
else:
|
else:
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
if ts: # Only update TS if one was sent.
|
if ts: # Only update TS if one was sent.
|
||||||
self.updateTS(source, channel, ts)
|
self.updateTS(source, channel, ts)
|
||||||
|
|
||||||
self.irc.users[source].channels.add(channel)
|
self.users[source].channels.add(channel)
|
||||||
self.irc.channels[channel].users.add(source)
|
self.channels[channel].users.add(source)
|
||||||
|
|
||||||
return {'channel': channel, 'users': [source], 'modes':
|
return {'channel': channel, 'users': [source], 'modes':
|
||||||
self.irc.channels[channel].modes, 'ts': ts or int(time.time())}
|
self.channels[channel].modes, 'ts': ts or int(time.time())}
|
||||||
|
|
||||||
handle_create = handle_join
|
handle_create = handle_join
|
||||||
def handle_end_of_burst(self, source, command, args):
|
def handle_end_of_burst(self, source, command, args):
|
||||||
"""Handles end of burst from our uplink."""
|
"""Handles end of burst from our uplink."""
|
||||||
# Send EOB acknowledgement; this is required by the P10 specification,
|
# Send EOB acknowledgement; this is required by the P10 specification,
|
||||||
# and needed if we want to be able to receive channel messages, etc.
|
# and needed if we want to be able to receive channel messages, etc.
|
||||||
if source == self.irc.uplink:
|
if source == self.uplink:
|
||||||
self._send_with_prefix(self.irc.sid, 'EA')
|
self._send_with_prefix(self.sid, 'EA')
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def handle_mode(self, source, command, args):
|
def handle_mode(self, source, command, args):
|
||||||
@ -1126,17 +1126,17 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# <- ABAAA OM #test +h ABAAA
|
# <- ABAAA OM #test +h ABAAA
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
target = self.irc.toLower(target)
|
target = self.toLower(target)
|
||||||
|
|
||||||
modestrings = args[1:]
|
modestrings = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modestrings)
|
changedmodes = self.parseModes(target, modestrings)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.applyModes(target, changedmodes)
|
||||||
|
|
||||||
# Call the CLIENT_OPERED hook if +o is being set.
|
# Call the CLIENT_OPERED hook if +o is being set.
|
||||||
if ('+o', None) in changedmodes and target in self.irc.users:
|
if ('+o', None) in changedmodes and target in self.users:
|
||||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.callHooks([target, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
if target in self.irc.users:
|
if target in self.users:
|
||||||
# Target was a user. Check for any cloak changes.
|
# Target was a user. Check for any cloak changes.
|
||||||
self.check_cloak_change(target)
|
self.check_cloak_change(target)
|
||||||
|
|
||||||
@ -1149,31 +1149,31 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# <- ABAAA L #test,#test2
|
# <- ABAAA L #test,#test2
|
||||||
# <- ABAAA L #test :test
|
# <- ABAAA L #test :test
|
||||||
|
|
||||||
channels = self.irc.toLower(args[0]).split(',')
|
channels = self.toLower(args[0]).split(',')
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
# We should only get PART commands for channels that exist, right??
|
# We should only get PART commands for channels that exist, right??
|
||||||
self.irc.channels[channel].removeuser(source)
|
self.channels[channel].removeuser(source)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.irc.users[source].channels.discard(channel)
|
self.users[source].channels.discard(channel)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?",
|
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?",
|
||||||
self.irc.name, channel, source)
|
self.name, channel, source)
|
||||||
try:
|
try:
|
||||||
reason = args[1]
|
reason = args[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = ''
|
reason = ''
|
||||||
|
|
||||||
# Clear empty non-permanent channels.
|
# Clear empty non-permanent channels.
|
||||||
if not self.irc.channels[channel].users:
|
if not self.channels[channel].users:
|
||||||
del self.irc.channels[channel]
|
del self.channels[channel]
|
||||||
|
|
||||||
return {'channels': channels, 'text': reason}
|
return {'channels': channels, 'text': reason}
|
||||||
|
|
||||||
def handle_kick(self, source, command, args):
|
def handle_kick(self, source, command, args):
|
||||||
"""Handles incoming KICKs."""
|
"""Handles incoming KICKs."""
|
||||||
# <- ABAAA K #TEST AyAAA :PyLink-devel
|
# <- ABAAA K #TEST AyAAA :PyLink-devel
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
kicked = args[1]
|
kicked = args[1]
|
||||||
|
|
||||||
self.handle_part(kicked, 'KICK', [channel, args[2]])
|
self.handle_part(kicked, 'KICK', [channel, args[2]])
|
||||||
@ -1187,12 +1187,12 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
def handle_topic(self, source, command, args):
|
def handle_topic(self, source, command, args):
|
||||||
"""Handles TOPIC changes."""
|
"""Handles TOPIC changes."""
|
||||||
# <- ABAAA T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
# <- ABAAA T #test GL!~gl@nefarious.midnight.vpn 1460852591 1460855795 :blah
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
|
|
||||||
oldtopic = self.irc.channels[channel].topic
|
oldtopic = self.channels[channel].topic
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
|
|
||||||
return {'channel': channel, 'setter': args[1], 'text': topic,
|
return {'channel': channel, 'setter': args[1], 'text': topic,
|
||||||
'oldtopic': oldtopic}
|
'oldtopic': oldtopic}
|
||||||
@ -1205,25 +1205,25 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
# - note that the target is a nickname, not a numeric.
|
# - note that the target is a nickname, not a numeric.
|
||||||
# <- ABAAA I PyLink-devel #services 1460948992
|
# <- ABAAA I PyLink-devel #services 1460948992
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
|
|
||||||
return {'target': target, 'channel': channel}
|
return {'target': target, 'channel': channel}
|
||||||
|
|
||||||
def handle_clearmode(self, numeric, command, args):
|
def handle_clearmode(self, numeric, command, args):
|
||||||
"""Handles CLEARMODE, which is used to clear a channel's modes."""
|
"""Handles CLEARMODE, which is used to clear a channel's modes."""
|
||||||
# <- ABAAA CM #test ovpsmikbl
|
# <- ABAAA CM #test ovpsmikbl
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
modes = args[1]
|
modes = args[1]
|
||||||
|
|
||||||
# Enumerate a list of our existing modes, including prefix modes.
|
# Enumerate a list of our existing modes, including prefix modes.
|
||||||
existing = list(self.irc.channels[channel].modes)
|
existing = list(self.channels[channel].modes)
|
||||||
for pmode, userlist in self.irc.channels[channel].prefixmodes.items():
|
for pmode, userlist in self.channels[channel].prefixmodes.items():
|
||||||
# Expand the prefix modes lists to individual ('o', 'UID') mode pairs.
|
# Expand the prefix modes lists to individual ('o', 'UID') mode pairs.
|
||||||
modechar = self.irc.cmodes.get(pmode)
|
modechar = self.cmodes.get(pmode)
|
||||||
existing += [(modechar, user) for user in userlist]
|
existing += [(modechar, user) for user in userlist]
|
||||||
|
|
||||||
# Back up the channel state.
|
# Back up the channel state.
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
|
|
||||||
changedmodes = []
|
changedmodes = []
|
||||||
|
|
||||||
@ -1233,14 +1233,14 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
# Check if each mode matches any that we're unsetting.
|
# Check if each mode matches any that we're unsetting.
|
||||||
if modechar in modes:
|
if modechar in modes:
|
||||||
if modechar in (self.irc.cmodes['*A']+self.irc.cmodes['*B']+''.join(self.irc.prefixmodes.keys())):
|
if modechar in (self.cmodes['*A']+self.cmodes['*B']+''.join(self.prefixmodes.keys())):
|
||||||
# Mode is a list mode, prefix mode, or one that always takes a parameter when unsetting.
|
# Mode is a list mode, prefix mode, or one that always takes a parameter when unsetting.
|
||||||
changedmodes.append(('-%s' % modechar, data))
|
changedmodes.append(('-%s' % modechar, data))
|
||||||
else:
|
else:
|
||||||
# Mode does not take an argument when unsetting.
|
# Mode does not take an argument when unsetting.
|
||||||
changedmodes.append(('-%s' % modechar, None))
|
changedmodes.append(('-%s' % modechar, None))
|
||||||
|
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.applyModes(channel, changedmodes)
|
||||||
return {'target': channel, 'modes': changedmodes, 'channeldata': oldobj}
|
return {'target': channel, 'modes': changedmodes, 'channeldata': oldobj}
|
||||||
|
|
||||||
def handle_account(self, numeric, command, args):
|
def handle_account(self, numeric, command, args):
|
||||||
@ -1250,7 +1250,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
target = args[0]
|
target = args[0]
|
||||||
|
|
||||||
if self.irc.serverdata.get('use_extended_accounts'):
|
if self.serverdata.get('use_extended_accounts'):
|
||||||
# Registration: <- AA AC ABAAA R GL 1459019072
|
# Registration: <- AA AC ABAAA R GL 1459019072
|
||||||
# Logout: <- AA AC ABAAA U
|
# Logout: <- AA AC ABAAA U
|
||||||
|
|
||||||
@ -1274,7 +1274,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
accountname = args[1]
|
accountname = args[1]
|
||||||
|
|
||||||
# Call this manually because we need the UID to be the sender.
|
# Call this manually because we need the UID to be the sender.
|
||||||
self.irc.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
# Check for any cloak changes now.
|
# Check for any cloak changes now.
|
||||||
self.check_cloak_change(target)
|
self.check_cloak_change(target)
|
||||||
@ -1285,7 +1285,7 @@ class P10Protocol(IRCS2SProtocol):
|
|||||||
text = args[1]
|
text = args[1]
|
||||||
|
|
||||||
# Assume a usermode +f change, and then update the cloak checking.
|
# Assume a usermode +f change, and then update the cloak checking.
|
||||||
self.irc.applyModes(target, [('+f', text)])
|
self.applyModes(target, [('+f', text)])
|
||||||
|
|
||||||
self.check_cloak_change(target)
|
self.check_cloak_change(target)
|
||||||
# We don't need to send any hooks here, check_cloak_change does that for us.
|
# We don't need to send any hooks here, check_cloak_change does that for us.
|
||||||
|
@ -19,7 +19,7 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
|
|
||||||
# Note: +r, +e, and +I support will be negotiated on link
|
# Note: +r, +e, and +I support will be negotiated on link
|
||||||
self.irc.cmodes = {'op': 'o', 'secret': 's', 'private': 'p', 'noextmsg': 'n', 'moderated': 'm',
|
self.cmodes = {'op': 'o', 'secret': 's', 'private': 'p', 'noextmsg': 'n', 'moderated': 'm',
|
||||||
'inviteonly': 'i', 'topiclock': 't', 'limit': 'l', 'ban': 'b', 'voice': 'v',
|
'inviteonly': 'i', 'topiclock': 't', 'limit': 'l', 'ban': 'b', 'voice': 'v',
|
||||||
'key': 'k', 'sslonly': 'S',
|
'key': 'k', 'sslonly': 'S',
|
||||||
'*A': 'beI',
|
'*A': 'beI',
|
||||||
@ -27,7 +27,7 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
'*C': 'l',
|
'*C': 'l',
|
||||||
'*D': 'imnpstrS'}
|
'*D': 'imnpstrS'}
|
||||||
|
|
||||||
self.irc.umodes = {
|
self.umodes = {
|
||||||
'invisible': 'i', 'callerid': 'g', 'oper': 'o', 'admin': 'a', 'sno_botfloods': 'b',
|
'invisible': 'i', 'callerid': 'g', 'oper': 'o', 'admin': 'a', 'sno_botfloods': 'b',
|
||||||
'sno_clientconnections': 'c', 'sno_extclientconnections': 'C', 'sno_debug': 'd',
|
'sno_clientconnections': 'c', 'sno_extclientconnections': 'C', 'sno_debug': 'd',
|
||||||
'sno_fullauthblock': 'f', 'sno_skill': 'k', 'locops': 'l',
|
'sno_fullauthblock': 'f', 'sno_skill': 'k', 'locops': 'l',
|
||||||
@ -51,23 +51,23 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
# parameters: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address,
|
# parameters: nickname, hopcount, nickTS, umodes, username, visible hostname, IP address,
|
||||||
# UID, gecos
|
# UID, gecos
|
||||||
|
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
|
|
||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
|
|
||||||
orig_realhost = realhost
|
orig_realhost = realhost
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
|
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
realhost=realhost, ip=ip, manipulatable=manipulatable)
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
self._send(server, "UID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
":{realname}".format(ts=ts, host=host,
|
":{realname}".format(ts=ts, host=host,
|
||||||
nick=nick, ident=ident, uid=uid,
|
nick=nick, ident=ident, uid=uid,
|
||||||
@ -86,11 +86,11 @@ class RatboxProtocol(TS6Protocol):
|
|||||||
def handle_realhost(self, uid, command, args):
|
def handle_realhost(self, uid, command, args):
|
||||||
"""Handles real host propagation."""
|
"""Handles real host propagation."""
|
||||||
log.debug('(%s) Got REALHOST %s for %s', args[0], uid)
|
log.debug('(%s) Got REALHOST %s for %s', args[0], uid)
|
||||||
self.irc.users[uid].realhost = args[0]
|
self.users[uid].realhost = args[0]
|
||||||
|
|
||||||
def handle_login(self, uid, command, args):
|
def handle_login(self, uid, command, args):
|
||||||
"""Handles login propagation on burst."""
|
"""Handles login propagation on burst."""
|
||||||
self.irc.users[uid].services_account = args[0]
|
self.users[uid].services_account = args[0]
|
||||||
return {'text': args[0]}
|
return {'text': args[0]}
|
||||||
|
|
||||||
Class = RatboxProtocol
|
Class = RatboxProtocol
|
||||||
|
272
protocols/ts6.py
272
protocols/ts6.py
@ -36,9 +36,9 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
|
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
uid = self.uidgen[server].next_uid()
|
uid = self.uidgen[server].next_uid()
|
||||||
@ -49,12 +49,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
ts = ts or int(time.time())
|
ts = ts or int(time.time())
|
||||||
realname = realname or conf.conf['bot']['realname']
|
realname = realname or conf.conf['bot']['realname']
|
||||||
realhost = realhost or host
|
realhost = realhost or host
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
|
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
self._send_with_prefix(server, "EUID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
self._send_with_prefix(server, "EUID {nick} 1 {ts} {modes} {ident} {host} {ip} {uid} "
|
||||||
"{realhost} * :{realname}".format(ts=ts, host=host,
|
"{realhost} * :{realname}".format(ts=ts, host=host,
|
||||||
@ -66,15 +66,15 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""Joins a PyLink client to a channel."""
|
"""Joins a PyLink client to a channel."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
# JOIN:
|
# JOIN:
|
||||||
# parameters: channelTS, channel, '+' (a plus sign)
|
# parameters: channelTS, channel, '+' (a plus sign)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.isInternalClient(client):
|
||||||
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to join %r to %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(client, "JOIN {ts} {channel} +".format(ts=self.irc.channels[channel].ts, channel=channel))
|
self._send_with_prefix(client, "JOIN {ts} {channel} +".format(ts=self.channels[channel].ts, channel=channel))
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -85,7 +85,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L821
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L821
|
||||||
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
||||||
@ -96,34 +96,34 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# their status ('@+', '@', '+' or ''), for example:
|
# their status ('@+', '@', '+' or ''), for example:
|
||||||
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'. All users must be behind the source server
|
# '@+1JJAAAAAB +2JJAAAA4C 1JJAAAADS'. All users must be behind the source server
|
||||||
# so it is not possible to use this message to force users to join a channel.
|
# so it is not possible to use this message to force users to join a channel.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
log.debug('(%s) sjoin: got %r for users', self.irc.name, users)
|
log.debug('(%s) sjoin: got %r for users', self.name, users)
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
modes = set(modes or self.irc.channels[channel].modes)
|
modes = set(modes or self.channels[channel].modes)
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
|
|
||||||
# Get all the ban modes in a separate list. These are bursted using a separate BMASK
|
# Get all the ban modes in a separate list. These are bursted using a separate BMASK
|
||||||
# command.
|
# command.
|
||||||
banmodes = {k: [] for k in self.irc.cmodes['*A']}
|
banmodes = {k: [] for k in self.cmodes['*A']}
|
||||||
regularmodes = []
|
regularmodes = []
|
||||||
log.debug('(%s) Unfiltered SJOIN modes: %s', self.irc.name, modes)
|
log.debug('(%s) Unfiltered SJOIN modes: %s', self.name, modes)
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
modechar = mode[0][-1]
|
modechar = mode[0][-1]
|
||||||
if modechar in self.irc.cmodes['*A']:
|
if modechar in self.cmodes['*A']:
|
||||||
# Mode character is one of 'beIq'
|
# Mode character is one of 'beIq'
|
||||||
if (modechar, mode[1]) in self.irc.channels[channel].modes:
|
if (modechar, mode[1]) in self.channels[channel].modes:
|
||||||
# Don't reset modes that are already set.
|
# Don't reset modes that are already set.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
banmodes[modechar].append(mode[1])
|
banmodes[modechar].append(mode[1])
|
||||||
else:
|
else:
|
||||||
regularmodes.append(mode)
|
regularmodes.append(mode)
|
||||||
log.debug('(%s) Filtered SJOIN modes to be regular modes: %s, banmodes: %s', self.irc.name, regularmodes, banmodes)
|
log.debug('(%s) Filtered SJOIN modes to be regular modes: %s, banmodes: %s', self.name, regularmodes, banmodes)
|
||||||
|
|
||||||
changedmodes = modes
|
changedmodes = modes
|
||||||
while users[:12]:
|
while users[:12]:
|
||||||
@ -135,22 +135,22 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
prefixes, user = userpair
|
prefixes, user = userpair
|
||||||
prefixchars = ''
|
prefixchars = ''
|
||||||
for prefix in prefixes:
|
for prefix in prefixes:
|
||||||
pr = self.irc.prefixmodes.get(prefix)
|
pr = self.prefixmodes.get(prefix)
|
||||||
if pr:
|
if pr:
|
||||||
prefixchars += pr
|
prefixchars += pr
|
||||||
changedmodes.add(('+%s' % prefix, user))
|
changedmodes.add(('+%s' % prefix, user))
|
||||||
namelist.append(prefixchars+user)
|
namelist.append(prefixchars+user)
|
||||||
uids.append(user)
|
uids.append(user)
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
users = users[12:]
|
users = users[12:]
|
||||||
namelist = ' '.join(namelist)
|
namelist = ' '.join(namelist)
|
||||||
self._send_with_prefix(server, "SJOIN {ts} {channel} {modes} :{users}".format(
|
self._send_with_prefix(server, "SJOIN {ts} {channel} {modes} :{users}".format(
|
||||||
ts=ts, users=namelist, channel=channel,
|
ts=ts, users=namelist, channel=channel,
|
||||||
modes=self.irc.joinModes(regularmodes)))
|
modes=self.joinModes(regularmodes)))
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
# Now, burst bans.
|
# Now, burst bans.
|
||||||
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
||||||
@ -158,12 +158,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# Max 15-3 = 12 bans per line to prevent cut off. (TS6 allows a max of 15 parameters per
|
# Max 15-3 = 12 bans per line to prevent cut off. (TS6 allows a max of 15 parameters per
|
||||||
# line)
|
# line)
|
||||||
if bans:
|
if bans:
|
||||||
log.debug('(%s) sjoin: bursting mode %s with bans %s, ts:%s', self.irc.name, bmode, bans, ts)
|
log.debug('(%s) sjoin: bursting mode %s with bans %s, ts:%s', self.name, bmode, bans, ts)
|
||||||
msgprefix = ':{sid} BMASK {ts} {channel} {bmode} :'.format(sid=server, ts=ts,
|
msgprefix = ':{sid} BMASK {ts} {channel} {bmode} :'.format(sid=server, ts=ts,
|
||||||
channel=channel, bmode=bmode)
|
channel=channel, bmode=bmode)
|
||||||
# Actually, we cut off at 17 arguments/line, since the prefix and command name don't count.
|
# Actually, we cut off at 17 arguments/line, since the prefix and command name don't count.
|
||||||
for msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE, max_args_per_line=17):
|
for msg in utils.wrapArguments(msgprefix, bans, S2S_BUFSIZE, max_args_per_line=17):
|
||||||
self.irc.send(msg)
|
self.send(msg)
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
@ -172,15 +172,15 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# c <- :0UYAAAAAA TMODE 0 #a +o 0T4AAAAAC
|
# c <- :0UYAAAAAA TMODE 0 #a +o 0T4AAAAAC
|
||||||
# u <- :0UYAAAAAA MODE 0UYAAAAAA :-Facdefklnou
|
# u <- :0UYAAAAAA MODE 0UYAAAAAA :-Facdefklnou
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self.irc.applyModes(target, modes)
|
self.applyModes(target, modes)
|
||||||
modes = list(modes)
|
modes = list(modes)
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.toLower(target)].ts
|
||||||
# TMODE:
|
# TMODE:
|
||||||
# parameters: channelTS, channel, cmode changes, opt. cmode parameters...
|
# parameters: channelTS, channel, cmode changes, opt. cmode parameters...
|
||||||
|
|
||||||
@ -189,40 +189,40 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
msgprefix = ':%s TMODE %s %s ' % (numeric, ts, target)
|
msgprefix = ':%s TMODE %s %s ' % (numeric, ts, target)
|
||||||
bufsize = S2S_BUFSIZE - len(msgprefix)
|
bufsize = S2S_BUFSIZE - len(msgprefix)
|
||||||
|
|
||||||
for modestr in self.irc.wrapModes(modes, bufsize, max_modes_per_msg=10):
|
for modestr in self.wrapModes(modes, bufsize, max_modes_per_msg=10):
|
||||||
self.irc.send(msgprefix + modestr)
|
self.send(msgprefix + modestr)
|
||||||
else:
|
else:
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.joinModes(modes)
|
||||||
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
self._send_with_prefix(numeric, 'MODE %s %s' % (target, joinedmodes))
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topicBurst(self, numeric, target, text):
|
||||||
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
"""Sends a topic change from a PyLink server. This is usually used on burst."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.isInternalServer(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
# TB
|
# TB
|
||||||
# capab: TB
|
# capab: TB
|
||||||
# source: server
|
# source: server
|
||||||
# propagation: broadcast
|
# propagation: broadcast
|
||||||
# parameters: channel, topicTS, opt. topic setter, topic
|
# parameters: channel, topicTS, opt. topic setter, topic
|
||||||
ts = self.irc.channels[target].ts
|
ts = self.channels[target].ts
|
||||||
servername = self.irc.servers[numeric].name
|
servername = self.servers[numeric].name
|
||||||
self._send_with_prefix(numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
|
self._send_with_prefix(numeric, 'TB %s %s %s :%s' % (target, ts, servername, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(numeric, 'INVITE %s %s %s' % (target, channel, self.irc.channels[channel].ts))
|
self._send_with_prefix(numeric, 'INVITE %s %s %s' % (target, channel, self.channels[channel].ts))
|
||||||
|
|
||||||
def knock(self, numeric, target, text):
|
def knock(self, numeric, target, text):
|
||||||
"""Sends a KNOCK from a PyLink client."""
|
"""Sends a KNOCK from a PyLink client."""
|
||||||
if 'KNOCK' not in self.irc.caps:
|
if 'KNOCK' not in self.caps:
|
||||||
log.debug('(%s) knock: Dropping KNOCK to %r since the IRCd '
|
log.debug('(%s) knock: Dropping KNOCK to %r since the IRCd '
|
||||||
'doesn\'t support it.', self.irc.name, target)
|
'doesn\'t support it.', self.name, target)
|
||||||
return
|
return
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
# No text value is supported here; drop it.
|
# No text value is supported here; drop it.
|
||||||
self._send_with_prefix(numeric, 'KNOCK %s' % target)
|
self._send_with_prefix(numeric, 'KNOCK %s' % target)
|
||||||
@ -231,12 +231,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
"""Updates the hostname of any connected client."""
|
"""Updates the hostname of any connected client."""
|
||||||
field = field.upper()
|
field = field.upper()
|
||||||
if field == 'HOST':
|
if field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGHOST %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s :%s' % (target, text))
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
# If the target isn't one of our clients, send hook payload
|
# If the target isn't one of our clients, send hook payload
|
||||||
# for other plugins to listen to.
|
# for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.callHooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
@ -245,7 +245,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
if source is None:
|
if source is None:
|
||||||
return
|
return
|
||||||
if target is not None:
|
if target is not None:
|
||||||
@ -257,10 +257,10 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.has_eob = False
|
self.has_eob = False
|
||||||
|
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80
|
||||||
chary_cmodes = { # TS6 generic modes (note that +p is noknock instead of private):
|
chary_cmodes = { # TS6 generic modes (note that +p is noknock instead of private):
|
||||||
@ -278,17 +278,17 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# Now, map all the ABCD type modes:
|
# Now, map all the ABCD type modes:
|
||||||
'*A': 'beIq', '*B': 'k', '*C': 'lfj', '*D': 'mnprstFLPQcgzCOAST'}
|
'*A': 'beIq', '*B': 'k', '*C': 'lfj', '*D': 'mnprstFLPQcgzCOAST'}
|
||||||
|
|
||||||
if self.irc.serverdata.get('use_owner'):
|
if self.serverdata.get('use_owner'):
|
||||||
chary_cmodes['owner'] = 'y'
|
chary_cmodes['owner'] = 'y'
|
||||||
self.irc.prefixmodes['y'] = '~'
|
self.prefixmodes['y'] = '~'
|
||||||
if self.irc.serverdata.get('use_admin'):
|
if self.serverdata.get('use_admin'):
|
||||||
chary_cmodes['admin'] = 'a'
|
chary_cmodes['admin'] = 'a'
|
||||||
self.irc.prefixmodes['a'] = '!'
|
self.prefixmodes['a'] = '!'
|
||||||
if self.irc.serverdata.get('use_halfop'):
|
if self.serverdata.get('use_halfop'):
|
||||||
chary_cmodes['halfop'] = 'h'
|
chary_cmodes['halfop'] = 'h'
|
||||||
self.irc.prefixmodes['h'] = '%'
|
self.prefixmodes['h'] = '%'
|
||||||
|
|
||||||
self.irc.cmodes = chary_cmodes
|
self.cmodes = chary_cmodes
|
||||||
|
|
||||||
# Define supported user modes
|
# Define supported user modes
|
||||||
chary_umodes = {'deaf': 'D', 'servprotect': 'S', 'admin': 'a',
|
chary_umodes = {'deaf': 'D', 'servprotect': 'S', 'admin': 'a',
|
||||||
@ -298,26 +298,26 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
'cloak': 'x', 'override': 'p',
|
'cloak': 'x', 'override': 'p',
|
||||||
# Now, map all the ABCD type modes:
|
# Now, map all the ABCD type modes:
|
||||||
'*A': '', '*B': '', '*C': '', '*D': 'DSaiowsQRgzlxp'}
|
'*A': '', '*B': '', '*C': '', '*D': 'DSaiowsQRgzlxp'}
|
||||||
self.irc.umodes = chary_umodes
|
self.umodes = chary_umodes
|
||||||
|
|
||||||
# Toggles support of shadowircd/elemental-ircd specific channel modes:
|
# Toggles support of shadowircd/elemental-ircd specific channel modes:
|
||||||
# +T (no notice), +u (hidden ban list), +E (no kicks), +J (blocks kickrejoin),
|
# +T (no notice), +u (hidden ban list), +E (no kicks), +J (blocks kickrejoin),
|
||||||
# +K (no repeat messages), +d (no nick changes), and user modes:
|
# +K (no repeat messages), +d (no nick changes), and user modes:
|
||||||
# +B (bot), +C (blocks CTCP), +D (deaf), +V (no invites), +I (hides channel list)
|
# +B (bot), +C (blocks CTCP), +D (deaf), +V (no invites), +I (hides channel list)
|
||||||
if self.irc.serverdata.get('use_elemental_modes'):
|
if self.serverdata.get('use_elemental_modes'):
|
||||||
elemental_cmodes = {'hiddenbans': 'u', 'nokick': 'E',
|
elemental_cmodes = {'hiddenbans': 'u', 'nokick': 'E',
|
||||||
'kicknorejoin': 'J', 'repeat': 'K', 'nonick': 'd',
|
'kicknorejoin': 'J', 'repeat': 'K', 'nonick': 'd',
|
||||||
'blockcaps': 'G'}
|
'blockcaps': 'G'}
|
||||||
self.irc.cmodes.update(elemental_cmodes)
|
self.cmodes.update(elemental_cmodes)
|
||||||
self.irc.cmodes['*D'] += ''.join(elemental_cmodes.values())
|
self.cmodes['*D'] += ''.join(elemental_cmodes.values())
|
||||||
|
|
||||||
elemental_umodes = {'noctcp': 'C', 'deaf': 'D', 'bot': 'B', 'noinvite': 'V',
|
elemental_umodes = {'noctcp': 'C', 'deaf': 'D', 'bot': 'B', 'noinvite': 'V',
|
||||||
'hidechans': 'I'}
|
'hidechans': 'I'}
|
||||||
self.irc.umodes.update(elemental_umodes)
|
self.umodes.update(elemental_umodes)
|
||||||
self.irc.umodes['*D'] += ''.join(elemental_umodes.values())
|
self.umodes['*D'] += ''.join(elemental_umodes.values())
|
||||||
|
|
||||||
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
# https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L55
|
||||||
f('PASS %s TS 6 %s' % (self.irc.serverdata["sendpass"], self.irc.sid))
|
f('PASS %s TS 6 %s' % (self.serverdata["sendpass"], self.sid))
|
||||||
|
|
||||||
# We request the following capabilities (for charybdis):
|
# We request the following capabilities (for charybdis):
|
||||||
|
|
||||||
@ -338,8 +338,8 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# EOPMOD: supports ETB (extended TOPIC burst) and =#channel messages for opmoderated +z
|
# EOPMOD: supports ETB (extended TOPIC burst) and =#channel messages for opmoderated +z
|
||||||
f('CAPAB :QS ENCAP EX CHW IE KNOCK SAVE SERVICES TB EUID RSFNC EOPMOD SAVETS_100')
|
f('CAPAB :QS ENCAP EX CHW IE KNOCK SAVE SERVICES TB EUID RSFNC EOPMOD SAVETS_100')
|
||||||
|
|
||||||
f('SERVER %s 0 :%s' % (self.irc.serverdata["hostname"],
|
f('SERVER %s 0 :%s' % (self.serverdata["hostname"],
|
||||||
self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']))
|
||||||
|
|
||||||
# Finally, end all the initialization with a PING - that's Charybdis'
|
# Finally, end all the initialization with a PING - that's Charybdis'
|
||||||
# way of saying end-of-burst :)
|
# way of saying end-of-burst :)
|
||||||
@ -352,7 +352,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
"""
|
"""
|
||||||
# <- PASS $somepassword TS 6 :42X
|
# <- PASS $somepassword TS 6 :42X
|
||||||
|
|
||||||
if args[0] != self.irc.serverdata['recvpass']:
|
if args[0] != self.serverdata['recvpass']:
|
||||||
# Check if recvpass is correct
|
# Check if recvpass is correct
|
||||||
raise ProtocolError('Recvpass from uplink server %s does not match configuration!' % servername)
|
raise ProtocolError('Recvpass from uplink server %s does not match configuration!' % servername)
|
||||||
|
|
||||||
@ -360,12 +360,12 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
raise ProtocolError("Remote protocol version is too old! Is this even TS6?")
|
raise ProtocolError("Remote protocol version is too old! Is this even TS6?")
|
||||||
|
|
||||||
numeric = args[-1]
|
numeric = args[-1]
|
||||||
log.debug('(%s) Found uplink SID as %r', self.irc.name, numeric)
|
log.debug('(%s) Found uplink SID as %r', self.name, numeric)
|
||||||
|
|
||||||
# Server name and SID are sent in different messages, so we fill this
|
# Server name and SID are sent in different messages, so we fill this
|
||||||
# with dummy information until we get the actual sid.
|
# with dummy information until we get the actual sid.
|
||||||
self.irc.servers[numeric] = IrcServer(None, '')
|
self.servers[numeric] = IrcServer(None, '')
|
||||||
self.irc.uplink = numeric
|
self.uplink = numeric
|
||||||
|
|
||||||
def handle_capab(self, numeric, command, args):
|
def handle_capab(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
@ -374,21 +374,21 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# We only get a list of keywords here. Charybdis obviously assumes that
|
# We only get a list of keywords here. Charybdis obviously assumes that
|
||||||
# we know what modes it supports (indeed, this is a standard list).
|
# we know what modes it supports (indeed, this is a standard list).
|
||||||
# <- CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SAVE SERVICES TB UNKLN
|
# <- CAPAB :BAN CHW CLUSTER ENCAP EOPMOD EUID EX IE KLN KNOCK MLOCK QS RSFNC SAVE SERVICES TB UNKLN
|
||||||
self.irc.caps = caps = args[0].split()
|
self.caps = caps = args[0].split()
|
||||||
|
|
||||||
for required_cap in self.required_caps:
|
for required_cap in self.required_caps:
|
||||||
if required_cap not in caps:
|
if required_cap not in caps:
|
||||||
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
raise ProtocolError('%s not found in TS6 capabilities list; this is required! (got %r)' % (required_cap, caps))
|
||||||
|
|
||||||
if 'EX' in caps:
|
if 'EX' in caps:
|
||||||
self.irc.cmodes['banexception'] = 'e'
|
self.cmodes['banexception'] = 'e'
|
||||||
if 'IE' in caps:
|
if 'IE' in caps:
|
||||||
self.irc.cmodes['invex'] = 'I'
|
self.cmodes['invex'] = 'I'
|
||||||
if 'SERVICES' in caps:
|
if 'SERVICES' in caps:
|
||||||
self.irc.cmodes['regonly'] = 'r'
|
self.cmodes['regonly'] = 'r'
|
||||||
|
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
|
|
||||||
def handle_ping(self, source, command, args):
|
def handle_ping(self, source, command, args):
|
||||||
"""Handles incoming PING commands."""
|
"""Handles incoming PING commands."""
|
||||||
@ -405,11 +405,11 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
try:
|
try:
|
||||||
destination = args[1]
|
destination = args[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
destination = self.irc.sid
|
destination = self.sid
|
||||||
if self.irc.isInternalServer(destination):
|
if self.isInternalServer(destination):
|
||||||
self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False)
|
self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False)
|
||||||
|
|
||||||
if destination == self.irc.sid and not self.has_eob:
|
if destination == self.sid and not self.has_eob:
|
||||||
# Charybdis' idea of endburst is just sending a PING. No, really!
|
# Charybdis' idea of endburst is just sending a PING. No, really!
|
||||||
# https://github.com/charybdis-ircd/charybdis/blob/dc336d1/modules/core/m_server.c#L484-L485
|
# https://github.com/charybdis-ircd/charybdis/blob/dc336d1/modules/core/m_server.c#L484-L485
|
||||||
self.has_eob = True
|
self.has_eob = True
|
||||||
@ -421,18 +421,18 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
"""Handles incoming SJOIN commands."""
|
"""Handles incoming SJOIN commands."""
|
||||||
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
# parameters: channelTS, channel, simple modes, opt. mode parameters..., nicklist
|
||||||
# <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB
|
# <- :0UY SJOIN 1451041566 #channel +nt :@0UYAAAAAB
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
modestring = args[2:-1] or args[2]
|
modestring = args[2:-1] or args[2]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parseModes(channel, modestring)
|
||||||
namelist = []
|
namelist = []
|
||||||
|
|
||||||
# Keep track of other modes that are added due to prefix modes being joined too.
|
# Keep track of other modes that are added due to prefix modes being joined too.
|
||||||
changedmodes = set(parsedmodes)
|
changedmodes = set(parsedmodes)
|
||||||
|
|
||||||
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.name, userlist, channel)
|
||||||
for userpair in userlist:
|
for userpair in userlist:
|
||||||
# charybdis sends this in the form "@+UID1, +UID2, UID3, @UID4"
|
# charybdis sends this in the form "@+UID1, +UID2, UID3, @UID4"
|
||||||
r = re.search(r'([^\d]*)(.*)', userpair)
|
r = re.search(r'([^\d]*)(.*)', userpair)
|
||||||
@ -440,30 +440,30 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
modeprefix = r.group(1) or ''
|
modeprefix = r.group(1) or ''
|
||||||
finalprefix = ''
|
finalprefix = ''
|
||||||
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
|
assert user, 'Failed to get the UID from %r; our regex needs updating?' % userpair
|
||||||
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)
|
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.name, modeprefix, user)
|
||||||
|
|
||||||
# Don't crash when we get an invalid UID.
|
# Don't crash when we get an invalid UID.
|
||||||
if user not in self.irc.users:
|
if user not in self.users:
|
||||||
log.debug('(%s) handle_sjoin: tried to introduce user %s not in our user list, ignoring...',
|
log.debug('(%s) handle_sjoin: tried to introduce user %s not in our user list, ignoring...',
|
||||||
self.irc.name, user)
|
self.name, user)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for m in modeprefix:
|
for m in modeprefix:
|
||||||
# Iterate over the mapping of prefix chars to prefixes, and
|
# Iterate over the mapping of prefix chars to prefixes, and
|
||||||
# find the characters that match.
|
# find the characters that match.
|
||||||
for char, prefix in self.irc.prefixmodes.items():
|
for char, prefix in self.prefixmodes.items():
|
||||||
if m == prefix:
|
if m == prefix:
|
||||||
finalprefix += char
|
finalprefix += char
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only save mode changes if the remote has lower TS than us.
|
# Only save mode changes if the remote has lower TS than us.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
# Statekeeping with timestamps
|
# Statekeeping with timestamps
|
||||||
their_ts = int(args[0])
|
their_ts = int(args[0])
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
self.updateTS(servernumeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
return {'channel': channel, 'users': namelist, 'modes': parsedmodes, 'ts': their_ts,
|
||||||
@ -476,24 +476,24 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
if args[0] == '0':
|
if args[0] == '0':
|
||||||
# /join 0; part the user from all channels
|
# /join 0; part the user from all channels
|
||||||
oldchans = self.irc.users[numeric].channels.copy()
|
oldchans = self.users[numeric].channels.copy()
|
||||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||||
self.irc.name, numeric, oldchans)
|
self.name, numeric, oldchans)
|
||||||
for channel in oldchans:
|
for channel in oldchans:
|
||||||
self.irc.channels[channel].users.discard(numeric)
|
self.channels[channel].users.discard(numeric)
|
||||||
self.irc.users[numeric].channels.discard(channel)
|
self.users[numeric].channels.discard(channel)
|
||||||
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||||
else:
|
else:
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
self.updateTS(numeric, channel, ts)
|
self.updateTS(numeric, channel, ts)
|
||||||
|
|
||||||
self.irc.users[numeric].channels.add(channel)
|
self.users[numeric].channels.add(channel)
|
||||||
self.irc.channels[channel].users.add(numeric)
|
self.channels[channel].users.add(numeric)
|
||||||
|
|
||||||
# We send users and modes here because SJOIN and JOIN both use one hook,
|
# We send users and modes here because SJOIN and JOIN both use one hook,
|
||||||
# for simplicity's sake (with plugins).
|
# for simplicity's sake (with plugins).
|
||||||
return {'channel': channel, 'users': [numeric], 'modes':
|
return {'channel': channel, 'users': [numeric], 'modes':
|
||||||
self.irc.channels[channel].modes, 'ts': ts}
|
self.channels[channel].modes, 'ts': ts}
|
||||||
|
|
||||||
def handle_euid(self, numeric, command, args):
|
def handle_euid(self, numeric, command, args):
|
||||||
"""Handles incoming EUID commands (user introduction)."""
|
"""Handles incoming EUID commands (user introduction)."""
|
||||||
@ -505,28 +505,28 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
realhost = None
|
realhost = None
|
||||||
|
|
||||||
log.debug('(%s) handle_euid got args: nick=%s ts=%s uid=%s ident=%s '
|
log.debug('(%s) handle_euid got args: nick=%s ts=%s uid=%s ident=%s '
|
||||||
'host=%s realname=%s realhost=%s ip=%s', self.irc.name, nick, ts, uid,
|
'host=%s realname=%s realhost=%s ip=%s', self.name, nick, ts, uid,
|
||||||
ident, host, realname, realhost, ip)
|
ident, host, realname, realhost, ip)
|
||||||
assert ts != 0, "Bad TS 0 for user %s" % uid
|
assert ts != 0, "Bad TS 0 for user %s" % uid
|
||||||
|
|
||||||
if ip == '0': # IP was invalid; something used for services.
|
if ip == '0': # IP was invalid; something used for services.
|
||||||
ip = '0.0.0.0'
|
ip = '0.0.0.0'
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(uid, [modes])
|
parsedmodes = self.parseModes(uid, [modes])
|
||||||
log.debug('Applying modes %s for %s', parsedmodes, uid)
|
log.debug('Applying modes %s for %s', parsedmodes, uid)
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.applyModes(uid, parsedmodes)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Call the OPERED UP hook if +o is being added to the mode list.
|
# Call the OPERED UP hook if +o is being added to the mode list.
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
otype = 'Server Administrator' if ('+a', None) in parsedmodes else 'IRC Operator'
|
otype = 'Server Administrator' if ('+a', None) in parsedmodes else 'IRC Operator'
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': otype}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': otype}])
|
||||||
|
|
||||||
# Set the accountname if present
|
# Set the accountname if present
|
||||||
if accountname != "*":
|
if accountname != "*":
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
@ -555,7 +555,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sid = args[2]
|
sid = args[2]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[sid] = IrcServer(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': sid, 'text': sdesc}
|
return {'name': servername, 'sid': sid, 'text': sdesc}
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
@ -563,35 +563,35 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
Handles 1) incoming legacy (no SID) server introductions,
|
Handles 1) incoming legacy (no SID) server introductions,
|
||||||
2) Sending server data in initial connection.
|
2) Sending server data in initial connection.
|
||||||
"""
|
"""
|
||||||
if numeric == self.irc.uplink and not self.irc.servers[numeric].name:
|
if numeric == self.uplink and not self.servers[numeric].name:
|
||||||
# <- SERVER charybdis.midnight.vpn 1 :charybdis test server
|
# <- SERVER charybdis.midnight.vpn 1 :charybdis test server
|
||||||
sname = args[0].lower()
|
sname = args[0].lower()
|
||||||
|
|
||||||
log.debug('(%s) Found uplink server name as %r', self.irc.name, sname)
|
log.debug('(%s) Found uplink server name as %r', self.name, sname)
|
||||||
self.irc.servers[numeric].name = sname
|
self.servers[numeric].name = sname
|
||||||
self.irc.servers[numeric].desc = args[-1]
|
self.servers[numeric].desc = args[-1]
|
||||||
|
|
||||||
# According to the TS6 protocol documentation, we should send SVINFO
|
# According to the TS6 protocol documentation, we should send SVINFO
|
||||||
# when we get our uplink's SERVER command.
|
# when we get our uplink's SERVER command.
|
||||||
self.irc.send('SVINFO 6 6 0 :%s' % int(time.time()))
|
self.send('SVINFO 6 6 0 :%s' % int(time.time()))
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': None, 'text': sdesc}
|
return {'name': servername, 'sid': None, 'text': sdesc}
|
||||||
|
|
||||||
def handle_tmode(self, numeric, command, args):
|
def handle_tmode(self, numeric, command, args):
|
||||||
"""Handles incoming TMODE commands (channel mode change)."""
|
"""Handles incoming TMODE commands (channel mode change)."""
|
||||||
# <- :42XAAAAAB TMODE 1437450768 #test -c+lkC 3 agte4
|
# <- :42XAAAAAB TMODE 1437450768 #test -c+lkC 3 agte4
|
||||||
# <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD
|
# <- :0UYAAAAAD TMODE 0 #a +h 0UYAAAAAD
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
modes = args[2:]
|
modes = args[2:]
|
||||||
changedmodes = self.irc.parseModes(channel, modes)
|
changedmodes = self.parseModes(channel, modes)
|
||||||
self.irc.applyModes(channel, changedmodes)
|
self.applyModes(channel, changedmodes)
|
||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
return {'target': channel, 'modes': changedmodes, 'ts': ts,
|
||||||
'channeldata': oldobj}
|
'channeldata': oldobj}
|
||||||
@ -601,42 +601,42 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
|
||||||
target = args[0]
|
target = args[0]
|
||||||
modestrings = args[1:]
|
modestrings = args[1:]
|
||||||
changedmodes = self.irc.parseModes(target, modestrings)
|
changedmodes = self.parseModes(target, modestrings)
|
||||||
self.irc.applyModes(target, changedmodes)
|
self.applyModes(target, changedmodes)
|
||||||
# Call the OPERED UP hook if +o is being set.
|
# Call the OPERED UP hook if +o is being set.
|
||||||
if ('+o', None) in changedmodes:
|
if ('+o', None) in changedmodes:
|
||||||
otype = 'Server Administrator' if ('a', None) in self.irc.users[target].modes else 'IRC Operator'
|
otype = 'Server Administrator' if ('a', None) in self.users[target].modes else 'IRC Operator'
|
||||||
self.irc.callHooks([target, 'CLIENT_OPERED', {'text': otype}])
|
self.callHooks([target, 'CLIENT_OPERED', {'text': otype}])
|
||||||
return {'target': target, 'modes': changedmodes}
|
return {'target': target, 'modes': changedmodes}
|
||||||
|
|
||||||
def handle_tb(self, numeric, command, args):
|
def handle_tb(self, numeric, command, args):
|
||||||
"""Handles incoming topic burst (TB) commands."""
|
"""Handles incoming topic burst (TB) commands."""
|
||||||
# <- :42X TB #chat 1467427448 GL!~gl@127.0.0.1 :test
|
# <- :42X TB #chat 1467427448 GL!~gl@127.0.0.1 :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
ts = args[1]
|
ts = args[1]
|
||||||
setter = args[2]
|
setter = args[2]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_etb(self, numeric, command, args):
|
def handle_etb(self, numeric, command, args):
|
||||||
"""Handles extended topic burst (ETB)."""
|
"""Handles extended topic burst (ETB)."""
|
||||||
# <- :00AAAAAAC ETB 0 #test 1470021157 GL :test | abcd
|
# <- :00AAAAAAC ETB 0 #test 1470021157 GL :test | abcd
|
||||||
# Same as TB, with extra TS and extensions arguments.
|
# Same as TB, with extra TS and extensions arguments.
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
setter = args[3]
|
setter = args[3]
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic}
|
||||||
|
|
||||||
def handle_invite(self, numeric, command, args):
|
def handle_invite(self, numeric, command, args):
|
||||||
"""Handles incoming INVITEs."""
|
"""Handles incoming INVITEs."""
|
||||||
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 12345
|
# <- :70MAAAAAC INVITE 0ALAAAAAA #blah 12345
|
||||||
target = args[0]
|
target = args[0]
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
try:
|
try:
|
||||||
ts = args[3]
|
ts = args[3]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -647,20 +647,20 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
def handle_chghost(self, numeric, command, args):
|
def handle_chghost(self, numeric, command, args):
|
||||||
"""Handles incoming CHGHOST commands."""
|
"""Handles incoming CHGHOST commands."""
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].host = newhost = args[1]
|
self.users[target].host = newhost = args[1]
|
||||||
return {'target': target, 'newhost': newhost}
|
return {'target': target, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_bmask(self, numeric, command, args):
|
def handle_bmask(self, numeric, command, args):
|
||||||
"""Handles incoming BMASK commands (ban propagation on burst)."""
|
"""Handles incoming BMASK commands (ban propagation on burst)."""
|
||||||
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
# <- :42X BMASK 1424222769 #dev b :*!test@*.isp.net *!badident@*
|
||||||
# This is used for propagating bans, not TMODE!
|
# This is used for propagating bans, not TMODE!
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
mode = args[2]
|
mode = args[2]
|
||||||
ts = int(args[0])
|
ts = int(args[0])
|
||||||
modes = []
|
modes = []
|
||||||
for ban in args[-1].split():
|
for ban in args[-1].split():
|
||||||
modes.append(('+%s' % mode, ban))
|
modes.append(('+%s' % mode, ban))
|
||||||
self.irc.applyModes(channel, modes)
|
self.applyModes(channel, modes)
|
||||||
return {'target': channel, 'modes': modes, 'ts': ts}
|
return {'target': channel, 'modes': modes, 'ts': ts}
|
||||||
|
|
||||||
def handle_472(self, numeric, command, args):
|
def handle_472(self, numeric, command, args):
|
||||||
@ -681,7 +681,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
log.warning('(%s) User %r attempted to set channel mode %r, but the '
|
log.warning('(%s) User %r attempted to set channel mode %r, but the '
|
||||||
'extension providing it isn\'t loaded! To prevent possible'
|
'extension providing it isn\'t loaded! To prevent possible'
|
||||||
' desyncs, try adding the line "loadmodule "extensions/%s.so";" to '
|
' desyncs, try adding the line "loadmodule "extensions/%s.so";" to '
|
||||||
'your IRCd configuration.', self.irc.name, setter, badmode,
|
'your IRCd configuration.', self.name, setter, badmode,
|
||||||
charlist[badmode])
|
charlist[badmode])
|
||||||
|
|
||||||
def handle_su(self, numeric, command, args):
|
def handle_su(self, numeric, command, args):
|
||||||
@ -696,7 +696,7 @@ class TS6Protocol(TS6BaseProtocol):
|
|||||||
account = '' # No account name means a logout
|
account = '' # No account name means a logout
|
||||||
|
|
||||||
uid = args[0]
|
uid = args[0]
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
def handle_rsfnc(self, numeric, command, args):
|
def handle_rsfnc(self, numeric, command, args):
|
||||||
"""
|
"""
|
||||||
|
@ -79,7 +79,7 @@ class TS6SIDGenerator():
|
|||||||
"""
|
"""
|
||||||
Returns the next unused TS6 SID for the server.
|
Returns the next unused TS6 SID for the server.
|
||||||
"""
|
"""
|
||||||
while ''.join(self.output) in self.irc.servers:
|
while ''.join(self.output) in self.servers:
|
||||||
# Increment until the SID we have doesn't already exist.
|
# Increment until the SID we have doesn't already exist.
|
||||||
self.increment()
|
self.increment()
|
||||||
sid = ''.join(self.output)
|
sid = ''.join(self.output)
|
||||||
@ -110,7 +110,7 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def _send_with_prefix(self, source, msg, **kwargs):
|
def _send_with_prefix(self, source, msg, **kwargs):
|
||||||
"""Sends a TS6-style raw command from a source numeric to the self.irc connection given."""
|
"""Sends a TS6-style raw command from a source numeric to the self.irc connection given."""
|
||||||
self.irc.send(':%s %s' % (source, msg), **kwargs)
|
self.send(':%s %s' % (source, msg), **kwargs)
|
||||||
|
|
||||||
def _expandPUID(self, uid):
|
def _expandPUID(self, uid):
|
||||||
"""
|
"""
|
||||||
@ -134,11 +134,11 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
def kick(self, numeric, channel, target, reason=None):
|
def kick(self, numeric, channel, target, reason=None):
|
||||||
"""Sends kicks from a PyLink client/server."""
|
"""Sends kicks from a PyLink client/server."""
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
if not reason:
|
if not reason:
|
||||||
reason = 'No reason given'
|
reason = 'No reason given'
|
||||||
|
|
||||||
@ -155,8 +155,8 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
def kill(self, numeric, target, reason):
|
def kill(self, numeric, target, reason):
|
||||||
"""Sends a kill from a PyLink client/server."""
|
"""Sends a kill from a PyLink client/server."""
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# From TS6 docs:
|
# From TS6 docs:
|
||||||
@ -167,41 +167,41 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
# the kill followed by a space and a parenthesized reason. To avoid overflow,
|
# the kill followed by a space and a parenthesized reason. To avoid overflow,
|
||||||
# it is recommended not to add anything to the path.
|
# it is recommended not to add anything to the path.
|
||||||
|
|
||||||
assert target in self.irc.users, "Unknown target %r for kill()!" % target
|
assert target in self.users, "Unknown target %r for kill()!" % target
|
||||||
|
|
||||||
if numeric in self.irc.users:
|
if numeric in self.users:
|
||||||
# Killer was an user. Follow examples of setting the path to be "killer.host!killer.nick".
|
# Killer was an user. Follow examples of setting the path to be "killer.host!killer.nick".
|
||||||
userobj = self.irc.users[numeric]
|
userobj = self.users[numeric]
|
||||||
killpath = '%s!%s' % (userobj.host, userobj.nick)
|
killpath = '%s!%s' % (userobj.host, userobj.nick)
|
||||||
elif numeric in self.irc.servers:
|
elif numeric in self.servers:
|
||||||
# Sender was a server; killpath is just its name.
|
# Sender was a server; killpath is just its name.
|
||||||
killpath = self.irc.servers[numeric].name
|
killpath = self.servers[numeric].name
|
||||||
else:
|
else:
|
||||||
# Invalid sender?! This shouldn't happen, but make the killpath our server name anyways.
|
# Invalid sender?! This shouldn't happen, but make the killpath our server name anyways.
|
||||||
log.warning('(%s) Invalid sender %s for kill(); using our server name instead.',
|
log.warning('(%s) Invalid sender %s for kill(); using our server name instead.',
|
||||||
self.irc.name, numeric)
|
self.name, numeric)
|
||||||
killpath = self.irc.servers[self.irc.sid].name
|
killpath = self.servers[self.sid].name
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'KILL %s :%s (%s)' % (target, killpath, reason))
|
self._send_with_prefix(numeric, 'KILL %s :%s (%s)' % (target, killpath, reason))
|
||||||
self.removeClient(target)
|
self.removeClient(target)
|
||||||
|
|
||||||
def nick(self, numeric, newnick):
|
def nick(self, numeric, newnick):
|
||||||
"""Changes the nick of a PyLink client."""
|
"""Changes the nick of a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
self._send_with_prefix(numeric, 'NICK %s %s' % (newnick, int(time.time())))
|
self._send_with_prefix(numeric, 'NICK %s %s' % (newnick, int(time.time())))
|
||||||
|
|
||||||
self.irc.users[numeric].nick = newnick
|
self.users[numeric].nick = newnick
|
||||||
|
|
||||||
# Update the NICK TS.
|
# Update the NICK TS.
|
||||||
self.irc.users[numeric].ts = int(time.time())
|
self.users[numeric].ts = int(time.time())
|
||||||
|
|
||||||
def part(self, client, channel, reason=None):
|
def part(self, client, channel, reason=None):
|
||||||
"""Sends a part from a PyLink client."""
|
"""Sends a part from a PyLink client."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.isInternalClient(client):
|
||||||
log.error('(%s) Error trying to part %r from %r (no such client exists)', self.irc.name, client, channel)
|
log.error('(%s) Error trying to part %r from %r (no such client exists)', self.name, client, channel)
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
msg = "PART %s" % channel
|
msg = "PART %s" % channel
|
||||||
if reason:
|
if reason:
|
||||||
@ -211,7 +211,7 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def quit(self, numeric, reason):
|
def quit(self, numeric, reason):
|
||||||
"""Quits a PyLink client."""
|
"""Quits a PyLink client."""
|
||||||
if self.irc.isInternalClient(numeric):
|
if self.isInternalClient(numeric):
|
||||||
self._send_with_prefix(numeric, "QUIT :%s" % reason)
|
self._send_with_prefix(numeric, "QUIT :%s" % reason)
|
||||||
self.removeClient(numeric)
|
self.removeClient(numeric)
|
||||||
else:
|
else:
|
||||||
@ -219,7 +219,7 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def message(self, numeric, target, text):
|
def message(self, numeric, target, text):
|
||||||
"""Sends a PRIVMSG from a PyLink client."""
|
"""Sends a PRIVMSG from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
|
|
||||||
# Mangle message targets for IRCds that require it.
|
# Mangle message targets for IRCds that require it.
|
||||||
@ -229,8 +229,8 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def notice(self, numeric, target, text):
|
def notice(self, numeric, target, text):
|
||||||
"""Sends a NOTICE from a PyLink client or server."""
|
"""Sends a NOTICE from a PyLink client or server."""
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
# Mangle message targets for IRCds that require it.
|
# Mangle message targets for IRCds that require it.
|
||||||
@ -240,11 +240,11 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
|
|
||||||
def topic(self, numeric, target, text):
|
def topic(self, numeric, target, text):
|
||||||
"""Sends a TOPIC change from a PyLink client."""
|
"""Sends a TOPIC change from a PyLink client."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
def spawnServer(self, name, sid=None, uplink=None, desc=None, endburst_delay=0):
|
||||||
"""
|
"""
|
||||||
@ -257,23 +257,23 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
option will be ignored if given.
|
option will be ignored if given.
|
||||||
"""
|
"""
|
||||||
# -> :0AL SID test.server 1 0XY :some silly pseudoserver
|
# -> :0AL SID test.server 1 0XY :some silly pseudoserver
|
||||||
uplink = uplink or self.irc.sid
|
uplink = uplink or self.sid
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
desc = desc or self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
desc = desc or self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
if sid is None: # No sid given; generate one!
|
if sid is None: # No sid given; generate one!
|
||||||
sid = self.sidgen.next_sid()
|
sid = self.sidgen.next_sid()
|
||||||
assert len(sid) == 3, "Incorrect SID length"
|
assert len(sid) == 3, "Incorrect SID length"
|
||||||
if sid in self.irc.servers:
|
if sid in self.servers:
|
||||||
raise ValueError('A server with SID %r already exists!' % sid)
|
raise ValueError('A server with SID %r already exists!' % sid)
|
||||||
for server in self.irc.servers.values():
|
for server in self.servers.values():
|
||||||
if name == server.name:
|
if name == server.name:
|
||||||
raise ValueError('A server named %r already exists!' % name)
|
raise ValueError('A server named %r already exists!' % name)
|
||||||
if not self.irc.isInternalServer(uplink):
|
if not self.isInternalServer(uplink):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
raise ValueError('Server %r is not a PyLink server!' % uplink)
|
||||||
if not utils.isServerName(name):
|
if not utils.isServerName(name):
|
||||||
raise ValueError('Invalid server name %r' % name)
|
raise ValueError('Invalid server name %r' % name)
|
||||||
self._send_with_prefix(uplink, 'SID %s 1 %s :%s' % (name, sid, desc))
|
self._send_with_prefix(uplink, 'SID %s 1 %s :%s' % (name, sid, desc))
|
||||||
self.irc.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
self.servers[sid] = IrcServer(uplink, name, internal=True, desc=desc)
|
||||||
return sid
|
return sid
|
||||||
|
|
||||||
def squit(self, source, target, text='No reason given'):
|
def squit(self, source, target, text='No reason given'):
|
||||||
@ -290,13 +290,13 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
self._send_with_prefix(source, 'AWAY :%s' % text)
|
self._send_with_prefix(source, 'AWAY :%s' % text)
|
||||||
else:
|
else:
|
||||||
self._send_with_prefix(source, 'AWAY')
|
self._send_with_prefix(source, 'AWAY')
|
||||||
self.irc.users[source].away = text
|
self.users[source].away = text
|
||||||
|
|
||||||
### HANDLERS
|
### HANDLERS
|
||||||
def handle_kick(self, source, command, args):
|
def handle_kick(self, source, command, args):
|
||||||
"""Handles incoming KICKs."""
|
"""Handles incoming KICKs."""
|
||||||
# :70MAAAAAA KICK #test 70MAAAAAA :some reason
|
# :70MAAAAAA KICK #test 70MAAAAAA :some reason
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
kicked = self._get_UID(args[1])
|
kicked = self._get_UID(args[1])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -304,18 +304,18 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
reason = ''
|
reason = ''
|
||||||
|
|
||||||
log.debug('(%s) Removing kick target %s from %s', self.irc.name, kicked, channel)
|
log.debug('(%s) Removing kick target %s from %s', self.name, kicked, channel)
|
||||||
self.handle_part(kicked, 'KICK', [channel, reason])
|
self.handle_part(kicked, 'KICK', [channel, reason])
|
||||||
return {'channel': channel, 'target': kicked, 'text': reason}
|
return {'channel': channel, 'target': kicked, 'text': reason}
|
||||||
|
|
||||||
def handle_nick(self, numeric, command, args):
|
def handle_nick(self, numeric, command, args):
|
||||||
"""Handles incoming NICK changes."""
|
"""Handles incoming NICK changes."""
|
||||||
# <- :70MAAAAAA NICK GL-devel 1434744242
|
# <- :70MAAAAAA NICK GL-devel 1434744242
|
||||||
oldnick = self.irc.users[numeric].nick
|
oldnick = self.users[numeric].nick
|
||||||
newnick = self.irc.users[numeric].nick = args[0]
|
newnick = self.users[numeric].nick = args[0]
|
||||||
|
|
||||||
# Update the nick TS.
|
# Update the nick TS.
|
||||||
self.irc.users[numeric].ts = ts = int(args[1])
|
self.users[numeric].ts = ts = int(args[1])
|
||||||
|
|
||||||
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
return {'newnick': newnick, 'oldnick': oldnick, 'ts': ts}
|
||||||
|
|
||||||
@ -330,12 +330,12 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
# -> :0AL000001 NICK Derp_ 1433728673
|
# -> :0AL000001 NICK Derp_ 1433728673
|
||||||
# <- :70M SAVE 0AL000001 1433728673
|
# <- :70M SAVE 0AL000001 1433728673
|
||||||
user = args[0]
|
user = args[0]
|
||||||
oldnick = self.irc.users[user].nick
|
oldnick = self.users[user].nick
|
||||||
self.irc.users[user].nick = user
|
self.users[user].nick = user
|
||||||
|
|
||||||
# TS6 SAVE sets nick TS to 100. This is hardcoded in InspIRCd and
|
# TS6 SAVE sets nick TS to 100. This is hardcoded in InspIRCd and
|
||||||
# charybdis.
|
# charybdis.
|
||||||
self.irc.users[user].ts = 100
|
self.users[user].ts = 100
|
||||||
|
|
||||||
return {'target': user, 'ts': 100, 'oldnick': oldnick}
|
return {'target': user, 'ts': 100, 'oldnick': oldnick}
|
||||||
|
|
||||||
@ -343,33 +343,33 @@ class TS6BaseProtocol(IRCS2SProtocol):
|
|||||||
"""Handles incoming TOPIC changes from clients. For topic bursts,
|
"""Handles incoming TOPIC changes from clients. For topic bursts,
|
||||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead."""
|
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead."""
|
||||||
# <- :70MAAAAAA TOPIC #test :test
|
# <- :70MAAAAAA TOPIC #test :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
topic = args[1]
|
topic = args[1]
|
||||||
|
|
||||||
oldtopic = self.irc.channels[channel].topic
|
oldtopic = self.channels[channel].topic
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
|
|
||||||
return {'channel': channel, 'setter': numeric, 'text': topic,
|
return {'channel': channel, 'setter': numeric, 'text': topic,
|
||||||
'oldtopic': oldtopic}
|
'oldtopic': oldtopic}
|
||||||
|
|
||||||
def handle_part(self, source, command, args):
|
def handle_part(self, source, command, args):
|
||||||
"""Handles incoming PART commands."""
|
"""Handles incoming PART commands."""
|
||||||
channels = self.irc.toLower(args[0]).split(',')
|
channels = self.toLower(args[0]).split(',')
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
# We should only get PART commands for channels that exist, right??
|
# We should only get PART commands for channels that exist, right??
|
||||||
self.irc.channels[channel].removeuser(source)
|
self.channels[channel].removeuser(source)
|
||||||
try:
|
try:
|
||||||
self.irc.users[source].channels.discard(channel)
|
self.users[source].channels.discard(channel)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?", self.irc.name, channel, source)
|
log.debug("(%s) handle_part: KeyError trying to remove %r from %r's channel list?", self.name, channel, source)
|
||||||
try:
|
try:
|
||||||
reason = args[1]
|
reason = args[1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
reason = ''
|
reason = ''
|
||||||
# Clear empty non-permanent channels.
|
# Clear empty non-permanent channels.
|
||||||
if not (self.irc.channels[channel].users or ((self.irc.cmodes.get('permanent'), None) in self.irc.channels[channel].modes)):
|
if not (self.channels[channel].users or ((self.cmodes.get('permanent'), None) in self.channels[channel].modes)):
|
||||||
del self.irc.channels[channel]
|
del self.channels[channel]
|
||||||
return {'channels': channels, 'text': reason}
|
return {'channels': channels, 'text': reason}
|
||||||
|
|
||||||
def handle_svsnick(self, source, command, args):
|
def handle_svsnick(self, source, command, args):
|
||||||
|
@ -34,7 +34,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
'EOS': 'ENDBURST'}
|
'EOS': 'ENDBURST'}
|
||||||
|
|
||||||
self.caps = []
|
self.caps = []
|
||||||
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
self.needed_caps = ["VL", "SID", "CHANMODES", "NOQUIT", "SJ3", "NICKIP", "UMODE2", "SJOIN"]
|
self.needed_caps = ["VL", "SID", "CHANMODES", "NOQUIT", "SJ3", "NICKIP", "UMODE2", "SJOIN"]
|
||||||
|
|
||||||
@ -47,11 +47,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
3.2 users), this will change the PUID given to the actual user's nick,
|
3.2 users), this will change the PUID given to the actual user's nick,
|
||||||
so that that the older IRCds can understand it.
|
so that that the older IRCds can understand it.
|
||||||
"""
|
"""
|
||||||
if uid in self.irc.users and '@' in uid:
|
if uid in self.users and '@' in uid:
|
||||||
# UID exists and has a @ in it, meaning it's a PUID (orignick@counter style).
|
# UID exists and has a @ in it, meaning it's a PUID (orignick@counter style).
|
||||||
# Return this user's nick accordingly.
|
# Return this user's nick accordingly.
|
||||||
nick = self.irc.users[uid].nick
|
nick = self.users[uid].nick
|
||||||
log.debug('(%s) Mangling target PUID %s to nick %s', self.irc.name, uid, nick)
|
log.debug('(%s) Mangling target PUID %s to nick %s', self.name, uid, nick)
|
||||||
return nick
|
return nick
|
||||||
return uid
|
return uid
|
||||||
|
|
||||||
@ -65,8 +65,8 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
Note: No nick collision / valid nickname checks are done here; it is
|
Note: No nick collision / valid nickname checks are done here; it is
|
||||||
up to plugins to make sure they don't introduce anything invalid.
|
up to plugins to make sure they don't introduce anything invalid.
|
||||||
"""
|
"""
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
if not self.irc.isInternalServer(server):
|
if not self.isInternalServer(server):
|
||||||
raise ValueError('Server %r is not a PyLink server!' % server)
|
raise ValueError('Server %r is not a PyLink server!' % server)
|
||||||
|
|
||||||
# Unreal 4.0 uses TS6-style UIDs. They don't start from AAAAAA like other IRCd's
|
# Unreal 4.0 uses TS6-style UIDs. They don't start from AAAAAA like other IRCd's
|
||||||
@ -81,11 +81,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
modes = set(modes) # Ensure type safety
|
modes = set(modes) # Ensure type safety
|
||||||
modes |= {('+x', None), ('+t', None)}
|
modes |= {('+x', None), ('+t', None)}
|
||||||
|
|
||||||
raw_modes = self.irc.joinModes(modes)
|
raw_modes = self.joinModes(modes)
|
||||||
u = self.irc.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
u = self.users[uid] = IrcUser(nick, ts, uid, server, ident=ident, host=host, realname=realname,
|
||||||
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
realhost=realhost, ip=ip, manipulatable=manipulatable, opertype=opertype)
|
||||||
self.irc.applyModes(uid, modes)
|
self.applyModes(uid, modes)
|
||||||
self.irc.servers[server].users.add(uid)
|
self.servers[server].users.add(uid)
|
||||||
|
|
||||||
# UnrealIRCd requires encoding the IP by first packing it into a binary format,
|
# UnrealIRCd requires encoding the IP by first packing it into a binary format,
|
||||||
# and then encoding the binary with Base64.
|
# and then encoding the binary with Base64.
|
||||||
@ -116,12 +116,12 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
def join(self, client, channel):
|
def join(self, client, channel):
|
||||||
"""Joins a PyLink client to a channel."""
|
"""Joins a PyLink client to a channel."""
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
if not self.irc.isInternalClient(client):
|
if not self.isInternalClient(client):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(client, "JOIN %s" % channel)
|
self._send_with_prefix(client, "JOIN %s" % channel)
|
||||||
self.irc.channels[channel].users.add(client)
|
self.channels[channel].users.add(client)
|
||||||
self.irc.users[client].channels.add(channel)
|
self.users[client].channels.add(channel)
|
||||||
|
|
||||||
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
def sjoin(self, server, channel, users, ts=None, modes=set()):
|
||||||
"""Sends an SJOIN for a group of users to a channel.
|
"""Sends an SJOIN for a group of users to a channel.
|
||||||
@ -132,17 +132,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
Example uses:
|
Example uses:
|
||||||
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
sjoin('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])
|
||||||
sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])
|
sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])
|
||||||
"""
|
"""
|
||||||
# <- :001 SJOIN 1444361345 #test :*@+1JJAAAAAB %2JJAAAA4C 1JJAAAADS
|
# <- :001 SJOIN 1444361345 #test :*@+1JJAAAAAB %2JJAAAA4C 1JJAAAADS
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
server = server or self.irc.sid
|
server = server or self.sid
|
||||||
assert users, "sjoin: No users sent?"
|
assert users, "sjoin: No users sent?"
|
||||||
if not server:
|
if not server:
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
|
|
||||||
changedmodes = set(modes or self.irc.channels[channel].modes)
|
changedmodes = set(modes or self.channels[channel].modes)
|
||||||
orig_ts = self.irc.channels[channel].ts
|
orig_ts = self.channels[channel].ts
|
||||||
ts = ts or orig_ts
|
ts = ts or orig_ts
|
||||||
uids = []
|
uids = []
|
||||||
itemlist = []
|
itemlist = []
|
||||||
@ -163,17 +163,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
uids.append(user)
|
uids.append(user)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
except KeyError: # Not initialized yet?
|
except KeyError: # Not initialized yet?
|
||||||
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.irc.name, channel, user)
|
log.debug("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, user)
|
||||||
|
|
||||||
# Track simple modes separately.
|
# Track simple modes separately.
|
||||||
simplemodes = set()
|
simplemodes = set()
|
||||||
for modepair in modes:
|
for modepair in modes:
|
||||||
if modepair[0][-1] in self.irc.cmodes['*A']:
|
if modepair[0][-1] in self.cmodes['*A']:
|
||||||
# Bans, exempts, invex get expanded to forms like "&*!*@some.host" in SJOIN.
|
# Bans, exempts, invex get expanded to forms like "&*!*@some.host" in SJOIN.
|
||||||
|
|
||||||
if (modepair[0][-1], modepair[1]) in self.irc.channels[channel].modes:
|
if (modepair[0][-1], modepair[1]) in self.channels[channel].modes:
|
||||||
# Mode is already set; skip it.
|
# Mode is already set; skip it.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -189,25 +189,25 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Modes are optional; add them if they exist
|
# Modes are optional; add them if they exist
|
||||||
if modes:
|
if modes:
|
||||||
sjoin_prefix += " %s" % self.irc.joinModes(simplemodes)
|
sjoin_prefix += " %s" % self.joinModes(simplemodes)
|
||||||
|
|
||||||
sjoin_prefix += " :"
|
sjoin_prefix += " :"
|
||||||
# Wrap arguments to the max supported S2S line length to prevent cutoff
|
# Wrap arguments to the max supported S2S line length to prevent cutoff
|
||||||
# (https://github.com/GLolol/PyLink/issues/378)
|
# (https://github.com/GLolol/PyLink/issues/378)
|
||||||
for line in utils.wrapArguments(sjoin_prefix, itemlist, S2S_BUFSIZE):
|
for line in utils.wrapArguments(sjoin_prefix, itemlist, S2S_BUFSIZE):
|
||||||
self.irc.send(line)
|
self.send(line)
|
||||||
|
|
||||||
self.irc.channels[channel].users.update(uids)
|
self.channels[channel].users.update(uids)
|
||||||
|
|
||||||
self.updateTS(server, channel, ts, changedmodes)
|
self.updateTS(server, channel, ts, changedmodes)
|
||||||
|
|
||||||
def ping(self, source=None, target=None):
|
def ping(self, source=None, target=None):
|
||||||
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
"""Sends a PING to a target server. Periodic PINGs are sent to our uplink
|
||||||
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
automatically by the Irc() internals; plugins shouldn't have to use this."""
|
||||||
source = source or self.irc.sid
|
source = source or self.sid
|
||||||
target = target or self.irc.uplink
|
target = target or self.uplink
|
||||||
if not (target is None or source is None):
|
if not (target is None or source is None):
|
||||||
self._send_with_prefix(source, 'PING %s %s' % (self.irc.servers[source].name, self.irc.servers[target].name))
|
self._send_with_prefix(source, 'PING %s %s' % (self.servers[source].name, self.servers[target].name))
|
||||||
|
|
||||||
def mode(self, numeric, target, modes, ts=None):
|
def mode(self, numeric, target, modes, ts=None):
|
||||||
"""
|
"""
|
||||||
@ -216,22 +216,22 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
"""
|
"""
|
||||||
# <- :unreal.midnight.vpn MODE #test +ntCo GL 1444361345
|
# <- :unreal.midnight.vpn MODE #test +ntCo GL 1444361345
|
||||||
|
|
||||||
if (not self.irc.isInternalClient(numeric)) and \
|
if (not self.isInternalClient(numeric)) and \
|
||||||
(not self.irc.isInternalServer(numeric)):
|
(not self.isInternalServer(numeric)):
|
||||||
raise LookupError('No such PyLink client/server exists.')
|
raise LookupError('No such PyLink client/server exists.')
|
||||||
|
|
||||||
self.irc.applyModes(target, modes)
|
self.applyModes(target, modes)
|
||||||
|
|
||||||
if utils.isChannel(target):
|
if utils.isChannel(target):
|
||||||
|
|
||||||
# Make sure we expand any PUIDs when sending outgoing modes...
|
# Make sure we expand any PUIDs when sending outgoing modes...
|
||||||
for idx, mode in enumerate(modes):
|
for idx, mode in enumerate(modes):
|
||||||
if mode[0][-1] in self.irc.prefixmodes:
|
if mode[0][-1] in self.prefixmodes:
|
||||||
log.debug('(%s) mode: expanding PUID of mode %s', self.irc.name, str(mode))
|
log.debug('(%s) mode: expanding PUID of mode %s', self.name, str(mode))
|
||||||
modes[idx] = (mode[0], self._expandPUID(mode[1]))
|
modes[idx] = (mode[0], self._expandPUID(mode[1]))
|
||||||
|
|
||||||
# The MODE command is used for channel mode changes only
|
# The MODE command is used for channel mode changes only
|
||||||
ts = ts or self.irc.channels[self.irc.toLower(target)].ts
|
ts = ts or self.channels[self.toLower(target)].ts
|
||||||
|
|
||||||
# 7 characters for "MODE", the space between MODE and the target, the space between the
|
# 7 characters for "MODE", the space between MODE and the target, the space between the
|
||||||
# target and mode list, and the space between the mode list and TS.
|
# target and mode list, and the space between the mode list and TS.
|
||||||
@ -242,7 +242,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
bufsize -= len(target)
|
bufsize -= len(target)
|
||||||
|
|
||||||
# Subtract the prefix (":SID " for servers or ":SIDAAAAAA " for servers)
|
# Subtract the prefix (":SID " for servers or ":SIDAAAAAA " for servers)
|
||||||
bufsize -= (5 if self.irc.isInternalServer(numeric) else 11)
|
bufsize -= (5 if self.isInternalServer(numeric) else 11)
|
||||||
|
|
||||||
# There is also an (undocumented) 15 args per line limit for MODE. The target, mode
|
# There is also an (undocumented) 15 args per line limit for MODE. The target, mode
|
||||||
# characters, and TS take up three args, so we're left with 12 spaces for parameters.
|
# characters, and TS take up three args, so we're left with 12 spaces for parameters.
|
||||||
@ -251,28 +251,28 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# * *** Warning! Possible desynch: MODE for channel #test ('+bbbbbbbbbbbb *!*@0.1 *!*@1.1 *!*@2.1 *!*@3.1 *!*@4.1 *!*@5.1 *!*@6.1 *!*@7.1 *!*@8.1 *!*@9.1 *!*@10.1 *!*@11.1') has fishy timestamp (12) (from pylink.local/pylink.local)
|
# * *** Warning! Possible desynch: MODE for channel #test ('+bbbbbbbbbbbb *!*@0.1 *!*@1.1 *!*@2.1 *!*@3.1 *!*@4.1 *!*@5.1 *!*@6.1 *!*@7.1 *!*@8.1 *!*@9.1 *!*@10.1 *!*@11.1') has fishy timestamp (12) (from pylink.local/pylink.local)
|
||||||
|
|
||||||
# Thanks to kevin and Jobe for helping me debug this!
|
# Thanks to kevin and Jobe for helping me debug this!
|
||||||
for modestring in self.irc.wrapModes(modes, bufsize, max_modes_per_msg=12):
|
for modestring in self.wrapModes(modes, bufsize, max_modes_per_msg=12):
|
||||||
self._send_with_prefix(numeric, 'MODE %s %s %s' % (target, modestring, ts))
|
self._send_with_prefix(numeric, 'MODE %s %s %s' % (target, modestring, ts))
|
||||||
else:
|
else:
|
||||||
# For user modes, the only way to set modes (for non-U:Lined servers)
|
# For user modes, the only way to set modes (for non-U:Lined servers)
|
||||||
# is through UMODE2, which sets the modes on the caller.
|
# is through UMODE2, which sets the modes on the caller.
|
||||||
# U:Lines can use SVSMODE/SVS2MODE, but I won't expect people to
|
# U:Lines can use SVSMODE/SVS2MODE, but I won't expect people to
|
||||||
# U:Line a PyLink daemon...
|
# U:Line a PyLink daemon...
|
||||||
if not self.irc.isInternalClient(target):
|
if not self.isInternalClient(target):
|
||||||
raise ProtocolError('Cannot force mode change on external clients!')
|
raise ProtocolError('Cannot force mode change on external clients!')
|
||||||
|
|
||||||
# XXX: I don't expect usermode changes to ever get cut off, but length
|
# XXX: I don't expect usermode changes to ever get cut off, but length
|
||||||
# checks could be added just to be safe...
|
# checks could be added just to be safe...
|
||||||
joinedmodes = self.irc.joinModes(modes)
|
joinedmodes = self.joinModes(modes)
|
||||||
self._send_with_prefix(target, 'UMODE2 %s' % joinedmodes)
|
self._send_with_prefix(target, 'UMODE2 %s' % joinedmodes)
|
||||||
|
|
||||||
def topicBurst(self, numeric, target, text):
|
def topicBurst(self, numeric, target, text):
|
||||||
"""Sends a TOPIC change from a PyLink server."""
|
"""Sends a TOPIC change from a PyLink server."""
|
||||||
if not self.irc.isInternalServer(numeric):
|
if not self.isInternalServer(numeric):
|
||||||
raise LookupError('No such PyLink server exists.')
|
raise LookupError('No such PyLink server exists.')
|
||||||
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
self._send_with_prefix(numeric, 'TOPIC %s :%s' % (target, text))
|
||||||
self.irc.channels[target].topic = text
|
self.channels[target].topic = text
|
||||||
self.irc.channels[target].topicset = True
|
self.channels[target].topicset = True
|
||||||
|
|
||||||
def updateClient(self, target, field, text):
|
def updateClient(self, target, field, text):
|
||||||
"""Updates the ident, host, or realname of any connected client."""
|
"""Updates the ident, host, or realname of any connected client."""
|
||||||
@ -282,44 +282,44 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
raise NotImplementedError("Changing field %r of a client is "
|
raise NotImplementedError("Changing field %r of a client is "
|
||||||
"unsupported by this protocol." % field)
|
"unsupported by this protocol." % field)
|
||||||
|
|
||||||
if self.irc.isInternalClient(target):
|
if self.isInternalClient(target):
|
||||||
# It is one of our clients, use SETIDENT/HOST/NAME.
|
# It is one of our clients, use SETIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send_with_prefix(target, 'SETIDENT %s' % text)
|
self._send_with_prefix(target, 'SETIDENT %s' % text)
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send_with_prefix(target, 'SETHOST %s' % text)
|
self._send_with_prefix(target, 'SETHOST %s' % text)
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send_with_prefix(target, 'SETNAME :%s' % text)
|
self._send_with_prefix(target, 'SETNAME :%s' % text)
|
||||||
else:
|
else:
|
||||||
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
# It is a client on another server, use CHGIDENT/HOST/NAME.
|
||||||
if field == 'IDENT':
|
if field == 'IDENT':
|
||||||
self.irc.users[target].ident = text
|
self.users[target].ident = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGIDENT %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGIDENT %s %s' % (target, text))
|
||||||
|
|
||||||
# Send hook payloads for other plugins to listen to.
|
# Send hook payloads for other plugins to listen to.
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGIDENT',
|
self.callHooks([self.sid, 'CHGIDENT',
|
||||||
{'target': target, 'newident': text}])
|
{'target': target, 'newident': text}])
|
||||||
|
|
||||||
elif field == 'HOST':
|
elif field == 'HOST':
|
||||||
self.irc.users[target].host = text
|
self.users[target].host = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGHOST %s %s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGHOST %s %s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGHOST',
|
self.callHooks([self.sid, 'CHGHOST',
|
||||||
{'target': target, 'newhost': text}])
|
{'target': target, 'newhost': text}])
|
||||||
|
|
||||||
elif field in ('REALNAME', 'GECOS'):
|
elif field in ('REALNAME', 'GECOS'):
|
||||||
self.irc.users[target].realname = text
|
self.users[target].realname = text
|
||||||
self._send_with_prefix(self.irc.sid, 'CHGNAME %s :%s' % (target, text))
|
self._send_with_prefix(self.sid, 'CHGNAME %s :%s' % (target, text))
|
||||||
|
|
||||||
self.irc.callHooks([self.irc.sid, 'CHGNAME',
|
self.callHooks([self.sid, 'CHGNAME',
|
||||||
{'target': target, 'newgecos': text}])
|
{'target': target, 'newgecos': text}])
|
||||||
|
|
||||||
def invite(self, numeric, target, channel):
|
def invite(self, numeric, target, channel):
|
||||||
"""Sends an INVITE from a PyLink client.."""
|
"""Sends an INVITE from a PyLink client.."""
|
||||||
if not self.irc.isInternalClient(numeric):
|
if not self.isInternalClient(numeric):
|
||||||
raise LookupError('No such PyLink client exists.')
|
raise LookupError('No such PyLink client exists.')
|
||||||
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))
|
||||||
|
|
||||||
@ -329,21 +329,21 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# sent to all ops in a channel.
|
# sent to all ops in a channel.
|
||||||
# <- :unreal.midnight.vpn NOTICE @#test :[Knock] by GL|!gl@hidden-1C620195 (test)
|
# <- :unreal.midnight.vpn NOTICE @#test :[Knock] by GL|!gl@hidden-1C620195 (test)
|
||||||
assert utils.isChannel(target), "Can only knock on channels!"
|
assert utils.isChannel(target), "Can only knock on channels!"
|
||||||
sender = self.irc.getServer(numeric)
|
sender = self.getServer(numeric)
|
||||||
s = '[Knock] by %s (%s)' % (self.irc.getHostmask(numeric), text)
|
s = '[Knock] by %s (%s)' % (self.getHostmask(numeric), text)
|
||||||
self._send_with_prefix(sender, 'NOTICE @%s :%s' % (target, s))
|
self._send_with_prefix(sender, 'NOTICE @%s :%s' % (target, s))
|
||||||
|
|
||||||
### HANDLERS
|
### HANDLERS
|
||||||
|
|
||||||
def post_connect(self):
|
def post_connect(self):
|
||||||
"""Initializes a connection to a server."""
|
"""Initializes a connection to a server."""
|
||||||
ts = self.irc.start_ts
|
ts = self.start_ts
|
||||||
self.irc.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
self.prefixmodes = {'q': '~', 'a': '&', 'o': '@', 'h': '%', 'v': '+'}
|
||||||
|
|
||||||
# Track usages of legacy (Unreal 3.2) nicks.
|
# Track usages of legacy (Unreal 3.2) nicks.
|
||||||
self.legacy_uidgen = utils.PUIDGenerator('U32user')
|
self.legacy_uidgen = utils.PUIDGenerator('U32user')
|
||||||
|
|
||||||
self.irc.umodes.update({'deaf': 'd', 'invisible': 'i', 'hidechans': 'p',
|
self.umodes.update({'deaf': 'd', 'invisible': 'i', 'hidechans': 'p',
|
||||||
'protected': 'q', 'registered': 'r',
|
'protected': 'q', 'registered': 'r',
|
||||||
'snomask': 's', 'vhost': 't', 'wallops': 'w',
|
'snomask': 's', 'vhost': 't', 'wallops': 'w',
|
||||||
'bot': 'B', 'cloak': 'x', 'ssl': 'z',
|
'bot': 'B', 'cloak': 'x', 'ssl': 'z',
|
||||||
@ -352,10 +352,10 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
'noctcp': 'T', 'showwhois': 'W',
|
'noctcp': 'T', 'showwhois': 'W',
|
||||||
'*A': '', '*B': '', '*C': '', '*D': 'dipqrstwBxzGHIRSTW'})
|
'*A': '', '*B': '', '*C': '', '*D': 'dipqrstwBxzGHIRSTW'})
|
||||||
|
|
||||||
f = self.irc.send
|
f = self.send
|
||||||
host = self.irc.serverdata["hostname"]
|
host = self.serverdata["hostname"]
|
||||||
|
|
||||||
f('PASS :%s' % self.irc.serverdata["sendpass"])
|
f('PASS :%s' % self.serverdata["sendpass"])
|
||||||
# https://github.com/unrealircd/unrealircd/blob/2f8cb55e/doc/technical/protoctl.txt
|
# https://github.com/unrealircd/unrealircd/blob/2f8cb55e/doc/technical/protoctl.txt
|
||||||
# We support the following protocol features:
|
# We support the following protocol features:
|
||||||
# SJOIN - supports SJOIN for user introduction
|
# SJOIN - supports SJOIN for user introduction
|
||||||
@ -374,11 +374,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# not work for any UnrealIRCd 3.2 users.
|
# not work for any UnrealIRCd 3.2 users.
|
||||||
# ESVID - Supports account names in services stamps instead of just the signon time.
|
# ESVID - Supports account names in services stamps instead of just the signon time.
|
||||||
# AFAIK this doesn't actually affect services' behaviour?
|
# AFAIK this doesn't actually affect services' behaviour?
|
||||||
f('PROTOCTL SJOIN SJ3 NOQUIT NICKv2 VL UMODE2 PROTOCTL NICKIP EAUTH=%s SID=%s VHP ESVID' % (self.irc.serverdata["hostname"], self.irc.sid))
|
f('PROTOCTL SJOIN SJ3 NOQUIT NICKv2 VL UMODE2 PROTOCTL NICKIP EAUTH=%s SID=%s VHP ESVID' % (self.serverdata["hostname"], self.sid))
|
||||||
sdesc = self.irc.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
sdesc = self.serverdata.get('serverdesc') or conf.conf['bot']['serverdesc']
|
||||||
f('SERVER %s 1 U%s-h6e-%s :%s' % (host, self.proto_ver, self.irc.sid, sdesc))
|
f('SERVER %s 1 U%s-h6e-%s :%s' % (host, self.proto_ver, self.sid, sdesc))
|
||||||
f('NETINFO 1 %s %s * 0 0 0 :%s' % (self.irc.start_ts, self.proto_ver, self.irc.serverdata.get("netname", self.irc.name)))
|
f('NETINFO 1 %s %s * 0 0 0 :%s' % (self.start_ts, self.proto_ver, self.serverdata.get("netname", self.name)))
|
||||||
self._send_with_prefix(self.irc.sid, 'EOS')
|
self._send_with_prefix(self.sid, 'EOS')
|
||||||
|
|
||||||
def handle_eos(self, numeric, command, args):
|
def handle_eos(self, numeric, command, args):
|
||||||
"""EOS is used to denote end of burst."""
|
"""EOS is used to denote end of burst."""
|
||||||
@ -418,50 +418,50 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
realname = args[-1]
|
realname = args[-1]
|
||||||
|
|
||||||
self.irc.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
self.users[uid] = IrcUser(nick, ts, uid, numeric, ident, host, realname, realhost, ip)
|
||||||
self.irc.servers[numeric].users.add(uid)
|
self.servers[numeric].users.add(uid)
|
||||||
|
|
||||||
# Handle user modes
|
# Handle user modes
|
||||||
parsedmodes = self.irc.parseModes(uid, [modestring])
|
parsedmodes = self.parseModes(uid, [modestring])
|
||||||
self.irc.applyModes(uid, parsedmodes)
|
self.applyModes(uid, parsedmodes)
|
||||||
|
|
||||||
# The cloaked (+x) host is completely separate from the displayed host
|
# The cloaked (+x) host is completely separate from the displayed host
|
||||||
# and real host in that it is ONLY shown if the user is +x (cloak mode
|
# and real host in that it is ONLY shown if the user is +x (cloak mode
|
||||||
# enabled) but NOT +t (vHost set).
|
# enabled) but NOT +t (vHost set).
|
||||||
self.irc.users[uid].cloaked_host = args[9]
|
self.users[uid].cloaked_host = args[9]
|
||||||
|
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
# If +o being set, call the CLIENT_OPERED internal hook.
|
# If +o being set, call the CLIENT_OPERED internal hook.
|
||||||
self.irc.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.callHooks([uid, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
if ('+x', None) not in parsedmodes:
|
if ('+x', None) not in parsedmodes:
|
||||||
# If +x is not set, update to use the person's real host.
|
# If +x is not set, update to use the person's real host.
|
||||||
self.irc.users[uid].host = realhost
|
self.users[uid].host = realhost
|
||||||
|
|
||||||
# Set the account name if present: if this is a number, set it to the user nick.
|
# Set the account name if present: if this is a number, set it to the user nick.
|
||||||
if ('+r', None) in parsedmodes and accountname.isdigit():
|
if ('+r', None) in parsedmodes and accountname.isdigit():
|
||||||
accountname = nick
|
accountname = nick
|
||||||
|
|
||||||
if not accountname.isdigit():
|
if not accountname.isdigit():
|
||||||
self.irc.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
self.callHooks([uid, 'CLIENT_SERVICES_LOGIN', {'text': accountname}])
|
||||||
|
|
||||||
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
return {'uid': uid, 'ts': ts, 'nick': nick, 'realhost': realhost, 'host': host, 'ident': ident, 'ip': ip}
|
||||||
|
|
||||||
def handle_pass(self, numeric, command, args):
|
def handle_pass(self, numeric, command, args):
|
||||||
# <- PASS :abcdefg
|
# <- PASS :abcdefg
|
||||||
if args[0] != self.irc.serverdata['recvpass']:
|
if args[0] != self.serverdata['recvpass']:
|
||||||
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
|
||||||
|
|
||||||
def handle_ping(self, numeric, command, args):
|
def handle_ping(self, numeric, command, args):
|
||||||
if numeric == self.irc.uplink:
|
if numeric == self.uplink:
|
||||||
self.irc.send('PONG %s :%s' % (self.irc.serverdata['hostname'], args[-1]), queue=False)
|
self.send('PONG %s :%s' % (self.serverdata['hostname'], args[-1]), queue=False)
|
||||||
|
|
||||||
def handle_server(self, numeric, command, args):
|
def handle_server(self, numeric, command, args):
|
||||||
"""Handles the SERVER command, which is used for both authentication and
|
"""Handles the SERVER command, which is used for both authentication and
|
||||||
introducing legacy (non-SID) servers."""
|
introducing legacy (non-SID) servers."""
|
||||||
# <- SERVER unreal.midnight.vpn 1 :U3999-Fhin6OoEM UnrealIRCd test server
|
# <- SERVER unreal.midnight.vpn 1 :U3999-Fhin6OoEM UnrealIRCd test server
|
||||||
sname = args[0]
|
sname = args[0]
|
||||||
if numeric == self.irc.uplink and not self.irc.connected.is_set(): # We're doing authentication
|
if numeric == self.uplink and not self.connected.is_set(): # We're doing authentication
|
||||||
for cap in self.needed_caps:
|
for cap in self.needed_caps:
|
||||||
if cap not in self.caps:
|
if cap not in self.caps:
|
||||||
raise ProtocolError("Not all required capabilities were met "
|
raise ProtocolError("Not all required capabilities were met "
|
||||||
@ -485,17 +485,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
if protover < self.min_proto_ver:
|
if protover < self.min_proto_ver:
|
||||||
raise ProtocolError("Protocol version too old! (needs at least %s "
|
raise ProtocolError("Protocol version too old! (needs at least %s "
|
||||||
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
"(Unreal 4.x), got %s)" % (self.min_proto_ver, protover))
|
||||||
self.irc.servers[numeric] = IrcServer(None, sname, desc=sdesc)
|
self.servers[numeric] = IrcServer(None, sname, desc=sdesc)
|
||||||
|
|
||||||
# Set irc.connected to True, meaning that protocol negotiation passed.
|
# Set irc.connected to True, meaning that protocol negotiation passed.
|
||||||
log.debug('(%s) self.irc.connected set!', self.irc.name)
|
log.debug('(%s) self.connected set!', self.name)
|
||||||
self.irc.connected.set()
|
self.connected.set()
|
||||||
else:
|
else:
|
||||||
# Legacy (non-SID) servers can still be introduced using the SERVER command.
|
# Legacy (non-SID) servers can still be introduced using the SERVER command.
|
||||||
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
# <- :services.int SERVER a.bc 2 :(H) [GL] a
|
||||||
servername = args[0].lower()
|
servername = args[0].lower()
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
self.servers[servername] = IrcServer(numeric, servername, desc=sdesc)
|
||||||
return {'name': servername, 'sid': None, 'text': sdesc}
|
return {'name': servername, 'sid': None, 'text': sdesc}
|
||||||
|
|
||||||
def handle_sid(self, numeric, command, args):
|
def handle_sid(self, numeric, command, args):
|
||||||
@ -504,7 +504,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
sname = args[0].lower()
|
sname = args[0].lower()
|
||||||
sid = args[2]
|
sid = args[2]
|
||||||
sdesc = args[-1]
|
sdesc = args[-1]
|
||||||
self.irc.servers[sid] = IrcServer(numeric, sname, desc=sdesc)
|
self.servers[sid] = IrcServer(numeric, sname, desc=sdesc)
|
||||||
return {'name': sname, 'sid': sid, 'text': sdesc}
|
return {'name': sname, 'sid': sid, 'text': sdesc}
|
||||||
|
|
||||||
def handle_squit(self, numeric, command, args):
|
def handle_squit(self, numeric, command, args):
|
||||||
@ -534,18 +534,18 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- PROTOCTL CHANMODES=beI,k,l,psmntirzMQNRTOVKDdGPZSCc NICKCHARS= SID=001 MLOCK TS=1441314501 EXTSWHOIS
|
# <- PROTOCTL CHANMODES=beI,k,l,psmntirzMQNRTOVKDdGPZSCc NICKCHARS= SID=001 MLOCK TS=1441314501 EXTSWHOIS
|
||||||
for cap in args:
|
for cap in args:
|
||||||
if cap.startswith('SID'):
|
if cap.startswith('SID'):
|
||||||
self.irc.uplink = cap.split('=', 1)[1]
|
self.uplink = cap.split('=', 1)[1]
|
||||||
elif cap.startswith('CHANMODES'):
|
elif cap.startswith('CHANMODES'):
|
||||||
# Parse all the supported channel modes.
|
# Parse all the supported channel modes.
|
||||||
supported_cmodes = cap.split('=', 1)[1]
|
supported_cmodes = cap.split('=', 1)[1]
|
||||||
self.irc.cmodes['*A'], self.irc.cmodes['*B'], self.irc.cmodes['*C'], self.irc.cmodes['*D'] = supported_cmodes.split(',')
|
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = supported_cmodes.split(',')
|
||||||
for namedmode, modechar in cmodes.items():
|
for namedmode, modechar in cmodes.items():
|
||||||
if modechar in supported_cmodes:
|
if modechar in supported_cmodes:
|
||||||
self.irc.cmodes[namedmode] = modechar
|
self.cmodes[namedmode] = modechar
|
||||||
self.irc.cmodes['*B'] += 'f' # Add +f to the list too, dunno why it isn't there.
|
self.cmodes['*B'] += 'f' # Add +f to the list too, dunno why it isn't there.
|
||||||
|
|
||||||
# Add in the supported prefix modes.
|
# Add in the supported prefix modes.
|
||||||
self.irc.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
|
self.cmodes.update({'halfop': 'h', 'admin': 'a', 'owner': 'q',
|
||||||
'op': 'o', 'voice': 'v'})
|
'op': 'o', 'voice': 'v'})
|
||||||
|
|
||||||
def handle_join(self, numeric, command, args):
|
def handle_join(self, numeric, command, args):
|
||||||
@ -553,38 +553,38 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- :GL JOIN #pylink,#test
|
# <- :GL JOIN #pylink,#test
|
||||||
if args[0] == '0':
|
if args[0] == '0':
|
||||||
# /join 0; part the user from all channels
|
# /join 0; part the user from all channels
|
||||||
oldchans = self.irc.users[numeric].channels.copy()
|
oldchans = self.users[numeric].channels.copy()
|
||||||
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
log.debug('(%s) Got /join 0 from %r, channel list is %r',
|
||||||
self.irc.name, numeric, oldchans)
|
self.name, numeric, oldchans)
|
||||||
for ch in oldchans:
|
for ch in oldchans:
|
||||||
self.irc.channels[ch].users.discard(numeric)
|
self.channels[ch].users.discard(numeric)
|
||||||
self.irc.users[numeric].channels.discard(ch)
|
self.users[numeric].channels.discard(ch)
|
||||||
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
return {'channels': oldchans, 'text': 'Left all channels.', 'parse_as': 'PART'}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for channel in args[0].split(','):
|
for channel in args[0].split(','):
|
||||||
# Normalize channel case.
|
# Normalize channel case.
|
||||||
channel = self.irc.toLower(channel)
|
channel = self.toLower(channel)
|
||||||
|
|
||||||
c = self.irc.channels[channel]
|
c = self.channels[channel]
|
||||||
|
|
||||||
self.irc.users[numeric].channels.add(channel)
|
self.users[numeric].channels.add(channel)
|
||||||
self.irc.channels[channel].users.add(numeric)
|
self.channels[channel].users.add(numeric)
|
||||||
# Call hooks manually, because one JOIN command in UnrealIRCd can
|
# Call hooks manually, because one JOIN command in UnrealIRCd can
|
||||||
# have multiple channels...
|
# have multiple channels...
|
||||||
self.irc.callHooks([numeric, command, {'channel': channel, 'users': [numeric], 'modes':
|
self.callHooks([numeric, command, {'channel': channel, 'users': [numeric], 'modes':
|
||||||
c.modes, 'ts': c.ts}])
|
c.modes, 'ts': c.ts}])
|
||||||
|
|
||||||
def handle_sjoin(self, numeric, command, args):
|
def handle_sjoin(self, numeric, command, args):
|
||||||
"""Handles the UnrealIRCd SJOIN command."""
|
"""Handles the UnrealIRCd SJOIN command."""
|
||||||
# <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC
|
# <- :001 SJOIN 1444361345 #test :001AAAAAA @001AAAAAB +001AAAAAC
|
||||||
# <- :001 SJOIN 1483250129 #services +nt :+001OR9V02 @*~001DH6901 &*!*@test "*!*@blah.blah '*!*@yes.no
|
# <- :001 SJOIN 1483250129 #services +nt :+001OR9V02 @*~001DH6901 &*!*@test "*!*@blah.blah '*!*@yes.no
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
chandata = self.irc.channels[channel].deepcopy()
|
chandata = self.channels[channel].deepcopy()
|
||||||
userlist = args[-1].split()
|
userlist = args[-1].split()
|
||||||
|
|
||||||
namelist = []
|
namelist = []
|
||||||
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.irc.name, userlist, channel)
|
log.debug('(%s) handle_sjoin: got userlist %r for %r', self.name, userlist, channel)
|
||||||
|
|
||||||
modestring = ''
|
modestring = ''
|
||||||
|
|
||||||
@ -599,7 +599,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# Strip extra spaces between the mode argument and the user list, if
|
# Strip extra spaces between the mode argument and the user list, if
|
||||||
# there are any. XXX: report this as a bug in unreal's s2s protocol?
|
# there are any. XXX: report this as a bug in unreal's s2s protocol?
|
||||||
modestring = [m for m in modestring if m]
|
modestring = [m for m in modestring if m]
|
||||||
parsedmodes = self.irc.parseModes(channel, modestring)
|
parsedmodes = self.parseModes(channel, modestring)
|
||||||
changedmodes = set(parsedmodes)
|
changedmodes = set(parsedmodes)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
@ -630,22 +630,22 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
modeprefix = (r.group(1) or '').replace("~", "&").replace("*", "~")
|
modeprefix = (r.group(1) or '').replace("~", "&").replace("*", "~")
|
||||||
finalprefix = ''
|
finalprefix = ''
|
||||||
|
|
||||||
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.irc.name, modeprefix, user)
|
log.debug('(%s) handle_sjoin: got modeprefix %r for user %r', self.name, modeprefix, user)
|
||||||
for m in modeprefix:
|
for m in modeprefix:
|
||||||
# Iterate over the mapping of prefix chars to prefixes, and
|
# Iterate over the mapping of prefix chars to prefixes, and
|
||||||
# find the characters that match.
|
# find the characters that match.
|
||||||
for char, prefix in self.irc.prefixmodes.items():
|
for char, prefix in self.prefixmodes.items():
|
||||||
if m == prefix:
|
if m == prefix:
|
||||||
finalprefix += char
|
finalprefix += char
|
||||||
namelist.append(user)
|
namelist.append(user)
|
||||||
self.irc.users[user].channels.add(channel)
|
self.users[user].channels.add(channel)
|
||||||
|
|
||||||
# Only merge the remote's prefix modes if their TS is smaller or equal to ours.
|
# Only merge the remote's prefix modes if their TS is smaller or equal to ours.
|
||||||
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
changedmodes |= {('+%s' % mode, user) for mode in finalprefix}
|
||||||
|
|
||||||
self.irc.channels[channel].users.add(user)
|
self.channels[channel].users.add(user)
|
||||||
|
|
||||||
our_ts = self.irc.channels[channel].ts
|
our_ts = self.channels[channel].ts
|
||||||
their_ts = int(args[0])
|
their_ts = int(args[0])
|
||||||
self.updateTS(numeric, channel, their_ts, changedmodes)
|
self.updateTS(numeric, channel, their_ts, changedmodes)
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- NICK GL32 2 1470699865 gl localhost unreal32.midnight.vpn GL +iowx hidden-1C620195 AAAAAAAAAAAAAAAAAAAAAQ== :realname
|
# <- NICK GL32 2 1470699865 gl localhost unreal32.midnight.vpn GL +iowx hidden-1C620195 AAAAAAAAAAAAAAAAAAAAAQ== :realname
|
||||||
# to this:
|
# to this:
|
||||||
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * hidden-1C620195 fwAAAQ== :realname
|
# <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * hidden-1C620195 fwAAAQ== :realname
|
||||||
log.debug('(%s) got legacy NICK args: %s', self.irc.name, ' '.join(args))
|
log.debug('(%s) got legacy NICK args: %s', self.name, ' '.join(args))
|
||||||
|
|
||||||
new_args = args[:] # Clone the old args list
|
new_args = args[:] # Clone the old args list
|
||||||
servername = new_args[5].lower() # Get the name of the users' server.
|
servername = new_args[5].lower() # Get the name of the users' server.
|
||||||
@ -682,7 +682,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# hosts from UnrealIRCd 3.2 users. Otherwise, +x host cloaking won't work!
|
# hosts from UnrealIRCd 3.2 users. Otherwise, +x host cloaking won't work!
|
||||||
new_args.insert(-2, args[4])
|
new_args.insert(-2, args[4])
|
||||||
|
|
||||||
log.debug('(%s) translating legacy NICK args to: %s', self.irc.name, ' '.join(new_args))
|
log.debug('(%s) translating legacy NICK args to: %s', self.name, ' '.join(new_args))
|
||||||
|
|
||||||
return self.handle_uid(servername, 'UID_LEGACY', new_args)
|
return self.handle_uid(servername, 'UID_LEGACY', new_args)
|
||||||
else:
|
else:
|
||||||
@ -705,11 +705,11 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
# Also, we need to get rid of that extra space following the +f argument. :|
|
# Also, we need to get rid of that extra space following the +f argument. :|
|
||||||
if utils.isChannel(args[0]):
|
if utils.isChannel(args[0]):
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
oldobj = self.irc.channels[channel].deepcopy()
|
oldobj = self.channels[channel].deepcopy()
|
||||||
|
|
||||||
modes = [arg for arg in args[1:] if arg] # normalize whitespace
|
modes = [arg for arg in args[1:] if arg] # normalize whitespace
|
||||||
parsedmodes = self.irc.parseModes(channel, modes)
|
parsedmodes = self.parseModes(channel, modes)
|
||||||
|
|
||||||
if parsedmodes:
|
if parsedmodes:
|
||||||
if parsedmodes[0][0] == '+&':
|
if parsedmodes[0][0] == '+&':
|
||||||
@ -717,12 +717,12 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# attempt to set modes by us was rejected for some reason (usually due to
|
# attempt to set modes by us was rejected for some reason (usually due to
|
||||||
# timestamps). Drop the mode change to prevent mode floods.
|
# timestamps). Drop the mode change to prevent mode floods.
|
||||||
log.debug("(%s) Received mode bounce %s in channel %s! Our TS: %s",
|
log.debug("(%s) Received mode bounce %s in channel %s! Our TS: %s",
|
||||||
self.irc.name, modes, channel, self.irc.channels[channel].ts)
|
self.name, modes, channel, self.channels[channel].ts)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.applyModes(channel, parsedmodes)
|
self.applyModes(channel, parsedmodes)
|
||||||
|
|
||||||
if numeric in self.irc.servers and args[-1].isdigit():
|
if numeric in self.servers and args[-1].isdigit():
|
||||||
# Sender is a server AND last arg is number. Perform TS updates.
|
# Sender is a server AND last arg is number. Perform TS updates.
|
||||||
their_ts = int(args[-1])
|
their_ts = int(args[-1])
|
||||||
if their_ts > 0:
|
if their_ts > 0:
|
||||||
@ -738,7 +738,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
hostname of the user given to or from their cloaked host if True.
|
hostname of the user given to or from their cloaked host if True.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
userobj = self.irc.users[uid]
|
userobj = self.users[uid]
|
||||||
final_modes = userobj.modes
|
final_modes = userobj.modes
|
||||||
oldhost = userobj.host
|
oldhost = userobj.host
|
||||||
|
|
||||||
@ -763,7 +763,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
if newhost != oldhost:
|
if newhost != oldhost:
|
||||||
# Only send a payload if the old and new hosts are different.
|
# Only send a payload if the old and new hosts are different.
|
||||||
self.irc.callHooks([uid, 'SETHOST',
|
self.callHooks([uid, 'SETHOST',
|
||||||
{'target': uid, 'newhost': newhost}])
|
{'target': uid, 'newhost': newhost}])
|
||||||
|
|
||||||
def handle_svsmode(self, numeric, command, args):
|
def handle_svsmode(self, numeric, command, args):
|
||||||
@ -772,8 +772,8 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
modes = args[1:]
|
modes = args[1:]
|
||||||
|
|
||||||
parsedmodes = self.irc.parseModes(target, modes)
|
parsedmodes = self.parseModes(target, modes)
|
||||||
self.irc.applyModes(target, parsedmodes)
|
self.applyModes(target, parsedmodes)
|
||||||
|
|
||||||
# If +x/-x is being set, update cloaked host info.
|
# If +x/-x is being set, update cloaked host info.
|
||||||
self.checkCloakChange(target, parsedmodes)
|
self.checkCloakChange(target, parsedmodes)
|
||||||
@ -818,7 +818,7 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
# <- :NickServ SVS2MODE 001SALZ01 +r
|
# <- :NickServ SVS2MODE 001SALZ01 +r
|
||||||
|
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
parsedmodes = self.irc.parseModes(target, args[1:])
|
parsedmodes = self.parseModes(target, args[1:])
|
||||||
|
|
||||||
if ('+r', None) in parsedmodes:
|
if ('+r', None) in parsedmodes:
|
||||||
# Umode +r is being set (log in)
|
# Umode +r is being set (log in)
|
||||||
@ -828,19 +828,19 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
# If one doesn't exist, make it the same as the nick, but only if the account name
|
# If one doesn't exist, make it the same as the nick, but only if the account name
|
||||||
# wasn't set already.
|
# wasn't set already.
|
||||||
if not self.irc.users[target].services_account:
|
if not self.users[target].services_account:
|
||||||
account = self.irc.getFriendlyName(target)
|
account = self.getFriendlyName(target)
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if account.isdigit():
|
if account.isdigit():
|
||||||
# If the +d argument is a number, ignore it and set the account name to the nick.
|
# If the +d argument is a number, ignore it and set the account name to the nick.
|
||||||
account = self.irc.getFriendlyName(target)
|
account = self.getFriendlyName(target)
|
||||||
|
|
||||||
elif ('-r', None) in parsedmodes:
|
elif ('-r', None) in parsedmodes:
|
||||||
# Umode -r being set.
|
# Umode -r being set.
|
||||||
|
|
||||||
if not self.irc.users[target].services_account:
|
if not self.users[target].services_account:
|
||||||
# User already has no account; ignore.
|
# User already has no account; ignore.
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -853,17 +853,17 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.irc.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
self.callHooks([target, 'CLIENT_SERVICES_LOGIN', {'text': account}])
|
||||||
|
|
||||||
def handle_umode2(self, numeric, command, args):
|
def handle_umode2(self, numeric, command, args):
|
||||||
"""Handles UMODE2, used to set user modes on oneself."""
|
"""Handles UMODE2, used to set user modes on oneself."""
|
||||||
# <- :GL UMODE2 +W
|
# <- :GL UMODE2 +W
|
||||||
parsedmodes = self.irc.parseModes(numeric, args)
|
parsedmodes = self.parseModes(numeric, args)
|
||||||
self.irc.applyModes(numeric, parsedmodes)
|
self.applyModes(numeric, parsedmodes)
|
||||||
|
|
||||||
if ('+o', None) in parsedmodes:
|
if ('+o', None) in parsedmodes:
|
||||||
# If +o being set, call the CLIENT_OPERED internal hook.
|
# If +o being set, call the CLIENT_OPERED internal hook.
|
||||||
self.irc.callHooks([numeric, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
self.callHooks([numeric, 'CLIENT_OPERED', {'text': 'IRC Operator'}])
|
||||||
|
|
||||||
self.checkCloakChange(numeric, parsedmodes)
|
self.checkCloakChange(numeric, parsedmodes)
|
||||||
|
|
||||||
@ -873,14 +873,14 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
"""Handles the TOPIC command."""
|
"""Handles the TOPIC command."""
|
||||||
# <- GL TOPIC #services GL 1444699395 :weeee
|
# <- GL TOPIC #services GL 1444699395 :weeee
|
||||||
# <- TOPIC #services devel.relay 1452399682 :test
|
# <- TOPIC #services devel.relay 1452399682 :test
|
||||||
channel = self.irc.toLower(args[0])
|
channel = self.toLower(args[0])
|
||||||
topic = args[-1]
|
topic = args[-1]
|
||||||
setter = args[1]
|
setter = args[1]
|
||||||
ts = args[2]
|
ts = args[2]
|
||||||
|
|
||||||
oldtopic = self.irc.channels[channel].topic
|
oldtopic = self.channels[channel].topic
|
||||||
self.irc.channels[channel].topic = topic
|
self.channels[channel].topic = topic
|
||||||
self.irc.channels[channel].topicset = True
|
self.channels[channel].topicset = True
|
||||||
|
|
||||||
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic,
|
return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic,
|
||||||
'oldtopic': oldtopic}
|
'oldtopic': oldtopic}
|
||||||
@ -888,42 +888,42 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
def handle_setident(self, numeric, command, args):
|
def handle_setident(self, numeric, command, args):
|
||||||
"""Handles SETIDENT, used for self ident changes."""
|
"""Handles SETIDENT, used for self ident changes."""
|
||||||
# <- :70MAAAAAB SETIDENT test
|
# <- :70MAAAAAB SETIDENT test
|
||||||
self.irc.users[numeric].ident = newident = args[0]
|
self.users[numeric].ident = newident = args[0]
|
||||||
return {'target': numeric, 'newident': newident}
|
return {'target': numeric, 'newident': newident}
|
||||||
|
|
||||||
def handle_sethost(self, numeric, command, args):
|
def handle_sethost(self, numeric, command, args):
|
||||||
"""Handles CHGHOST, used for self hostname changes."""
|
"""Handles CHGHOST, used for self hostname changes."""
|
||||||
# <- :70MAAAAAB SETIDENT some.host
|
# <- :70MAAAAAB SETIDENT some.host
|
||||||
self.irc.users[numeric].host = newhost = args[0]
|
self.users[numeric].host = newhost = args[0]
|
||||||
|
|
||||||
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
||||||
# target.
|
# target.
|
||||||
self.irc.applyModes(numeric, [('+x', None), ('+t', None)])
|
self.applyModes(numeric, [('+x', None), ('+t', None)])
|
||||||
|
|
||||||
return {'target': numeric, 'newhost': newhost}
|
return {'target': numeric, 'newhost': newhost}
|
||||||
|
|
||||||
def handle_setname(self, numeric, command, args):
|
def handle_setname(self, numeric, command, args):
|
||||||
"""Handles SETNAME, used for self real name/gecos changes."""
|
"""Handles SETNAME, used for self real name/gecos changes."""
|
||||||
# <- :70MAAAAAB SETNAME :afdsafasf
|
# <- :70MAAAAAB SETNAME :afdsafasf
|
||||||
self.irc.users[numeric].realname = newgecos = args[0]
|
self.users[numeric].realname = newgecos = args[0]
|
||||||
return {'target': numeric, 'newgecos': newgecos}
|
return {'target': numeric, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_chgident(self, numeric, command, args):
|
def handle_chgident(self, numeric, command, args):
|
||||||
"""Handles CHGIDENT, used for denoting ident changes."""
|
"""Handles CHGIDENT, used for denoting ident changes."""
|
||||||
# <- :GL CHGIDENT GL test
|
# <- :GL CHGIDENT GL test
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].ident = newident = args[1]
|
self.users[target].ident = newident = args[1]
|
||||||
return {'target': target, 'newident': newident}
|
return {'target': target, 'newident': newident}
|
||||||
|
|
||||||
def handle_chghost(self, numeric, command, args):
|
def handle_chghost(self, numeric, command, args):
|
||||||
"""Handles CHGHOST, used for denoting hostname changes."""
|
"""Handles CHGHOST, used for denoting hostname changes."""
|
||||||
# <- :GL CHGHOST GL some.host
|
# <- :GL CHGHOST GL some.host
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].host = newhost = args[1]
|
self.users[target].host = newhost = args[1]
|
||||||
|
|
||||||
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
# When SETHOST or CHGHOST is used, modes +xt are implicitly set on the
|
||||||
# target.
|
# target.
|
||||||
self.irc.applyModes(target, [('+x', None), ('+t', None)])
|
self.applyModes(target, [('+x', None), ('+t', None)])
|
||||||
|
|
||||||
return {'target': target, 'newhost': newhost}
|
return {'target': target, 'newhost': newhost}
|
||||||
|
|
||||||
@ -931,14 +931,14 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
"""Handles CHGNAME, used for denoting real name/gecos changes."""
|
"""Handles CHGNAME, used for denoting real name/gecos changes."""
|
||||||
# <- :GL CHGNAME GL :afdsafasf
|
# <- :GL CHGNAME GL :afdsafasf
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
self.irc.users[target].realname = newgecos = args[1]
|
self.users[target].realname = newgecos = args[1]
|
||||||
return {'target': target, 'newgecos': newgecos}
|
return {'target': target, 'newgecos': newgecos}
|
||||||
|
|
||||||
def handle_invite(self, numeric, command, args):
|
def handle_invite(self, numeric, command, args):
|
||||||
"""Handles incoming INVITEs."""
|
"""Handles incoming INVITEs."""
|
||||||
# <- :GL INVITE PyLink-devel :#a
|
# <- :GL INVITE PyLink-devel :#a
|
||||||
target = self._get_UID(args[0])
|
target = self._get_UID(args[0])
|
||||||
channel = self.irc.toLower(args[1])
|
channel = self.toLower(args[1])
|
||||||
# We don't actually need to process this; it's just something plugins/hooks can use
|
# We don't actually need to process this; it's just something plugins/hooks can use
|
||||||
return {'target': target, 'channel': channel}
|
return {'target': target, 'channel': channel}
|
||||||
|
|
||||||
@ -958,6 +958,6 @@ class UnrealProtocol(TS6BaseProtocol):
|
|||||||
|
|
||||||
if args[0] == 'alltime':
|
if args[0] == 'alltime':
|
||||||
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
# XXX: We override notice() here because that abstraction doesn't allow messages from servers.
|
||||||
self._send_with_prefix(self.irc.sid, 'NOTICE %s :*** Server=%s time()=%d' % (source, self.irc.hostname(), time.time()))
|
self._send_with_prefix(self.sid, 'NOTICE %s :*** Server=%s time()=%d' % (source, self.hostname(), time.time()))
|
||||||
|
|
||||||
Class = UnrealProtocol
|
Class = UnrealProtocol
|
||||||
|
Loading…
Reference in New Issue
Block a user