upstream updates
This commit is contained in:
parent
5367d4a4fb
commit
65f67a7a04
|
@ -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 *
|
||||
|
|
|
@ -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 ''
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue