Changeset 143

Show
Ignore:
Timestamp:
05/06/02 23:04:15 (6 years ago)
Author:
sholloway
Message:

Documentation and optimization

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/RBJabber/RBJabber/Client.py

    r140 r143  
    3131##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3232 
     33"""Modular Jabber client 
     34 
     35Dependencies: 
     36    Foundation.SubjectObserver 
     37    Foundation.Jabber 
     38 
     39The Jabber protocol can be found at http://jabber.org,  
     40and at the time of writting, specifically at  
     41http://docs.jabber.org/general/html/protocol.html 
     42 
     43""" 
     44 
    3345#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3446#~ Imports  
    3547#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3648 
    37 import weakref 
    3849from xml.sax.saxutils import escape, quoteattr 
    39 from Foundation import XMLObjectify, SubjectObserver 
    40 import JID 
    41 import Base 
    42  
    43 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    44 #~ Jabber Node Classes 
    45 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    46  
    47 class JabberNode(XMLObjectify.ObjectifiedXML): 
    48     def __init__(self, client, parent, namespace, node, attributes): 
    49         self._client = weakref.proxy(client) 
    50         XMLObjectify.ObjectifiedXML.__init__(self, client, parent, namespace, node, attributes) 
    51  
    52     def Content(self): 
    53         return [element[1] for element in self._elements if element[0][1] == ''] 
    54  
    55     def Elements(self, node=None, namespace=None): 
    56         elementList = self._elements 
    57         if namespace is not None: 
    58             elementList = [element for element in elementList if element[0][0] == namespace] 
    59         if node is None: elementList = [element[1] for element in elementList if element[0][1]] 
    60         elif node == '*': elementList = [element[1] for element in elementList] 
    61         else: elementList = [element[1] for element in elementList if element[0][1] == node] 
    62         return elementList 
    63  
    64 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    65  
    66 class JabberClientNode(JabberNode): 
    67     def _xmlInitComplete(self): 
    68         if self.__namespace__ == 'jabber:client': 
    69             self._client.stream.UpdateObserversEx({self.__node__: self}) 
    70  
    71 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    72  
    73 class JabberStream(JabberNode): 
    74     def _xmlInitStarted(self): 
    75         self._client.stream.UpdateObservers(settings=self) 
    76     def _addElement(self, namespace, node, element): pass 
    77     def _addData(self, data): pass 
    78  
    79 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    80  
    81 class JabberStreamError(JabberNode): 
    82     eJabberStreamError = 'JabberStreamError' 
    83  
    84     def _xmlInitComplete(self): 
    85         self._client.stream.UpdateObserversEx({self.__node__: self}) 
    86         self._client.Shutdown() 
    87         raise self.eJabberStreamError, ''.join(self._toXML()) 
    88           
    89 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    90  
    91 class JabberClientPresence(JabberClientNode): 
    92     _default_attributes = {'from':'', 'type':'available', 'id':'default_presence_id'} 
    93  
    94 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    95  
    96 class JabberClientMessage(JabberClientNode): 
    97     _default_attributes = {'from':'', 'to':'', 'type':'message', 'id':'default_message_id'} 
    98  
    99 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    100  
    101 class JabberClientIQ(JabberClientNode): 
    102     _default_attributes = {'from':'', 'type':'get', 'id':'default_iq_id'} 
     50from Foundation.Jabber import Base 
     51from Foundation.Jabber import ClientNodes 
     52from Foundation.Jabber import JID 
     53from Foundation import SubjectObserver 
    10354 
    10455#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    10657#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    10758 
    108 class Client(Base.Client): 
    109     DefaultJabberNode = JabberNode 
     59class Client(Base.BasicClient): 
     60    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     61    #~ Constants / Variables / Etc.  
     62    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     63 
    11064    JabberNodeMap = { 
    111         ('http://etherx.jabber.org/streams', 'stream'): JabberStream, 
    112         ('http://etherx.jabber.org/streams', 'error'): JabberStreamError, 
    113         ('jabber:client', 'presence'): JabberClientPresence, 
    114         ('jabber:client', 'message'): JabberClientMessage, 
    115         ('jabber:client', 'iq'): JabberClientIQ, 
     65        # http://etherx.jabber.org/streams nodes 
     66        ('http://etherx.jabber.org/streams', 'stream'): ClientNodes.JabberStream, 
     67        ('http://etherx.jabber.org/streams', 'error'): ClientNodes.JabberStreamError, 
     68 
     69        # jabber:client nodes 
     70        ('jabber:client', 'presence'): ClientNodes.JabberClientPresence, 
     71        ('jabber:client', 'message'): ClientNodes.JabberClientMessage, 
     72        ('jabber:client', 'iq'): ClientNodes.JabberClientIQ, 
     73 
     74        None: ClientNodes.JabberNode, # Default if no match with a defined (namespace, node) 
    11675        } 
    11776 
    11877    stream = None 
     78    information = None 
    11979    SupportedNS = None 
     80 
     81    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     82    #~ Special  
     83    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    12084 
    12185    def __init__(self, *args, **kw): 
    12286        self.stream = SubjectObserver.BidableCategorySubject() 
    12387        self.information = SubjectObserver.AttributedSubject.AttributedSubject() 
    124         apply(Base.Client.__init__, (self,) + args, kw) 
     88        apply(Base.BasicClient.__init__, (self,) + args, kw) 
    12589         
    12690        self.SupportedNS = [] 
    12791        self.__NextID = 0 
    12892 
     93    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     94    #~ Protected Methods  
     95    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     96 
    12997    def _GetNextID(self): 
     98        """Used by various methods to obtain a "new id" for a message.""" 
    13099        self.__NextID += 1 
    131100        return str(self.__NextID) 
    132101 
     102    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     103    #~ Public Methods  
     104    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     105 
    133106    def Authenticate(self, username, password, resource, callback=None): 
     107        """Authenticates with the Jabber server.   
     108        Assumes basic authentication protocol. 
     109 
     110        Returns the iqAuthQuery instance that is sent. 
     111 
     112        See also: iqAuthQuery""" 
    134113        import iqAuthQuery 
    135114        query = iqAuthQuery.iqAuthQuery(self, username, password, resource, callback) 
     
    137116 
    138117    def Register(self, username, password, callback=None): 
     118        """Registers a new account with the Jabber server.   
     119        Assumes basic registration protocol. 
     120 
     121        Returns the iqQuery instance that is sent. 
     122 
     123        See also: iqQuery""" 
    139124        import iqQuery 
    140125        strXML = '<username>%s</username><password>%s</password>' % (username, password) 
     
    144129 
    145130    def QueryRoster(self, callback=None): 
     131        """Requests the jabber:iq:roster for the currently authenticated JID. 
     132 
     133        Returns the iqRosterQuery instance that is sent. 
     134 
     135        See also: iqRosterQuery""" 
    146136        import iqRosterQuery 
    147137        return iqRosterQuery.iqRosterQuery(self, callback) 
    148138 
    149139    def BrowseJID(self, toJID, callback): 
     140        """Requests the jabber:iq:browse data for the currently authenticated JID. 
     141 
     142        Returns the iqQuery instance that is sent. 
     143 
     144        See also: iqQuery""" 
    150145        import iqQuery 
    151146        query = iqQuery.iqQuery(self, callback) 
     
    153148        return query 
    154149 
     150    def SetPublicData(self, namespace, xml): 
     151        """Saves xml data into namespace. 
     152 
     153        Returns the sent XML.""" 
     154        return self.SendXML('''<iq id='%s' type='set'><query xmlns='%s'>%s</query></iq> ''' % (self._GetNextID(), namespace, xml)) 
     155    SetData = SetPublicData # Alias 
     156  
    155157    def SetPrivateData(self, xml): 
     158        """Saves xml data into the jabber:iq:private. 
     159 
     160        Returns the sent XML.""" 
    156161        return self.SetData('jabber:iq:private', xml) 
    157162 
    158     def SetData(self, namespace, xml): 
    159         return self.SendXML('''<iq id='%s' type='set'><query xmlns='%s'>%s</query></iq> ''' % (self._GetNextID(), namespace, xml)) 
    160   
     163    def GetPublicData(self, namespace, callback=None, xml=''): 
     164        """Requests the data stored in namespace. 
     165 
     166        Returns the iqQuery instance that is sent. 
     167 
     168        See also: iqQuery""" 
     169        return self.Query(namespace, '', type='get', callback=callback, xml=xml) 
     170    GetData = GetPublicData # Alias 
     171 
    161172    def GetPrivateData(self, callback=None, xml=''): 
     173        """Requests the data stored in jabber:iq:private. 
     174 
     175        Returns the iqQuery instance that is sent. 
     176 
     177        See also: iqQuery""" 
    162178        return self.GetData('jabber:iq:private', callback=callback, xml=xml) 
    163179 
    164     def GetData(self, namespace, callback=None, xml=''): 
    165         return self.Query(namespace, '', type='get', callback=callback, xml=xml) 
    166  
    167180    def Query(self, namespace, toJID='', type='get', callback=None, xml=''): 
     181        """Sends a jabber:client iq query to toJID. 
     182 
     183        Returns the iqQuery instance that is sent. 
     184 
     185        See also: iqQuery""" 
    168186        import iqQuery 
    169187        query = iqQuery.iqQuery(self, callback) 
     
    172190 
    173191    def Message(self, toJID, body='', subject='', type='message', id=None, xml=''): 
     192        """Sends a jabber:client message to toJID. 
     193 
     194        Returns the sent XML.""" 
    174195        idMessage = id or self._GetNextID() 
    175196        strXML = '<message id=%s to=%s type=%s>' % (quoteattr(idMessage), quoteattr(toJID), quoteattr(type)) 
     
    182203        return self.SendXML(strXML) 
    183204 
    184     def Subscribe(self, toJID='', type='subscribe'): 
    185         return self.Presence(toJID=toJID, type=type) 
    186  
    187     def Unsubscribe(self, toJID='', type='unsubscribe'): 
    188         return self.Presence(toJID=toJID, type=type) 
    189  
    190205    def Presence(self, toJID='', status='', show='', type='available', xml=''): 
     206        """Sends a jabber:client presence to toJID or everyone if not toJID. 
     207 
     208        Returns the sent XML.""" 
    191209        idPresence = self._GetNextID() 
    192210        strXML = '<presence id=%s ' % quoteattr(idPresence) 
     
    195213        strXML += '>' 
    196214 
    197         if show: 
    198             strXML += '<show>%s</show>'  % escape(show)  
    199         if status:  
    200             strXML += '<status>%s</status>' % escape(status) 
    201         if xml: 
    202             strXML += xml 
     215        if show: strXML += '<show>%s</show>'  % escape(show)  
     216        if status: strXML += '<status>%s</status>' % escape(status) 
     217        if xml: strXML += xml 
    203218        strXML += '</presence>' 
    204219        return self.SendXML(strXML) 
    205220 
    206 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    207 #~ Testing  
    208 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    209  
    210 if __name__=='__main__': 
    211     import Test 
    212  
     221    def Subscribe(self, toJID, type='subscribe'): 
     222        """Sends a jabber:client presence subscribe request to toJID. 
     223 
     224        Returns the sent XML.""" 
     225        return self.Presence(toJID=toJID, type=type) 
     226 
     227    def Unsubscribe(self, toJID, type='unsubscribe'): 
     228        """Sends a jabber:client presence unsubscribe request to toJID. 
     229 
     230        Returns the sent XML.""" 
     231        return self.Presence(toJID=toJID, type=type) 
     232 
     233 
  • trunk/RBJabber/RBJabber/Conference.py

    r138 r143  
    3131##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3232 
     33"""Conference implementation for Jabber conferencing 
     34 
     35The Jabber protocol can be found at http://jabber.org,  
     36and at the time of writting, specifically at  
     37http://docs.jabber.org/general/html/protocol.html 
     38 
     39Specifically, this module relates to jabber:iq:conference. 
     40""" 
     41 
    3342#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3443#~ Imports  
     
    4554 
    4655class Conference(JabberSubjectBase): 
     56    """Sets up a Jabber conference at a specific conference JID. 
     57 
     58    Note that conferencing is a Jabber module, and as such, needs to 
     59    be enabled on the Jabber server itself. 
     60    """ 
     61     
     62    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     63    #~ Special  
     64    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     65 
    4766    def __init__(self, JC, ConferenceJID): 
    4867        JabberSubjectBase.__init__(self) 
     
    6281 
    6382    def __del__(self): 
    64         if self.JC(): 
    65             self.Presence(type='unavailable') 
     83        """Disconnects from the conference if the Jabber Client still exists""" 
     84        if self.JC(): self.Presence(type='unavailable') 
     85 
     86    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     87    #~ Protected Methods  
     88    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    6689 
    6790    def _CallConference(self, subject, **UpdateDict): 
    68         pass 
     91        """Dummy method to place in the stream Observer.  Should never be called.""" 
     92        assert 0 
    6993 
    7094    def _BidConference(self, subject, **UpdateDict): 
     95        """Selects only the messages off the jabber stream that are pertinate to the conference JID""" 
    7196        for each in UpdateDict.itervalues(): 
    7297            if each.from_ not in self.ConferenceJID: 
     
    75100        return apply(self.__super.Bid, (subject,), UpdateDict) 
    76101         
    77     def Ask(self, callback): 
    78         self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='get', callback=callback) 
     102    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     103    #~ Public Methods  
     104    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    79105 
    80     def JoinAs(self, NickName, Secret='', callback=None): 
    81         if not isinstance(NickName, (list, tuple)): NickName = (NickName,) 
    82         xmlNickName = ''.join(['<nick>%s</nick>' % x for x in NickName]) 
     106    def Disconnect(self): 
     107        """Disconnects from the conference by sending and unavailable presence""" 
     108        return self.Presence(type='unavailable') 
     109 
     110    def GetInformation(self, callback): 
     111        """Requests information about the conference in question.   
     112         
     113        Returns iqQuery object.""" 
     114        return self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='get', callback=callback) 
     115 
     116    def JoinAs(self, Nick, Secret='', callback=None): 
     117        """Joins the conference using Nick(s), and Secret if need be.   
     118         
     119        Returns iqQuery object.""" 
     120        if not isinstance(Nick, (list, tuple)): Nick = (Nick,) 
     121        xmlNick = ''.join(['<nick>%s</nick>' % x for x in Nick]) 
    83122        xmlSecret = Secret and ('<secret>%s</secret>' % Secret) or '' 
    84         self.JC().Presence(toJID=self.ConferenceJID
    85         self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='set', xml=xmlNickName + xmlSecret, callback=callback) 
     123        self.Presence(
     124        return self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='set', xml=xmlNick + xmlSecret, callback=callback) 
    86125 
     126    def ChangeNick(self, Nick, callback=None): 
     127        """Changes the nickname of the conference connection""" 
     128        if not isinstance(Nick, (list, tuple)): Nick = (Nick,) 
     129        xmlNick = ''.join(['<nick>%s</nick>' % x for x in Nick]) 
     130        return self.JC().Query('jabber:iq:conference', toJID=self.ConferenceJID, type='set', xml=xmlNick, callback=callback) 
     131         
    87132    def Invite(self, *args, **kw): 
     133        """Invites a JID to join the conference""" 
    88134        kw['xml'] = kw.get('xml', '') + '<x xmlns="jabber:x:conference" jid="%s"/>' % self.ConferenceJID 
    89135        return apply(self.JC().Message, args, kw) 
    90136 
    91137    def Message(self, *args, **kw): 
     138        """Sends a message to the conference""" 
    92139        return apply(self.JC().Message, (self.ConferenceJID, ) + args, kw) 
    93140 
    94141    def Presence(self, *args, **kw): 
     142        """Sends a presence to the conference""" 
    95143        return apply(self.JC().Presence, (self.ConferenceJID, ) + args, kw) 
    96144 
     
    114162    conf.Message('Hello from RB development!!!') 
    115163    jc.ProcessPending(1.0) 
    116     try: 
     164    try:  
    117165        while 1: jc.Process(1.0) 
    118     except KeyboardInterrupt: 
    119         pass 
    120     #del conf 
    121     #try: 
    122     #    while 1: jc.Process(1.0) 
    123     #except KeyboardInterrupt: 
    124     #    pass 
     166    except KeyboardInterrupt: pass 
    125167    print "Test complete." 
    126168 
  • trunk/RBJabber/RBJabber/JID.py

    r108 r143  
    3131##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3232 
     33"""JID class and convenience functions. 
     34 
     35Dependencies: 
     36    re 
     37""" 
     38 
    3339#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3440#~ Imports  
     
    4046#~ Constants / Variables / Etc.  
    4147#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     48 
     49__all__ = ['reJabberURL', 'reJabberJID', 'JID'] 
    4250 
    4351_restrID = '''[\w-]+(?:[\w\.-]+[\w-]+)?''' 
     
    4755reJabberJID = re.compile('''(?:(%s)@)?(%s)(?:/(%s))?''' % (_restrID, _restrServer, _restrResource)) 
    4856 
    49 __all__ = ['reJabberURL', 'reJabberJID'] 
    50  
    5157#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    5258#~ Definitions  
     
    5460 
    5561def splitnorm(strJID, start=1, end=4): 
     62    """Splits username@server/resource into [username, server, resource],  
     63    and normalizes username and server.  Returns a start to end-1 of the list""" 
    5664    result = reJabberJID.split(strJID)[start:end] 
    5765    return [x and x.lower() or x for x in result[1-start:3-start]] + result[3-start:] 
    5866 
    5967def split(strJID, start=1, end=4): 
     68    """Splits username@server/resource into [username, server, resource].   
     69    Returns a start to end-1 of the list""" 
    6070    return reJabberJID.split(strJID)[start:end] 
    6171 
    6272def splitex(strJID, start=1, end=4, normalize=0): 
     73    """Uses normalize flag to choose between split and splitnorm.""" 
    6374    if normalize: return splitnorm(strJID, start, end) 
    6475    else: return split(strJID, start, end) 
    6576 
    6677def cmp_(strJIDa, strJIDb, resource=1): 
     78    """Compares two JIDs, including resources if flag is set.   
     79    Note that case is insensitive in username and server, but IS sensitive in the resource.""" 
    6780    jida = splitnorm(strJIDa, end=3+resource) 
    6881    jidb = splitnorm(strJIDb, end=3+resource) 
     
    7083 
    7184def compare(*args, **kw): 
     85    """Same as cmp_, except returns a python truth value instead of a cmp result.""" 
    7286    return 0 == apply(cmp_, args, kw) 
    7387 
    7488def contains(strJIDa, strJIDb): 
     89    """Returns true if strJIDb is strJIDb, or strJIDb is qualified by a resource. 
     90    Will fail if strJIDa has a resource and is not the same JID as strJIDb.""" 
    7591    jida = filter(None, splitnorm(strJIDa, end=4)) 
    7692    jidb = filter(None, splitnorm(strJIDb, end=3))[-len(jida):] 
     
    7894 
    7995def join(*args): 
     96    """Combines a (username, server, resource) into username@server/resource, but does so  
     97    in several different formats""" 
    8098    if isinstance(args[0], (tuple, list)): 
    8199        args = tuple(args[0]) 
     
    89107    else: return '' 
    90108 
    91 def username(strJID, normalize=0): return splitex(strJID, 1, 2, normalize)[0] 
    92 def server(strJID, normalize=0): return splitex(strJID, 2, 3, normalize)[0] 
    93 def resource(strJID, normalize=0): return splitex(strJID, 3, 4, normalize)[0] 
    94 def noresource(strJID, normalize=0): return join(splitex(strJID, 1, 3, normalize)) 
    95 def nominal(strJID): return join(splitnorm(strJID, 1, 3)) 
    96 def normalize(strJID): return join(splitnorm(strJID)) 
     109def username(strJID, normalize=0):  
     110    """Extracts the username from the JID""" 
     111    return splitex(strJID, 1, 2, normalize)[0] 
     112def server(strJID, normalize=0):  
     113    """Extracts the server from the JID""" 
     114    return splitex(strJID, 2, 3, normalize)[0] 
     115def resource(strJID, normalize=0):  
     116    """Extracts the resource from the JID""" 
     117    return splitex(strJID, 3, 4, normalize)[0] 
     118def noresource(strJID, normalize=0):  
     119    """Returns the JID without the resource qualification""" 
     120    return join(splitex(strJID, 1, 3, normalize)) 
     121def nominal(strJID):  
     122    """Returns the JID with username and server lowercased, and no resource""" 
     123    return join(splitnorm(strJID, 1, 3)) 
     124def normalize(strJID):  
     125    """Returns the JID with the username and server lowercased, but retaining the resource""" 
     126    return join(splitnorm(strJID)) 
    97127 
    98128#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    102132class JID(str): 
    103133    """An object-oriented way of dealing with JIDs""" 
     134 
     135    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     136    #~ Public Methods  
     137    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     138 
    104139    split = split 
    105140    splitnorm = splitnorm 
     
    118153        return Class(apply(join, args)) 
    119154    join = classmethod(join) 
     155 
     156    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     157    #~ Special  
     158    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    120159 
    121160    __cmp__ = cmp_ 
  • trunk/RBJabber/RBJabber/JabberConnection.py

    r129 r143  
    3131##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3232 
     33"""Very basic Jabber client. 
     34 
     35Dependencies: 
     36    Foundation.SmartSelect 
     37    Foundation.XMLBuilder 
     38    socket 
     39 
     40The Jabber protocol can be found at http://jabber.org,  
     41and at the time of writting, specifically at  
     42http://docs.jabber.org/general/html/protocol.html 
     43 
     44""" 
     45 
    3346#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    3447#~ Imports                                            
     
    3952from Foundation.ContextApply import BindCallable 
    4053import JID 
    41 from JID import reJabberURL, reJabberJID 
    4254import socket 
    4355import select 
     
    5466#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    5567 
    56 class Client(XMLBuilderMixin, SmartSelect.SmartSelectClientBase): 
     68class BasicClient(XMLBuilderMixin, SmartSelect.SmartSelectClientBase): 
     69    """Very basic Jabber client, including: 
     70        - Smart select socket handling,  
     71        - Structured XML parsing,  
     72        - Initializing and finalizing connection with the Jabber Server 
     73    """ 
     74 
    5775    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    5876    #~ Constants / Variables / Etc.  
     
    6078 
    6179    ServerJID = None 
    62     DefaultJabberNode = XMLBuilderObjectBase 
    63     JabberNodeMap = {} 
     80    JabberNodeMap = {None: XMLBuilderObjectBase} 
    6481 
    6582    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    7693        self._sendData = '' 
    7794        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    78         params = reJabberURL.split(JabberServerURL)[1:-1] 
     95        params = JID.reJabberURL.split(JabberServerURL)[1:-1] 
    7996        self._socket.connect((params[0], port)) 
    8097        self.fileno = self._socket.fileno 
     
    95112 
    96113    def BuildJID(self, username='', server='', resource=''): 
     114        """Returns a JID that will resolve to username@server/resource.   
     115        Additionally, if you simply want the corresponding JID on the current  
     116        Jabber Server, all you need to pass is the username.""" 
    97117        return JID.JID.join(username, server or self.ServerJID, resource) 
    98118         
    99     def SendXML(self, xmlData): 
    100         self._sendData += xmlData 
    101         return xmlData 
     119    def SendXML(self, xml): 
     120        """Sends bare-bones xml data on the socket.   
     121        Note: the xml is not checked for validity, but the Jabber Servers get 
     122        pretty upset when the data is invalid, and they tend to sever the socket.""" 
     123        self._sendData += xml 
     124        return xml 
    102125 
    103126    def Shutdown(self): 
     127        """Politely disconnects the socket and parser from the jabber stream""" 
    104128        if __debug__: print 'Disconnecting %s from %s' % (self.__class__.__name__, self.ServerJID) 
    105129        self._SendXMLImmediate('%s' % _xmlJabberFooter) 
     
    109133 
    110134    def _SetDebug(self, fileIn, fileOut): 
     135        """Sets the debug input and output files (or file-like-objects).   
     136        These can also be passed in when creating the Jabber Client""" 
    111137        self._fileIn = fileIn 
    112138        self._fileOut = fileOut 
     
    117143 
    118144    def _start_element(self, name, attributes): 
     145        """Used internally by the XML parsing mechanism.   
     146        This override replaces jid-related attributes with a JID class instance.""" 
    119147        if name[:7] == 'jabber:': 
    120148            for key in ('to', 'from', 'jid'): 
    121149                if key in attributes:  
    122150                    attributes[key] = JID.JID(attributes[key]) 
    123         return super(Client, self)._start_element(name, attributes) 
     151        return self.__super._start_element(name, attributes) 
    124152 
    125153    def _GetElementFactory(self, owner, parent, namespace, node, attributes): 
    126         result = None 
    127  
    128         if self._elements: 
    129             # Try to get a factory from the parent 
    130             result = self._elements[-1]._xmlChildFactory(owner, parent, namespace, node, attributes) 
    131  
    132         if not result: 
    133             # Otherwise, try to load it from the disk 
    134             result = self.JabberNodeMap.get((namespace, node), self.DefaultJabberNode) 
    135  
    136         return result 
     154        """Again used by the XML parsing mechanism to find an Element class factory to  
     155        represent the node defined by (owner, parent, namespace, node, attributes).""" 
     156        return self.JabberNodeMap.get((namespace, node), self.JabberNodeMap[None]) 
    137157 
    138158    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    140160    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    141161 
    142     def _NeedsRead(self): return self._socket and 1 
     162    def _NeedsRead(self):  
     163        """Used by the SmartSelect mechanism to signal when reading is needed.""" 
     164        return self._socket and 1 
    143165    def _ProcessRead(self): 
     166        """Called by the SmartSelect mechanism when the socket is ready to be read from.""" 
    144167        data = self._socket.recv(8192) 
    145168        if self._fileIn: print >> self._fileIn, data 
    146169        self._parser.Parse(data) 
    147170          
    148     def _NeedsWrite(self): return self._socket and self._sendData and 1 or 0 
     171    def _NeedsWrite(self):  
     172        """Used by the SmartSelect mechanism to signal when writing is needed.""" 
     173        return self._socket and self._sendData and 1 or 0 
    149174    def _ProcessWrite(self): 
     175        """Called by the SmartSelect mechanism when the socket is ready to be written to.""" 
    150176        nSent = self._socket.send(self._sendData) 
    151177        if self._fileOut: print >> self._fileOut, self._sendData[:nSent] 
     
    156182    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    157183 
    158     def _SendXMLImmediate(self, xmlData): 
    159         self._sendData += xmlData 
    160         while self._sendData: 
     184    def _SendXMLImmediate(self, xml): 
     185        """Similar to SendXML, except that _SendXMLImmediate only returns when all the pending 
     186        XML data has been written to the socket.""" 
     187        self._sendData += xml 
     188        while self._sendData and select.select([], [self._socket], []): 
    161189            nSent = self._socket.send(self._sendData) 
    162190            if self._fileOut: print >> self._fileOut, self._sendData[:nSent] 
    163191            self._sendData = self._sendData[nSent:] 
    164192 
     193# Keep the super around for speed 
     194BasicClient._BasicClient__super = super(BasicClient)