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

[WIP] IRC stack and protocol revamp #475

Merged
merged 62 commits into from Jul 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ed33c8d
utils: add a default to DeprecatedAttributesObject so that it works a…
Jun 3, 2017
8f82b92
utils: add CamelCaseToSnakeCase class, which wraps missing attributes…
Jun 3, 2017
d4fae02
Irc: migrate functions to camel case
Jun 3, 2017
8dae235
Merge branch 'devel' into wip/irc-explosion
Jun 16, 2017
47e36a9
classes: break Irc into three classes: PyLinkNetworkCore, PyLinkNetwo…
Jun 16, 2017
d98d522
Move Irc.runline => PyLinkNetworkCore.parse_protocol_command
Jun 16, 2017
c4f6d62
Drop Irc prefix from IrcServer/User/Channel classes
Jun 16, 2017
2a978c4
Rename PyLinkIRCNetwork -> IRCNetwork
Jun 16, 2017
37d8e8a
Irc: break protocol-agnostic [dis]connect code into _pre/_post functi…
Jun 16, 2017
45ae1dd
Merge ts6 and p10 handle_events, handle_privmsg into ircs2s_common (#…
May 21, 2017
7ca98eb
Split IRC-specific code from classes.Protocol into a new IRCCommonPro…
Jun 17, 2017
a60d746
protocols: sed -i 's/_getUid/_get_UID/g'
Jun 17, 2017
f8155ff
protocols: sed -i 's/_getSid/_get_SID/g'
Jun 17, 2017
df18e31
WIP: merge IRCNetwork and Protocol classes together
Jun 25, 2017
8acf39c
protocols: rename _send to _send_with_prefix to avoid clashing with p…
Jun 25, 2017
d0209f7
Rewrite network intitialization bits
Jun 25, 2017
eef7a73
classes: migrate self.proto calls to self
Jun 25, 2017
7814914
classes, protocols: convert self.irc usage to self
Jun 25, 2017
748c1bc
coremods: migrate irc.proto calls to irc
Jun 25, 2017
4696519
plugins: migrate irc.proto calls to irc
Jun 25, 2017
04c18f0
docs: get rid of self.irc, self.proto, irc.proto
Jun 25, 2017
710a276
IRCNetwork: rename run() -> _run_irc(), this is a private function
Jun 27, 2017
fb34392
IRCNetwork: mark schedule_ping, process_queue as private
Jun 27, 2017
c3cdf63
Move some IRC-specific attributes to IRCNetwork
Jun 27, 2017
62784a6
IRCNetwork: error when attempting to start multiple connection thread…
Jun 27, 2017
928dbf8
Move more IRC-specific attributes into IRCNetwork.init_vars()
Jun 27, 2017
ad2d5a5
Move ts_lock definition into PyLinkNetworkCoreWithUtils
Jun 27, 2017
56f1c9e
NetworkCore: fix irc.protoname definition
Jun 27, 2017
5e7529d
Move some functions back into NetworkCore
Jun 27, 2017
56275c5
NetworkCore: rename removeClient -> _remove_client (no migration stub)
Jun 27, 2017
91fe7e0
utils.CC2SC: use self.__class__ to get the name of the current subclass
Jun 27, 2017
6684f9b
utils.CC2SC: slightly reword the "missing attribute" error
Jun 27, 2017
310f3f2
protocols: rename various parse* functions (no migration stub)
Jun 28, 2017
77357b7
clientbot: rename various private functions
Jun 28, 2017
c9c01de
ts6_common: continue using self.irc in TS6SIDGenerator
Jun 30, 2017
6734793
ircs2s_common: add missing ProtocolError import
Jun 30, 2017
58a4215
ratbox: fix support for merged Irc/proto
Jun 30, 2017
d01e797
Merge handle_part into IRCS2SProtocol (#454)
Jun 30, 2017
7cfc63d
ts6: fix wrong argument count when parsing INVITE ts
Jun 30, 2017
2034bfc
IRCS2SProtocol: sort handle_* methods alphabetically
Jun 30, 2017
741528b
Merge handle_invite into IRCS2SProtocol (#454)
Jun 30, 2017
930a7e1
unreal: checkCloakChange -> _check_cloak_change
Jun 30, 2017
3f240bd
p10: mark check_cloak_change as private
Jun 30, 2017
5647229
CamelCaseToSnakeCase: add deprecation warnings
Jun 30, 2017
3913a90
utils: remove parseModes, applyModes wrappers
Jun 30, 2017
a4e3215
protocols: migrate away from camel case
Jun 30, 2017
10bca67
coremods, plugins: migrate to snake case
Jun 30, 2017
8e9a99f
ServiceBot: migrate to snake case
Jun 30, 2017
0c7fb86
classes, relay, ircs2s_c: tweak/remove various debug statements
Jun 30, 2017
ea45543
control: fix REHASH for 2.x protocol modules
Jun 30, 2017
f60dc8f
protocols: spawnClient -> spawn_client
jlu5 Jul 1, 2017
d084617
protocols: spawnServer -> spawn_server
jlu5 Jul 1, 2017
f38b9c9
protocols: topicBurst -> topic_burst
jlu5 Jul 1, 2017
927fa9a
protocols: updateClient -> update_client
jlu5 Jul 1, 2017
61ed209
coremods, plugins: migrate to snake case for protocol communication
jlu5 Jul 1, 2017
a73300e
classes.Channel: migrate to snake case
jlu5 Jul 1, 2017
6d7e2c6
DeprecatedAttributesObject: don't clobber __ variables
jlu5 Jul 1, 2017
7d68c03
various: convert sortPrefixes/getPrefixModes calls to snake case
jlu5 Jul 1, 2017
b81a03f
protocols: Channel.removeuser -> Channel.remove_user
jlu5 Jul 1, 2017
a204d2b
core: convert IrcUser calls to User
jlu5 Jul 1, 2017
54d7fe6
protocols: convert IrcServer usage to Server
jlu5 Jul 1, 2017
06ef421
classes: clean up references to deprecated classes/methods
jlu5 Jul 1, 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
1,637 changes: 774 additions & 863 deletions classes.py

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion coremods/control.py
Expand Up @@ -130,7 +130,11 @@ def _rehash():
# Connect any new networks or disconnected networks if they aren't already.
if (network not in world.networkobjects) or (not world.networkobjects[network].connection_thread.is_alive()):
proto = utils.getProtocolModule(sdata['protocol'])
world.networkobjects[network] = classes.Irc(network, proto, new_conf)

# API note: 2.0.x style of starting network connections
world.networkobjects[network] = newirc = proto.Class(network)
newirc.connect()

log.info('Finished reloading PyLink configuration.')

if os.name == 'posix':
Expand Down
8 changes: 4 additions & 4 deletions coremods/corecommands.py
Expand Up @@ -18,12 +18,12 @@ def _login(irc, source, username):
irc.users[source].account = username
irc.reply('Successfully logged in as %s.' % username)
log.info("(%s) Successful login to %r by %s",
irc.name, username, irc.getHostmask(source))
irc.name, username, irc.get_hostmask(source))

def _loginfail(irc, source, username):
"""Internal function to process login failures."""
irc.error('Incorrect credentials.')
log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.getHostmask(source))
log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.get_hostmask(source))

@utils.add_cmd
def identify(irc, source, args):
Expand Down Expand Up @@ -86,7 +86,7 @@ def load(irc, source, args):
if name in world.plugins:
irc.reply("Error: %r is already loaded." % name)
return
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.getHostmask(source))
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
try:
world.plugins[name] = pl = utils.loadPlugin(name)
except ImportError as e:
Expand Down Expand Up @@ -119,7 +119,7 @@ def unload(irc, source, args):
modulename = utils.PLUGIN_PREFIX + name

if name in world.plugins:
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.getHostmask(source))
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
pl = world.plugins[name]
log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))

Expand Down
24 changes: 12 additions & 12 deletions coremods/exttargets.py
Expand Up @@ -41,10 +41,10 @@ def account(irc, host, uid):
homenet, realuid)
return False

slogin = irc.toLower(userobj.services_account)
slogin = irc.to_lower(userobj.services_account)

# Split the given exttarget host into parts, so we know how many to look for.
groups = list(map(irc.toLower, host.split(':')))
groups = list(map(irc.to_lower, host.split(':')))
log.debug('(%s) exttargets.account: groups to match: %s', irc.name, groups)

if len(groups) == 1:
Expand Down Expand Up @@ -74,10 +74,10 @@ def ircop(irc, host, uid):

