3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-24 03:29:28 +01:00

Separate UID/SID generators into various protocol modules (#199)

This commit is contained in:
James Lu 2016-04-24 21:08:07 -07:00
parent a069ce8cb4
commit 2c60aa6395
7 changed files with 108 additions and 107 deletions

View File

@ -9,7 +9,7 @@ import utils
from log import log from log import log
from classes import * from classes import *
from ts6 import TS6Protocol from ts6 import *
class HybridProtocol(TS6Protocol): class HybridProtocol(TS6Protocol):
def __init__(self, irc): def __init__(self, irc):
@ -100,7 +100,7 @@ class HybridProtocol(TS6Protocol):
raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server) raise ValueError('Server %r is not a PyLink internal PseudoServer!' % server)
# Create an UIDGenerator instance for every SID, so that each gets # Create an UIDGenerator instance for every SID, so that each gets
# distinct values. # distinct values.
uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid() uid = self.uidgen.setdefault(server, TS6UIDGenerator(server)).next_uid()
# EUID: # EUID:
# parameters: nickname, hopcount, nickTS, umodes, username, # parameters: nickname, hopcount, nickTS, umodes, username,
# visible hostname, IP address, UID, real hostname, account name, gecos # visible hostname, IP address, UID, real hostname, account name, gecos

View File

@ -15,7 +15,7 @@ import utils
from log import log from log import log
from classes import * from classes import *
from ts6_common import TS6BaseProtocol from ts6_common import *
class InspIRCdProtocol(TS6BaseProtocol): class InspIRCdProtocol(TS6BaseProtocol):
def __init__(self, irc): def __init__(self, irc):
@ -48,7 +48,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
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
# distinct values. # distinct values.
uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid() uid = self.uidgen.setdefault(server, TS6UIDGenerator(server)).next_uid()
ts = ts or int(time.time()) ts = ts or int(time.time())
realname = realname or self.irc.botdata['realname'] realname = realname or self.irc.botdata['realname']
realhost = realhost or host realhost = realhost or host
@ -390,7 +390,7 @@ class InspIRCdProtocol(TS6BaseProtocol):
# name it anything you like. The former is config default, # name it anything you like. The former is config default,
# but I personally prefer the latter. # but I personally prefer the latter.
name = 'owner' name = 'owner'
if name == 'c_registered': if name == 'c_registered':
# Be consistent with other protocols # Be consistent with other protocols
name = 'registered' name = 'registered'

View File

@ -16,6 +16,14 @@ import utils
from log import log from log import log
from classes import * from classes import *
class P10UIDGenerator(utils.IncrementalUIDGenerator):
"""Implements an incremental P10 UID Generator."""
def __init__(self, sid):
self.allowedchars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]'
self.length = 3
super().__init__(sid)
def p10b64encode(num, length=2): def p10b64encode(num, length=2):
""" """
Encodes a given numeric using P10 Base64 numeric nicks, as documented at Encodes a given numeric using P10 Base64 numeric nicks, as documented at
@ -242,7 +250,7 @@ class P10Protocol(Protocol):
# Create an UIDGenerator instance for every SID, so that each gets # Create an UIDGenerator instance for every SID, so that each gets
# distinct values. # distinct values.
uid = self.uidgen.setdefault(server, utils.P10UIDGenerator(server)).next_uid() uid = self.uidgen.setdefault(server, P10UIDGenerator(server)).next_uid()
# Fill in all the values we need # Fill in all the values we need
ts = ts or int(time.time()) ts = ts or int(time.time())

View File

@ -14,7 +14,7 @@ import utils
from log import log from log import log
from classes import * from classes import *
from ts6_common import TS6BaseProtocol from ts6_common import *
class TS6Protocol(TS6BaseProtocol): class TS6Protocol(TS6BaseProtocol):
def __init__(self, irc): def __init__(self, irc):
@ -40,7 +40,7 @@ class TS6Protocol(TS6BaseProtocol):
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
# distinct values. # distinct values.
uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid() uid = self.uidgen.setdefault(server, TS6UIDGenerator(server)).next_uid()
# EUID: # EUID:
# parameters: nickname, hopcount, nickTS, umodes, username, # parameters: nickname, hopcount, nickTS, umodes, username,
# visible hostname, IP address, UID, real hostname, account name, gecos # visible hostname, IP address, UID, real hostname, account name, gecos

View File

@ -4,6 +4,7 @@ ts6_common.py: Common base protocol class with functions shared by the UnrealIRC
import sys import sys
import os import os
import string
# Import hacks to access utils and classes... # Import hacks to access utils and classes...
curdir = os.path.dirname(__file__) curdir = os.path.dirname(__file__)
@ -13,6 +14,94 @@ import utils
from log import log from log import log
from classes import * from classes import *
class TS6SIDGenerator():
"""
TS6 SID Generator. <query> is a 3 character string with any combination of
uppercase letters, digits, and #'s. it must contain at least one #,
which are used by the generator as a wildcard. On every next_sid() call,
the first available wildcard character (from the right) will be
incremented to generate the next SID.
When there are no more available SIDs left (SIDs are not reused, only
incremented), RuntimeError is raised.
Example queries:
"1#A" would give: 10A, 11A, 12A ... 19A, 1AA, 1BA ... 1ZA (36 total results)
"#BQ" would give: 0BQ, 1BQ, 2BQ ... 9BQ (10 total results)
"6##" would give: 600, 601, 602, ... 60Y, 60Z, 610, 611, ... 6ZZ (1296 total results)
"""
def __init__(self, irc):
self.irc = irc
try:
self.query = query = list(irc.serverdata["sidrange"])
except KeyError:
raise RuntimeError('(%s) "sidrange" is missing from your server configuration block!' % irc.name)
self.iters = self.query.copy()
self.output = self.query.copy()
self.allowedchars = {}
qlen = len(query)
assert qlen == 3, 'Incorrect length for a SID (must be 3, got %s)' % qlen
assert '#' in query, "Must be at least one wildcard (#) in query"
for idx, char in enumerate(query):
# Iterate over each character in the query string we got, along
# with its index in the string.
assert char in (string.digits+string.ascii_uppercase+"#"), \
"Invalid character %r found." % char
if char == '#':
if idx == 0: # The first char be only digits
self.allowedchars[idx] = string.digits
else:
self.allowedchars[idx] = string.digits+string.ascii_uppercase
self.iters[idx] = iter(self.allowedchars[idx])
self.output[idx] = self.allowedchars[idx][0]
next(self.iters[idx])
def increment(self, pos=2):
"""
Increments the SID generator to the next available SID.
"""
if pos < 0:
# Oh no, we've wrapped back to the start!
raise RuntimeError('No more available SIDs!')
it = self.iters[pos]
try:
self.output[pos] = next(it)
except TypeError: # This position is not an iterator, but a string.
self.increment(pos-1)
except StopIteration:
self.output[pos] = self.allowedchars[pos][0]
self.iters[pos] = iter(self.allowedchars[pos])
next(self.iters[pos])
self.increment(pos-1)
def next_sid(self):
"""
Returns the next unused TS6 SID for the server.
"""
while ''.join(self.output) in self.irc.servers:
# Increment until the SID we have doesn't already exist.
self.increment()
sid = ''.join(self.output)
return sid
class TS6UIDGenerator(utils.IncrementalUIDGenerator):
"""Implements an incremental TS6 UID Generator."""
def __init__(self, sid):
# Define the options for IncrementalUIDGenerator, and then
# initialize its functions.
# TS6 UIDs are 6 characters in length (9 including the SID).
# They go from ABCDEFGHIJKLMNOPQRSTUVWXYZ -> 0123456789 -> wrap around:
# e.g. AAAAAA, AAAAAB ..., AAAAA8, AAAAA9, AAAABA, etc.
self.allowedchars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456879'
self.length = 6
super().__init__(sid)
class TS6BaseProtocol(Protocol): class TS6BaseProtocol(Protocol):
def __init__(self, irc): def __init__(self, irc):
@ -22,7 +111,7 @@ class TS6BaseProtocol(Protocol):
self.uidgen = {} self.uidgen = {}
# SID generator for TS6. # SID generator for TS6.
self.sidgen = utils.TS6SIDGenerator(irc) self.sidgen = TS6SIDGenerator(irc)
def _send(self, source, msg): def _send(self, source, msg):
"""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."""

View File

@ -16,7 +16,7 @@ sys.path += [curdir, os.path.dirname(curdir)]
import utils import utils
from log import log from log import log
from classes import * from classes import *
from ts6_common import TS6BaseProtocol from ts6_common import *
class UnrealProtocol(TS6BaseProtocol): class UnrealProtocol(TS6BaseProtocol):
def __init__(self, irc): def __init__(self, irc):
@ -72,7 +72,7 @@ class UnrealProtocol(TS6BaseProtocol):
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
# do, but that doesn't matter to us... # do, but that doesn't matter to us...
uid = self.uidgen.setdefault(server, utils.TS6UIDGenerator(server)).next_uid() uid = self.uidgen.setdefault(server, TS6UIDGenerator(server)).next_uid()
ts = ts or int(time.time()) ts = ts or int(time.time())
realname = realname or self.irc.botdata['realname'] realname = realname or self.irc.botdata['realname']
realhost = realhost or host realhost = realhost or host

