root/branches/gajim_0.5.1/common/jabber.py

Revision 313, 53.4 kB (checked in by asterix, 4 years ago)

send a packet on the socket every minute for NAT timeouts

  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
Line 
1##   jabber.py
2##
3##   Copyright (C) 2001 Matthew Allum
4##
5##   This program is free software; you can redistribute it and/or modify
6##   it under the terms of the GNU Lesser General Public License as published
7##   by the Free Software Foundation; either version 2, or (at your option)
8##   any later version.
9##
10##   This program is distributed in the hope that it will be useful,
11##   but WITHOUT ANY WARRANTY; without even the implied warranty of
12##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13##   GNU Lesser General Public License for more details.
14##
15
16
17"""\
18
19__intro__
20
21jabber.py is a Python module for the jabber instant messaging protocol.
22jabber.py deals with the xml parsing and socket code, leaving the programmer
23to concentrate on developing quality jabber based applications with Python.
24
25The eventual aim is to produce a fully featured easy to use library for
26creating both jabber clients and servers.
27
28jabber.py requires at least python 2.0 and the XML expat parser module
29( included in the standard Python distrubution ).
30
31It is developed on Linux but should run happily on over Unix's and win32.
32
33__Usage__
34
35jabber.py basically subclasses the xmlstream classs and provides the
36processing of jabber protocol elements into object instances as well
37'helper' functions for parts of the protocol such as authentication
38and roster management.
39
40An example of usage for a simple client would be ( only psuedo code !)
41
42<> Read documentation on jabber.org for the jabber protocol.
43
44<> Birth a jabber.Client object with your jabber servers host
45
46<> Define callback functions for the protocol elements you want to use
47   and optionally a disconnection.
48
49<> Authenticate with the server via auth method, or register via the
50   reg methods to get an account.
51
52<> Call requestRoster() and sendPresence()
53
54<> loop over process(). Send Iqs,messages and presences by birthing
55   them via there respective clients , manipulating them and using
56   the Client's send() method.
57
58<> Respond to incoming elements passed to your callback functions.
59
60<> Find bugs :)
61
62
63"""
64
65# $Id$
66
67import xmlstream
68import sha, time
69
70debug=xmlstream.debug
71
72VERSION = xmlstream.VERSION
73
74False = 0;
75True  = 1;
76
77timeout = 300
78NAT_TIMEOUT = 60
79
80DBG_INIT, DBG_ALWAYS = debug.DBG_INIT, debug.DBG_ALWAYS
81DBG_DISPATCH = 'jb-dispatch'            ; debug.debug_flags.append( DBG_DISPATCH )
82DBG_NODE = 'jb-node'                    ; debug.debug_flags.append( DBG_NODE)
83DBG_NODE_IQ = 'jb-node-iq'              ; debug.debug_flags.append( DBG_NODE_IQ )
84DBG_NODE_MESSAGE = 'jb-node-message'    ; debug.debug_flags.append( DBG_NODE_MESSAGE )
85DBG_NODE_PRESENCE = 'jb-node-pressence' ; debug.debug_flags.append( DBG_NODE_PRESENCE )
86DBG_NODE_UNKNOWN = 'jb-node-unknown'    ; debug.debug_flags.append( DBG_NODE_UNKNOWN )
87
88
89#
90# JANA core namespaces
91#  from http://www.jabber.org/jana/namespaces.php as of 2003-01-12
92#  "myname" means that namespace didnt have a name in the jabberd headers
93#
94NS_AGENT      = "jabber:iq:agent"
95NS_AGENTS     = "jabber:iq:agents"
96NS_AUTH       = "jabber:iq:auth"
97NS_CLIENT     = "jabber:client"
98NS_DELAY      = "jabber:x:delay"
99NS_OOB        = "jabber:iq:oob"
100NS_REGISTER   = "jabber:iq:register"
101NS_ROSTER     = "jabber:iq:roster"
102NS_XROSTER    = "jabber:x:roster" # myname
103NS_SERVER     = "jabber:server"
104NS_TIME       = "jabber:iq:time"
105NS_VERSION    = "jabber:iq:version"
106
107NS_COMP_ACCEPT  = "jabber:component:accept" # myname
108NS_COMP_CONNECT = "jabber:component:connect" # myname
109
110
111
112#
113# JANA JEP namespaces, ordered by JEP
114#  from http://www.jabber.org/jana/namespaces.php as of 2003-01-12
115#  all names by jaclu
116#
117_NS_PROTOCOL  = "http://jabber.org/protocol" # base for other
118NS_PASS       = "jabber:iq:pass" # JEP-0003
119NS_XDATA      = "jabber:x:data" # JEP-0004
120NS_RPC        = "jabber:iq:rpc" # JEP-0009
121NS_BROWSE     = "jabber:iq:browse" # JEP-0011
122NS_LAST       = "jabber:iq:last" #JEP-0012
123NS_PRIVACY    = "jabber:iq:privacy" # JEP-0016
124NS_XEVENT     = "jabber:x:event" # JEP-0022
125NS_XEXPIRE    = "jabber:x:expire" # JEP-0023
126NS_XENCRYPTED = "jabber:x:encrypted" # JEP-0027
127NS_XSIGNED    = "jabber:x:signed" # JEP-0027
128NS_P_MUC      = _NS_PROTOCOL + "/muc" # JEP-0045
129NS_P_MUC_ADMIN = NS_P_MUC + "#admin" # JEP-0045
130NS_P_MUC_OWNER = NS_P_MUC + "#owner" # JEP-0045
131NS_P_MUC_USER  = NS_P_MUC + "#user" # JEP-0045
132NS_VCARD      = "vcard-temp" # JEP-0054
133
134
135#
136# Non JANA aproved, ordered by JEP
137#  all names by jaclu
138#
139_NS_P_DISCO     = _NS_PROTOCOL + "/disco" # base for other
140NS_P_DISC_INFO  = _NS_P_DISCO + "#info" # JEP-0030
141NS_P_DISC_ITEMS = _NS_P_DISCO + "#items" # JEP-0030
142NS_P_COMMANDS   = _NS_PROTOCOL + "/commands" # JEP-0050
143
144
145"""
146 2002-01-11 jaclu
147
148 Defined in jabberd/lib/lib.h, but not JANA aproved and not used in jabber.py
149 so commented out, should/could propably be removed...
150
151 NS_ADMIN      = "jabber:iq:admin"
152 NS_AUTH_OK    = "jabber:iq:auth:0k"
153 NS_CONFERENCE = "jabber:iq:conference"
154 NS_ENVELOPE   = "jabber:x:envelope"
155 NS_FILTER     = "jabber:iq:filter"
156 NS_GATEWAY    = "jabber:iq:gateway"
157 NS_OFFLINE    = "jabber:x:offline"
158 NS_PRIVATE    = "jabber:iq:private"
159 NS_SEARCH     = "jabber:iq:search"
160 NS_XDBGINSERT = "jabber:xdb:ginsert"
161 NS_XDBNSLIST  = "jabber:xdb:nslist"
162 NS_XHTML      = "http://www.w3.org/1999/xhtml"
163 NS_XOOB       = "jabber:x:oob"
164 NS_COMP_EXECUTE = "jabber:component:execute" # myname
165"""
166
167
168## Possible constants for Roster class .... hmmm ##
169RS_SUB_BOTH    = 0
170RS_SUB_FROM    = 1
171RS_SUB_TO      = 2
172
173RS_ASK_SUBSCRIBE   = 1
174RS_ASK_UNSUBSCRIBE = 0
175
176RS_EXT_ONLINE   = 2
177RS_EXT_OFFLINE  = 1
178RS_EXT_PENDING  = 0
179
180#############################################################################
181
182def ustr(what):
183    """If sending object is already a unicode str, just
184       return it, otherwise convert it using xmlstream.ENCODING"""
185    if type(what) == type(u''):
186        r = what
187    else:
188        try: r = what.__str__()
189        except AttributeError: r = str(what)
190        # make sure __str__() didnt return a unicode
191        if type(r) <> type(u''):
192            r = unicode(r,xmlstream.ENCODING,'replace')
193    return r
194xmlstream.ustr = ustr
195
196class NodeProcessed(Exception): pass   # currently only for Connection._expectedIqHandler
197
198class Connection(xmlstream.Client):
199    """Forms the base for both Client and Component Classes"""
200    def __init__(self, host, port, namespace,
201                 debug=[], log=False, connection=xmlstream.TCP, hostIP=None, proxy=None):
202
203        xmlstream.Client.__init__(self, host, port, namespace,
204                                  debug=debug, log=log,
205                                  connection=connection,
206                                  hostIP=hostIP, proxy=proxy)
207        self.handlers={}
208        self.registerProtocol('unknown', Protocol)
209        self.registerProtocol('iq', Iq)
210        self.registerProtocol('message', Message)
211        self.registerProtocol('presence', Presence)
212
213        self.registerHandler('iq',self._expectedIqHandler,system=True)
214
215        self._expected = {}
216
217        self._id = 0;
218        self._lastIncome = time.time()
219
220        self.lastErr = ''
221        self.lastErrCode = 0
222
223    def setMessageHandler(self, func, type='', chainOutput=False):
224        """Back compartibility method"""
225        print "WARNING! setMessageHandler(...) method is obsolette, use registerHandler('message',...) instead."
226        return self.registerHandler('message', func, type, chained=chainOutput)
227
228    def setPresenceHandler(self, func, type='', chainOutput=False):
229        """Back compartibility method"""
230        print "WARNING! setPresenceHandler(...) method is obsolette, use registerHandler('presence',...) instead."
231        return self.registerHandler('presence', func, type, chained=chainOutput)
232
233    def setIqHandler(self, func, type='', ns=''):
234        """Back compartibility method"""
235        print "WARNING! setIqHandler(...) method is obsolette, use registerHandler('iq',...) instead."
236        return self.registerHandler('iq', func, type, ns)
237
238    def header(self):
239        self.DEBUG("stream: sending initial header",DBG_INIT)
240        str = u"<?xml version='1.0' encoding='UTF-8' ?>   \
241                <stream:stream to='%s' xmlns='%s'" % ( self._host,
242                                                       self._namespace )
243
244        if self._outgoingID: str = str + " id='%s' " % self._outgoingID
245        str = str + " xmlns:stream='http://etherx.jabber.org/streams'>"
246        self.send(str)
247        self.process(timeout)
248
249    def send(self, what):
250        """Sends a jabber protocol element (Node) to the server"""
251        xmlstream.Client.write(self,ustr(what))
252
253    def _expectedIqHandler(self, conn, iq_obj):
254        if iq_obj.getAttr('id') and \
255           self._expected.has_key(iq_obj.getAttr('id')):
256            self._expected[iq_obj.getAttr('id')] = iq_obj
257            raise NodeProcessed('No need for further Iq processing.')
258
259    def dispatch(self,stanza):
260        """Called internally when a 'protocol element' is received.
261           Builds the relevant jabber.py object and dispatches it
262           to a relevant function or callback."""
263        self._lastIncome = time.time()
264        name=stanza.getName()
265        if not self.handlers.has_key(name):
266            self.DEBUG("whats a tag -> " + name,DBG_NODE_UNKNOWN)
267            name='unknown'
268        else:
269            self.DEBUG("Got %s stanza"%name, DBG_NODE)
270
271        stanza=self.handlers[name][type](node=stanza)
272
273        typ=stanza.getType()
274        if not typ: typ=''
275        try:
276            ns=stanza.getQuery()
277            if not ns: ns=''
278        except: ns=''
279        self.DEBUG("dispatch called for: name->%s ns->%s"%(name,ns),DBG_DISPATCH)
280
281        if typ and ns: typns=typ+ns
282        else: typns=''
283        if not self.handlers[name].has_key(ns): ns=''
284        if not self.handlers[name].has_key(typ): typ=''
285        if not self.handlers[name].has_key(typns): typns=''
286
287        chain=[]
288        for key in ['default',typ,ns,typns]: # we will use all handlers: from very common to very particular
289            if key: chain += self.handlers[name][key]
290
291        output=''
292        user=True
293        for handler in chain:
294            try:
295                if user or handler['system']:
296                    if handler['chain']: output=handler['func'](self,stanza,output)
297                    else: handler['func'](self,stanza)
298            except NodeProcessed: user=False
299
300    def registerProtocol(self,tag_name,Proto):
301        """Registers a protocol in protocol processing chain. You MUST register
302           a protocol before you register any handler function for it.
303           First parameter, that passed to this function is the tag name that
304           belongs to all protocol elements. F.e.: message, presence, iq, xdb, ...
305           Second parameter is the [ancestor of] Protocol class, which instance will
306           built from the received node with call
307
308                if received_packet.getName()==tag_name:
309                    stanza = Proto(node = received_packet)
310        """
311        self.handlers[tag_name]={type:Proto, 'default':[]}
312
313    def registerHandler(self,name,handler,type='',ns='',chained=False, makefirst=False, system=False):
314        """Sets the callback func for processing incoming stanzas.
315           Multiple callback functions can be set which are called in
316           succession. Callback can optionally raise an NodeProcessed error to
317           stop stanza from further processing. A type and namespace attributes can
318           also be optionally passed so the callback is only called when a stanza of
319           this type is received. Namespace attribute MUST be omitted if you
320           registering an Iq processing handler.
321
322           If 'chainOutput' is set to False (the default), the given function
323           should be defined as follows:
324
325                def myCallback(c, p)
326
327           Where the first parameter is the Client object, and the second
328           parameter is the [ancestor of] Protocol object representing the stanza
329           which was received.
330
331           If 'chainOutput' is set to True, the output from the various
332           handler functions will be chained together.  In this case,
333           the given callback function should be defined like this:
334
335                def myCallback(c, p, output)
336
337           Where 'output' is the value returned by the previous
338           callback function.  For the first callback routine, 'output' will be
339           set to an empty string.
340
341           'makefirst' argument gives you control over handler prioriy in its type
342           and namespace scope. Note that handlers for particular type or namespace always
343           have lower priority that common handlers.
344        """
345        if not type and not ns: type='default'
346        if not self.handlers[name].has_key(type+ns): self.handlers[name][type+ns]=[]
347        if makefirst: self.handlers[name][type+ns].insert(0, {'chain':chained,'func':handler,'system':system})
348        else: self.handlers[name][type+ns].append({'chain':chained,'func':handler,'system':system})
349
350    def setDisconnectHandler(self, func):
351        """Set the callback for a disconnect.
352           The given function will be called with a single parameter (the
353           connection object) when the connection is broken unexpectedly (eg,
354           in response to sending badly formed XML).  self.lastErr and
355           self.lastErrCode will be set to the error which caused the
356           disconnection, if any.
357        """
358        self.disconnectHandler = func
359
360    ## functions for sending element with ID's ##
361
362    def waitForResponse(self, ID, timeout=timeout):
363        """Blocks untils a protocol element with the given id is received.
364           If an error is received, waitForResponse returns None and
365           self.lastErr and self.lastErrCode is set to the received error.  If
366           the operation times out (which only happens if a timeout value is
367           given), waitForResponse will return None and self.lastErr will be
368           set to "Timeout".
369           Changed default from timeout=0 to timeout=300 to avoid hangs in
370           scripts and such.
371           If you _really_ want no timeout, just set it to 0"""
372        ID = ustr(ID)
373        self._expected[ID] = None
374        has_timed_out = False
375
376        abort_time = time.time() + timeout
377        if timeout:
378            self.DEBUG("waiting with timeout:%s for %s" % (timeout,ustr(ID)),DBG_NODE_IQ)
379        else:
380            self.DEBUG("waiting for %s" % ustr(ID),DBG_NODE_IQ)
381
382        while (not self._expected[ID]) and not has_timed_out:
383            if not self.process(0.2): return None
384            if timeout and (time.time() > abort_time):
385                has_timed_out = True
386        if has_timed_out:
387            self.lastErr = "Timeout"
388            return None
389        response = self._expected[ID]
390        del self._expected[ID]
391        if response.getErrorCode():
392            self.lastErr     = response.getError()
393            self.lastErrCode = response.getErrorCode()
394            return None
395        return response
396
397    def SendAndWaitForResponse(self, obj, ID=None, timeout=timeout):
398        """Sends a protocol element object and blocks until a response with
399           the same ID is received.  The received protocol object is returned
400           as the function result. """
401        if ID is None :
402            ID = obj.getID()
403            if ID is None:
404                ID = self.getAnID()
405                obj.setID(ID)
406        ID = ustr(ID)
407        self.send(obj)
408        return self.waitForResponse(ID,timeout)
409
410    def getAnID(self):
411        """Returns a unique ID"""
412        self._id = self._id + 1
413        return ustr(self._id)
414
415    def process(self, timeout=0):
416        if time.time() > self._lastIncome + NAT_TIMEOUT:
417            self._lastIncome = time.time()
418            iq = Iq(type="get", to=self._host, query=NS_LAST)
419            if not self.SendAndWaitForResponse(iq, timeout=30):
420                self.disconnectHandler(self)
421        return xmlstream.Client.process(self, timeout)
422
423#############################################################################
424
425class Client(Connection):
426    """Class for managing a client connection to a jabber server."""
427    def __init__(self, host, port=5222, debug=[], log=False,
428                 connection=xmlstream.TCP, hostIP=None, proxy=None):
429
430        Connection.__init__(self, host, port, NS_CLIENT, debug, log,
431                            connection=connection, hostIP=hostIP, proxy=proxy)
432
433        self.registerHandler('iq',self._IqRosterManage,'result',NS_ROSTER,system=True)
434        self.registerHandler('iq',self._IqRosterManage,'set',NS_ROSTER,system=True)
435        self.registerHandler('iq',self._IqRegisterResult,'result',NS_REGISTER,system=True)
436        self.registerHandler('iq',self._IqAgentsResult,'result',NS_AGENTS,system=True)
437        self.registerHandler('presence',self._presenceHandler,system=True)
438
439        self._roster = Roster()
440        self._agents = {}
441        self._reg_info = {}
442        self._reg_agent = ''
443
444    def disconnect(self):
445        """Safely disconnects from the connected server"""
446        self.send(Presence(type='unavailable'))
447        xmlstream.Client.disconnect(self)
448
449    def sendPresence(self,type=None,priority=None,show=None,status=None,signedStatus=None):
450        """Sends a presence protocol element to the server.
451           Used to inform the server that you are online"""
452        presence = Presence(type=type,priority=priority,show=show,status=status)
453        if signedStatus:
454            presence.setX(NS_XSIGNED).insertData(signedStatus)
455        self.send(presence)
456
457    sendInitPresence=sendPresence
458
459    def _presenceHandler(self, conn, pres_obj):
460        who = ustr(pres_obj.getFrom())
461        type = pres_obj.getType()
462        self.DEBUG("presence type is %s" % type,DBG_NODE_PRESENCE)
463        if type == 'available' or not type:
464            self.DEBUG("roster setting %s to online" % who,DBG_NODE_PRESENCE)
465            self._roster._setOnline(who,'online')
466        elif type == 'unavailable':
467            self.DEBUG("roster setting %s to offline" % who,DBG_NODE_PRESENCE)
468            self._roster._setOnline(who,'offline')
469        self._roster._setShow(who,pres_obj.getShow())
470        self._roster._setStatus(who,pres_obj.getStatus())
471
472    def _IqRosterManage(self, conn, iq_obj):
473        "NS_ROSTER and type in [result,set]"
474        for item in iq_obj.getQueryNode().getChildren():
475            jid  = item.getAttr('jid')
476            name = item.getAttr('name')
477            sub  = item.getAttr('subscription')
478            ask  = item.getAttr('ask')
479
480            groups = []
481            for group in item.getTags("group"):
482                groups.append(group.getData())
483
484            if jid:
485                if sub == 'remove' or (sub == 'none' and not ask):
486                    self._roster._remove(jid)
487                else:
488                    self._roster._set(jid=jid, name=name,
489                                      groups=groups, sub=sub,
490                                      ask=ask)
491            else:
492                self.DEBUG("roster - jid not defined ?",DBG_NODE_IQ)
493
494    def _IqRegisterResult(self, conn, iq_obj):
495        "NS_REGISTER and type==result"
496        self._reg_info = {}
497        for item in iq_obj.getQueryNode().getChildren():
498            self._reg_info[item.getName()] = item.getData()
499
500    def _IqAgentsResult(self, conn, iq_obj):
501        "NS_AGENTS and type==result"
502        self.DEBUG("got agents result",DBG_NODE_IQ)
503        self._agents = {}
504        for agent in iq_obj.getQueryNode().getChildren():
505            if agent.getName() == 'agent': ## hmmm
506                self._agents[agent.getAttr('jid')] = {}
507                for info in agent.getChildren():
508                    self._agents[agent.getAttr('jid')][info.getName()] = info.getData()
509
510    def auth(self,username,passwd,resource):
511        """Authenticates and logs in to the specified jabber server
512           Automatically selects the 'best' authentication method
513           provided by the server.
514           Supports plain text, digest and zero-k authentication.
515
516           Returns True if the login was successful, False otherwise.
517        """
518        auth_get_iq = Iq(type='get')
519        auth_get_iq.setID('auth-get')
520        q = auth_get_iq.setQuery(NS_AUTH)
521        q.insertTag('username').insertData(username)
522        self.send(auth_get_iq)
523
524        auth_response = self.waitForResponse("auth-get")
525        if auth_response == None:
526            return False # Error
527        else:
528            auth_ret_node = auth_response
529
530        auth_ret_query = auth_ret_node.getTag('query')
531        self.DEBUG("auth-get node arrived!",(DBG_INIT,DBG_NODE_IQ))
532
533        auth_set_iq = Iq(type='set')
534        auth_set_iq.setID('auth-set')
535
536        q = auth_set_iq.setQuery(NS_AUTH)
537        q.insertTag('username').insertData(username)
538        q.insertTag('resource').insertData(resource)
539
540        if auth_ret_query.getTag('token'):
541
542            token = auth_ret_query.getTag('token').getData()
543            seq = auth_ret_query.getTag('sequence').getData()
544            self.DEBUG("zero-k authentication supported",(DBG_INIT,DBG_NODE_IQ))
545            hash = sha.new(sha.new(passwd).hexdigest()+token).hexdigest()
546            for foo in xrange(int(seq)): hash = sha.new(hash).hexdigest()
547            q.insertTag('hash').insertData(hash)
548
549        elif auth_ret_query.getTag('digest'):
550
551            self.DEBUG("digest authentication supported",(DBG_INIT,DBG_NODE_IQ))
552            digest = q.insertTag('digest')
553            digest.insertData(sha.new(
554                self.getIncomingID() + passwd).hexdigest() )
555        else:
556            self.DEBUG("plain text authentication supported",(DBG_INIT,DBG_NODE_IQ))
557            q.insertTag('password').insertData(passwd)
558
559        iq_result = self.SendAndWaitForResponse(auth_set_iq)
560
561        if iq_result==None:
562             return False
563        if iq_result.getError() is None:
564            return True
565        else:
566           self.lastErr     = iq_result.getError()
567           self.lastErrCode = iq_result.getErrorCode()
568           # raise error(iq_result.getError()) ?
569           return False
570        return True
571
572    ## Roster 'helper' func's - also see the Roster class ##
573
574    def requestRoster(self):
575        """Requests the roster from the server and returns a
576           Roster() class instance."""
577        rost_iq = Iq(type='get')
578        rost_iq.setQuery(NS_ROSTER)
579        self.SendAndWaitForResponse(rost_iq)
580        self.DEBUG("got roster response",DBG_NODE_IQ)
581        self.DEBUG("roster -> %s" % ustr(self._roster),DBG_NODE_IQ)
582        return self._roster
583
584
585    def getRoster(self):
586        """Returns the current Roster() class instance. Does
587           not contact the server."""
588        return self._roster
589
590
591    def addRosterItem(self, jid):
592        """ Send off a request to subscribe to the given jid.
593        """
594        self.send(Presence(to=jid, type="subscribe"))
595
596
597    def updateRosterItem(self, jid, name=None, groups=None):
598        """ Update the information stored in the roster about a roster item.
599
600            'jid' is the Jabber ID of the roster entry; 'name' is the value to
601            set the entry's name to, and 'groups' is a list of groups to which
602            this roster entry can belong.  If either 'name' or 'groups' is not
603            specified, that value is not updated in the roster.
604        """
605        iq = Iq(type='set')
606        item = iq.setQuery(NS_ROSTER).insertTag('item')
607        item.putAttr('jid', ustr(jid))
608        if name != None: item.putAttr('name', name)
609        if groups != None:
610            for group in groups:
611                item.insertTag('group').insertData(group)
612        dummy = self.SendAndWaitForResponse(iq) # Do we need to wait??
613
614
615    def removeRosterItem(self,jid):
616        """Removes an item with Jabber ID jid from both the
617           server's roster and the local internal Roster()
618           instance"""
619        rost_iq = Iq(type='set')
620        q = rost_iq.setQuery(NS_ROSTER).insertTag('item')
621        q.putAttr('jid', ustr(jid))
622        q.putAttr('subscription', 'remove')
623        self.SendAndWaitForResponse(rost_iq)
624        return self._roster
625
626    ## Registration 'helper' funcs ##
627
628    def requestRegInfo(self,agent=''):
629        """Requests registration info from the server.
630           Returns the Iq object received from the server."""
631        if agent.find('.') == -1:
632            if agent: agent += '.'
633            agent += self._host
634        self._reg_info = {}
635        reg_iq = Iq(type='get', to = agent)
636        reg_iq.setQuery(NS_REGISTER)
637        self.DEBUG("Requesting reg info from %s:" % agent, DBG_NODE_IQ)
638        self.DEBUG(ustr(reg_iq),DBG_NODE_IQ)
639        return self.SendAndWaitForResponse(reg_iq)
640
641
642    def getRegInfo(self):
643        """Returns a dictionary of fields requested by the server for a
644           registration attempt.  Each dictionary entry maps from the name of
645           the field to the field's current value (either as returned by the
646           server or set programmatically by calling self.setRegInfo(). """
647        return self._reg_info
648
649
650    def setRegInfo(self,key,val):
651        """Sets a name/value attribute. Note: requestRegInfo must be
652           called before setting."""
653        self._reg_info[key] = val
654
655
656    def sendRegInfo(self, agent=''):
657        """Sends the populated registration dictionary back to the server"""
658        if agent.find('.') == -1:
659            if agent: agent += '.'
660            agent += self._host
661        reg_iq = Iq(to = agent, type='set')
662        q = reg_iq.setQuery(NS_REGISTER)
663        for info in self._reg_info.keys():
664            q.insertTag(info).putData(self._reg_info[info])
665        return self.SendAndWaitForResponse(reg_iq)
666
667
668    def deregister(self, agent=''):
669        """ Send off a request to deregister with the server or with the given
670            agent.  Returns True if successful, else False.
671
672            Note that you must be authorised before attempting to deregister.
673        """
674        if agent:
675            if agent.find('.') == -1:
676                agent += '.' + self._host
677            self.send(Presence(to=agent,type='unsubscribed'))       # This is enough f.e. for icqv7t or jit
678        else: agent = self._host
679        q = self.requestRegInfo()
680        kids = q.getQueryPayload