if len(groups) == 1:
# 1st scenario.
return irc.isOper(uid, allowAuthed=False)
return irc.is_oper(uid, allowAuthed=False)
else:
# 2nd scenario. Use matchHost (ircmatch) to match the opertype glob to the opertype.
return irc.matchHost(groups[1], irc.users[uid].opertype)
# 2nd scenario. Use match_host (ircmatch) to match the opertype glob to the opertype.
return irc.match_host(groups[1], irc.users[uid].opertype)

@bind
def server(irc, host, uid):
Expand All @@ -93,10 +93,10 @@ def server(irc, host, uid):
log.debug('(%s) exttargets.server: groups to match: %s', irc.name, groups)

if len(groups) >= 2:
sid = irc.getServer(uid)
sid = irc.get_server(uid)
query = groups[1]
# Return True if the SID matches the query or the server's name glob matches it.
return sid == query or irc.matchHost(query, irc.getFriendlyName(sid))
return sid == query or irc.match_host(query, irc.get_friendly_name(sid))
# $server alone is invalid. Don't match anything.
return False

Expand All @@ -123,7 +123,7 @@ def channel(irc, host, uid):
return uid in irc.channels[channel].users
elif len(groups) >= 3:
# For things like #channel:op, check if the query is in the user's prefix modes.
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].getPrefixModes(uid))
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].get_prefix_modes(uid))

@bind
def pylinkacc(irc, host, uid):
Expand All @@ -134,8 +134,8 @@ def pylinkacc(irc, host, uid):
$pylinkacc -> Returns True if the target is logged in to PyLink.
$pylinkacc:accountname -> Returns True if the target's PyLink login matches the one given.
"""
login = irc.toLower(irc.users[uid].account)
groups = list(map(irc.toLower, host.split(':')))
login = irc.to_lower(irc.users[uid].account)
groups = list(map(irc.to_lower, host.split(':')))
log.debug('(%s) exttargets.pylinkacc: groups to match: %s', irc.name, groups)

if len(groups) == 1:
Expand Down Expand Up @@ -187,6 +187,6 @@ def exttarget_and(irc, host, uid):
targets = targets[1:-1]
targets = list(filter(None, targets.split('+')))
log.debug('exttargets_and: using raw subtargets list %r (original query=%r)', targets, host)
# Wrap every subtarget into irc.matchHost and return True if all subtargets return True.
return all(map(lambda sub_exttarget: irc.matchHost(sub_exttarget, uid), targets))
# Wrap every subtarget into irc.match_host and return True if all subtargets return True.
return all(map(lambda sub_exttarget: irc.match_host(sub_exttarget, uid), targets))
world.exttarget_handlers['and'] = exttarget_and
32 changes: 16 additions & 16 deletions coremods/handlers.py
Expand Up @@ -11,18 +11,18 @@ def handle_whois(irc, source, command, args):
target = args['target']
user = irc.users.get(target)

f = lambda num, source, text: irc.proto.numeric(irc.sid, num, source, text)
f = lambda num, source, text: irc.numeric(irc.sid, num, source, text)

# Get the server that the target is on.
server = irc.getServer(target)
server = irc.get_server(target)

if user is None: # User doesn't exist
# <- :42X 401 7PYAAAAAB GL- :No such nick/channel
nick = target
f(401, source, "%s :No such nick/channel" % nick)
else:
nick = user.nick
sourceisOper = ('o', None) in irc.users[source].modes
sourceis_oper = ('o', None) in irc.users[source].modes
sourceisBot = (irc.umodes.get('bot'), None) in irc.users[source].modes

# Get the full network name.
Expand All @@ -35,7 +35,7 @@ def handle_whois(irc, source, command, args):
# 319: RPL_WHOISCHANNELS; Show public channels of the target, respecting
# hidechans umodes for non-oper callers.
isHideChans = (irc.umodes.get('hidechans'), None) in user.modes
if (not isHideChans) or (isHideChans and sourceisOper):
if (not isHideChans) or (isHideChans and sourceis_oper):
public_chans = []
for chan in user.channels:
c = irc.channels[chan]
Expand All @@ -44,11 +44,11 @@ def handle_whois(irc, source, command, args):

if ((irc.cmodes.get('secret'), None) in c.modes or \
(irc.cmodes.get('private'), None) in c.modes) \
and not (sourceisOper or source in c.users):
and not (sourceis_oper or source in c.users):
continue

# Show the highest prefix mode like a regular IRCd does, if there are any.
prefixes = c.getPrefixModes(target)
prefixes = c.get_prefix_modes(target)
if prefixes:
highest = prefixes[-1]

Expand All @@ -74,7 +74,7 @@ def handle_whois(irc, source, command, args):
# 2) +H is set, but the caller is oper
# 3) +H is set, but whois_use_hideoper is disabled in config
isHideOper = (irc.umodes.get('hideoper'), None) in user.modes
if (not isHideOper) or (isHideOper and sourceisOper) or \
if (not isHideOper) or (isHideOper and sourceis_oper) or \
(isHideOper and not conf.conf['bot'].get('whois_use_hideoper', True)):
# Let's be gramatically correct. (If the opertype starts with a vowel,
# write "an Operator" instead of "a Operator")
Expand All @@ -84,9 +84,9 @@ def handle_whois(irc, source, command, args):

# 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd to show user modes.
# Only show this to opers!
if sourceisOper:
if sourceis_oper:
f(378, source, "%s :is connecting from %s@%s %s" % (nick, user.ident, user.realhost, user.ip))
f(379, source, '%s :is using modes %s' % (nick, irc.joinModes(user.modes, sort=True)))
f(379, source, '%s :is using modes %s' % (nick, irc.join_modes(user.modes, sort=True)))

# 301: used to show away information if present
away_text = user.away
Expand All @@ -101,7 +101,7 @@ def handle_whois(irc, source, command, args):
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook, unless the
# caller is marked a bot and the whois_show_extensions_to_bots option is False
if (sourceisBot and conf.conf['bot'].get('whois_show_extensions_to_bots')) or (not sourceisBot):
irc.callHooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
irc.call_hooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
else:
log.debug('(%s) coremods.handlers.handle_whois: skipping custom whois handlers because '
'caller %s is marked as a bot', irc.name, source)
Expand All @@ -116,15 +116,15 @@ def handle_mode(irc, source, command, args):
modes = args['modes']
# If the sender is not a PyLink client, and the target IS a protected
# client, revert any forced deoper attempts.
if irc.isInternalClient(target) and not irc.isInternalClient(source):
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.isManipulatableClient(target)):
irc.proto.mode(irc.sid, target, {('+o', None)})
if irc.is_internal_client(target) and not irc.is_internal_client(source):
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.is_manipulatable_client(target)):
irc.mode(irc.sid, target, {('+o', None)})
utils.add_hook(handle_mode, 'MODE')

def handle_operup(irc, source, command, args):
"""Logs successful oper-ups on networks."""
otype = args.get('text', 'IRC Operator')
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.getHostmask(source))
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.get_hostmask(source))
irc.users[source].opertype = otype

utils.add_hook(handle_operup, 'CLIENT_OPERED')
Expand All @@ -143,11 +143,11 @@ def handle_version(irc, source, command, args):
"""Handles requests for the PyLink server version."""
# 351 syntax is usually "<server version>. <server hostname> :<anything else you want to add>
fullversion = irc.version()
irc.proto.numeric(irc.sid, 351, source, fullversion)
irc.numeric(irc.sid, 351, source, fullversion)
utils.add_hook(handle_version, 'VERSION')

def handle_time(irc, source, command, args):
"""Handles requests for the PyLink server time."""
timestring = time.ctime()
irc.proto.numeric(irc.sid, 391, source, '%s :%s' % (irc.hostname(), timestring))
irc.numeric(irc.sid, 391, source, '%s :%s' % (irc.hostname(), timestring))
utils.add_hook(handle_time, 'TIME')
10 changes: 5 additions & 5 deletions coremods/permissions.py
Expand Up @@ -60,22 +60,22 @@ def checkPermissions(irc, uid, perms, also_show=[]):
"""
# For old (< 1.1 login blocks):
# If the user is logged in, they automatically have all permissions.
if irc.matchHost('$pylinkacc', uid) and conf.conf['login'].get('user'):
if irc.match_host('$pylinkacc', uid) and conf.conf['login'].get('user'):
log.debug('permissions: overriding permissions check for old-style admin user %s',
irc.getHostmask(uid))
irc.get_hostmask(uid))
return True

# Iterate over all hostmask->permission list mappings.
for host, permlist in permissions.copy().items():
log.debug('permissions: permlist for %s: %s', host, permlist)
if irc.matchHost(host, uid):
if irc.match_host(host, uid):
# Now, iterate over all the perms we are looking for.
for perm in permlist:
# Use irc.matchHost to expand globs in an IRC-case insensitive and wildcard
# Use irc.match_host to expand globs in an IRC-case insensitive and wildcard
# friendly way. e.g. 'xyz.*.#Channel\' will match 'xyz.manage.#channel|' on IRCds
# using the RFC1459 casemapping.
log.debug('permissions: checking if %s glob matches anything in %s', perm, permlist)
if any(irc.matchHost(perm, p) for p in perms):
if any(irc.match_host(perm, p) for p in perms):
return True
raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" %
(', '.join(perms+also_show)))
16 changes: 8 additions & 8 deletions coremods/service_support.py
Expand Up @@ -14,7 +14,7 @@ def spawn_service(irc, source, command, args):
# Service name
name = args['name']

if name != 'pylink' and not irc.proto.hasCap('can-spawn-clients'):
if name != 'pylink' and not irc.has_cap('can-spawn-clients'):
log.debug("(%s) Not spawning service %s because the server doesn't support spawning clients",
irc.name, name)
return
Expand Down Expand Up @@ -49,16 +49,16 @@ def spawn_service(irc, source, command, args):

# Track the service's UIDs on each network.
log.debug('(%s) spawn_service: Using nick %s for service %s', irc.name, nick, name)
u = irc.nickToUid(nick)
if u and irc.isInternalClient(u): # If an internal client exists, reuse it.
u = irc.nick_to_uid(nick)
if u and irc.is_internal_client(u): # If an internal client exists, reuse it.
log.debug('(%s) spawn_service: Using existing client %s/%s', irc.name, u, nick)
userobj = irc.users[u]
else:
log.debug('(%s) spawn_service: Spawning new client %s', irc.name, nick)
userobj = irc.proto.spawnClient(nick, ident, host, modes=modes, opertype="PyLink Service",
userobj = irc.spawn_client(nick, ident, host, modes=modes, opertype="PyLink Service",
manipulatable=sbot.manipulatable)

# Store the service name in the IrcUser object for easier access.
# Store the service name in the User object for easier access.
userobj.service = name

sbot.uids[irc.name] = u = userobj.uid
Expand Down Expand Up @@ -101,7 +101,7 @@ def handle_kill(irc, source, command, args):
"""Handle KILLs to PyLink service bots, respawning them as needed."""
target = args['target']
userdata = args.get('userdata')
sbot = irc.getServiceBot(target)
sbot = irc.get_service_bot(target)
servicename = None

if userdata and hasattr(userdata, 'service'): # Look for the target's service name attribute
Expand All @@ -118,7 +118,7 @@ def handle_kick(irc, source, command, args):
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
kicked = args['target']
channel = args['channel']
sbot = irc.getServiceBot(kicked)
sbot = irc.get_service_bot(kicked)
if sbot:
sbot.join(irc, channel)
utils.add_hook(handle_kick, 'KICK')
Expand All @@ -128,7 +128,7 @@ def handle_commands(irc, source, command, args):
target = args['target']
text = args['text']

sbot = irc.getServiceBot(target)
sbot = irc.get_service_bot(target)
if sbot:
sbot.call_cmd(irc, source, text)

Expand Down
2 changes: 1 addition & 1 deletion docs/technical/hooks-reference.md
Expand Up @@ -163,7 +163,7 @@ Some hooks do not map directly to IRC commands, but to events that protocol modu

- **PYLINK_CUSTOM_WHOIS**: `{'target': UID1, 'server': SID1}`
- This hook is called by `coremods/handlers.py` during its WHOIS handling process, to allow plugins to provide custom WHOIS information. The `target` field represents the target UID, while the `server` field represents the SID that should be replying to the WHOIS request. The source of the payload is the user using `/whois`.
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.proto.numeric()` to reply to the source from the given server.
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.numeric()` to reply to the source from the given server.
- This hook replaces the pre-0.8.x fashion of defining custom WHOIS handlers, which was non-standard and poorly documented.

## Commands handled WITHOUT hooks
Expand Down