upstream updates

This commit is contained in:
Norman Rasmussen 2006-01-18 20:46:29 +00:00
parent 5367d4a4fb
commit 65f67a7a04
9 changed files with 239 additions and 82 deletions

View File

@ -26,6 +26,6 @@ and use only methods for access all values you should not have any problems.
"""
import simplexml,protocol,debug,auth,transports,roster,dispatcher,features,browser,filetransfer
import simplexml,protocol,debug,auth,transports,roster,dispatcher,features,browser,filetransfer,commands
from client import *
from protocol import *

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.33 2005/11/30 17:05:40 normanr Exp $
# $Id: auth.py,v 1.35 2006/01/18 19:26:43 normanr Exp $
"""
Provides library with all Non-SASL and SASL authentication mechanisms.
@ -259,3 +259,48 @@ class Bind(PlugIn):
else:
self.DEBUG('Binding failed: timeout expired.','error')
return ''
class ComponentBind(PlugIn):
""" ComponentBind some JID to the current connection to allow router know of our location."""
def __init__(self):
PlugIn.__init__(self)
self.DBG_LINE='bind'
self.bound=None
self.needsUnregister=None
def plugin(self,owner):
""" Start resource binding, if allowed at this time. Used internally. """
if self._owner.Dispatcher.Stream.features:
try: self.FeaturesHandler(self._owner.Dispatcher,self._owner.Dispatcher.Stream.features)
except NodeProcessed: pass
else:
self._owner.RegisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
self.needsUnregister=1
def plugout(self):
""" Remove ComponentBind handler from owner's dispatcher. Used internally. """
if self.needsUnregister:
self._owner.UnregisterHandler('features',self.FeaturesHandler,xmlns=NS_STREAMS)
def FeaturesHandler(self,conn,feats):
""" Determine if server supports resource binding and set some internal attributes accordingly. """
if not feats.getTag('bind',namespace=NS_BIND):
self.bound='failure'
self.DEBUG('Server does not requested binding.','error')
return
if feats.getTag('session',namespace=NS_SESSION): self.session=1
else: self.session=-1
self.bound=[]
def Bind(self,domain=None):
""" Perform binding. Use provided domain name (if not provided). """
while self.bound is None and self._owner.Process(1): pass
resp=self._owner.SendAndWaitForResponse(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1))
if resp and resp.getAttr('error'):
self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error')
elif resp:
self.DEBUG('Successfully bound.','ok')
return 'ok'
else:
self.DEBUG('Binding failed: timeout expired.','error')
return ''

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.35 2005/04/30 10:17:19 snakeru Exp $
# $Id: client.py,v 1.52 2006/01/02 19:40:55 normanr Exp $
"""
Provides PlugIn class functionality to develop extentions for xmpppy.
@ -31,6 +31,7 @@ Debug.Debug.colors['nodebuilder']=debug.color_brown
Debug.Debug.colors['client']=debug.color_cyan
Debug.Debug.colors['component']=debug.color_cyan
Debug.Debug.colors['dispatcher']=debug.color_green
Debug.Debug.colors['browser']=debug.color_blue
Debug.Debug.colors['auth']=debug.color_yellow
Debug.Debug.colors['roster']=debug.color_magenta
Debug.Debug.colors['ibb']=debug.color_yellow
@ -111,6 +112,7 @@ class CommonClient:
self._registered_name=None
self.RegisterDisconnectHandler(self.DisconnectHandler)
self.connected=''
self._component=0
def RegisterDisconnectHandler(self,handler):
""" Register handler that will be called on disconnect."""
@ -147,6 +149,11 @@ class CommonClient:
""" Example of reconnection method. In fact, it can be used to batch connection and auth as well. """
handlerssave=self.Dispatcher.dumpHandlers()
self.Dispatcher.PlugOut()
if self.__dict__.has_key('NonSASL'): self.NonSASL.PlugOut()
if self.__dict__.has_key('SASL'): self.SASL.PlugOut()
if self.__dict__.has_key('TLS'): self.TLS.PlugOut()
if self.__dict__.has_key('HTTPPROXYsocket'): self.HTTPPROXYsocket.PlugOut()
if self.__dict__.has_key('TCPsocket'): self.TCPsocket.PlugOut()
if not self.connect(server=self._Server,proxy=self._Proxy): return
if not self.auth(self._User,self._Password,self._Resource): return
self.Dispatcher.restoreHandlers(handlerssave)
@ -159,16 +166,20 @@ class CommonClient:
if hasattr(self, 'Connection'):
return self.Connection._sock.getsockname()
def connect(self,server=None,proxy=None, ssl=None):
""" Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream. """
def connect(self,server=None,proxy=None,ssl=None,use_srv=None):
""" Make a tcp/ip connection, protect it with tls/ssl if possible and start XMPP stream.
Returns None or 'tcp' or 'tls', depending on the result."""
if not server: server=(self.Server,self.Port)
if proxy: connected=transports.HTTPPROXYsocket(proxy,server).PlugIn(self)
else: connected=transports.TCPsocket(server).PlugIn(self)
if not connected: return
if proxy: socket=transports.HTTPPROXYsocket(proxy,server,use_srv)
else: socket=transports.TCPsocket(server,use_srv)
connected=socket.PlugIn(self)
if not connected:
socket.PlugOut()
return
self._Server,self._Proxy=server,proxy
self.connected='tcp'
if (ssl is None and self.Connection.getPort() in (5223, 443)) or ssl:
try:
try: # FIXME. This should be done in transports.py
transports.TLS().PlugIn(self,now=1)
self.connected='ssl'
except socket.sslerror:
@ -182,16 +193,16 @@ class CommonClient:
class Client(CommonClient):
""" Example client class, based on CommonClient. """
def connect(self,server=None,proxy=None,secure=None):
def connect(self,server=None,proxy=None,secure=None,use_srv=True):
""" Connect to jabber server. If you want to specify different ip/port to connect to you can
pass it as tuple as first parameter. If there is HTTP proxy between you and server -
specify it's address and credentials (if needed) in the second argument
pass it as tuple as first parameter. If there is HTTP proxy between you and server
specify it's address and credentials (if needed) in the second argument.
If you want ssl/tls support to be discovered and enable automatically - leave third argument as None. (ssl will be autodetected only if port is 5223 or 443)
If you want to force SSL start (i.e. if port 5223 or 443 is remapped to some non-standard port) then set it to 1
If you want to disable tls/ssl support completely, set it to 0
If you want to force SSL start (i.e. if port 5223 or 443 is remapped to some non-standard port) then set it to 1.
If you want to disable tls/ssl support completely, set it to 0.
Example: connect(('192.168.5.5',5222),{'host':'proxy.my.net','port':8080,'user':'me','password':'secret'})
Returns '' (on no connection) or 'tcp' or 'tls', depending on the result."""
if not CommonClient.connect(self,server,proxy,secure) or secure<>None and not secure: return self.connected
Returns '' or 'tcp' or 'tls', depending on the result."""
if not CommonClient.connect(self,server,proxy,secure,use_srv) or secure<>None and not secure: return self.connected
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
@ -247,19 +258,31 @@ class Client(CommonClient):
class Component(CommonClient):
""" Component class. The only difference from CommonClient is ability to perform component authentication. """
def __init__(self,server,port=5347,typ=None,debug=['always', 'nodebuilder']):
def __init__(self,server,port=5347,typ=None,debug=['always', 'nodebuilder'],domains=None,component=0):
""" Init function for Components.
As components use a different auth mechanism which includes the namespace of the component.
Jabberd1.4 and Ejabberd use the default namespace then for all client messages.
Jabberd2 uses jabber:client."""
Jabberd2 uses jabber:client.
'server' argument is a server name that you are connecting to (f.e. "localhost").
'port' can be specified if 'server' resolves to correct IP. If it is not then you'll need to specify IP
and port while calling "connect()"."""
CommonClient.__init__(self,server,port=port,debug=debug)
self.typ=typ
self.component=component
if domains:
self.domains=domains
else:
self.domains=[server]
def connect(self,server=None,proxy=None):
""" This will connect to the server, and if the features tag is found then set
the namespace to be jabber:client as that is required for jabberd2"""
the namespace to be jabber:client as that is required for jabberd2.
'server' and 'proxy' arguments have the same meaning as in xmpp.Client.connect() """
if self.component:
self.Namespace=auth.NS_COMPONENT_1
self.Server=server[0]
CommonClient.connect(self,server=server,proxy=proxy)
if self.typ=='jabberd2' or not self.typ and self.Dispatcher.Stream.features != None:
if self.connected and (self.typ=='jabberd2' or not self.typ and self.Dispatcher.Stream.features != None):
self.defaultNamespace=auth.NS_CLIENT
self.Dispatcher.RegisterNamespace(self.defaultNamespace)
self.Dispatcher.RegisterProtocol('iq',dispatcher.Iq)
@ -267,7 +290,32 @@ class Component(CommonClient):
self.Dispatcher.RegisterProtocol('presence',dispatcher.Presence)
return self.connected
def auth(self,name,password,dup=None):
def auth(self,name,password,dup=None,sasl=0):
""" Authenticate component "name" with password "password"."""
self._User,self._Password,self._Resource=name,password,''
return auth.NonSASL(name,password,'').PlugIn(self)
try:
if self.component: sasl=1
if sasl: auth.SASL(name,password).PlugIn(self)
if not sasl or self.SASL.startsasl=='not-supported':
if auth.NonSASL(name,password,'').PlugIn(self):
self.connected+='+old_auth'
return 'old_auth'
return
self.SASL.auth()
while self.SASL.startsasl=='in-process' and self.Process(): pass
if self.SASL.startsasl=='success':
if self.component:
self._component=self.component
for domain in self.domains:
auth.ComponentBind().PlugIn(self)
while self.ComponentBind.bound is None: self.Process()
if (not self.ComponentBind.Bind(domain)):
self.ComponentBind.PlugOut()
return
self.ComponentBind.PlugOut()
self.connected+='+sasl'
return 'sasl'
else:
raise auth.NotAuthorized(self.SASL.startsasl)
except:
self.DEBUG(self.DBG,"Failed to authenticate %s"%name,'error')

