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

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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. Provides library with all Non-SASL and SASL authentication mechanisms.
@ -97,7 +97,8 @@ class NonSASL(PlugIn):
class SASL(PlugIn): class SASL(PlugIn):
""" Implements SASL authentication. """ """ Implements SASL authentication. """
def plugin(self,owner): 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): def auth(self,username,password):
""" Start authentication. Result can be obtained via "SASL.startsasl" attribute and will be """ 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): def FeaturesHandler(self,conn,feats):
""" Used to determine if server supports SASL auth. Used internally. """ """ Used to determine if server supports SASL auth. Used internally. """
if not feats.getTag('mechanisms',namespace=NS_SASL): if not feats.getTag('mechanisms',namespace=NS_SASL):
self.startsasl='failure' self.startsasl='not-supported'
self.DEBUG('SASL not supported by server','error') self.DEBUG('SASL not supported by server','error')
return return
mecs=[] mecs=[]
@ -213,7 +214,7 @@ class Bind(PlugIn):
except NodeProcessed: pass except NodeProcessed: pass
else: self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) 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. """ """ Remove Bind handler from owner's dispatcher. Used internally. """
self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS) self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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. """Browser module provides DISCO server framework for your application.
This functionality can be used for very different purposes - from publishing This functionality can be used for very different purposes - from publishing
@ -27,7 +27,9 @@ from dispatcher import *
from client import PlugIn from client import PlugIn
class Browser(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. to your application.
All processing will be performed in the handlers registered in the browser All processing will be performed in the handlers registered in the browser
instance. You can register any number of handlers ensuring that for each instance. You can register any number of handlers ensuring that for each
@ -85,14 +87,14 @@ class Browser(PlugIn):
def plugin(self, owner): def plugin(self, owner):
""" Registers it's own iq handlers in your application dispatcher instance. """ Registers it's own iq handlers in your application dispatcher instance.
Used internally.""" Used internally."""
owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO) owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_INFO)
owner.RegisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_ITEMS) owner.RegisterHandler('iq',self._DiscoveryHandler,typ='get',ns=NS_DISCO_ITEMS)
def plugout(self): def plugout(self):
""" Unregisters browser's iq handlers from your application dispatcher instance. """ Unregisters browser's iq handlers from your application dispatcher instance.
Used internally.""" Used internally."""
self._owner.UnregisterHandler('iq',self._DiscoveryHandler,ns=NS_DISCO_INFO) self._owner.UnregisterHandler('iq',self._DiscoveryHandler,typ='get',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_ITEMS)
def _traversePath(self,node,jid,set=0): def _traversePath(self,node,jid,set=0):
""" Returns dictionary and key or None,None """ Returns dictionary and key or None,None
@ -136,7 +138,7 @@ class Browser(PlugIn):
{'jid':'jid2','action':'action2','node':'node2','name':'name2'}, {'jid':'jid2','action':'action2','node':'node2','name':'name2'},
{'jid':'jid3','node':'node3','name':'name3'}, {'jid':'jid3','node':'node3','name':'name3'},
{'jid':'jid4','node':'node4'} {'jid':'jid4','node':'node4'}
] ],
'info' :{ 'info' :{
'ids':[ 'ids':[
{'category':'category1','type':'type1','name':'name1'}, {'category':'category1','type':'type1','name':'name1'},
@ -182,7 +184,9 @@ class Browser(PlugIn):
to handle the request. Used internally. to handle the request. Used internally.
""" """
handler=self.getDiscoHandler(request.getQuerynode(),request.getTo()) 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') rep=request.buildReply('result')
q=rep.getTag('query') q=rep.getTag('query')
if request.getQueryNS()==NS_DISCO_ITEMS: if request.getQueryNS()==NS_DISCO_ITEMS:

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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. Provides PlugIn class functionality to develop extentions for xmpppy.
@ -105,7 +105,7 @@ class CommonClient:
self.debug_flags.append(self.DBG) self.debug_flags.append(self.DBG)
self._owner=self self._owner=self
self._registered_name=None self._registered_name=None
# self.RegisterDisconnectHandler(self.DisconnectHandler) self.RegisterDisconnectHandler(self.DisconnectHandler)
self.connected='' self.connected=''
def RegisterDisconnectHandler(self,handler): def RegisterDisconnectHandler(self,handler):
@ -123,7 +123,7 @@ class CommonClient:
self.disconnect_handlers.reverse() self.disconnect_handlers.reverse()
for i in self.disconnect_handlers: i() for i in self.disconnect_handlers: i()
self.disconnect_handlers.reverse() 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): def DisconnectHandler(self):
""" Default disconnect handler. Just raises an IOError. """ Default disconnect handler. Just raises an IOError.
@ -156,9 +156,9 @@ class CommonClient:
if not connected: return if not connected: return
self._Server,self._Proxy=server,proxy self._Server,self._Proxy=server,proxy
self.connected='tcp' self.connected='tcp'
# if self.Connection.getPort()==5223: if self.Connection.getPort()==5223:
# transports.TLS().PlugIn(self,now=1) transports.TLS().PlugIn(self,now=1)
# self.connected='tls' self.connected='tls'
dispatcher.Dispatcher().PlugIn(self) dispatcher.Dispatcher().PlugIn(self)
while self.Dispatcher.Stream._document_attrs is None: self.Process(1) 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': 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. 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'})""" 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 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 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 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 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 while not self.TLS.starttls and self.Process(): pass
# if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected if self.TLS.starttls<>'success': self.event('tls_failed'); return self.connected
# self.connected='tls' self.connected='tls'
return self.connected return self.connected
def auth(self,user,password,resource=''): def auth(self,user,password,resource=''):
""" Authenticate connnection and bind resource. If resource is not provided """ Authenticate connnection and bind resource. If resource is not provided
random one or library name used. """ random one or library name used. """
self._User,self._Password,self._Resource=user,password,resource 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 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': 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 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 auth.SASL().PlugIn(self)
else: self.SASL.startsasl='failure' if self.SASL.startsasl=='not-supported':
if self.SASL.startsasl=='failure':
if not resource: resource='xmpppy' if not resource: resource='xmpppy'
if auth.NonSASL(user,password,resource).PlugIn(self): if auth.NonSASL(user,password,resource).PlugIn(self):
self.connected+='+old_auth' self.connected+='+old_auth'
return '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) auth.Bind().PlugIn(self)
while self.Bind.bound is None: self.Process() while self.Bind.bound is None: self.Process()
if self.Bind.Bind(resource): 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 ## Ad-Hoc Command manager
## Mike Albon (c) 5th January 2005 ## Mike Albon (c) 5th January 2005
@ -34,8 +34,6 @@ What it supplies:
from xmpp.protocol import * from xmpp.protocol import *
from xmpp.client import PlugIn from xmpp.client import PlugIn
NS_COMMANDS='http://jabber.org/protocol/commands'
class Commands(PlugIn): class Commands(PlugIn):
"""Commands is an ancestor of Plugin and can be attached to any session. """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] return self._handlers[jid][name]
class Command_Handler_Prototype(PlugIn): 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: The parameters are as follows:
name : the name of the command within the jabber environment name : the name of the command within the jabber environment
@ -241,14 +242,19 @@ class Command_Handler_Prototype(PlugIn):
return self.discoinfo return self.discoinfo
class TestCommand(Command_Handler_Prototype): 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' name = 'testcommand'
description = 'a noddy example command' description = 'a noddy example command'
def __init__(self): def __init__(self):
""" Init internal constants. """
Command_Handler_Prototype.__init__(self) Command_Handler_Prototype.__init__(self)
self.pi = 3.14 self.pi = 3.14
self.initial = {'execute':self.cmdFirstStage} self.initial = {'execute':self.cmdFirstStage}
def cmdFirstStage(self,conn,request): def cmdFirstStage(self,conn,request):
""" Determine """
# This is the only place this should be repeated as all other stages should have SessionIDs # This is the only place this should be repeated as all other stages should have SessionIDs
try: try:
session = request.getTagAttr('command','sessionid') session = request.getTagAttr('command','sessionid')

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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 Main xmpppy mechanism. Provides library with methods to assign different handlers
@ -55,6 +55,7 @@ class Dispatcher(PlugIn):
self.handlers=handlers self.handlers=handlers
def _init(self): def _init(self):
""" Registers default namespaces/protocols/handlers. Used internally. """
self.RegisterNamespace('unknown') self.RegisterNamespace('unknown')
self.RegisterNamespace(NS_STREAMS) self.RegisterNamespace(NS_STREAMS)
self.RegisterNamespace(self._owner.defaultNamespace) self.RegisterNamespace(self._owner.defaultNamespace)
@ -62,6 +63,7 @@ class Dispatcher(PlugIn):
self.RegisterProtocol('presence',Presence) self.RegisterProtocol('presence',Presence)
self.RegisterProtocol('message',Message) self.RegisterProtocol('message',Message)
self.RegisterDefaultHandler(self.returnStanzaHandler) self.RegisterDefaultHandler(self.returnStanzaHandler)
# self.RegisterHandler('error',self.streamErrorHandler,xmlns=NS_STREAMS)
def plugin(self, owner): def plugin(self, owner):
""" Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally.""" """ Plug the Dispatcher instance into Client class instance and send initial stream header. Used internally."""
@ -74,6 +76,7 @@ class Dispatcher(PlugIn):
self.StreamInit() self.StreamInit()
def plugout(self): def plugout(self):
""" Prepares instance to be destructed. """
self.Stream.dispatch=None self.Stream.dispatch=None
self.Stream.DEBUG=None self.Stream.DEBUG=None
self.Stream.features=None self.Stream.features=None
@ -99,7 +102,10 @@ class Dispatcher(PlugIn):
Returns: Returns:
1) length of processed data if some data were processed; 1) length of processed data if some data were processed;
2) '0' string if no data were processed but link is alive; 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 if time.time() > self._lastIncome + 60: #1 min
iq = Iq('get', NS_LAST, to=self._owner.Server) iq = Iq('get', NS_LAST, to=self._owner.Server)
self.send(iq) self.send(iq)
@ -107,10 +113,12 @@ class Dispatcher(PlugIn):
self.disconnected() self.disconnected()
for handler in self._cycleHandlers: handler(self) for handler in self._cycleHandlers: handler(self)
if self._owner.Connection.pending_data(timeout): 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.Stream.Parse(data)
self._lastIncome = time.time() if data:
return len(data) self._lastIncome = time.time()
return len(data)
return '0' # It means that nothing is received but link is alive. return '0' # It means that nothing is received but link is alive.
def RegisterNamespace(self,xmlns,order='info'): def RegisterNamespace(self,xmlns,order='info'):
@ -190,6 +198,16 @@ class Dispatcher(PlugIn):
if stanza.getType() in ['get','set']: if stanza.getType() in ['get','set']:
conn.send(Error(stanza,ERR_FEATURE_NOT_IMPLEMENTED)) 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): def RegisterCycleHandler(self,handler):
""" Register handler that will be called on every Dispatcher.Process() call. """ """ Register handler that will be called on every Dispatcher.Process() call. """
if handler not in self._cycleHandlers: self._cycleHandlers.append(handler) 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 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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. 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) if df: return DataForm(node=df)
df=DataForm(typ='form') df=DataForm(typ='form')
for i in resp.getQueryPayload(): for i in resp.getQueryPayload():
try: #FIXME: temporary patch by Alexey to make it work :| if type(i)<>type(iq): pass
if i.getName()=='instructions': df.addInstructions(i.getData()) elif i.getName()=='instructions': df.addInstructions(i.getData())
else: df.setField(i.getName()).setValue(i.getData()) else: df.setField(i.getName()).setValue(i.getData())
except: pass
return df return df
def register(disp,host,info): def register(disp,host,info):

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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 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_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BROWSE ='jabber:iq:browse' NS_BROWSE ='jabber:iq:browse'
NS_CLIENT ='jabber:client' NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept' NS_COMPONENT_ACCEPT='jabber:component:accept'
NS_DATA ='jabber:x:data' # JEP-0004 NS_DATA ='jabber:x:data' # JEP-0004
NS_DELAY ='jabber:x:delay' NS_DELAY ='jabber:x:delay'
NS_DIALBACK ='jabber:server:dialback' NS_DIALBACK ='jabber:server:dialback'
NS_DISCO_INFO ='http://jabber.org/protocol/disco#info' NS_DISCO_INFO ='http://jabber.org/protocol/disco#info'
NS_DISCO_ITEMS ='http://jabber.org/protocol/disco#items' NS_DISCO_ITEMS ='http://jabber.org/protocol/disco#items'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_GROUPCHAT ='gc-1.0' NS_GROUPCHAT ='gc-1.0'
NS_IBB ='http://jabber.org/protocol/ibb' NS_IBB ='http://jabber.org/protocol/ibb'
NS_INVISIBLE ='presence-invisible' # jabberd2 NS_INVISIBLE ='presence-invisible' # jabberd2
@ -42,8 +42,9 @@ NS_IQ ='iq' # jabberd2
NS_LAST ='jabber:iq:last' NS_LAST ='jabber:iq:last'
NS_MESSAGE ='message' # jabberd2 NS_MESSAGE ='message' # jabberd2
NS_MUC ='http://jabber.org/protocol/muc' 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_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_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013
NS_PRESENCE ='presence' # jabberd2 NS_PRESENCE ='presence' # jabberd2
NS_PRIVACY ='jabber:iq:privacy' NS_PRIVACY ='jabber:iq:privacy'
@ -55,7 +56,6 @@ NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl'
NS_SEARCH ='jabber:iq:search' NS_SEARCH ='jabber:iq:search'
NS_SERVER ='jabber:server' NS_SERVER ='jabber:server'
NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' 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_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
NS_STREAMS ='http://etherx.jabber.org/streams' NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time' 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_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp' NS_VCARD ='vcard-temp'
NS_VERSION ='jabber:iq:version' NS_VERSION ='jabber:iq:version'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
NS_SIGNED ='jabber:x:signed' # JEP-0027
xmpp_stream_error_conditions=""" xmpp_stream_error_conditions="""
bad-format -- -- -- The entity has sent XML that cannot be processed. bad-format -- -- -- The entity has sent XML that cannot be processed.
@ -140,8 +142,60 @@ def isResultNode(node):
def isErrorNode(node): def isErrorNode(node):
""" Returns true if the node is a negative reply. """ """ Returns true if the node is a negative reply. """
return node and node.getType()=='error' return node and node.getType()=='error'
class NodeProcessed(Exception): class NodeProcessed(Exception):
""" Exception that should be raised by handler when the handling should be stopped. """ """ 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: class JID:
""" JID object. JID can be built from string, modified, compared, serialised into string. """ """ JID object. JID can be built from string, modified, compared, serialised into string. """
@ -345,6 +399,16 @@ class Presence(Protocol):
def getStatus(self): def getStatus(self):
""" Returns the status string of the message. """ """ Returns the status string of the message. """
return self.getTagData('status') 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): def _muc_getItemAttr(self,tag,attr):
for xtag in self.getTags('x'): for xtag in self.getTags('x'):
for child in xtag.getTags(tag): for child in xtag.getTags(tag):
@ -373,15 +437,6 @@ class Presence(Protocol):
def getStatusCode(self): def getStatusCode(self):
"""Returns the status code of the presence (for groupchat)""" """Returns the status code of the presence (for groupchat)"""
return self._muc_getItemAttr('status','code') 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): class Iq(Protocol):
""" XMPP Iq object - get/set dialog mechanism. """ """ XMPP Iq object - get/set dialog mechanism. """
@ -613,6 +668,7 @@ class DataForm(Node):
for i in field.getTags('value'): val.append(i.getData()) for i in field.getTags('value'): val.append(i.getData())
else: val=field.getTagData('value') else: val=field.getTagData('value')
ret[name]=val ret[name]=val
if self.getTag('instructions'): ret['instructions']=self.getInstructions()
return ret return ret
def __getitem__(self,name): def __getitem__(self,name):
""" Simple dictionary interface for getting datafields values by their names.""" """ 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 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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 Simple roster implementation. Can be used though for different tasks like
@ -98,7 +98,7 @@ class Roster(PlugIn):
if not pres.getTimestamp(): pres.setTimestamp() if not pres.getTimestamp(): pres.setTimestamp()
res['timestamp']=pres.getTimestamp() res['timestamp']=pres.getTimestamp()
elif typ=='unavailable' and item['resources'].has_key(jid.getResource()): del item['resources'][jid.getResource()] 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): def _getItemData(self,jid,dataname):
""" Return specific jid's representation in internal format. Used internally. """ """ Return specific jid's representation in internal format. Used internally. """
@ -159,9 +159,6 @@ class Roster(PlugIn):
def getItems(self): def getItems(self):
""" Return list of all [bare] JIDs that the roster is currently tracks.""" """ Return list of all [bare] JIDs that the roster is currently tracks."""
return self._data.keys() return self._data.keys()
def getRaw(self):
""" Returns internal representation of roster."""
return self._data
def keys(self): def keys(self):
""" Same as getItems. Provided for the sake of dictionary interface.""" """ Same as getItems. Provided for the sake of dictionary interface."""
return self._data.keys() return self._data.keys()
@ -184,3 +181,6 @@ class Roster(PlugIn):
""" Unauthorise JID 'jid'. Use for declining authorisation request """ Unauthorise JID 'jid'. Use for declining authorisation request
or for removing existing authorization. """ or for removing existing authorization. """
self._owner.send(Presence(jid,'unsubscribed')) 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 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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. """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.""" 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)) else: self.data.append(ustr(i))
def __str__(self,fancy=0): def __str__(self,fancy=0):
s = (fancy-1) * 2 * ' ' + "<" + self.name
""" Method used to dump node into textual representation. """ Method used to dump node into textual representation.
if "fancy" argument is set to True produces indented output for readability.""" if "fancy" argument is set to True produces indented output for readability."""
s = (fancy-1) * 2 * ' ' + "<" + self.name
if self.namespace: if self.namespace:
if not self.parent or self.parent.namespace!=self.namespace: if not self.parent or self.parent.namespace!=self.namespace:
s = s + ' xmlns="%s"'%self.namespace s = s + ' xmlns="%s"'%self.namespace
@ -249,7 +249,7 @@ class Node:
raise AttributeError raise AttributeError
class T: 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 __init__(self,node): self.__dict__['node']=node
def __getattr__(self,attr): return self.node.setTag(attr) def __getattr__(self,attr): return self.node.setTag(attr)
def __setattr__(self,attr,val): def __setattr__(self,attr,val):

View File

@ -12,7 +12,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details. ## 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 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.""" """Serialise exception into pre-cached descriptive string."""
return self._comment return self._comment
BUFLEN=1024
class TCPsocket(PlugIn): class TCPsocket(PlugIn):
""" This class defines direct TCP connection method. """ """ This class defines direct TCP connection method. """
def __init__(self, server=None): def __init__(self, server=None):
@ -89,21 +90,27 @@ class TCPsocket(PlugIn):
del self._owner.Connection del self._owner.Connection
def receive(self): def receive(self):
""" Reads all pending incoming data. Calls owner's disconnected() method if appropriate.""" """ Reads all pending incoming data.
try: received = self._recv(1024) 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 = '' except: received = ''
while select.select([self._sock],[],[],0)[0]: while self.pending_data(0):
try: add = self._recv(1024) try: add = self._recv(BUFLEN)
except: add='' except: add=''
received +=add received +=add
if not add: break if not add: break
if len(received): # length of 0 means disconnect if len(received): # length of 0 means disconnect
self._seen_data=1
self.DEBUG(received,'got') self.DEBUG(received,'got')
else: else:
self.DEBUG('Socket error while receiving data','error') self.DEBUG('Socket error while receiving data','error')
self._owner.disconnected() self._owner.disconnected()
raise IOError("Disconnected from server")
return received return received
def send(self,raw_data): def send(self,raw_data):
@ -167,18 +174,28 @@ class HTTPPROXYsocket(TCPsocket):
connector.append('Proxy-Authorization: Basic '+credentials) connector.append('Proxy-Authorization: Basic '+credentials)
connector.append('\r\n') connector.append('\r\n')
self.send('\r\n'.join(connector)) 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) try: proto,code,desc=reply.split('\n')[0].split(' ',2)
except: raise error('Invalid proxy reply') except: raise error('Invalid proxy reply')
if code<>'200': if code<>'200':
self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error') self.DEBUG('Invalid proxy reply: %s %s %s'%(proto,code,desc),'error')
self._owner.disconnected() self._owner.disconnected()
return 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') self.DEBUG("Authentification successfull. Jabber server contacted.",'ok')
return 'ok' return 'ok'
def DEBUG(self,text,severity): 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) return self._owner.DEBUG(DBG_CONNECT_PROXY,text,severity)
class TLS(PlugIn): class TLS(PlugIn):
@ -202,8 +219,8 @@ class TLS(PlugIn):
""" Unregisters TLS handler's from owner's dispatcher. Take note that encription """ 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.""" 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('features',self.FeaturesHandler,xmlns=NS_STREAMS)
# self._owner.UnregisterHandler('proceed',self.StartTLSHandler,xmlns=NS_TLS) self._owner.UnregisterHandlerOnce('proceed',self.StartTLSHandler,xmlns=NS_TLS)
# self._owner.UnregisterHandler('failure',self.StartTLSHandler,xmlns=NS_TLS) self._owner.UnregisterHandlerOnce('failure',self.StartTLSHandler,xmlns=NS_TLS)
def FeaturesHandler(self, conn, feats): def FeaturesHandler(self, conn, feats):
""" Used to analyse server <features/> tag for TLS support. """ 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) self._owner.Connection.send('<starttls xmlns="%s"/>'%NS_TLS)
raise NodeProcessed 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): def _startSSL(self):
""" Immidiatedly switch socket to TLS mode. Used internally.""" """ Immidiatedly switch socket to TLS mode. Used internally."""
""" Here we should switch pending_data to hint mode."""
tcpsock=self._owner.Connection tcpsock=self._owner.Connection
tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None) tcpsock._sslObj = socket.ssl(tcpsock._sock, None, None)
tcpsock._sslIssuer = tcpsock._sslObj.issuer() tcpsock._sslIssuer = tcpsock._sslObj.issuer()
tcpsock._sslServer = tcpsock._sslObj.server() tcpsock._sslServer = tcpsock._sslObj.server()
tcpsock._recv = tcpsock._sslObj.read tcpsock._recv = tcpsock._sslObj.read
tcpsock._send = tcpsock._sslObj.write 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' self.starttls='success'
def StartTLSHandler(self, conn, starttls): def StartTLSHandler(self, conn, starttls):