mirror of
				https://github.com/jlu5/PyLink.git
				synced 2025-10-31 15:07:25 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			146 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import time
 | |
| import sys
 | |
| import os
 | |
| import re
 | |
| import time
 | |
| 
 | |
| curdir = os.path.dirname(__file__)
 | |
| sys.path += [curdir, os.path.dirname(curdir)]
 | |
| import utils
 | |
| from log import log
 | |
| import proto_common
 | |
| from classes import *
 | |
| 
 | |
| casemapping = 'ascii'
 | |
| proto_ver = 2351
 | |
| 
 | |
| hook_map = {}
 | |
| 
 | |
| def spawnClient(irc, nick, ident, host, **kwargs):
 | |
|     pass
 | |
| 
 | |
| def joinClient(irc, client, channel):
 | |
|     pass
 | |
| 
 | |
| def pingServer(irc, source=None, target=None):
 | |
|     source = source or irc.sid
 | |
|     target = target or irc.uplink
 | |
|     if not (target is None or source is None):
 | |
|         _send(irc, source, 'PING %s %s' % (irc.servers[source].name, irc.servers[target].name))
 | |
| 
 | |
| def _send(irc, sid, msg):
 | |
|     irc.send(':%s %s' % (sid, msg))
 | |
| 
 | |
| def connect(irc):
 | |
|     ts = irc.start_ts
 | |
|     irc.caps = []
 | |
| 
 | |
|     f = irc.send
 | |
|     host = irc.serverdata["hostname"]
 | |
|     f('PASS :%s' % irc.serverdata["sendpass"])
 | |
|     # https://github.com/unrealircd/unrealircd/blob/2f8cb55e/doc/technical/protoctl.txt
 | |
|     # We support the following protocol features:
 | |
|     # SJ3 - extended SJOIN
 | |
|     # NOQUIT - QUIT messages aren't sent for all users in a netsplit
 | |
|     # NICKv2 - Extended NICK command, sending MODE and CHGHOST info with it
 | |
|     # SID - Use UIDs and SIDs (unreal 3.4)
 | |
|     # VL - Sends version string in below SERVER message
 | |
|     # UMODE2 - used for users setting modes on themselves (one less argument needed)
 | |
|     # EAUTH - Early auth? (Unreal 3.4 linking protocol)
 | |
|     f('PROTOCTL SJ3 NOQUIT NICKv2 VL UMODE2 PROTOCTL EAUTH=%s SID=%s' % (irc.serverdata["hostname"], irc.sid))
 | |
|     sdesc = irc.serverdata.get('serverdesc') or irc.botdata['serverdesc']
 | |
|     f('SERVER %s 1 U%s-h6e-%s :%s' % (host, proto_ver, irc.sid, sdesc))
 | |
|     # Now, we wait until remote sends its NETINFO command (handle_netinfo),
 | |
|     # so we can find and use a matching netname, preventing netname mismatch
 | |
|     # errors.
 | |
| 
 | |
| def handle_netinfo(irc, numeric, command, args):
 | |
|     # <- NETINFO maxglobal currenttime protocolversion cloakhash 0 0 0 :networkname
 | |
|     # "maxglobal" is the amount of maximum global users we've seen so far.
 | |
|     # We'll just set it to 1 (the PyLink client), since this is completely
 | |
|     # arbitrary.
 | |
|     irc.send('NETINFO 1 %s %s * 0 0 0 :%s' % (irc.start_ts, proto_ver, args[-1]))
 | |
|     _send(irc, irc.sid, 'EOS')
 | |
| 
 | |
| def handle_uid(irc, numeric, command, args):
 | |
|     # <- :001 UID GL 0 1441306929 gl localhost 0018S7901 0 +iowx * midnight-1C620195 fwAAAQ== :realname
 | |
|     # <- :001 UID GL| 1 1441312224 gl localhost 001J7FZ02 0 +iwx midnight-1C620195 midnight-1C620195 fwAAAQ== :realname
 | |
|     pass
 | |
| 
 | |
| def handle_pass(irc, numeric, command, args):
 | |
|     # <- PASS :abcdefg
 | |
|     if args[0] != irc.serverdata['recvpass']:
 | |
|         raise ProtocolError("Error: RECVPASS from uplink does not match configuration!")
 | |
| 
 | |
| def handle_ping(irc, numeric, command, args):
 | |
|     if numeric == irc.uplink:
 | |
|         irc.send('PONG %s :%s' % (irc.serverdata['hostname'], args[-1]))
 | |
| 
 | |
| def handle_pong(irc, source, command, args):
 | |
|     log.debug('(%s) Ping received from %s for %s.', irc.name, source, args[-1])
 | |
|     if source in (irc.uplink, irc.servers[irc.uplink].name) and args[-1] == irc.serverdata['hostname']:
 | |
|         log.debug('(%s) Set irc.lastping.', irc.name)
 | |
|         irc.lastping = time.time()
 | |
| 
 | |
| def handle_server(irc, numeric, command, args):
 | |
|     # <- SERVER unreal.midnight.vpn 1 :UnrealIRCd test server
 | |
|     sname = args[0]
 | |
|     # TODO: handle server introductions from other servers
 | |
|     if numeric == irc.uplink:
 | |
|         irc.servers[numeric] = IrcServer(None, sname)
 | |
|     else:
 | |
|         raise NotImplementedError
 | |
| 
 | |
| _unrealCmodes = {'l': 'limit', 'c': 'blockcolor', 'G': 'censor',
 | |
|                  'D': 'delayjoin', 'n': 'noextmsg', 's': 'secret',
 | |
|                  'T': 'nonotice', 'z': 'sslonly', 'b': 'ban', 'V': 'noinvite',
 | |
|                  'Z': 'issecure', 'r': 'registered', 'N': 'nonick',
 | |
|                  'e': 'banexception', 'R': 'regonly', 'M': 'regmoderated',
 | |
|                  'p': 'private', 'Q': 'nokick', 'P': 'permanent', 'k': 'key',
 | |
|                  'C': 'noctcp', 'O': 'operonly', 'S': 'stripcolor',
 | |
|                  'm': 'moderated', 'K': 'noknock', 'o': 'op', 'v': 'voice',
 | |
|                  'I': 'invex', 't': 'topiclock'}
 | |
| def handle_protoctl(irc, numeric, command, args):
 | |
|     # <- PROTOCTL NOQUIT NICKv2 SJOIN SJOIN2 UMODE2 VL SJ3 TKLEXT TKLEXT2 NICKIP ESVID
 | |
|     # <- PROTOCTL CHANMODES=beI,k,l,psmntirzMQNRTOVKDdGPZSCc NICKCHARS= SID=001 MLOCK TS=1441314501 EXTSWHOIS
 | |
|     irc.caps += args
 | |
|     for cap in args:
 | |
|         if cap.startswith('SID'):
 | |
|             irc.uplink = cap.split('=', 1)[1]
 | |
|         elif cap.startswith('CHANMODES'):
 | |
|             cmodes = cap.split('=', 1)[1]
 | |
|             irc.cmodes['*A'], irc.cmodes['*B'], irc.cmodes['*C'], irc.cmodes['*D'] = cmodes.split(',')
 | |
|             for m in cmodes:
 | |
|                 if m in _unrealCmodes:
 | |
|                     irc.cmodes[_unrealCmodes[m]] = m
 | |
| 
 | |
| def handle_events(irc, data):
 | |
|     # Unreal's protocol has three styles of commands, @servernumeric, :user, and plain commands.
 | |
|     # e.g. NICK introduction looks like:
 | |
|     #   <- NICK nick hopcount timestamp	username hostname server service-identifier-token +usermodes virtualhost :realname
 | |
|     # while PRIVMSG looks like:
 | |
|     #   <- :source ! target :message
 | |
|     # and SJOIN looks like:
 | |
|     #   <- @servernumeric SJOIN <ts> <chname> [<modes>] [<mode para> ...] :<[[*~@%+]member] [&"ban/except] ...>
 | |
|     # Same deal as TS6 with :'s indicating a long argument lasting to the
 | |
|     # end of the line.
 | |
|     args = proto_common.parseArgs(data.split(" "))
 | |
|     # Message starts with a SID/UID prefix.
 | |
|     if args[0][0] in ':@':
 | |
|         numeric = args[0].lstrip(':@')
 | |
|         command = args[1]
 | |
|         args = args[2:]
 | |
|     else:
 | |
|         # Raw command w/o explicit sender, assume it's being sent by our uplink.
 | |
|         numeric = irc.uplink
 | |
|         command = args[0]
 | |
|         args = args[1:]
 | |
|     try:
 | |
|         func = globals()['handle_'+command.lower()]
 | |
|     except KeyError:  # unhandled command
 | |
|         pass
 | |
|     else:
 | |
|         parsed_args = func(irc, numeric, command, args)
 | |
|         if parsed_args is not None:
 | |
|             return [numeric, command, parsed_args]
 | 