View File

@ -1,4 +1,4 @@
## $Id: commands.py,v 1.10 2005/10/07 23:17:09 normanr Exp $
## $Id: commands.py,v 1.11 2005/11/30 17:03:11 normanr Exp $
## Ad-Hoc Command manager
## Mike Albon (c) 5th January 2005
@ -125,7 +125,7 @@ class Commands(PlugIn):
conn.send(Error(request,ERR_ITEM_NOT_FOUND))
raise NodeProcessed
elif typ == 'info':
return {'ids':[],'features':[]}
return {'ids':[{'category':'automation','type':'command-list'}],'features':[]}
def addCommand(self,name,cmddisco,cmdexecute,jid=''):
"""The method to call if adding a new command to the session, the requred parameters of cmddisco and cmdexecute are the methods to enable that command to be executed"""
@ -197,7 +197,7 @@ class Command_Handler_Prototype(PlugIn):
self.sessioncount = 0
self.sessions = {}
# Disco information for command list pre-formatted as a tuple
self.discoinfo = {'ids':[{'category':'automation','type':'command','name':self.description}],'features': self.discofeatures}
self.discoinfo = {'ids':[{'category':'automation','type':'command-node','name':self.description}],'features': self.discofeatures}
self._jid = jid
def plugin(self,owner):

View File

@ -40,16 +40,16 @@ in this code
import sys
import traceback
import time
import os
import types
colornames = ['none', 'black', 'red', 'green', 'brown', 'blue', 'magenta',
'cyan', 'light_gray', 'dark_gray', 'bright_red', 'bright_green', 'yellow',
'bright_blue', 'purple', 'bright_cyan', 'white']
if os.environ.has_key('TERM'):
colors_enabled=True
else:
colors_enabled=False
color_none = chr(27) + "[0m"
color_black = chr(27) + "[30m"
@ -69,11 +69,7 @@ color_purple = chr(27) + "[35;1m"
color_bright_cyan = chr(27) + "[36;1m"
color_white = chr(27) + "[37;1m"
if os.name == 'nt':
for colorname in colornames:
name = 'color_' + colorname
mod = compile("%s = ''" % name, 'gajim', 'exec')
eval(mod)
"""
Define your flags in yor modules like this:
@ -125,7 +121,7 @@ class Debug:
#
# active_flags are those that will trigger output
#
active_flags = None,
active_flags = None,
#
# Log file should be file object or file namne
#
@ -136,7 +132,7 @@ class Debug:
# with prefix = chr(27) + '[34m'
# sufix = chr(27) + '[37;1m\n'
#
prefix = 'DEBUG: ',
prefix = 'DEBUG: ',
sufix = '\n',
#
# If you want unix style timestamps,
@ -144,7 +140,7 @@ class Debug:
# 1 before prefix, good when prefix is a string
# 2 after prefix, good when prefix is a color
#
time_stamp = 0,
time_stamp = 0,
#
# flag_show should normaly be of, but can be turned on to get a
# good view of what flags are actually used for calls,
@ -204,7 +200,7 @@ class Debug:
mod_name = ""
self.show('Debug created for %s%s' % (caller.f_code.co_filename,
mod_name ))
self.show(' flags defined: %s' % ' '.join( self.active ))
self.show(' flags defined: %s' % ','.join( self.active ))
if type(flag_show) in (type(''), type(None)):
self.flag_show = flag_show
@ -397,12 +393,19 @@ class Debug:
colors={}
def Show(self, flag, msg, prefix=''):
msg=msg.replace('\r','\\r').replace('\n','\\n')
if self.colors.has_key(prefix): msg=self.colors[prefix]+msg+color_none
msg=msg.replace('\r','\\r').replace('\n','\\n').replace('><','>\n <')
if not colors_enabled: pass
elif self.colors.has_key(prefix): msg=self.colors[prefix]+msg+color_none
else: msg=color_none+msg
if self.colors.has_key(flag): prefixcolor=self.colors[flag]
if not colors_enabled: prefixcolor=''
elif self.colors.has_key(flag): prefixcolor=self.colors[flag]
else: prefixcolor=color_none
if prefix=='error':
_exception = sys.exc_info()
if _exception[0]:
msg=msg+'\n'+''.join(traceback.format_exception(_exception[0], _exception[1], _exception[2])).rstrip()
prefix= self.prefix+prefixcolor+(flag+' '*12)[:12]+' '+(prefix+' '*6)[:6]
self.show(msg, flag, prefix)

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.35 2005/05/07 03:26:51 snakeru Exp $
# $Id: dispatcher.py,v 1.40 2006/01/18 19:26:43 normanr Exp $
"""
Main xmpppy mechanism. Provides library with methods to assign different handlers
@ -115,18 +115,21 @@ 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.
"""
for handler in self._cycleHandlers: handler(self)
if len(self._pendingExceptions) > 0:
_pendingException = self._pendingExceptions.pop()
raise _pendingException[0], _pendingException[1], _pendingException[2]
if self._owner.Connection.pending_data(timeout):
try: data=self._owner.Connection.receive()
try: data=self._owner.Connection.receive()
except IOError: return
self.Stream.Parse(data)
if len(self._pendingExceptions) > 0:
_pendingException = self._pendingExceptions.pop()
raise _pendingException[0], _pendingException[1], _pendingException[2]
_pendingException = self._pendingExceptions.pop()
raise _pendingException[0], _pendingException[1], _pendingException[2]
return len(data)
return '0' # It means that nothing is received but link is alive.
@ -234,13 +237,30 @@ class Dispatcher(PlugIn):
3) data that comes along with event. Depends on event."""
if self._eventHandler: self._eventHandler(realm,event,data)
def dispatch(self,stanza,session=None):
def dispatch(self,stanza,session=None,direct=0):
""" Main procedure that performs XMPP stanza recognition and calling apppropriate handlers for it.
Called internally. """
if not session: session=self
session.Stream._mini_dom=None
name=stanza.getName()
if not direct and self._owner._component:
if name == 'route':
if stanza.getAttr('error') == None:
if len(stanza.getChildren()) == 1:
stanza = stanza.getChildren()[0]
name=stanza.getName()
else:
for each in stanza.getChildren():
self.dispatch(each,session,direct=1)
return
elif name == 'presence':
return
elif name in ('features','bind'):
pass
else:
raise UnsupportedStanzaType(name)
if name=='features': session.Stream.features=stanza
xmlns=stanza.getNamespace()
@ -251,7 +271,7 @@ class Dispatcher(PlugIn):
self.DEBUG("Unknown stanza: " + name,'warn')
name='unknown'
else:
self.DEBUG("Got %s stanza"%name, 'ok')
self.DEBUG("Got %s/%s stanza"%(xmlns,name), 'ok')
if stanza.__class__.__name__=='Node': stanza=self.handlers[xmlns][name][type](node=stanza)
@ -281,7 +301,6 @@ class Dispatcher(PlugIn):
try: cb(session,stanza,**args)
except Exception, typ:
if typ.__class__.__name__<>'NodeProcessed': raise
else:
session.DEBUG("Expected stanza arrived!",'ok')
session._expected[ID]=stanza
@ -344,6 +363,16 @@ class Dispatcher(PlugIn):
stanza.setID(_ID)
else: _ID=stanza.getID()
if self._owner._registered_name and not stanza.getAttr('from'): stanza.setAttr('from',self._owner._registered_name)
if self._owner._component and stanza.getName()!='bind':
to=self._owner.Server
if stanza.getTo() and stanza.getTo().getDomain():
to=stanza.getTo().getDomain()
frm=stanza.getFrom()
if frm.getDomain():
frm=frm.getDomain()
route=Protocol('route',to=to,frm=frm,payload=[stanza])
stanza=route
stanza.setNamespace(self._owner.Namespace)
stanza.setParent(self._metastream)
self._owner_send(stanza)
return _ID

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.20 2005/04/30 07:43:01 snakeru Exp $
# $Id: features.py,v 1.22 2005/09/30 20:13:04 mikealbon Exp $
"""
This module contains variable stuff that is not worth splitting into separate modules.
@ -60,7 +60,7 @@ def discoverInfo(disp,jid,node=None):
""" Query remote object about info that it publishes. Returns identities and features lists."""
""" According to JEP-0030:
query MAY have node attribute
identity: MUST HAVE category and type attributes and MAY HAVE name attribute.
identity: MUST HAVE category and name attributes and MAY HAVE type attribute.
feature: MUST HAVE var attribute"""
identities , features = [] , []
for i in _discover(disp,NS_DISCO_INFO,jid,node):

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.41 2005/05/02 08:36:41 snakeru Exp $
# $Id: protocol.py,v 1.52 2006/01/09 22:08:57 normanr Exp $
"""
Protocol module contains tools that is needed for processing of
@ -21,61 +21,79 @@ xmpp-related data structures.
from simplexml import Node,ustr
import time
NS_ACTIVITY ='http://jabber.org/protocol/activity' # JEP-0108
NS_ADDRESS ='http://jabber.org/protocol/address' # JEP-0033
NS_AGENTS ='jabber:iq:agents'
NS_AMP ='http://jabber.org/protocol/amp'
NS_AMP_ERRORS =NS_AMP+'#errors'
NS_AUTH ='jabber:iq:auth'
NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
NS_BROWSE ='jabber:iq:browse'
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
NS_CAPS ='http://jabber.org/protocol/caps' # JEP-0115
NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # JEP-0085
NS_CLIENT ='jabber:client'
NS_COMMANDS ='http://jabber.org/protocol/commands'
NS_COMPONENT_ACCEPT='jabber:component:accept'
NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0'
NS_COMPRESS ='http://jabber.org/protocol/compress' # JEP-0138
NS_CONFERENCE ='jabber:x:conference'
NS_DATA ='jabber:x:data' # JEP-0004
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_DISCO ='http://jabber.org/protocol/disco'
NS_DISCO_INFO =NS_DISCO+'#info'
NS_DISCO_ITEMS =NS_DISCO+'#items'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_EVENT ='jabber:x:event' # JEP-0022
NS_FEATURE ='http://jabber.org/protocol/feature-neg'
NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096
NS_GEOLOC ='http://jabber.org/protocol/geoloc' # JEP-0080
NS_GROUPCHAT ='gc-1.0'
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070
NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # JEP-0124
NS_IBB ='http://jabber.org/protocol/ibb'
NS_INVISIBLE ='presence-invisible' # jabberd2
NS_IQ ='iq' # jabberd2
NS_INVISIBLE ='presence-invisible' # Jabberd2
NS_IQ ='iq' # Jabberd2
NS_LAST ='jabber:iq:last'
NS_MESSAGE ='message' # jabberd2
NS_MESSAGE ='message' # Jabberd2
NS_MOOD ='http://jabber.org/protocol/mood' # JEP-0107
NS_MUC ='http://jabber.org/protocol/muc'
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_MUC_USER =NS_MUC+'#user'
NS_MUC_ADMIN =NS_MUC+'#admin'
NS_MUC_OWNER =NS_MUC+'#owner'
NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html' # JEP-0013
NS_PHYSLOC ='http://jabber.org/protocol/physloc' # JEP-0112
NS_PRESENCE ='presence' # Jabberd2
NS_PRIVACY ='jabber:iq:privacy'
NS_PRIVATE ='jabber:iq:private'
NS_PUBSUB ='http://jabber.org/protocol/pubsub' # JEP-0060
NS_REGISTER ='jabber:iq:register'
NS_ROSTER ='jabber:iq:roster'
NS_RPC ='jabber:iq:rpc' # JEP-0009
NS_ROSTERX ='http://jabber.org/protocol/rosterx' # JEP-0144
NS_RPC ='jabber:iq:rpc' # JEP-0009
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_SI ='http://jabber.org/protocol/si' # JEP-0096
NS_SI_PUB ='http://jabber.org/protocol/sipub' # JEP-0137
NS_SIGNED ='jabber:x:signed' # JEP-0027
NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
NS_STREAM ='http://affinix.com/jabber/stream'
NS_STREAMS ='http://etherx.jabber.org/streams'
NS_TIME ='jabber:iq:time'
NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
NS_VACATION ='http://jabber.org/protocol/vacation'
NS_VCARD ='vcard-temp'
NS_GMAILNOTIFY ='google:mail:notify'
NS_GMAILNOTIFY ='google:mail:notify'
NS_VCARD_UPDATE =NS_VCARD+':x:update'
NS_VERSION ='jabber:iq:version'
NS_ENCRYPTED ='jabber:x:encrypted' # JEP-0027
NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # JEP-0130
NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # JEP-0071
NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # JEP-0141
NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate' # JEP-0122
NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
NS_SIGNED ='jabber:x:signed' # JEP-0027
NS_SI ='http://jabber.org/protocol/si' # JEP-0096
NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096
NS_FEATURE ='http://jabber.org/protocol/feature-neg'
NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # JEP-0065
NS_DISCO ='http://jabber.org/protocol/disco#info' # JEP-0095
NS_STREAM ='http://affinix.com/jabber/stream'
NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth' # JEP-0070
xmpp_stream_error_conditions="""
bad-format -- -- -- The entity has sent XML that cannot be processed.
@ -676,7 +694,7 @@ class DataForm(Node):
for field in self.getTags('field'):
name=field.getAttr('var')
typ=field.getType()
if type(typ) in [type(''),type(u'')] and typ[-6:]=='multi':
if type(typ) in [type(''),type(u'')] and typ[-6:]=='-multi':
val=[]
for i in field.getTags('value'): val.append(i.getData())
else: val=field.getTagData('value')

View File

@ -66,7 +66,7 @@ class error:
class TCPsocket(PlugIn):
""" This class defines direct TCP connection method. """
def __init__(self, server=None):
def __init__(self, server=None, use_srv=True):
""" Cache connection point 'server'. 'server' is the tuple of (host, port)
absolutely the same as standard tcp socket uses. """
PlugIn.__init__(self)
@ -165,12 +165,12 @@ class HTTPPROXYsocket(TCPsocket):
""" HTTP (CONNECT) proxy connection class. Uses TCPsocket as the base class
redefines only connect method. Allows to use HTTP proxies like squid with
(optionally) simple authentication (using login and password). """
def __init__(self,proxy,server):
def __init__(self,proxy,server,use_srv=True):
""" Caches proxy and target addresses.
'proxy' argument is a dictionary with mandatory keys 'host' and 'port' (proxy address)
and optional keys 'user' and 'password' to use for authentication.
'server' argument is a tuple of host and port - just like TCPsocket uses. """
TCPsocket.__init__(self,server)
TCPsocket.__init__(self,server,use_srv)
self.DBG_LINE=DBG_CONNECT_PROXY
self._proxy=proxy
@ -196,14 +196,23 @@ 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'
@ -247,8 +256,13 @@ 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()