Skip to content
This repository has been archived by the owner on Jan 4, 2023. It is now read-only.

ngIRCd protocol support #479

Merged
merged 55 commits into from Jul 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
990a928
relay: re-add 'CLAIM #channel -'
jlu5 Jul 3, 2017
8ddcc4d
Move part, quit, message, notice, topic, _send_with_prefix, _expandPU…
jlu5 Jul 3, 2017
7803409
protocols: merge _expandPUID into ircs2s_common
jlu5 Jul 3, 2017
8bf65f3
ircs2s_common: implicitly expand PUIDs in _send_with_prefix
jlu5 Jul 3, 2017
091c763
Initial ngIRCd protocol stub
jlu5 Jul 3, 2017
e9d7ac3
ngircd: remove duplicate function
jlu5 Jul 3, 2017
ec308ac
protocols: move 005 handling code to IRCCommonProtocol
jlu5 Jul 3, 2017
a5e7d76
IRCCommonProtocol: only update the same tokens once per connection
jlu5 Jul 3, 2017
06d69aa
clientbot: fix self.connected.set() order
jlu5 Jul 3, 2017
66af57e
IRCCommonProtocol: handle EXCEPTS, INVEX, NICKLEN, DEAF, CALLERID in 005
jlu5 Jul 3, 2017
4cdae54
IRCCommonProtocol: fix type of maxnicklen
jlu5 Jul 3, 2017
b6b1cbe
ngircd: send our own server negotiation info to complete the connection
jlu5 Jul 3, 2017
7b2f93f
ngircd: send an UID hook in user introductions
jlu5 Jul 3, 2017
2e5fc24
ngircd: handle CHANINFO (channel mode/topic bursts) and NJOIN (userli…
jlu5 Jul 3, 2017
276b0b2
protocols: move handle_pong to IRCCommonProtocol
jlu5 Jul 5, 2017
43af9d1
protocols: move ping() into IRCCommonProtocol
jlu5 Jul 5, 2017
970b387
core: rename ping() to _ping_uplink(), and drop the unused source/tar…
jlu5 Jul 5, 2017
37f0dcb
ngircd: fix SQUIT user tracking
jlu5 Jul 5, 2017
84a6cec
p10: fix endburst_delay note
jlu5 Jul 5, 2017
759210a
ngircd: add inbound & outbound JOIN, SERVER
jlu5 Jul 5, 2017
42a2530
ngircd: don't leave user TS none in spawn_client
jlu5 Jul 5, 2017
b780070
ngircd: implement nick changing
jlu5 Jul 5, 2017
9132556
PyLinkNetworkCore: fix __repr__ definition
jlu5 Jul 5, 2017
5d4f214
Move squit() to ircs2s_common
jlu5 Jul 5, 2017
163f009
IRCCommonProtocol: also expand PSIDs in _expandPUID
jlu5 Jul 5, 2017
4e082c2
PUIDGenerator: allow custom counter start values
jlu5 Jul 5, 2017
449b547
ngircd: properly track server tokens so that users spawn on the right…
jlu5 Jul 5, 2017
3729b23
Move KICK handlers to IRCS2SProtocol
jlu5 Jul 5, 2017
aa4e933
IRCS2SProtocol: expand nicks to UIDs in handle_kill
jlu5 Jul 5, 2017
db06ff4
Move handle_topic to IRCS2SProtocol
jlu5 Jul 5, 2017
c2e65ff
IRCCommonProtocol: alias topic_burst to topic by default (#480)
jlu5 Jul 5, 2017
30b9f47
unreal: remove handle_kill override; unneeded as of aa4e9335aa7dac588…
jlu5 Jul 5, 2017
d2d176b
IRCS2SProtocol: fix UnboundLocalError in "message coming from wrong w…
jlu5 Jul 5, 2017
69f3ae5
ts6: fix wrong argument count when parsing INVITE ts
jlu5 Jul 5, 2017
1acd654
ts6: fix 'ts' value type in handle_invite
jlu5 Jul 5, 2017
58558c8
ngircd: ignore KILLs not meant for us
jlu5 Jul 5, 2017
1e5985b
Merge remote-tracking branch 'origin/beta' into wip/ngircd
jlu5 Jul 5, 2017
56c8b90
IRCS2SProtocol: handle both killpath-based and preformatted kill reasons
jlu5 Jul 5, 2017
694b501
Move numeric() into IRCS2SProtocol
jlu5 Jul 5, 2017
faa5b72
docs: update protocol-modules graphic
jlu5 Jul 5, 2017
45dad63
Move handle_mode into IRCS2SProtocol
jlu5 Jul 7, 2017
4cd1ed5
ngircd: add an outgoing sjoin() function using NJOIN
jlu5 Jul 7, 2017
961e8ae
ngircd: add outgoing MODE command
jlu5 Jul 7, 2017
28313fd
ngircd: send burst modes after NJOIN, if there are any
jlu5 Jul 7, 2017
3d0ccad
ngircd: sort handler functions alphabetically
jlu5 Jul 7, 2017
73464e5
ngircd: fill in mode definitions
jlu5 Jul 7, 2017
b2b5037
ngircd: fix setting umodes
jlu5 Jul 7, 2017
085b4ca
protocols: handle usermode-based away (i.e. ngircd +a)
jlu5 Jul 7, 2017
d149576
protocols: move invite() into IRCS2SProtocol
jlu5 Jul 7, 2017
6d3d2b2
IRCS2SProtocol: ignore attempts to ping the uplink before the link is…
jlu5 Jul 7, 2017
b0eb165
ngircd: add a stub for KNOCK
jlu5 Jul 7, 2017
f29c951
ngircd: remove has-ts from protocol capabilities
jlu5 Jul 7, 2017
6636a19
ngircd: implement handler for METADATA
jlu5 Jul 7, 2017
fa2c5d9
IRCS2SProtocol: fix extraneous umode based AWAY messages
jlu5 Jul 7, 2017
57c86c6
ngircd: implement update_client()
jlu5 Jul 7, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions classes.py
Expand Up @@ -171,7 +171,7 @@ def init_vars(self):
self.log_setup()

def __repr__(self):
return "<%s object for network %r>" % (self.__class__.name, self.name)
return "<%s object for network %r>" % (self.__class__.__name__, self.name)

### General utility functions
def call_hooks(self, hook_args):
Expand Down Expand Up @@ -1117,7 +1117,7 @@ def init_vars(self, *args, **kwargs):

def _schedule_ping(self):
"""Schedules periodic pings in a loop."""
self.ping()
self._ping_uplink()

self.pingTimer = threading.Timer(self.pingfreq, self._schedule_ping)
self.pingTimer.daemon = True
Expand Down Expand Up @@ -1415,6 +1415,9 @@ def __init__(self, nick, ts, uid, server, ident='null', host='null',
# For "serious" service clients, this should always be False.
self.manipulatable = manipulatable

# Cloaked host for IRCds that use it
self.cloaked_host = None

def __repr__(self):
return 'User(%s/%s)' % (self.uid, self.nick)
IrcUser = User
Expand Down
41 changes: 30 additions & 11 deletions docs/technical/protocol-modules.dot
@@ -1,29 +1,48 @@
/* Graph showing inheritance with the current PyLink protocol protocols:
/* Graph showing inheritance with the current PyLink protocol modules:
* Update using: dot -Tpng protocol-modules.dot > protocol-modules.png
*/

digraph G {
ratio = 0.8; /* make the graph wider than tall */

edge [ penwidth=0.75, color="#111111CC" ];
subgraph cluster_core {
label="Core classes (pylinkirc.classes)";
style="filled";
node [style="filled",color="white"];
color="#90EE90";

"PyLinkNetworkCore" -> "PyLinkNetworkCoreWithUtils" -> "IRCNetwork";
}

subgraph cluster_helper {
label="Protocol module helpers";
label="Protocol module helpers\n(pylinkirc.protocols.ircs2s_common)";
style="filled";
node [style="filled",color="white"];
color="lightblue";

"ircs2s_common.py" -> "ts6_common.py";
"IRCNetwork" -> "IRCCommonProtocol" -> "IRCS2SProtocol" -> "TS6BaseProtocol";

subgraph cluster_helper {
label="pylinkirc.protocols.ts6_common";
style="filled";
color="lightcyan";

"TS6BaseProtocol";
}
}

subgraph cluster_pluggable {
label="Pluggable (full) protocol modules";
label="Complete protocol modules (pylinkirc.protocols.*)";
style="filled";
node [style="filled",color="white"];
color="khaki";

"ircs2s_common.py" -> "p10.py";
"ts6_common.py" -> "ts6.py" -> "hybrid.py";
"ts6.py" -> "ratbox.py";
"ts6_common.py" -> "inspircd.py";
"ts6_common.py" -> "unreal.py";
"clientbot.py";
"IRCS2SProtocol" -> "p10";
"IRCS2SProtocol" -> "ngircd";
"TS6BaseProtocol" -> "ts6" -> "hybrid";
"ts6" -> "ratbox";
"TS6BaseProtocol" -> "inspircd";
"TS6BaseProtocol" -> "unreal";
"IRCCommonProtocol" -> "clientbot";
}
}
Binary file modified docs/technical/protocol-modules.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 8 additions & 55 deletions protocols/clientbot.py
Expand Up @@ -8,7 +8,6 @@
from pylinkirc.classes import *

FALLBACK_REALNAME = 'PyLink Relay Mirror Client'
COMMON_PREFIXMODES = [('h', 'halfop'), ('a', 'admin'), ('q', 'owner'), ('y', 'owner')]
IRCV3_CAPABILITIES = {'multi-prefix', 'sasl'}

class ClientbotWrapperProtocol(IRCCommonProtocol):
Expand All @@ -25,7 +24,7 @@ def __init__(self, *args, **kwargs):
# This is just a fallback. Actual casemapping is fetched by handle_005()
self.casemapping = 'ascii'

self.caps = {}
self._caps = {}
self.ircv3_caps = set()
self.ircv3_caps_available = {}

Expand All @@ -42,19 +41,12 @@ def __init__(self, *args, **kwargs):
# are essentially all fatal errors for connections.
self.handle_463 = self.handle_464 = self.handle_465 = self.handle_error

def _expandPUID(self, uid):
"""
Returns the real nick for the given PUID.
"""
if uid in self.users:
nick = self.users[uid].nick
log.debug('(%s) Mangling target PUID %s to nick %s', self.name, uid, nick)
return nick
return uid
self._use_builtin_005_handling = True

def post_connect(self):
"""Initializes a connection to a server."""
# (Re)initialize counter-based pseudo UID generators
super().post_connect()
self.uidgen = utils.PUIDGenerator('PUID')
self.sidgen = utils.PUIDGenerator('ClientbotInternalSID')

Expand All @@ -68,7 +60,7 @@ def post_connect(self):
# Clear states from last connect
self.who_received.clear()
self.kick_queue.clear()
self.caps.clear()
self._caps.clear()
self.ircv3_caps.clear()
self.ircv3_caps_available.clear()

Expand Down Expand Up @@ -253,9 +245,9 @@ def notice(self, source, target, text):
# Wrap around message(), which does all the text formatting for us.
self.message(source, target, text, notice=True)

def ping(self, source=None, target=None):
def _ping_uplink(self):
"""
Sends PING to the uplink.
Sends a PING to the uplink.
"""
if self.uplink:
self.send('PING %s' % self.get_friendly_name(self.uplink))
Expand Down Expand Up @@ -586,40 +578,6 @@ def handle_001(self, source, command, args):
# enumerate our uplink
self.uplink = source

def handle_005(self, source, command, args):
"""
Handles 005 / RPL_ISUPPORT.
"""
self.caps.update(self.parse_isupport(args[1:-1]))
log.debug('(%s) handle_005: self.caps is %s', self.name, self.caps)

if 'CHANMODES' in self.caps:
self.cmodes['*A'], self.cmodes['*B'], self.cmodes['*C'], self.cmodes['*D'] = \
self.caps['CHANMODES'].split(',')
log.debug('(%s) handle_005: cmodes: %s', self.name, self.cmodes)

if 'USERMODES' in self.caps:
self.umodes['*A'], self.umodes['*B'], self.umodes['*C'], self.umodes['*D'] = \
self.caps['USERMODES'].split(',')
log.debug('(%s) handle_005: umodes: %s', self.name, self.umodes)

self.casemapping = self.caps.get('CASEMAPPING', self.casemapping)
log.debug('(%s) handle_005: casemapping set to %s', self.name, self.casemapping)

if 'PREFIX' in self.caps:
self.prefixmodes = prefixmodes = self.parse_isupport_prefixes(self.caps['PREFIX'])
log.debug('(%s) handle_005: prefix modes set to %s', self.name, self.prefixmodes)

# Autodetect common prefix mode names.
for char, modename in COMMON_PREFIXMODES:
# Don't overwrite existing named mode definitions.
if char in self.prefixmodes and modename not in self.cmodes:
self.cmodes[modename] = char
log.debug('(%s) handle_005: autodetecting mode %s (%s) as %s', self.name,
char, self.prefixmodes[char], modename)

self.connected.set()

def handle_376(self, source, command, args):
"""
Handles end of MOTD numerics, used to start things like autoperform.
Expand All @@ -630,9 +588,11 @@ def handle_376(self, source, command, args):
self.send(line)

# Virtual endburst hook.
self.connected.set() # Note, this should always be set before sending ENDBURST
if not self.has_eob:
self.has_eob = True
return {'parse_as': 'ENDBURST'}

handle_422 = handle_376

def handle_353(self, source, command, args):
Expand Down Expand Up @@ -931,13 +891,6 @@ def handle_ping(self, source, command, args):
"""
self.send('PONG :%s' % args[0], queue=False)

def handle_pong(self, source, command, args):
"""
Handles incoming PONG.
"""
if source == self.uplink:
self.lastping = time.time()

def handle_privmsg(self, source, command, args):
"""Handles incoming PRIVMSG/NOTICE."""
# <- :sender PRIVMSG #dev :afasfsa
Expand Down
44 changes: 0 additions & 44 deletions protocols/inspircd.py
Expand Up @@ -243,12 +243,6 @@ def topic_burst(self, numeric, target, text):
self.channels[target].topic = text
self.channels[target].topicset = True

def invite(self, numeric, target, channel):
"""Sends an INVITE from a PyLink client.."""
if not self.is_internal_client(numeric):
raise LookupError('No such PyLink client exists.')
self._send_with_prefix(numeric, 'INVITE %s %s' % (target, channel))

def knock(self, numeric, target, text):
"""Sends a KNOCK from a PyLink client."""
if not self.is_internal_client(numeric):
Expand Down Expand Up @@ -308,14 +302,6 @@ def update_client(self, target, field, text):
self.call_hooks([self.sid, 'CHGNAME',
{'target': target, 'newgecos': text}])

def ping(self, source=None, target=None):
"""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."""
source = source or self.sid
target = target or self.uplink
if not (target is None or source is None):
self._send_with_prefix(source, 'PING %s %s' % (source, target))

def numeric(self, source, numeric, target, text):
"""Sends raw numerics from a server to a remote client."""
# InspIRCd 2.0 syntax (undocumented):
Expand Down Expand Up @@ -384,12 +370,6 @@ def endburstf():
self._send_with_prefix(sid, 'ENDBURST')
return sid

def squit(self, source, target, text='No reason given'):
"""SQUITs a PyLink server."""
# -> :9PY SQUIT 9PZ :blah, blah
self._send_with_prefix(source, 'SQUIT %s :%s' % (target, text))
self.handle_squit(source, 'SQUIT', [target, text])

### Core / command handlers

def post_connect(self):
Expand Down Expand Up @@ -629,17 +609,6 @@ def handle_fmode(self, numeric, command, args):
return {'target': channel, 'modes': changedmodes, 'ts': ts,
'channeldata': oldobj}

def handle_mode(self, numeric, command, args):
"""Handles incoming user mode changes."""
# In InspIRCd, MODE is used for setting user modes and
# FMODE is used for channel modes:
# <- :70MAAAAAA MODE 70MAAAAAA -i+xc
target = args[0]
modestrings = args[1:]
changedmodes = self.parse_modes(target, modestrings)
self.apply_modes(target, changedmodes)
return {'target': target, 'modes': changedmodes}

def handle_idle(self, numeric, command, args):
"""
Handles the IDLE command, sent between servers in remote WHOIS queries.
Expand Down Expand Up @@ -781,19 +750,6 @@ def handle_version(self, numeric, command, args):
Stub VERSION handler (does nothing) to override the one in ts6_common.
"""

def handle_kill(self, source, command, args):
"""Handles incoming KILLs."""
killed = args[0]
# Depending on whether the IRCd sends explicit QUIT messages for
# killed clients, the user may or may not have automatically been
# removed from our user list.
# If not, we have to assume that KILL = QUIT and remove them
# ourselves.
data = self.users.get(killed)
if data:
self._remove_client(killed)
return {'target': killed, 'text': args[1], 'userdata': data}

def handle_sakick(self, source, command, args):
"""Handles forced kicks (SAKICK)."""
# <- :1MLAAAAAD ENCAP 0AL SAKICK #test 0ALAAAAAB :test
Expand Down