diff --git a/src/common/connection.py b/src/common/connection.py
index d51982ee9..536ac238c 100644
--- a/src/common/connection.py
+++ b/src/common/connection.py
@@ -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:
diff --git a/src/common/xmpp/auth.py b/src/common/xmpp/auth.py
index 5803d0d29..898a9c654 100644
--- a/src/common/xmpp/auth.py
+++ b/src/common/xmpp/auth.py
@@ -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)
diff --git a/src/common/xmpp/browser.py b/src/common/xmpp/browser.py
index 729b8b176..39c488104 100644
--- a/src/common/xmpp/browser.py
+++ b/src/common/xmpp/browser.py
@@ -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:
diff --git a/src/common/xmpp/client.py b/src/common/xmpp/client.py
index 62b5faaa7..85bedd777 100644
--- a/src/common/xmpp/client.py
+++ b/src/common/xmpp/client.py
@@ -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):
diff --git a/src/common/xmpp/commands.py b/src/common/xmpp/commands.py
index 6bbead07e..1c3ee6e46 100644
--- a/src/common/xmpp/commands.py
+++ b/src/common/xmpp/commands.py
@@ -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')
diff --git a/src/common/xmpp/dispatcher.py b/src/common/xmpp/dispatcher.py
index 2a0c4b4eb..4e8b911df 100644
--- a/src/common/xmpp/dispatcher.py
+++ b/src/common/xmpp/dispatcher.py
@@ -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)
diff --git a/src/common/xmpp/features.py b/src/common/xmpp/features.py
index 985373340..9660077db 100644
--- a/src/common/xmpp/features.py
+++ b/src/common/xmpp/features.py
@@ -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):
diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py
index afc6f4033..9fafb0d0e 100644
--- a/src/common/xmpp/protocol.py
+++ b/src/common/xmpp/protocol.py
@@ -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."""
diff --git a/src/common/xmpp/roster.py b/src/common/xmpp/roster.py
index 6d6cba4da..0ceef00a1 100644
--- a/src/common/xmpp/roster.py
+++ b/src/common/xmpp/roster.py
@@ -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
diff --git a/src/common/xmpp/simplexml.py b/src/common/xmpp/simplexml.py
index 7d2da078c..3d360ab58 100644
--- a/src/common/xmpp/simplexml.py
+++ b/src/common/xmpp/simplexml.py
@@ -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):
diff --git a/src/common/xmpp/transports.py b/src/common/xmpp/transports.py
index 7cf9c9a84..7af9dcfa8 100644
--- a/src/common/xmpp/transports.py
+++ b/src/common/xmpp/transports.py
@@ -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 tag for TLS support.
@@ -217,14 +234,25 @@ class TLS(PlugIn):
self._owner.Connection.send(''%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):