mirror of
				https://github.com/jlu5/PyLink.git
				synced 2025-10-30 22:47:24 +01:00 
			
		
		
		
	Merge branch 'wip/rework-endburst' into devel
This commit is contained in:
		
						commit
						19bd3ec0b2
					
				
							
								
								
									
										23
									
								
								classes.py
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								classes.py
									
									
									
									
									
								
							| @ -468,16 +468,13 @@ class PyLinkNetworkCore(structures.DeprecatedAttributesObject, structures.CamelC | ||||
|         if not userobj: | ||||
|             return False | ||||
| 
 | ||||
|         # Look for the "service" attribute in the User object, if one exists. | ||||
|         try: | ||||
|             sname = userobj.service | ||||
|             # Warn if the service name we fetched isn't a registered service. | ||||
|             if sname not in world.services.keys(): | ||||
|                 log.warning("(%s) User %s / %s had a service bot record to a service that doesn't " | ||||
|                             "exist (%s)!", self.name, uid, userobj.nick, sname) | ||||
|             return world.services.get(sname) | ||||
|         except AttributeError: | ||||
|             return False | ||||
|         # Look for the "service" attribute in the User object,sname = userobj.service | ||||
|         # Warn if the service name we fetched isn't a registered service. | ||||
|         sname = userobj.service | ||||
|         if sname is not None and sname not in world.services.keys(): | ||||
|             log.warning("(%s) User %s / %s had a service bot record to a service that doesn't " | ||||
|                         "exist (%s)!", self.name, uid, userobj.nick, sname) | ||||
|         return world.services.get(sname) | ||||
| 
 | ||||
| structures._BLACKLISTED_COPY_TYPES.append(PyLinkNetworkCore) | ||||
| 
 | ||||
| @ -1596,6 +1593,9 @@ class User(): | ||||
|         # Cloaked host for IRCds that use it | ||||
|         self.cloaked_host = None | ||||
| 
 | ||||
|         # Stores service bot name if applicable | ||||
|         self.service = None | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return 'User(%s/%s)' % (self.uid, self.nick) | ||||
| IrcUser = User | ||||
| @ -1619,6 +1619,9 @@ class Server(): | ||||
|         self.desc = desc | ||||
|         self._irc = irc | ||||
| 
 | ||||
|         # Has the server finished bursting yet? | ||||
|         self.has_eob = False | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return 'Server(%s)' % self.name | ||||
| 
 | ||||
|  | ||||
| @ -22,8 +22,17 @@ def spawn_service(irc, source, command, args): | ||||
|     # Get the ServiceBot object. | ||||
|     sbot = world.services[name] | ||||
| 
 | ||||
|     old_userobj = irc.users.get(sbot.uids.get(irc.name)) | ||||
|     if old_userobj and old_userobj.service: | ||||
|         # A client already exists, so don't respawn it. | ||||
|         log.debug('(%s) spawn_service: Not respawning service %r as service client %r already exists.', irc.name, name, | ||||
|                   irc.pseudoclient.nick) | ||||
|         return | ||||
| 
 | ||||
|     if name == 'pylink' and irc.pseudoclient: | ||||
|         # irc.pseudoclient already exists, for protocols like clientbot | ||||
|         # irc.pseudoclient already exists, reuse values from it but | ||||
|         # spawn a new client. This is used for protocols like Clientbot, | ||||
|         # so that they can override the main service nick, among other things. | ||||
|         log.debug('(%s) spawn_service: Using existing nick %r for service %r', irc.name, irc.pseudoclient.nick, name) | ||||
|         userobj = irc.pseudoclient | ||||
|         userobj.opertype = "PyLink Service" | ||||
|  | ||||
| @ -217,7 +217,7 @@ def get_prefix_modes(irc, remoteirc, channel, user, mlist=None): | ||||
|     return modes | ||||
| 
 | ||||
| def spawn_relay_server(irc, remoteirc): | ||||
|     if irc.connected.wait(TCONDITION_TIMEOUT): | ||||
|     if irc.connected.is_set(): | ||||
|         try: | ||||
|             # ENDBURST is delayed by 3 secs on supported IRCds to prevent | ||||
|             # triggering join-flood protection and the like. | ||||
| @ -358,7 +358,7 @@ def get_remote_user(irc, remoteirc, user, spawn_if_missing=True, times_tagged=0) | ||||
|     spawning one if it doesn't exist and spawn_if_missing is True.""" | ||||
| 
 | ||||
|     # Wait until the network is working before trying to spawn anything. | ||||
|     if irc.connected.wait(TCONDITION_TIMEOUT): | ||||
|     if irc.connected.is_set(): | ||||
|         # Don't spawn clones for registered service bots. | ||||
|         sbot = irc.get_service_bot(user) | ||||
|         if sbot: | ||||
|  | ||||
| @ -633,10 +633,9 @@ class ClientbotWrapperProtocol(IRCCommonProtocol): | ||||
|             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'} | ||||
|         self.connected.set()  # Note, this should always be set before the actual ENDBURST hook | ||||
|         self.servers[self.uplink].has_eob = True | ||||
|         return {'parse_as': 'ENDBURST'} | ||||
| 
 | ||||
|     handle_422 = handle_376 | ||||
| 
 | ||||
|  | ||||
| @ -17,13 +17,11 @@ class HybridProtocol(TS6Protocol): | ||||
|         self.casemapping = 'ascii' | ||||
|         self.caps = {} | ||||
|         self.hook_map = {'EOB': 'ENDBURST', 'TBURST': 'TOPIC', 'SJOIN': 'JOIN'} | ||||
|         self.has_eob = False | ||||
|         self.protocol_caps -= {'slash-in-hosts'} | ||||
| 
 | ||||
|     def post_connect(self): | ||||
|         """Initializes a connection to a server.""" | ||||
|         ts = self.start_ts | ||||
|         self.has_eob = False | ||||
|         f = self.send | ||||
| 
 | ||||
|         # https://github.com/grawity/irc-docs/blob/master/server/ts6.txt#L80 | ||||
| @ -227,11 +225,15 @@ class HybridProtocol(TS6Protocol): | ||||
|         return {'channel': channel, 'setter': setter, 'ts': ts, 'text': topic} | ||||
| 
 | ||||
|     def handle_eob(self, numeric, command, args): | ||||
|         log.debug('(%s) end of burst received', self.name) | ||||
|         if not self.has_eob:  # Only call ENDBURST hooks if we haven't already. | ||||
|             return {} | ||||
|         """EOB (end-of-burst) handler.""" | ||||
|         log.debug('(%s) end of burst received from %s', self.name, numeric) | ||||
|         if not self.servers[numeric].has_eob: | ||||
|             # Don't fight with TS6's generic PING-as-EOB | ||||
|             self.servers[numeric].has_eob = True | ||||
| 
 | ||||
|         self.has_eob = True | ||||
|             if numeric == self.uplink: | ||||
|                 self.connected.set() | ||||
|             return {} | ||||
| 
 | ||||
|     def handle_svsmode(self, numeric, command, args): | ||||
|         """ | ||||
|  | ||||
| @ -520,9 +520,6 @@ class InspIRCdProtocol(TS6BaseProtocol): | ||||
|             log.debug('(%s) self.prefixmodes set to %r', self.name, | ||||
|                       self.prefixmodes) | ||||
| 
 | ||||
|             # Finally, set the irc.connected (protocol negotiation complete) | ||||
|             # state to True. | ||||
|             self.connected.set() | ||||
|         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 | ||||
|             self.modsupport = args[-1].split() | ||||
| @ -716,6 +713,9 @@ class InspIRCdProtocol(TS6BaseProtocol): | ||||
| 
 | ||||
|     def handle_endburst(self, numeric, command, args): | ||||
|         """ENDBURST handler; sends a hook with empty contents.""" | ||||
|         self.servers[numeric].has_eob = True | ||||
|         if numeric == self.uplink: | ||||
|             self.connected.set() | ||||
|         return {} | ||||
| 
 | ||||
|     def handle_away(self, numeric, command, args): | ||||
|  | ||||
| @ -229,26 +229,32 @@ class NgIRCdProtocol(IRCS2SProtocol): | ||||
|             raise LookupError('No such PyLink client exists.') | ||||
|         log.debug('(%s) sjoin: got %r for users', self.name, users) | ||||
| 
 | ||||
|         njoin_prefix = ':%s NJOIN %s :' % (self._expandPUID(self.sid), channel) | ||||
|         njoin_prefix = ':%s NJOIN %s :' % (self._expandPUID(server), channel) | ||||
|         # Format the user list into strings such as @user1, +user2, user3, etc. | ||||
|         nicks_to_send = ['%s%s' % (''.join(self.prefixmodes[modechar] for modechar in userpair[0] if modechar in self.prefixmodes), | ||||
|                                    self._expandPUID(userpair[1])) for userpair in users] | ||||
| 
 | ||||
|         # Use 13 args max per line: this is equal to the max of 15 minus the command name and target channel. | ||||
|         for message in utils.wrapArguments(njoin_prefix, nicks_to_send, self.S2S_BUFSIZE, separator=',', max_args_per_line=13): | ||||
|             self.send(message) | ||||
| 
 | ||||
|         # Add the affected users to our state. | ||||
|         nicks_to_send = [] | ||||
|         for userpair in users: | ||||
|             uid = userpair[1] | ||||
|             prefixes, uid = userpair | ||||
| 
 | ||||
|             if uid not in self.users: | ||||
|                 log.warning('(%s) Trying to NJOIN missing user %s?', self.name, uid) | ||||
|                 continue | ||||
|             elif uid in self._channels[channel].users: | ||||
|                 # Don't rejoin users already in the channel, this causes errors with ngIRCd. | ||||
|                 continue | ||||
| 
 | ||||
|             self._channels[channel].users.add(uid) | ||||
|             try: | ||||
|                 self.users[uid].channels.add(channel) | ||||
|             except KeyError:  # Not initialized yet? | ||||
|                 log.warning("(%s) sjoin: KeyError trying to add %r to %r's channel list?", self.name, channel, uid) | ||||
|             self.users[uid].channels.add(channel) | ||||
| 
 | ||||
|             self.apply_modes(channel, (('+%s' % prefix, uid) for prefix in userpair[0])) | ||||
| 
 | ||||
|             nicks_to_send.append(''.join(self.prefixmodes[modechar] for modechar in userpair[0]) + \ | ||||
|                                  self._expandPUID(userpair[1])) | ||||
| 
 | ||||
|         if nicks_to_send: | ||||
|             # Use 13 args max per line: this is equal to the max of 15 minus the command name and target channel. | ||||
|             for message in utils.wrapArguments(njoin_prefix, nicks_to_send, self.S2S_BUFSIZE, separator=',', max_args_per_line=13): | ||||
|                 self.send(message) | ||||
| 
 | ||||
|         if modes: | ||||
|             # Burst modes separately if there are any. | ||||
|             log.debug("(%s) sjoin: bursting modes %r for channel %r now", self.name, modes, channel) | ||||
| @ -504,16 +510,20 @@ class NgIRCdProtocol(IRCS2SProtocol): | ||||
|         assert 'IRC+' in args[1], "Linking to non-ngIRCd server using this protocol module is not supported" | ||||
| 
 | ||||
|     def handle_ping(self, source, command, args): | ||||
|         if source == self.uplink: | ||||
|             self._send_with_prefix(self.sid, 'PONG %s :%s' % (self._expandPUID(self.sid), args[-1]), queue=False) | ||||
|         """ | ||||
|         Handles incoming PINGs (and implicit end of burst). | ||||
|         """ | ||||
|         self._send_with_prefix(self.sid, 'PONG %s :%s' % (self._expandPUID(self.sid), args[-1]), queue=False) | ||||
| 
 | ||||
|             if not self.has_eob: | ||||
|                 # Treat the first PING we receive as end of burst. | ||||
|                 self.has_eob = True | ||||
|         if not self.servers[source].has_eob: | ||||
|             # Treat the first PING we receive as end of burst. | ||||
|             self.servers[source].has_eob = True | ||||
| 
 | ||||
|             if source == self.uplink: | ||||
|                 self.connected.set() | ||||
| 
 | ||||
|                 # Return the endburst hook. | ||||
|                 return {'parse_as': 'ENDBURST'} | ||||
|             # Return the endburst hook. | ||||
|             return {'parse_as': 'ENDBURST'} | ||||
| 
 | ||||
|     def handle_server(self, source, command, args): | ||||
|         """ | ||||
|  | ||||
| @ -824,7 +824,6 @@ class P10Protocol(IRCS2SProtocol): | ||||
| 
 | ||||
|         self.send('SERVER %s 1 %s %s J10 %s]]] +s6 :%s' % (name, ts, ts, sid, desc)) | ||||
|         self._send_with_prefix(sid, "EB") | ||||
|         self.connected.set() | ||||
| 
 | ||||
|     def handle_server(self, source, command, args): | ||||
|         """Handles incoming server introductions.""" | ||||
| @ -1110,15 +1109,19 @@ class P10Protocol(IRCS2SProtocol): | ||||
| 
 | ||||
|         return {'channel': channel, 'users': [source], 'modes': | ||||
|                 self._channels[channel].modes, 'ts': ts or int(time.time())} | ||||
| 
 | ||||
|     handle_create = handle_join | ||||
| 
 | ||||
|     def handle_end_of_burst(self, source, command, args): | ||||
|         """Handles end of burst from our uplink.""" | ||||
|         """Handles end of burst from servers.""" | ||||
| 
 | ||||
|         # Send EOB acknowledgement; this is required by the P10 specification, | ||||
|         # and needed if we want to be able to receive channel messages, etc. | ||||
|         if source == self.uplink: | ||||
|             self._send_with_prefix(self.sid, 'EA') | ||||
|             return {} | ||||
|             self.connected.set() | ||||
| 
 | ||||
|         self.servers[source].has_eob = True | ||||
|         return {} | ||||
| 
 | ||||
|     def handle_kick(self, source, command, args): | ||||
|         """Handles incoming KICKs.""" | ||||
|  | ||||
| @ -20,9 +20,6 @@ class TS6Protocol(TS6BaseProtocol): | ||||
|         self.hook_map = {'SJOIN': 'JOIN', 'TB': 'TOPIC', 'TMODE': 'MODE', 'BMASK': 'MODE', | ||||
|                          'EUID': 'UID', 'RSFNC': 'SVSNICK', 'ETB': 'TOPIC', 'USERMODE': 'MODE'} | ||||
| 
 | ||||
|         # Track whether we've received end-of-burst from the uplink. | ||||
|         self.has_eob = False | ||||
| 
 | ||||
|         self.required_caps = {'EUID', 'SAVE', 'TB', 'ENCAP', 'QS', 'CHW'} | ||||
| 
 | ||||
|         # From ChatIRCd: https://github.com/ChatLounge/ChatIRCd/blob/master/doc/technical/ChatIRCd-extra.txt | ||||
| @ -264,7 +261,6 @@ class TS6Protocol(TS6BaseProtocol): | ||||
|     def post_connect(self): | ||||
|         """Initializes a connection to a server.""" | ||||
|         ts = self.start_ts | ||||
|         self.has_eob = False | ||||
| 
 | ||||
|         f = self.send | ||||
| 
 | ||||
| @ -423,9 +419,6 @@ class TS6Protocol(TS6BaseProtocol): | ||||
|         if 'SERVICES' in caps: | ||||
|             self.cmodes['regonly'] = 'r' | ||||
| 
 | ||||
|         log.debug('(%s) self.connected set!', self.name) | ||||
|         self.connected.set() | ||||
| 
 | ||||
|     def handle_ping(self, source, command, args): | ||||
|         """Handles incoming PING commands.""" | ||||
|         # PING: | ||||
| @ -445,10 +438,14 @@ class TS6Protocol(TS6BaseProtocol): | ||||
|         if self.is_internal_server(destination): | ||||
|             self._send_with_prefix(destination, 'PONG %s %s' % (destination, source), queue=False) | ||||
| 
 | ||||
|             if destination == self.sid and not self.has_eob: | ||||
|                 # Charybdis' endburst is just sending a PING to the other server. | ||||
|             if not self.servers[source].has_eob: | ||||
|                 # TS6 endburst is just sending a PING to the other server. | ||||
|                 # https://github.com/charybdis-ircd/charybdis/blob/dc336d1/modules/core/m_server.c#L484-L485 | ||||
|                 self.has_eob = True | ||||
|                 self.servers[source].has_eob = True | ||||
| 
 | ||||
|                 if source == self.uplink: | ||||
|                     log.debug('(%s) self.connected set!', self.name) | ||||
|                     self.connected.set() | ||||
| 
 | ||||
|                 # Return the endburst hook. | ||||
|                 return {'parse_as': 'ENDBURST'} | ||||
|  | ||||
| @ -379,6 +379,9 @@ class UnrealProtocol(TS6BaseProtocol): | ||||
| 
 | ||||
|     def handle_eos(self, numeric, command, args): | ||||
|         """EOS is used to denote end of burst.""" | ||||
|         self.servers[numeric].has_eob = True | ||||
|         if numeric == self.uplink: | ||||
|             self.connected.set() | ||||
|         return {} | ||||
| 
 | ||||
|     def handle_uid(self, numeric, command, args): | ||||
| @ -482,9 +485,6 @@ class UnrealProtocol(TS6BaseProtocol): | ||||
|                                     "(Unreal 4.x), got %s)" % (self.min_proto_ver, protover)) | ||||
|             self.servers[numeric] = Server(self, None, sname, desc=sdesc) | ||||
| 
 | ||||
|             # Set irc.connected to True, meaning that protocol negotiation passed. | ||||
|             log.debug('(%s) self.connected set!', self.name) | ||||
|             self.connected.set() | ||||
|         else: | ||||
|             # Legacy (non-SID) servers can still be introduced using the SERVER command. | ||||
|             # <- :services.int SERVER a.bc 2 :(H) [GL] a | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 James Lu
						James Lu