View File

@ -43,9 +43,6 @@ class IncrementalUIDGenerator():
""" """
def __init__(self, sid): def __init__(self, sid):
# TS6 UIDs are 6 characters in length (9 including the SID).
# They wrap from ABCDEFGHIJKLMNOPQRSTUVWXYZ -> 0123456789 -> wrap around:
# (e.g. AAAAAA, AAAAAB ..., AAAAA8, AAAAA9, AAAABA)
if not (hasattr(self, 'allowedchars') and hasattr(self, 'length')): if not (hasattr(self, 'allowedchars') and hasattr(self, 'length')):
raise RuntimeError("Allowed characters list not defined. Subclass " raise RuntimeError("Allowed characters list not defined. Subclass "
"%s by defining self.allowedchars and self.length " "%s by defining self.allowedchars and self.length "
@ -80,99 +77,6 @@ class IncrementalUIDGenerator():
self.increment() self.increment()
return uid return uid
class TS6UIDGenerator(IncrementalUIDGenerator):
"""Implements an incremental TS6 UID Generator."""
def __init__(self, sid):
# Define the options for IncrementalUIDGenerator, and then
# initialize its functions.
self.allowedchars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456879'
self.length = 6
super().__init__(sid)
class P10UIDGenerator(IncrementalUIDGenerator):
"""Implements an incremental P10 UID Generator."""
def __init__(self, sid):
self.allowedchars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789[]'
self.length = 3
super().__init__(sid)
class TS6SIDGenerator():
"""
TS6 SID Generator. <query> is a 3 character string with any combination of
uppercase letters, digits, and #'s. it must contain at least one #,
which are used by the generator as a wildcard. On every next_sid() call,
the first available wildcard character (from the right) will be
incremented to generate the next SID.
When there are no more available SIDs left (SIDs are not reused, only
incremented), RuntimeError is raised.
Example queries:
"1#A" would give: 10A, 11A, 12A ... 19A, 1AA, 1BA ... 1ZA (36 total results)
"#BQ" would give: 0BQ, 1BQ, 2BQ ... 9BQ (10 total results)
"6##" would give: 600, 601, 602, ... 60Y, 60Z, 610, 611, ... 6ZZ (1296 total results)
"""
def __init__(self, irc):
self.irc = irc
try:
self.query = query = list(irc.serverdata["sidrange"])
except KeyError:
raise RuntimeError('(%s) "sidrange" is missing from your server configuration block!' % irc.name)
self.iters = self.query.copy()
self.output = self.query.copy()
self.allowedchars = {}
qlen = len(query)
assert qlen == 3, 'Incorrect length for a SID (must be 3, got %s)' % qlen
assert '#' in query, "Must be at least one wildcard (#) in query"
for idx, char in enumerate(query):
# Iterate over each character in the query string we got, along
# with its index in the string.
assert char in (string.digits+string.ascii_uppercase+"#"), \
"Invalid character %r found." % char
if char == '#':
if idx == 0: # The first char be only digits
self.allowedchars[idx] = string.digits
else:
self.allowedchars[idx] = string.digits+string.ascii_uppercase
self.iters[idx] = iter(self.allowedchars[idx])
self.output[idx] = self.allowedchars[idx][0]
next(self.iters[idx])
def increment(self, pos=2):
"""
Increments the SID generator to the next available SID.
"""
if pos < 0:
# Oh no, we've wrapped back to the start!
raise RuntimeError('No more available SIDs!')
it = self.iters[pos]
try:
self.output[pos] = next(it)
except TypeError: # This position is not an iterator, but a string.
self.increment(pos-1)
except StopIteration:
self.output[pos] = self.allowedchars[pos][0]
self.iters[pos] = iter(self.allowedchars[pos])
next(self.iters[pos])
self.increment(pos-1)
def next_sid(self):
"""
Returns the next unused TS6 SID for the server.
"""
while ''.join(self.output) in self.irc.servers:
# Increment until the SID we have doesn't already exist.
self.increment()
sid = ''.join(self.output)
return sid
def add_cmd(func, name=None): def add_cmd(func, name=None):
"""Binds an IRC command function to the given command name.""" """Binds an IRC command function to the given command name."""
if name is None: if name is None: