xmpppy changes:

- Sync with latest CVS version + gajim patches.
- streamErrorHandler disabled. If you want enable it - unrem it at line 66 of
  dispatcher.py
- TLS re-enabled. Should work fine now
- disconnection TLS plugout re-enabled. Didn't successed in reproducing
  problem though so maybe not fixed.
- My temporary fixes replaced with CVS ones.

gajim connection.py changes (in chunks order):
- getTags is incorrect. Replaced with getChildren
- browseAgents is absent from xmpppy. Fixed with manual node send. Made use of
  buildReply method and con argument
- formatting fix
- crude fixed register problem. I do not know how to do it properly. It is
  client stuff. Honest! I have no idea how to make it non-blocking and yet make
  library to fallback to older protocols.
  getInstructions() moved to xmpppy
Thanks Alexey !
This commit is contained in:
Yann Leboulanger 2005-05-07 10:57:40 +00:00
parent 66249dafb5
commit 56d60f3fd5
11 changed files with 194 additions and 88 deletions

View File

@ -328,7 +328,7 @@ class Connection:
for key in q.getAttrs().keys():
attr[key.encode('utf8')] = q.getAttr(key).encode('utf8')
identities = [attr]
for node in q.getTags():
for node in q.getChildren():
if node.getName() == 'ns':
features.append(node.getData())
else:
@ -387,24 +387,22 @@ class Connection:
features.append(i.getAttr('var'))
jid = str(iq_obj.getFrom())
if not identities:
self.connection.browseAgents(jid, node)
self.connection.send(common.xmpp.Iq(typ = 'get', queryNS = \
common.xmpp.NS_AGENTS))
else:
self.dispatch('AGENT_INFO_INFO', (jid, node, identities, features))
self.discoverItems(jid, node)
def _VersionCB(self, con, iq_obj):
gajim.log.debug('VersionCB')
f = iq_obj.getFrom()
iq_obj.setFrom(iq_obj.getTo())
iq_obj.setTo(f)
iq_obj.setType('result')
iq_obj = iq_obj.buildReply('result')
qp = iq_obj.getTag('query')
qp.setTagData('name', 'Gajim')
qp.setTagData('version', gajim.version)
send_os = gajim.config.get('send_os_info')
if send_os:
qp.setTagData('os', get_os_info())
self.connection.send(iq_obj)
con.send(iq_obj)
def _VersionResultCB(self, con, iq_obj):
gajim.log.debug('VersionResultCB')
@ -638,8 +636,7 @@ class Connection:
prio = str(gajim.config.get_per('accounts', self.name, 'priority'))
p = common.xmpp.Presence(typ = ptype, priority = prio, show = status,
status = msg)
if signed: p.setTag(common.xmpp.NS_SIGNED + ' x').setData(
signed)
if signed: p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed)
self.connection.send(p)
self.dispatch('STATUS', status)
@ -712,17 +709,14 @@ class Connection:
def request_agents(self, jid, node):
if self.connection:
self.connection.send(common.xmpp.Iq(to = jid, typ = 'get',
queryNS = common.xmpp.NS_BROWSE))
self.discoverInfo(jid, node)
def ask_register_agent_info(self, agent):
if not self.connection:
return None
data = common.xmpp.features.getRegInfo(self.connection, agent) # FIXME: blocking
info = data.asDict()
instructions = data.getInstructions()
if instructions:
info['instructions'] = instructions
return info
return common.xmpp.features.getRegInfo(self.connection, agent).asDict() # FIXME: blocking
def register_agent(self, agent, info):
if not self.connection:

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: auth.py,v 1.25 2005/03/08 19:36:29 snakeru Exp $
# $Id: auth.py,v 1.27 2005/04/30 10:17:20 snakeru Exp $
"""
Provides library with all Non-SASL and SASL authentication mechanisms.
@ -97,7 +97,8 @@ class NonSASL(PlugIn):
class SASL(PlugIn):
""" Implements SASL authentication. """
def plugin(self,owner):
self.startsasl=None
if not self._owner.Dispatcher.Stream._document_attrs.has_key('version'): self.startsasl='not-supported'
else: self.startsasl=None
def auth(self,username,password):
""" Start authentication. Result can be obtained via "SASL.startsasl" attribute and will be
@ -120,7 +121,7 @@ class SASL(PlugIn):
def FeaturesHandler(self,conn,feats):
""" Used to determine if server supports SASL auth. Used internally. """
if not feats.getTag('mechanisms',namespace=NS_SASL):
self.startsasl='failure'
self.startsasl='not-supported'
self.DEBUG('SASL not supported by server','error')
return
mecs=[]
@ -213,7 +214,7 @@ class Bind(PlugIn):
except NodeProcessed: pass
else: self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
def plugout(self,owner):
def plugout(self):
""" Remove Bind handler from owner's dispatcher. Used internally. """
self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: browser.py,v 1.8 2004/12/24 19:56:35 snakeru Exp $
# $Id: browser.py,v 1.9 2005/04/30 07:13:33 snakeru Exp $
"""Browser module provides DISCO server framework for your application.
This functionality can be used for very different purposes - from publishing
@ -27,7 +27,9 @@ from dispatcher import *
from client import PlugIn
class Browser(PlugIn):
""" Standart xmpppy class that is ancestor of PlugIn and can be attached
""" WARNING! This class is for components only. It will not work in client mode!
Standart xmpppy class that is ancestor of PlugIn and can be attached
to your application.
All processing will be performed in the handlers registered in the browser
instance. You can register any number of handlers ensuring that for each
@ -85,14 +87,14 @@ class Browser(PlugIn):
def plugin(self, owner):
""" Registers it's own iq handlers in your application dispatcher instance.
Used internally."""
owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO)
owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_ITEMS)
owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO)
owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS)
def plugout(self):
""" Unregisters browser's iq handlers from your application dispatcher instance.
Used internally."""
self._owner.UnregisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO)
self._owner.UnregisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_ITEMS)
self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO)
self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS)
def _traversePath(self,node,jid,set=0):
""" Returns dictionary and key or None,None
@ -136,7 +138,7 @@ class Browser(PlugIn):
{'jid':'jid2','action':'action2','node':'node2','name':'name2'},
{'jid':'jid3','node':'node3','name':'name3'},
{'jid':'jid4','node':'node4'}
]
],
'info' :{
'ids':[
{'category':'category1','type':'type1','name':'name1'},
@ -182,7 +184,9 @@ class Browser(PlugIn):
to handle the request. Used internally.
"""
handler=self.getDiscoHandler(request.getQuerynode(),request.getTo())
if not handler: return conn.send(Error(request,ERR_ITEM_NOT_FOUND))
if not handler:
conn.send(Error(request,ERR_ITEM_NOT_FOUND))
raise NodeProcessed
rep=request.buildReply('result')
q=rep.getTag('query')
if request.getQueryNS()==NS_DISCO_ITEMS:

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: client.py,v 1.33 2005/04/10 08:09:23 snakeru Exp $
# $Id: client.py,v 1.35 2005/04/30 10:17:19 snakeru Exp $
"""
Provides PlugIn class functionality to develop extentions for xmpppy.
@ -105,7 +105,7 @@ class CommonClient:
self.debug_flags.append(self.DBG)
self._owner=self
self._registered_name=None
# self.RegisterDisconnectHandler(self.DisconnectHandler)
self.RegisterDisconnectHandler(self.DisconnectHandler)
self.connected=''
def RegisterDisconnectHandler(self,handler):
@ -123,7 +123,7 @@ class CommonClient:
self.disconnect_handlers.reverse()
for i in self.disconnect_handlers: i()
self.disconnect_handlers.reverse()
# if self.__dict__.has_key('TLS'): self.TLS.PlugOut()
if self.__dict__.has_key('TLS'): self.TLS.PlugOut()
def DisconnectHandler(self):
""" Default disconnect handler. Just raises an IOError.
@ -156,9 +156,9 @@ class CommonClient:
if not connected: return
self._Server,self._Proxy=server,proxy
self.connected='tcp'
# if self.Connection.getPort()==5223:
# transports.TLS().PlugIn(self,now=1)
# self.connected='tls'
if self.Connection.getPort()==5223:
transports.TLS().PlugIn(self,now=1)
self.connected='tls'
dispatcher.Dispatcher().PlugIn(self)
while self.Dispatcher.Stream._document_attrs is None: self.Process(1)
if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0':
@ -173,32 +173,32 @@ class Client(CommonClient):
specify it's address and credentials (if needed) in the second argument.
Example: connect(('192.168.5.5':5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'})"""
if not CommonClient.connect(self,server,proxy): return self.connected
# transports.TLS().PlugIn(self)
transports.TLS().PlugIn(self)
if not self.Dispatcher.Stream._document_attrs.has_key('version') or not self.Dispatcher.Stream._document_attrs['version']=='1.0': return self.connected
while not self.Dispatcher.Stream.features and self.Process(): pass # If we get version 1.0 stream the features tag MUST BE presented
# if not self.Dispatcher.Stream.features.getTag('starttls'): return self.connected # TLS not supported by server
# while not self.TLS.starttls and self.Process(): pass
# if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected
# self.connected='tls'
if not self.Dispatcher.Stream.features.getTag('starttls'): return self.connected # TLS not supported by server
while not self.TLS.starttls and self.Process(): pass
if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected
self.connected='tls'
return self.connected
def auth(self,user,password,resource=''):
""" Authenticate connnection and bind resource. If resource is not provided
random one or library name used. """
self._User,self._Password,self._Resource=user,password,resource
auth.SASL().PlugIn(self)
self.SASL.auth(user,password)
while not self.Dispatcher.Stream._document_attrs and self.Process(): pass
if self.Dispatcher.Stream._document_attrs.has_key('version') and self.Dispatcher.Stream._document_attrs['version']=='1.0':
while not self.Dispatcher.Stream.features and self.Process(): pass # If we get version 1.0 stream the features tag MUST BE presented
while self.SASL.startsasl=='in-process' and self.Process(): pass
else: self.SASL.startsasl='failure'
if self.SASL.startsasl=='failure':
auth.SASL().PlugIn(self)
if self.SASL.startsasl=='not-supported':
if not resource: resource='xmpppy'
if auth.NonSASL(user,password,resource).PlugIn(self):
self.connected+='+old_auth'
return 'old_auth'
else:
return
self.SASL.auth(user,password)
while self.SASL.startsasl=='in-process' and self.Process(): pass
if self.SASL.startsasl=='success':
auth.Bind().PlugIn(self)
while self.Bind.bound is None: self.Process()
if self.Bind.Bind(resource):

View File

@ -1,4 +1,4 @@
## $Id: commands.py,v 1.3 2005/03/08 19:50:43 snakeru Exp $
## $Id: commands.py,v 1.4 2005/04/30 07:33:11 snakeru Exp $
## Ad-Hoc Command manager
## Mike Albon (c) 5th January 2005
@ -34,8 +34,6 @@ What it supplies:
from xmpp.protocol import *
from xmpp.client import PlugIn
NS_COMMANDS='http://jabber.org/protocol/commands'
class Commands(PlugIn):
"""Commands is an ancestor of Plugin and can be attached to any session.
@ -167,7 +165,10 @@ class Commands(PlugIn):
return self._handlers[jid][name]
class Command_Handler_Prototype(PlugIn):
"""This is a prototype command handler, as each command uses a disco method and execute method you can implement it any way you like, however this is my first attempt at making a generic handler that you can hang process stages on too. There is an example command below.
"""This is a prototype command handler, as each command uses a disco method
and execute method you can implement it any way you like, however this is
my first attempt at making a generic handler that you can hang process
stages on too. There is an example command below.
The parameters are as follows:
name : the name of the command within the jabber environment
@ -241,14 +242,19 @@ class Command_Handler_Prototype(PlugIn):
return self.discoinfo
class TestCommand(Command_Handler_Prototype):
""" Example class. You should read source if you wish to understate how it works.
Generally, it presents a "master" that giudes user through to calculate something.
"""
name = 'testcommand'
description = 'a noddy example command'
def __init__(self):
""" Init internal constants. """
Command_Handler_Prototype.__init__(self)
self.pi = 3.14
self.initial = {'execute':self.cmdFirstStage}
def cmdFirstStage(self,conn,request):
""" Determine """
# This is the only place this should be repeated as all other stages should have SessionIDs
try:
session = request.getTagAttr('command','sessionid')

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: dispatcher.py,v 1.31 2005/03/08 19:36:29 snakeru Exp $
# $Id: dispatcher.py,v 1.34 2005/05/02 08:36:41 snakeru Exp $
"""
Main xmpppy mechanism. Provides library with methods to assign different handlers
@ -55,6 +55,7 @@ class Dispatcher(PlugIn):
self.handlers=handlers
def _init(self):
""" Registers default namespaces/protocols/handlers. Used internally. """
self.RegisterNamespace('unknown')
self.RegisterNamespace(NS_STREAMS)
self.RegisterNamespace(self._owner.defaultNamespace)
@ -62,6 +63,7 @@ class Dispatcher(PlugIn):
self.RegisterProtocol('presence',Presence)
self.RegisterProtocol('message',Message)
self.RegisterDefaultHandler(self.returnStanzaHandler)
# self.RegisterHandler('error',self.streamErrorHandler,xmlns=NS_STREAMS)
def plugin(self, owner):
""" Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally."""
@ -74,6 +76,7 @@ class Dispatcher(PlugIn):
self.StreamInit()
def plugout(self):
""" Prepares instance to be destructed. """
self.Stream.dispatch=None
self.Stream.DEBUG=None
self.Stream.features=None
@ -99,7 +102,10 @@ class Dispatcher(PlugIn):
Returns:
1) length of processed data if some data were processed;
2) '0' string if no data were processed but link is alive;
3) 0 (zero) if underlying connection is closed."""
3) 0 (zero) if underlying connection is closed.
Take note that in case of disconnection detect during Process() call
disconnect handlers are called automatically.
"""
if time.time() > self._lastIncome + 60: #1 min
iq = Iq('get', NS_LAST, to=self._owner.Server)
self.send(iq)
@ -107,10 +113,12 @@ class Dispatcher(PlugIn):
self.disconnected()
for handler in self._cycleHandlers: handler(self)
if self._owner.Connection.pending_data(timeout):
data=self._owner.Connection.receive()
try: data=self._owner.Connection.receive()
except IOError: return
self.Stream.Parse(data)
self._lastIncome = time.time()
return len(data)
if data:
self._lastIncome = time.time()
return len(data)
return '0' # It means that nothing is received but link is alive.
def RegisterNamespace(self,xmlns,order='info'):
@ -190,6 +198,16 @@ class Dispatcher(PlugIn):
if stanza.getType() in ['get','set']:
conn.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED))
def streamErrorHandler(self,conn,error):
name,text='error',error.getData()
for tag in error.getChildren():
if tag.getNamespace()==NS_XMPP_STREAMS:
if tag.getName()=='text': text=tag.getData()
else: name=tag.getName()
if name in stream_exceptions.keys(): exc=stream_exceptions[name]
else: exc=StreamError
raise exc((name,text))
def RegisterCycleHandler(self,handler):
""" Register handler that will be called on every Dispatcher.Process() call. """
if handler not in self._cycleHandlers: self._cycleHandlers.append(handler)

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: features.py,v 1.19 2004/12/25 20:06:59 snakeru Exp $
# $Id: features.py,v 1.20 2005/04/30 07:43:01 snakeru Exp $
"""
This module contains variable stuff that is not worth splitting into separate modules.
@ -88,10 +88,9 @@ def getRegInfo(disp,host,info={}):
if df: return DataForm(node=df)
df=DataForm(typ='form')
for i in resp.getQueryPayload():
try: #FIXME: temporary patch by Alexey to make it work :|
if i.getName()=='instructions': df.addInstructions(i.getData())
else: df.setField(i.getName()).setValue(i.getData())
except: pass
if type(i)<>type(iq): pass
elif i.getName()=='instructions': df.addInstructions(i.getData())
else: df.setField(i.getName()).setValue(i.getData())
return df
def register(disp,host,info):

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: protocol.py,v 1.39 2005/03/08 19:36:29 snakeru Exp $
# $Id: protocol.py,v 1.41 2005/05/02 08:36:41 snakeru Exp $
"""
Protocol module contains tools that is needed for processing of
@ -28,13 +28,13 @@ NS_AUTH ='jabber:iq:auth'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BROWSE ='jabber:iq:browse'
NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept'
NS_DATA ='jabber:x:data' # JEP-0004
NS_DELAY ='jabber:x:delay'
NS_DIALBACK ='jabber:server:dialback'
NS_DISCO_INFO ='http://jabber.org/protocol/disco#info'
NS_DISCO_ITEMS ='http://jabber.org/protocol/disco#items'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_GROUPCHAT ='gc-1.0'
NS_IBB ='http://jabber.org/protocol/ibb'
NS_INVISIBLE ='presence-invisible' # jabberd2
@ -42,8 +42,9 @@ NS_IQ ='iq' # jabberd2
NS_LAST ='jabber:iq:last'
NS_MESSAGE ='message' # jabberd2
NS_MUC ='http://jabber.org/protocol/muc'
NS_MUC_OWNER ='http://jabber.org/protocol/muc#owner'
NS_MUC_USER ='http://jabber.org/protocol/muc#user'
NS_MUC_ADMIN ='http://jabber.org/protocol/muc#admin'
NS_MUC_OWNER ='http://jabber.org/protocol/muc#owner'
NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013
NS_PRESENCE ='presence' # jabberd2
NS_PRIVACY ='jabber:iq:privacy'
@ -55,7 +56,6 @@ NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl'
NS_SEARCH ='jabber:iq:search'
NS_SERVER ='jabber:server'
NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session'
NS_SIGNED ='jabber:x:signed' # JEP-0027
NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time'
@ -63,7 +63,9 @@ NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
NS_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp'
NS_VERSION ='jabber:iq:version'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
NS_SIGNED ='jabber:x:signed' # JEP-0027
xmpp_stream_error_conditions="""
bad-format -- -- -- The entity has sent XML that cannot be processed.
@ -140,8 +142,60 @@ def isResultNode(node):
def isErrorNode(node):
""" Returns true if the node is a negative reply. """
return node and node.getType()=='error'
class NodeProcessed(Exception):
""" Exception that should be raised by handler when the handling should be stopped. """
class StreamError(Exception):
""" Base exception class for stream errors."""
class BadFormat(StreamError): pass
class BadNamespacePrefix(StreamError): pass
class Conflict(StreamError): pass
class ConnectionTimeout(StreamError): pass
class HostGone(StreamError): pass
class HostUnknown(StreamError): pass
class ImproperAddressing(StreamError): pass
class InternalServerError(StreamError): pass
class InvalidFrom(StreamError): pass
class InvalidID(StreamError): pass
class InvalidNamespace(StreamError): pass
class InvalidXML(StreamError): pass
class NotAuthorized(StreamError): pass
class PolicyViolation(StreamError): pass
class RemoteConnectionFailed(StreamError): pass
class ResourceConstraint(StreamError): pass
class RestrictedXML(StreamError): pass
class SeeOtherHost(StreamError): pass
class SystemShutdown(StreamError): pass
class UndefinedCondition(StreamError): pass
class UnsupportedEncoding(StreamError): pass
class UnsupportedStanzaType(StreamError): pass
class UnsupportedVersion(StreamError): pass
class XMLNotWellFormed(StreamError): pass
stream_exceptions = {'bad-format': BadFormat,
'bad-namespace-prefix': BadNamespacePrefix,
'conflict': Conflict,
'connection-timeout': ConnectionTimeout,
'host-gone': HostGone,
'host-unknown': HostUnknown,
'improper-addressing': ImproperAddressing,
'internal-server-error': InternalServerError,
'invalid-from': InvalidFrom,
'invalid-id': InvalidID,
'invalid-namespace': InvalidNamespace,
'invalid-xml': InvalidXML,
'not-authorized': NotAuthorized,
'policy-violation': PolicyViolation,
'remote-connection-failed': RemoteConnectionFailed,
'resource-constraint': ResourceConstraint,
'restricted-xml': RestrictedXML,
'see-other-host': SeeOtherHost,
'system-shutdown': SystemShutdown,
'undefined-condition': UndefinedCondition,
'unsupported-encoding': UnsupportedEncoding,
'unsupported-stanza-type': UnsupportedStanzaType,
'unsupported-version': UnsupportedVersion,
'xml-not-well-formed': XMLNotWellFormed}
class JID:
""" JID object. JID can be built from string, modified, compared, serialised into string. """
@ -345,6 +399,16 @@ class Presence(Protocol):
def getStatus(self):
""" Returns the status string of the message. """
return self.getTagData('status')
def setPriority(self,val):
""" Sets the priority of the message. """
self.setTagData('priority',val)
def setShow(self,val):
""" Sets the show value of the message. """
self.setTagData('show',val)
def setStatus(self,val):
""" Sets the status string of the message. """
self.setTagData('status',val)
def _muc_getItemAttr(self,tag,attr):
for xtag in self.getTags('x'):
for child in xtag.getTags(tag):
@ -373,15 +437,6 @@ class Presence(Protocol):
def getStatusCode(self):
"""Returns the status code of the presence (for groupchat)"""
return self._muc_getItemAttr('status','code')
def setPriority(self,val):
""" Sets the priority of the message. """
self.setTagData('priority',val)
def setShow(self,val):
""" Sets the show value of the message. """
self.setTagData('show',val)
def setStatus(self,val):
""" Sets the status string of the message. """
self.setTagData('status',val)
class Iq(Protocol):
""" XMPP Iq object - get/set dialog mechanism. """
@ -613,6 +668,7 @@ class DataForm(Node):
for i in field.getTags('value'): val.append(i.getData())
else: val=field.getTagData('value')
ret[name]=val
if self.getTag('instructions'): ret['instructions']=self.getInstructions()
return ret
def __getitem__(self,name):
""" Simple dictionary interface for getting datafields values by their names."""

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: roster.py,v 1.16 2005/02/25 05:49:03 snakeru Exp $
# $Id: roster.py,v 1.17 2005/05/02 08:38:49 snakeru Exp $
"""
Simple roster implementation. Can be used though for different tasks like
@ -98,7 +98,7 @@ class Roster(PlugIn):
if not pres.getTimestamp(): pres.setTimestamp()
res['timestamp']=pres.getTimestamp()
elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()]
# Need to handle type='error' also
# Need to handle type='error' also
def _getItemData(self,jid,dataname):
""" Return specific jid's representation in internal format. Used internally. """
@ -159,9 +159,6 @@ class Roster(PlugIn):
def getItems(self):
""" Return list of all [bare] JIDs that the roster is currently tracks."""
return self._data.keys()
def getRaw(self):
""" Returns internal representation of roster."""
return self._data
def keys(self):
""" Same as getItems. Provided for the sake of dictionary interface."""
return self._data.keys()
@ -184,3 +181,6 @@ class Roster(PlugIn):
""" Unauthorise JID 'jid'. Use for declining authorisation request
or for removing existing authorization. """
self._owner.send(Presence(jid,'unsubscribed'))
def getRaw(self):
"""Returns the internal data representation of the roster."""
return self._data

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: simplexml.py,v 1.26 2005/04/10 08:21:35 snakeru Exp $
# $Id: simplexml.py,v 1.27 2005/04/30 07:20:27 snakeru Exp $
"""Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams.
I'm personally using it in many other separate projects. It is designed to be as standalone as possible."""
@ -77,9 +77,9 @@ class Node:
else: self.data.append(ustr(i))
def __str__(self,fancy=0):
s = (fancy-1) * 2 * ' ' + "<" + self.name
""" Method used to dump node into textual representation.
if "fancy" argument is set to True produces indented output for readability."""
s = (fancy-1) * 2 * ' ' + "<" + self.name
if self.namespace:
if not self.parent or self.parent.namespace!=self.namespace:
s = s + ' xmlns="%s"'%self.namespace
@ -249,7 +249,7 @@ class Node:
raise AttributeError
class T:
""" Auxiliary class used to quick acces to node's child nodes. """
""" Auxiliary class used to quick access to node's child nodes. """
def __init__(self,node): self.__dict__['node']=node
def __getattr__(self,attr): return self.node.setTag(attr)
def __setattr__(self,attr,val):

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
# $Id: transports.py,v 1.15 2004/12/25 20:06:59 snakeru Exp $
# $Id: transports.py,v 1.18 2005/04/30 08:56:36 snakeru Exp $
"""
This module contains the low-level implementations of xmpppy connect methods or
@ -42,6 +42,7 @@ class error:
"""Serialise exception into pre-cached descriptive string."""
return self._comment
BUFLEN=1024
class TCPsocket(PlugIn):
""" This class defines direct TCP connection method. """
def __init__(self, server=None):
@ -89,21 +90,27 @@ class TCPsocket(PlugIn):
del self._owner.Connection
def receive(self):
""" Reads all pending incoming data. Calls owner's disconnected() method if appropriate."""
try: received = self._recv(1024)
""" Reads all pending incoming data.
In case of disconnection calls owner's disconnected() method and then raises IOError exception."""
try: received = self._recv(BUFLEN)
except socket.sslerror:
self._seen_data=0
return ''
except: received = ''
while select.select([self._sock],[],[],0)[0]:
try: add = self._recv(1024)
while self.pending_data(0):
try: add = self._recv(BUFLEN)
except: add=''
received +=add
if not add: break
if len(received): # length of 0 means disconnect
self._seen_data=1
self.DEBUG(received,'got')
else:
self.DEBUG('Socket error while receiving data','error')
self._owner.disconnected()
raise IOError("Disconnected from server")
return received
def send(self,raw_data):
@ -167,18 +174,28 @@ class HTTPPROXYsocket(TCPsocket):
connector.append('Proxy-Authorization: Basic '+credentials)
connector.append('\r\n')
self.send('\r\n'.join(connector))
reply = self.receive().replace('\r','')
try: reply = self.receive().replace('\r','')
except IOError:
self.DEBUG('Proxy suddenly disconnected','error')
self._owner.disconnected()
return
try: proto,code,desc=reply.split('\n')[0].split(' ',2)
except: raise error('Invalid proxy reply')
if code<>'200':
self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error')
self._owner.disconnected()
return
while reply.find('\n\n') == -1: reply += self.receive().replace('\r','')
while reply.find('\n\n') == -1:
try: reply += self.receive().replace('\r','')
except IOError:
self.DEBUG('Proxy suddenly disconnected','error')
self._owner.disconnected()
return
self.DEBUG("Authentification successfull. Jabber server contacted.",'ok')
return 'ok'
def DEBUG(self,text,severity):
"""Overwrites DEBUG tag to allow debug output be presented as "CONNECTproxy"."""
return self._owner.DEBUG(DBG_CONNECT_PROXY,text,severity)
class TLS(PlugIn):
@ -202,8 +219,8 @@ class TLS(PlugIn):
""" Unregisters TLS handler's from owner's dispatcher. Take note that encription
can not be stopped once started. You can only break the connection and start over."""
self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
# self._owner.UnregisterHandler('proceed',self.StartTLSHandler,xmlns=NS_TLS)
# self._owner.UnregisterHandler('failure',self.StartTLSHandler,xmlns=NS_TLS)
self._owner.UnregisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS)
self._owner.UnregisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS)
def FeaturesHandler(self, conn, feats):
""" Used to analyse server <features/> tag for TLS support.
@ -217,14 +234,25 @@ class TLS(PlugIn):
self._owner.Connection.send('<starttls xmlns="%s"/>'%NS_TLS)
raise NodeProcessed
def pending_data(self,timeout=0):
""" Returns true if there possible is a data ready to be read. """
return self._tcpsock._seen_data or select.select([self._tcpsock._sock],[],[],timeout)[0]
def _startSSL(self):
""" Immidiatedly switch socket to TLS mode. Used internally."""
""" Here we should switch pending_data to hint mode."""
tcpsock=self._owner.Connection
tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
tcpsock._sslIssuer = tcpsock._sslObj.issuer()
tcpsock._sslServer = tcpsock._sslObj.server()
tcpsock._recv = tcpsock._sslObj.read
tcpsock._send = tcpsock._sslObj.write
tcpsock._seen_data=1
self._tcpsock=tcpsock
tcpsock.pending_data=self.pending_data
tcpsock._sock.setblocking(0)
self.starttls='success'
def StartTLSHandler(self, conn, starttls):