Jabberpy V0.5

This commit is contained in:
Yann Leboulanger 2004-03-06 04:11:57 +00:00
parent cf2c7b5037
commit fa49d30545
2 changed files with 108 additions and 106 deletions

View File

@ -1,4 +1,4 @@
## jabber.py
## jabber.py
##
## Copyright (C) 2001 Matthew Allum
##
@ -86,7 +86,7 @@ DBG_NODE_UNKNOWN = 'jb-node-unknown' ; debug.debug_flags.append( DBG_NODE_UNK
#
# JANA core namespaces
# JANA core namespaces
# from http://www.jabber.org/jana/namespaces.php as of 2003-01-12
# "myname" means that namespace didnt have a name in the jabberd headers
#
@ -107,7 +107,7 @@ NS_COMP_ACCEPT = "jabber:component:accept" # myname
NS_COMP_CONNECT = "jabber:component:connect" # myname
#
# JANA JEP namespaces, ordered by JEP
# from http://www.jabber.org/jana/namespaces.php as of 2003-01-12
@ -140,10 +140,10 @@ NS_P_COMMANDS = _NS_PROTOCOL + "/commands" # JEP-0050
"""
2002-01-11 jaclu
Defined in jabberd/lib/lib.h, but not JANA aproved and not used in jabber.py
so commented out, should/could propably be removed...
NS_ADMIN = "jabber:iq:admin"
NS_AUTH_OK = "jabber:iq:auth:0k"
NS_CONFERENCE = "jabber:iq:conference"
@ -189,13 +189,13 @@ def ustr(what):
return r
xmlstream.ustr = ustr
class NodeProcessed(Exception): pass # currently only for Connection._expectedIqHandler
class NodeProcessed(Exception): pass # currently only for Connection._expectedIqHandler
class Connection(xmlstream.Client):
"""Forms the base for both Client and Component Classes"""
def __init__(self, host, port, namespace,
debug=False, log=False, connection=xmlstream.TCP, hostIP=None, proxy=None):
debug=[], log=False, connection=xmlstream.TCP, hostIP=None, proxy=None):
xmlstream.Client.__init__(self, host, port, namespace,
debug=debug, log=log,
connection=connection,
@ -207,11 +207,11 @@ class Connection(xmlstream.Client):
self.registerProtocol('presence', Presence)
self.registerHandler('iq',self._expectedIqHandler,system=True)
self._expected = {}
self._id = 0;
self.lastErr = ''
self.lastErrCode = 0
@ -230,13 +230,13 @@ class Connection(xmlstream.Client):
print "WARNING! setIqHandler(...) method is obsolette, use registerHandler('iq',...) instead."
return self.registerHandler('iq', func, type, ns)
def header(self):
def header(self):
self.DEBUG("stream: sending initial header",DBG_INIT)
str = u"<?xml version='1.0' encoding='UTF-8' ?> \
<stream:stream to='%s' xmlns='%s'" % ( self._host,
self._namespace )
if self._outgoingID: str = str + " id='%s' " % self._outgoingID
if self._outgoingID: str = str + " id='%s' " % self._outgoingID
str = str + " xmlns:stream='http://etherx.jabber.org/streams'>"
self.send(str)
self.process(timeout)
@ -276,7 +276,7 @@ class Connection(xmlstream.Client):
if not self.handlers[name].has_key(ns): ns=''
if not self.handlers[name].has_key(typ): typ=''
if not self.handlers[name].has_key(typns): typns=''
if typ==typns: typns=''
if typ==typns: typns=''
chain=[]
for key in ['default',typ,ns,typns]: # we will use all handlers: from very common to very particular
@ -307,10 +307,10 @@ class Connection(xmlstream.Client):
def registerHandler(self,name,handler,type='',ns='',chained=False, makefirst=False, system=False):
"""Sets the callback func for processing incoming stanzas.
Multiple callback functions can be set which are called in
succession. Callback can optionally raise an NodeProcessed error to
stop stanza from further processing. A type and namespace attributes can
succession. Callback can optionally raise an NodeProcessed error to
stop stanza from further processing. A type and namespace attributes can
also be optionally passed so the callback is only called when a stanza of
this type is received. Namespace attribute MUST be omitted if you
this type is received. Namespace attribute MUST be omitted if you
registering an Iq processing handler.
If 'chainOutput' is set to False (the default), the given function
@ -359,7 +359,7 @@ class Connection(xmlstream.Client):
self.lastErr and self.lastErrCode is set to the received error. If
the operation times out (which only happens if a timeout value is
given), waitForResponse will return None and self.lastErr will be
set to "Timeout".
set to "Timeout".
Changed default from timeout=0 to timeout=300 to avoid hangs in
scripts and such.
If you _really_ want no timeout, just set it to 0"""
@ -372,7 +372,7 @@ class Connection(xmlstream.Client):
self.DEBUG("waiting with timeout:%s for %s" % (timeout,ustr(ID)),DBG_NODE_IQ)
else:
self.DEBUG("waiting for %s" % ustr(ID),DBG_NODE_IQ)
while (not self._expected[ID]) and not has_timed_out:
if not self.process(0.2): return None
if timeout and (time.time() > abort_time):
@ -405,17 +405,17 @@ class Connection(xmlstream.Client):
"""Returns a unique ID"""
self._id = self._id + 1
return ustr(self._id)
#############################################################################
class Client(Connection):
"""Class for managing a client connection to a jabber server."""
def __init__(self, host, port=5222, debug=False, log=False,
def __init__(self, host, port=5222, debug=[], log=False,
connection=xmlstream.TCP, hostIP=None, proxy=None):
Connection.__init__(self, host, port, NS_CLIENT, debug, log,
connection=connection, hostIP=hostIP, proxy=proxy)
self.registerHandler('iq',self._IqRosterManage,'result',NS_ROSTER,system=True)
self.registerHandler('iq',self._IqRosterManage,'set',NS_ROSTER,system=True)
self.registerHandler('iq',self._IqRegisterResult,'result',NS_REGISTER,system=True)
@ -478,7 +478,7 @@ class Client(Connection):
"NS_REGISTER and type==result"
self._reg_info = {}
for item in iq_obj.getQueryNode().getChildren():
self._reg_info[item.getName()] = item.getData()
self._reg_info[item.getName()] = item.getData()
def _IqAgentsResult(self, conn, iq_obj):
"NS_AGENTS and type==result"
@ -515,13 +515,13 @@ class Client(Connection):
auth_set_iq = Iq(type='set')
auth_set_iq.setID('auth-set')
q = auth_set_iq.setQuery(NS_AUTH)
q.insertTag('username').insertData(username)
q.insertTag('resource').insertData(resource)
if auth_ret_query.getTag('token'):
token = auth_ret_query.getTag('token').getData()
seq = auth_ret_query.getTag('sequence').getData()
self.DEBUG("zero-k authentication supported",(DBG_INIT,DBG_NODE_IQ))
@ -538,7 +538,7 @@ class Client(Connection):
else:
self.DEBUG("plain text authentication supported",(DBG_INIT,DBG_NODE_IQ))
q.insertTag('password').insertData(passwd)
iq_result = self.SendAndWaitForResponse(auth_set_iq)
if iq_result==None:
@ -592,7 +592,6 @@ class Client(Connection):
if groups != None:
for group in groups:
item.insertTag('group').insertData(group)
# self.send(iq)
dummy = self.SendAndWaitForResponse(iq) # Do we need to wait??
@ -620,7 +619,7 @@ class Client(Connection):
# self.DEBUG("Requesting reg info from %s%s:" % (agent, self._host), DBG_NODE_IQ)
self.DEBUG("Requesting reg info from %s:" % agent, DBG_NODE_IQ)
self.DEBUG(ustr(reg_iq),DBG_NODE_IQ)
return self.SendAndWaitForResponse(reg_iq)
return self.SendAndWaitForResponse(reg_iq)
def getRegInfo(self):
@ -656,14 +655,16 @@ class Client(Connection):
Note that you must be authorised before attempting to deregister.
"""
if agent:
agent = agent + '.'
self.send(Presence(to=agent+self._host,type='unsubscribed')) # This is enough f.e. for icqv7t or jit
# agent = agent + '.'
# self.send(Presence(to=agent+self._host,type='unsubscribed')) # This is enough f.e. for icqv7t or jit
self.send(Presence(to=agent,type='unsubscribed')) # This is enough f.e. for icqv7t or jit
if agent is None: agent = ''
q = self.requestRegInfo()
kids = q.getQueryPayload()
keyTag = kids.getTag("key")
iq = Iq(to=agent+self._host, type="set")
# iq = Iq(to=agent+self._host, type="set")
iq = Iq(to=agent, type="set")
iq.setQuery(NS_REGISTER)
iq.setQueryNode("")
q = iq.getQueryNode()
@ -766,7 +767,7 @@ class Protocol(xmlstream.Node):
try: return JID(self.getAttr('to'))
except: return None
def getFrom(self):
"""Returns the 'from' attribute as a JID object."""
try: return JID(self.getAttr('from'))
@ -808,7 +809,7 @@ class Protocol(xmlstream.Node):
def getX(self,index=0):
"""Returns the x namespace, optionally passed an index if there are
multiple tags."""
try: return self.getXNodes('x')[index].namespace
try: return self.getXNodes()[index].namespace
except: return None
@ -829,10 +830,10 @@ class Protocol(xmlstream.Node):
if type(payload) == type('') or type(payload) == type(u''):
payload = xmlstream.NodeBuilder(payload).getDom()
x.kids = [] # should be a method for this realy
x.kids = [] # should be a method for this realy
x.insertNode(payload)
def getXPayload(self, val=None):
"""Returns the x tags' payload as a list of Node instances."""
nodes = []
@ -849,7 +850,7 @@ class Protocol(xmlstream.Node):
nodes.append(xnode.kids[0])
return nodes
def getXNode(self, val=None):
"""Returns the x Node instance. If there are multiple tags
the first Node is returned. For multiple X nodes use getXNodes
@ -904,7 +905,7 @@ class Message(Protocol):
except: return None
def getSubject(self):
def getSubject(self):
"""Returns the message's subject."""
try: return self.getTag('subject').getData()
except: return None
@ -928,7 +929,7 @@ class Message(Protocol):
else:
body = self.insertTag('body').putData(val)
def setSubject(self,val):
"""Sets the message subject text."""
subj = self.getTag('subject')
@ -959,7 +960,7 @@ class Message(Protocol):
automatically set."""
m = Message(to=self.getFrom(), body=reply_txt)
if not self.getType() == None:
m.setType(self.getType())
m.setType(self.getType())
t = self.getThread()
if t: m.setThread(t)
return m
@ -993,7 +994,7 @@ class Presence(Protocol):
"""Returns the presence priority"""
try: return self.getTag('priority').getData()
except: return None
def setShow(self,val):
"""Sets the presence show"""
show = self.getTag('show')
@ -1014,7 +1015,7 @@ class Presence(Protocol):
#############################################################################
class Iq(Protocol):
class Iq(Protocol):
"""Class for creating and managing jabber <iq> protocol
elements"""
def __init__(self, to=None, type=None, query=None, attrs=None, frm=None, payload=[], node=None):
@ -1069,12 +1070,12 @@ class Iq(Protocol):
if not add: q.kids = []
q.insertNode(payload)
def getQueryPayload(self):
"""Returns the query's payload as a Node list"""
q = self.getQueryNode()
if q: return q.kids
def getQueryNode(self):
"""Returns any textual data contained by the query tag"""
try: return self.getTag('query')
@ -1141,7 +1142,7 @@ class Roster:
def getStatus(self, jid): ## extended
"""Returns the 'status' value for a Roster item with the given jid."""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['status']
return None
@ -1149,7 +1150,7 @@ class Roster:
def getShow(self, jid): ## extended
"""Returns the 'show' value for a Roster item with the given jid."""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['show']
return None
@ -1158,16 +1159,16 @@ class Roster:
def getOnline(self,jid): ## extended
"""Returns the 'online' status for a Roster item with the given jid.
"""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['online']
return None
def getSub(self,jid):
"""Returns the 'subscription' status for a Roster item with the given
jid."""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['sub']
return None
@ -1175,7 +1176,7 @@ class Roster:
def getName(self,jid):
"""Returns the 'name' for a Roster item with the given jid."""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['name']
return None
@ -1192,7 +1193,7 @@ class Roster:
def getAsk(self,jid):
"""Returns the 'ask' status for a Roster item with the given jid."""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
return self._data[jid]['ask']
return None
@ -1258,12 +1259,12 @@ class Roster:
def _setOnline(self,jid,val):
"""Used internally - private"""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
self._data[jid]['online'] = val
if self._listener != None:
self._listener("update", jid, {'online' : val})
else: ## fall back
else: ## fall back
jid_basic = JID(jid).getStripped()
if self._data.has_key(jid_basic):
self._data[jid_basic]['online'] = val
@ -1273,12 +1274,12 @@ class Roster:
def _setShow(self,jid,val):
"""Used internally - private"""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
self._data[jid]['show'] = val
self._data[jid]['show'] = val
if self._listener != None:
self._listener("update", jid, {'show' : val})
else: ## fall back
else: ## fall back
jid_basic = JID(jid).getStripped()
if self._data.has_key(jid_basic):
self._data[jid_basic]['show'] = val
@ -1288,12 +1289,12 @@ class Roster:
def _setStatus(self,jid,val):
"""Used internally - private"""
jid = ustr(jid)
jid = ustr(jid)
if self._data.has_key(jid):
self._data[jid]['status'] = val
if self._listener != None:
self._listener("update", jid, {'status' : val})
else: ## fall back
else: ## fall back
jid_basic = JID(jid).getStripped()
if self._data.has_key(jid_basic):
self._data[jid_basic]['status'] = val
@ -1320,12 +1321,12 @@ class JID:
bits = jid.split('@', 1)
self.node = bits[0]
jid = bits[1]
if jid.find('/') == -1:
self.domain = jid
self.resource = ''
else:
self.domain, self.resource = jid.split('/', 1)
self.domain, self.resource = jid.split('/', 1)
else:
self.node = node
self.domain = domain
@ -1342,7 +1343,7 @@ class JID:
def getBasic(self):
"""Returns a jid string with no resource"""
return self.node + '@' + self.domain
return self.node + '@' + self.domain
def getNode(self):
"""Returns JID Node as string"""
@ -1375,7 +1376,7 @@ class JID:
def getStripped(self):
"""Returns a JID string with no resource"""
"""Returns a JID string with no resource"""
if self.node: return self.node + '@' + self.domain
else: return self.domain
@ -1395,7 +1396,7 @@ class JID:
class Component(Connection):
"""docs to come soon... """
def __init__(self, host, port, connection=xmlstream.TCP,
debug=False, log=False, ns=NS_COMP_ACCEPT, hostIP=None, proxy=None):
debug=[], log=False, ns=NS_COMP_ACCEPT, hostIP=None, proxy=None):
Connection.__init__(self, host, port, namespace=ns, debug=debug,
log=log, connection=connection, hostIP=hostIP, proxy=proxy)
self._auth_OK = False
@ -1404,7 +1405,7 @@ class Component(Connection):
def auth(self,secret):
"""will disconnect on failure"""
self.send( u"<handshake id='1'>%s</handshake>"
self.send( u"<handshake id='1'>%s</handshake>"
% sha.new( self.getIncomingID() + secret ).hexdigest()
)
while not self._auth_OK:
@ -1434,7 +1435,7 @@ class Log(Protocol):
## eg: <log type='warn' from='component'>Hello Log File</log>
def __init__(self, attrs=None, type=None, frm=None, to=None, payload=[], node=None):
Protocol.__init__(self, 'log', attrs=attrs, type=type, frm=frm, to=to, payload=payload, node=node)
def setBody(self,val):
"Sets the log message text."
self.getTag('log').putData(val)

View File

@ -1,4 +1,4 @@
## xmlstream.py
## xmlstream.py
##
## Copyright (C) 2001 Matthew Allum
##
@ -28,7 +28,7 @@ case.
"""
# $Id: xmlstream.py,v 1.42 2004/01/08 15:47:40 snakeru Exp $
# $Id$
import time, sys, re, socket
from select import select
@ -37,7 +37,7 @@ import xml.parsers.expat
import debug
_debug=debug
VERSION = "0.5-rc1"
VERSION = "0.5"
False = 0
True = 1
@ -78,7 +78,7 @@ class error:
self.value = str(value)
def __str__(self):
return self.value
class Node:
"""A simple XML DOM like class"""
def __init__(self, tag=None, parent=None, attrs={}, payload=[], node=None):
@ -93,7 +93,7 @@ class Node:
if parent: self.parent = parent
# if self.parent and not self.namespace: self.namespace=self.parent.namespace # Doesn't checked if this neccessary
# if self.parent and not self.namespace: self.namespace=self.parent.namespace # Doesn't checked if this neccessary
for attr in attrs.keys():
self.attrs[attr]=attrs[attr]
@ -101,7 +101,7 @@ class Node:
for i in payload:
if type(i)==type(self): self.insertNode(i)
else: self.insertXML(i)
# self.insertNode(Node(node=i)) # Alternative way. Needs perfomance testing.
# self.insertNode(Node(node=i)) # Alternative way. Needs perfomance testing.
def setParent(self, node):
"Set the nodes parent node."
@ -127,29 +127,29 @@ class Node:
"Get a value for the nodes named attribute."
try: return self.attrs[key]
except: return None
def putData(self, data):
"Set the nodes textual data"
"Set the nodes textual data"
self.data.append(data)
def insertData(self, data):
"Set the nodes textual data"
"Set the nodes textual data"
self.data.append(data)
def getData(self):
"Return the nodes textual data"
"Return the nodes textual data"
return ''.join(self.data)
def getDataAsParts(self):
"Return the node data as an array"
"Return the node data as an array"
return self.data
def getNamespace(self):
"Returns the nodes namespace."
"Returns the nodes namespace."
return self.namespace
def setNamespace(self, namespace):
"Set the nodes namespace."
"Set the nodes namespace."
self.namespace = namespace
def insertTag(self, name=None, attrs={}, payload=[], node=None):
@ -178,7 +178,7 @@ class Node:
def _xmlnode2str(self, parent=None):
"""Returns an xml ( string ) representation of the node
and it children"""
s = "<" + self.name
s = "<" + self.name
if self.namespace:
if parent and parent.namespace != self.namespace:
s = s + " xmlns = '%s' " % self.namespace
@ -186,7 +186,7 @@ class Node:
val = ustr(self.attrs[key])
s = s + " %s='%s'" % ( key, XMLescape(val) )
s = s + ">"
cnt = 0
cnt = 0
if self.kids != None:
for a in self.kids:
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
@ -199,12 +199,13 @@ class Node:
s = s + "</" + self.name + ">"
return s
def getTag(self, name):
def getTag(self, name, index=None):
"""Returns a child node with tag name. Returns None
if not found."""
for node in self.kids:
if node.getName() == name:
return node
if not index: return node
if index is not None: index-=1
return None
def getTags(self, name):
@ -214,7 +215,7 @@ class Node:
if node.getName() == name:
nodes.append(node)
return nodes
def getChildren(self):
"""Returns a nodes children"""
return self.kids
@ -242,7 +243,7 @@ class NodeBuilder:
self.__depth = 0
self._dispatch_depth = 1
if data: self._parser.Parse(data,1)
def unknown_starttag(self, tag, attrs):
@ -293,8 +294,8 @@ class NodeBuilder:
class Stream(NodeBuilder):
"""Extention of NodeBuilder class. Handles stream of XML stanzas.
Calls dispatch method for every child of root node
"""Extention of NodeBuilder class. Handles stream of XML stanzas.
Calls dispatch method for every child of root node
(stream:stream for jabber stream).
attributes _read, _write and _reader must be set by external entity
"""
@ -371,7 +372,7 @@ class Stream(NodeBuilder):
except:
self.DEBUG("xmlstream write threw error",DBG_CONN_ERROR)
self.disconnected(self)
def process(self, timeout=0):
"""Receives incoming data (if any) and processes it.
Waits for data no more than timeout seconds."""
@ -379,7 +380,7 @@ class Stream(NodeBuilder):
data = self.read()
self._parser.Parse(data)
return len(data)
return '0' # Zero means that nothing received but link is alive.
return '0' # Zero means that nothing received but link is alive.
def disconnect(self):
"""Close the stream and socket"""
@ -387,7 +388,7 @@ class Stream(NodeBuilder):
while self.process(): pass
self._sock.close()
self._sock = None
def disconnected(self,conn):
"""Called when a Network Error or disconnection occurs."""
try: self.disconnectHandler(conn)
@ -399,7 +400,7 @@ class Stream(NodeBuilder):
raise error("Standart disconnectionHandler called. Replace it with appropriate for your client.")
def log(self, data, inout=''):
"""Logs data to the specified filehandle. Data is time stamped
"""Logs data to the specified filehandle. Data is time stamped
and prefixed with inout"""
if self._logFH is not None:
if self._timestampLog:
@ -431,7 +432,7 @@ class Client(Stream):
Stream.__init__(self, namespace, debug, log, id)
self._host = host
self._port = port
self._port = port
self._sock = sock
self._connection = connection
if hostIP: self._hostIP = hostIP
@ -528,33 +529,33 @@ class Client(Stream):
self.DEBUG('unknown connection type',DBG_CONN_ERROR)
raise IOError('unknown connection type')
class Server:
class Server:
def now(self): return time.ctime(time.time())
def __init__(self, maxclients=10):
self.host = ''
self.host = ''
self.port = 5222
self.streams = []
# make main sockets for accepting new client requests
self.mainsocks, self.readsocks, self.writesocks = [], [], []
self.portsock = socket(AF_INET, SOCK_STREAM)
self.portsock.bind((self.host, self.port))
self.portsock.listen(maxclients)
self.portsock.bind((self.host, self.port))
self.portsock.listen(maxclients)
self.mainsocks.append(self.portsock) # add to main list to identify
self.readsocks.append(self.portsock) # add to select inputs list
self.readsocks.append(self.portsock) # add to select inputs list
# event loop: listen and multiplex until server process killed
def serve(self):
print 'select-server loop starting'
while 1:
print "LOOPING"
readables, writeables, exceptions = select(self.readsocks,
@ -562,7 +563,7 @@ class Server:
for sockobj in readables:
if sockobj in self. mainsocks: # for ready input sockets
newsock, address = sockobj.accept() # accept not block
print 'Connect:', address, id(newsock)
print 'Connect:', address, id(newsock)
self.readsocks.append(newsock)
self._makeNewStream(newsock)
# add to select list, wait
@ -571,9 +572,9 @@ class Server:
data = sockobj.recv(1024)
# recv should not block
print '\tgot', data, 'on', id(sockobj)
if not data: # if closed by the clients
if not data: # if closed by the clients
sockobj.close() # close here and remv from
self.readsocks.remove(sockobj)
self.readsocks.remove(sockobj)
else:
# this may block: should really select for writes too
sockobj.send('Echo=>%s' % data)
@ -592,7 +593,7 @@ class Server:
for s in self.streams:
socks.append(s.getSocket())
return socks
def _getStreamFromSocket(self, sock):
for s in self.streams:
if s.getSocket() == sock: