bye bye jabber.py [I like it, it rhymes :P]
This commit is contained in:
parent
adccebd4f3
commit
a23ed9704c
|
@ -1,408 +0,0 @@
|
|||
## debug.py
|
||||
##
|
||||
## Copyright (C) 2003 Jacob Lundqvist
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published
|
||||
## by the Free Software Foundation; either version 2, or (at your option)
|
||||
## any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
|
||||
_version_ = '1.4.0'
|
||||
|
||||
"""\
|
||||
|
||||
Generic debug class
|
||||
|
||||
Other modules can always define extra debug flags for local usage, as long as
|
||||
they make sure they append them to debug_flags
|
||||
|
||||
Also its always a good thing to prefix local flags with something, to reduce risk
|
||||
of coliding flags. Nothing breaks if two flags would be identical, but it might
|
||||
activate unintended debugging.
|
||||
|
||||
flags can be numeric, but that makes analysing harder, on creation its
|
||||
not obvious what is activated, and when flag_show is given, output isnt
|
||||
really meaningfull.
|
||||
|
||||
This Debug class can either be initialized and used on app level, or used independantly
|
||||
by the individual classes.
|
||||
|
||||
For samples of usage, see samples subdir in distro source, and selftest
|
||||
in this code
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
from string import join
|
||||
|
||||
import types
|
||||
|
||||
|
||||
debug_flags = []
|
||||
|
||||
color_none = chr(27) + "[0m"
|
||||
color_black = chr(27) + "[30m"
|
||||
color_red = chr(27) + "[31m"
|
||||
color_green = chr(27) + "[32m"
|
||||
color_brown = chr(27) + "[33m"
|
||||
color_blue = chr(27) + "[34m"
|
||||
color_magenta = chr(27) + "[35m"
|
||||
color_cyan = chr(27) + "[36m"
|
||||
color_light_gray = chr(27) + "[37m"
|
||||
color_dark_gray = chr(27) + "[30;1m"
|
||||
color_bright_red = chr(27) + "[31;1m"
|
||||
color_bright_green = chr(27) + "[32;1m"
|
||||
color_yellow = chr(27) + "[33;1m"
|
||||
color_bright_blue = chr(27) + "[34;1m"
|
||||
color_purple = chr(27) + "[35;1m"
|
||||
color_bright_cyan = chr(27) + "[36;1m"
|
||||
color_white = chr(27) + "[37;1m"
|
||||
|
||||
|
||||
"""
|
||||
Define your flags in yor modules like this:
|
||||
|
||||
from debug import *
|
||||
|
||||
DBG_INIT = 'init' ; debug_flags.append( DBG_INIT )
|
||||
DBG_CONNECTION = 'connection' ; debug_flags.append( DBG_CONNECTION )
|
||||
|
||||
The reason for having a double statement wis so we can validate params
|
||||
and catch all undefined debug flags
|
||||
|
||||
This gives us control over all used flags, and makes it easier to allow
|
||||
global debugging in your code, just do something like
|
||||
|
||||
foo = Debug( debug_flags )
|
||||
|
||||
group flags, that is a flag in it self containing multiple flags should be
|
||||
defined without the debug_flags.append() sequence, since the parts are already
|
||||
in the list, also they must of course be defined after the flags they depend on ;)
|
||||
example:
|
||||
|
||||
DBG_MULTI = [ DBG_INIT, DBG_CONNECTION ]
|
||||
|
||||
|
||||
|
||||
NoDebug
|
||||
-------
|
||||
To speed code up, typically for product releases or such
|
||||
use this class instead if you globaly want to disable debugging
|
||||
"""
|
||||
DBG_INIT = 'init' ; debug_flags.append( DBG_INIT )
|
||||
DBG_ALWAYS = 'always' ; debug_flags.append( DBG_ALWAYS )
|
||||
|
||||
|
||||
class NoDebug:
|
||||
def __init__( self, *args, **kwargs ):
|
||||
pass
|
||||
def show( self, *args, **kwargs):
|
||||
pass
|
||||
def is_active( self, flag ):
|
||||
pass
|
||||
def active_set( self, active_flags = None ):
|
||||
return 0
|
||||
|
||||
|
||||
LINE_FEED = '\n'
|
||||
|
||||
|
||||
class Debug:
|
||||
def __init__( self,
|
||||
#
|
||||
# active_flags are those that will trigger output
|
||||
#
|
||||
active_flags = None,
|
||||
#
|
||||
# Log file should be file object or file namne
|
||||
#
|
||||
log_file = sys.stderr,
|
||||
#
|
||||
# prefix and sufix can either be set globaly or per call.
|
||||
# personally I use this to color code debug statements
|
||||
# with prefix = chr(27) + '[34m'
|
||||
# sufix = chr(27) + '[37;1m\n'
|
||||
#
|
||||
prefix = color_red + 'DEBUG: ' + color_white,
|
||||
sufix = '\n',
|
||||
#
|
||||
# If you want unix style timestamps,
|
||||
# 0 disables timestamps
|
||||
# 1 before prefix, good when prefix is a string
|
||||
# 2 after prefix, good when prefix is a color
|
||||
#
|
||||
time_stamp = 2,
|
||||
#
|
||||
# flag_show should normaly be of, but can be turned on to get a
|
||||
# good view of what flags are actually used for calls,
|
||||
# if it is not None, it should be a string
|
||||
# flags for current call will be displayed
|
||||
# with flag_show as separator
|
||||
# recomended values vould be '-' or ':', but any string goes
|
||||
#
|
||||
flag_show = None,
|
||||
#
|
||||
# If you dont want to validate flags on each call to
|
||||
# show(), set this to 0
|
||||
#
|
||||
validate_flags = 1,
|
||||
#
|
||||
# If you dont want the welcome message, set to 0
|
||||
# default is to show welcome if any flags are active
|
||||
welcome = -1,
|
||||
#
|
||||
# Non plain-ascii encodings can benefit from it
|
||||
encoding = None
|
||||
):
|
||||
|
||||
if type(active_flags) not in [type([]), type(())]:
|
||||
print '***'
|
||||
print '*** Invalid or oldformat debug param given: %s' % active_flags
|
||||
print '*** please correct your param, should be of [] type!'
|
||||
print '*** Due to this, full debuging is enabled'
|
||||
active_flags=[DBG_ALWAYS]
|
||||
|
||||
if welcome == -1:
|
||||
if active_flags and len(active_flags):
|
||||
welcome = 1
|
||||
else:
|
||||
welcome = 0
|
||||
|
||||
self._remove_dupe_flags()
|
||||
if log_file:
|
||||
if type( log_file ) is type(''):
|
||||
try:
|
||||
self._fh = open(log_file,'w')
|
||||
except:
|
||||
print 'ERROR: can open %s for writing'
|
||||
sys.exit(0)
|
||||
else: ## assume its a stream type object
|
||||
self._fh = log_file
|
||||
else:
|
||||
self._fh = sys.stdout
|
||||
|
||||
if time_stamp not in (0,1,2):
|
||||
msg2 = '%s' % time_stamp
|
||||
raise 'Invalid time_stamp param', msg2
|
||||
self.prefix = prefix
|
||||
self.sufix = sufix
|
||||
self.time_stamp = time_stamp
|
||||
self.flag_show = None # must be initialised after possible welcome
|
||||
self.validate_flags = validate_flags
|
||||
self.encoding = encoding
|
||||
|
||||
self.active_set( active_flags )
|
||||
if welcome:
|
||||
self.show('')
|
||||
caller = sys._getframe(1) # used to get name of caller
|
||||
try:
|
||||
mod_name= ":%s" % caller.f_locals['__name__']
|
||||
except:
|
||||
mod_name = ""
|
||||
self.show('Debug created for %s%s' % (caller.f_code.co_filename,
|
||||
mod_name ))
|
||||
self.show(' flags defined: %s' % join( self.active ))
|
||||
|
||||
if type(flag_show) in (type(''), type(None)):
|
||||
self.flag_show = flag_show
|
||||
else:
|
||||
msg2 = '%s' % type(flag_show )
|
||||
raise 'Invalid type for flag_show!', msg2
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def show( self, msg, flag = None, prefix = None, sufix = None,
|
||||
lf = 0 ):
|
||||
"""
|
||||
flag can be of folowing types:
|
||||
None - this msg will always be shown if any debugging is on
|
||||
flag - will be shown if flag is active
|
||||
(flag1,flag2,,,) - will be shown if any of the given flags
|
||||
are active
|
||||
|
||||
if prefix / sufix are not given, default ones from init will be used
|
||||
|
||||
lf = -1 means strip linefeed if pressent
|
||||
lf = 1 means add linefeed if not pressent
|
||||
"""
|
||||
|
||||
if self.validate_flags:
|
||||
self._validate_flag( flag )
|
||||
|
||||
if not self.is_active(flag):
|
||||
return
|
||||
if prefix:
|
||||
pre = prefix
|
||||
else:
|
||||
pre = self.prefix
|
||||
if sufix:
|
||||
suf = sufix
|
||||
else:
|
||||
suf = self.sufix
|
||||
|
||||
if self.time_stamp == 2:
|
||||
output = '%s%s ' % ( pre,
|
||||
time.strftime('%b %d %H:%M:%S',
|
||||
time.localtime(time.time() )),
|
||||
)
|
||||
elif self.time_stamp == 1:
|
||||
output = '%s %s' % ( time.strftime('%b %d %H:%M:%S',
|
||||
time.localtime(time.time() )),
|
||||
pre,
|
||||
)
|
||||
else:
|
||||
output = pre
|
||||
|
||||
if self.flag_show:
|
||||
if flag:
|
||||
output = '%s%s%s' % ( output, flag, self.flag_show )
|
||||
else:
|
||||
# this call uses the global default,
|
||||
# dont print "None", just show the separator
|
||||
output = '%s %s' % ( output, self.flag_show )
|
||||
|
||||
if type(msg)==type(u'') and self.encoding:
|
||||
msg=msg.encode(self.encoding, 'replace')
|
||||
|
||||
output = '%s%s%s' % ( output, msg, suf )
|
||||
if lf:
|
||||
# strip/add lf if needed
|
||||
last_char = output[-1]
|
||||
if lf == 1 and last_char != LINE_FEED:
|
||||
output = output + LINE_FEED
|
||||
elif lf == -1 and last_char == LINE_FEED:
|
||||
output = output[:-1]
|
||||
try:
|
||||
self._fh.write( output )
|
||||
except:
|
||||
# unicode strikes again ;)
|
||||
s=u''
|
||||
for i in range(len(output)):
|
||||
if ord(output[i]) < 128:
|
||||
c = output[i]
|
||||
else:
|
||||
c = '?'
|
||||
s=s+c
|
||||
self._fh.write( '%s%s%s' % ( pre, s, suf ))
|
||||
self._fh.flush()
|
||||
|
||||
|
||||
def is_active( self, flag ):
|
||||
'If given flag(s) should generate output.'
|
||||
|
||||
# try to abort early to quicken code
|
||||
if not self.active:
|
||||
return 0
|
||||
if not flag or flag in self.active or DBG_ALWAYS in self.active:
|
||||
return 1
|
||||
else:
|
||||
# check for multi flag type:
|
||||
if type( flag ) in ( type(()), type([]) ):
|
||||
for s in flag:
|
||||
if s in self.active:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def active_set( self, active_flags = None ):
|
||||
"returns 1 if any flags where actually set, otherwise 0."
|
||||
r = 0
|
||||
ok_flags = []
|
||||
if not active_flags:
|
||||
#no debuging at all
|
||||
self.active = []
|
||||
elif type( active_flags ) in ( types.TupleType, types.ListType ):
|
||||
flags = self._as_one_list( active_flags )
|
||||
for t in flags:
|
||||
if t not in debug_flags:
|
||||
print 'Invalid debugflag given', t
|
||||
else:
|
||||
ok_flags.append( t )
|
||||
|
||||
self.active = ok_flags
|
||||
r = 1
|
||||
else:
|
||||
# assume comma string
|
||||
try:
|
||||
flags = active_flags.split(',')
|
||||
except:
|
||||
self.show( '***' )
|
||||
self.show( '*** Invalid debug param given: %s' % active_flags )
|
||||
self.show( '*** please correct your param!' )
|
||||
self.show( '*** due to this, full debuging is enabled' )
|
||||
self.active = debug_flags
|
||||
|
||||
for f in flags:
|
||||
s = f.strip()
|
||||
ok_flags.append( s )
|
||||
self.active = ok_flags
|
||||
|
||||
self._remove_dupe_flags()
|
||||
return r
|
||||
|
||||
def active_get( self ):
|
||||
"returns currently active flags."
|
||||
return self.active
|
||||
|
||||
|
||||
def _as_one_list( self, items ):
|
||||
""" init param might contain nested lists, typically from group flags.
|
||||
|
||||
This code organises lst and remves dupes
|
||||
"""
|
||||
if type( items ) <> type( [] ) and type( items ) <> type( () ):
|
||||
return [ items ]
|
||||
r = []
|
||||
for l in items:
|
||||
if type( l ) == type([]):
|
||||
lst2 = self._as_one_list( l )
|
||||
for l2 in lst2:
|
||||
self._append_unique_str(r, l2 )
|
||||
elif l == None:
|
||||
continue
|
||||
else:
|
||||
self._append_unique_str(r, l )
|
||||
return r
|
||||
|
||||
|
||||
def _append_unique_str( self, lst, item ):
|
||||
"""filter out any dupes."""
|
||||
if type(item) <> type(''):
|
||||
msg2 = '%s' % item
|
||||
raise 'Invalid item type (should be string)',msg2
|
||||
if item not in lst:
|
||||
lst.append( item )
|
||||
return lst
|
||||
|
||||
|
||||
def _validate_flag( self, flags ):
|
||||
'verify that flag is defined.'
|
||||
if flags:
|
||||
for f in self._as_one_list( flags ):
|
||||
if not f in debug_flags:
|
||||
msg2 = '%s' % f
|
||||
raise 'Invalid debugflag given', msg2
|
||||
|
||||
def _remove_dupe_flags( self ):
|
||||
"""
|
||||
if multiple instances of Debug is used in same app,
|
||||
some flags might be created multiple time, filter out dupes
|
||||
"""
|
||||
global debug_flags
|
||||
|
||||
unique_flags = []
|
||||
for f in debug_flags:
|
||||
if f not in unique_flags:
|
||||
unique_flags.append(f)
|
||||
debug_flags = unique_flags
|
||||
|
|
@ -1,603 +0,0 @@
|
|||
## xmlstream.py
|
||||
##
|
||||
## Copyright (C) 2001 Matthew Allum
|
||||
##
|
||||
## This program is free software; you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published
|
||||
## by the Free Software Foundation; either version 2, or (at your option)
|
||||
## any later version.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
|
||||
|
||||
"""\
|
||||
xmlstream.py provides simple functionality for implementing
|
||||
XML stream based network protocols. It is used as a base
|
||||
for jabber.py.
|
||||
|
||||
xmlstream.py manages the network connectivity and xml parsing
|
||||
of the stream. When a complete 'protocol element' ( meaning a
|
||||
complete child of the xmlstreams root ) is parsed the dipatch
|
||||
method is called with a 'Node' instance of this structure.
|
||||
The Node class is a very simple XML DOM like class for
|
||||
manipulating XML documents or 'protocol elements' in this
|
||||
case.
|
||||
|
||||
"""
|
||||
|
||||
# $Id$
|
||||
|
||||
import time, sys, re, socket
|
||||
from select import select
|
||||
from base64 import encodestring
|
||||
import xml.parsers.expat
|
||||
import debug
|
||||
_debug=debug
|
||||
|
||||
VERSION = "0.5"
|
||||
|
||||
False = 0
|
||||
True = 1
|
||||
|
||||
TCP = 1
|
||||
STDIO = 0
|
||||
TCP_SSL = 2
|
||||
|
||||
ENCODING = 'utf-8' # Though it is uncommon, this is the only right setting.
|
||||
ustr = str
|
||||
|
||||
BLOCK_SIZE = 1024 ## Number of bytes to get at at time via socket
|
||||
## transactions
|
||||
|
||||
DBG_INIT, DBG_ALWAYS = debug.DBG_INIT, debug.DBG_ALWAYS
|
||||
DBG_CONN_ERROR = 'conn-error' ; debug.debug_flags.append( DBG_CONN_ERROR )
|
||||
DBG_XML_PARSE = 'xml-parse' ; debug.debug_flags.append( DBG_XML_PARSE )
|
||||
DBG_XML_RAW = 'xml-raw' ; debug.debug_flags.append( DBG_XML_RAW )
|
||||
DBG_XML = [ DBG_XML_PARSE, DBG_XML_RAW ] # sample multiflag
|
||||
|
||||
|
||||
def XMLescape(txt):
|
||||
"Escape XML entities"
|
||||
txt = txt.replace("&", "&")
|
||||
txt = txt.replace("<", "<")
|
||||
txt = txt.replace(">", ">")
|
||||
return txt
|
||||
|
||||
def XMLunescape(txt):
|
||||
"Unescape XML entities"
|
||||
txt = txt.replace(">", ">")
|
||||
txt = txt.replace("<", "<")
|
||||
txt = txt.replace("&", "&")
|
||||
return txt
|
||||
|
||||
class error:
|
||||
def __init__(self, value):
|
||||
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):
|
||||
if node:
|
||||
if type(node)<>type(self): node=NodeBuilder(node).getDom()
|
||||
self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = \
|
||||
node.name,node.namespace,node.attrs,node.data,node.kids,node.parent
|
||||
else:
|
||||
self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = 'tag','',{},[],[],None
|
||||
|
||||
if tag: self.namespace, self.name = (['']+tag.split())[-2:]
|
||||
|
||||
if parent: self.parent = parent
|
||||
|
||||
# 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]
|
||||
|
||||
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.
|
||||
|
||||
def setParent(self, node):
|
||||
"Set the nodes parent node."
|
||||
self.parent = node
|
||||
|
||||
def getParent(self):
|
||||
"return the nodes parent node."
|
||||
return self.parent
|
||||
|
||||
def getName(self):
|
||||
"Set the nodes tag name."
|
||||
return self.name
|
||||
|
||||
def setName(self,val):
|
||||
"Set the nodes tag name."
|
||||
self.name = val
|
||||
|
||||
def putAttr(self, key, val):
|
||||
"Add a name/value attribute to the node."
|
||||
self.attrs[key] = val
|
||||
|
||||
def getAttr(self, key):
|
||||
"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"
|
||||
self.data.append(data)
|
||||
|
||||
def insertData(self, data):
|
||||
"Set the nodes textual data"
|
||||
self.data.append(data)
|
||||
|
||||
def getData(self):
|
||||
"Return the nodes textual data"
|
||||
return ''.join(self.data)
|
||||
|
||||
def getDataAsParts(self):
|
||||
"Return the node data as an array"
|
||||
return self.data
|
||||
|
||||
def getNamespace(self):
|
||||
"Returns the nodes namespace."
|
||||
return self.namespace
|
||||
|
||||
def setNamespace(self, namespace):
|
||||
"Set the nodes namespace."
|
||||
self.namespace = namespace
|
||||
|
||||
def insertTag(self, name=None, attrs={}, payload=[], node=None):
|
||||
""" Add a child tag of name 'name' to the node.
|
||||
|
||||
Returns the newly created node.
|
||||
"""
|
||||
newnode = Node(tag=name, parent=self, attrs=attrs, payload=payload, node=node)
|
||||
self.kids.append(newnode)
|
||||
return newnode
|
||||
|
||||
def insertNode(self, node):
|
||||
"Add a child node to the node"
|
||||
self.kids.append(node)
|
||||
return node
|
||||
|
||||
def insertXML(self, xml_str):
|
||||
"Add raw xml as a child of the node"
|
||||
newnode = NodeBuilder(xml_str).getDom()
|
||||
self.kids.append(newnode)
|
||||
return newnode
|
||||
|
||||
def __str__(self):
|
||||
return self._xmlnode2str()
|
||||
|
||||
def _xmlnode2str(self, parent=None):
|
||||
"""Returns an xml ( string ) representation of the node
|
||||
and it children"""
|
||||
s = "<" + self.name
|
||||
if self.namespace:
|
||||
if parent and parent.namespace != self.namespace:
|
||||
s = s + " xmlns = '%s' " % self.namespace
|
||||
for key in self.attrs.keys():
|
||||
val = ustr(self.attrs[key])
|
||||
s = s + " %s='%s'" % ( key, XMLescape(val) )
|
||||
s = s + ">"
|
||||
cnt = 0
|
||||
if self.kids != None:
|
||||
for a in self.kids:
|
||||
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
|
||||
s = s + a._xmlnode2str(parent=self)
|
||||
cnt=cnt+1
|
||||
if (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt])
|
||||
if not self.kids and s[-1:]=='>':
|
||||
s=s[:-1]+' />'
|
||||
else:
|
||||
s = s + "</" + self.name + ">"
|
||||
return s
|
||||
|
||||
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:
|
||||
if not index: return node
|
||||
if index is not None: index-=1
|
||||
return None
|
||||
|
||||
def getTags(self, name):
|
||||
"""Like getTag but returns a list with matching child nodes"""
|
||||
nodes=[]
|
||||
for node in self.kids:
|
||||
if node.getName() == name:
|
||||
nodes.append(node)
|
||||
return nodes
|
||||
|
||||
def getChildren(self):
|
||||
"""Returns a nodes children"""
|
||||
return self.kids
|
||||
|
||||
def removeTag(self,tag):
|
||||
"""Pops out specified child and returns it."""
|
||||
if type(tag)==type(self):
|
||||
try:
|
||||
self.kids.remove(tag)
|
||||
return tag
|
||||
except: return None
|
||||
for node in self.kids:
|
||||
if node.getName()==tag:
|
||||
self.kids.remove(node)
|
||||
return node
|
||||
|
||||
class NodeBuilder:
|
||||
"""builds a 'minidom' from data parsed to it. Primarily for insertXML
|
||||
method of Node"""
|
||||
def __init__(self,data=None):
|
||||
self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ')
|
||||
self._parser.StartElementHandler = self.unknown_starttag
|
||||
self._parser.EndElementHandler = self.unknown_endtag
|
||||
self._parser.CharacterDataHandler = self.handle_data
|
||||
|
||||
self.__depth = 0
|
||||
self._dispatch_depth = 1
|
||||
|
||||
if data: self._parser.Parse(data,1)
|
||||
|
||||
def unknown_starttag(self, tag, attrs):
|
||||
"""XML Parser callback"""
|
||||
self.__depth = self.__depth + 1
|
||||
self.DEBUG("DEPTH -> %i , tag -> %s, attrs -> %s" % \
|
||||
(self.__depth, tag, str(attrs)),DBG_XML_PARSE )
|
||||
if self.__depth == self._dispatch_depth:
|
||||
self._mini_dom = Node(tag=tag, attrs=attrs)
|
||||
self._ptr = self._mini_dom
|
||||
elif self.__depth > self._dispatch_depth:
|
||||
self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs))
|
||||
self._ptr = self._ptr.kids[-1]
|
||||
else: ## it the stream tag:
|
||||
if attrs.has_key('id'):
|
||||
self._incomingID = attrs['id']
|
||||
self.last_is_data = False
|
||||
|
||||
def unknown_endtag(self, tag ):
|
||||
"""XML Parser callback"""
|
||||
self.DEBUG("DEPTH -> %i" % self.__depth,DBG_XML_PARSE)
|
||||
if self.__depth == self._dispatch_depth:
|
||||
self.dispatch(self._mini_dom)
|
||||
elif self.__depth > self._dispatch_depth:
|
||||
self._ptr = self._ptr.parent
|
||||
else:
|
||||
self.DEBUG("*** Stream terminated ? ****",DBG_CONN_ERROR)
|
||||
self.__depth = self.__depth - 1
|
||||
self.last_is_data = False
|
||||
|
||||
def handle_data(self, data):
|
||||
"""XML Parser callback"""
|
||||
self.DEBUG("data-> " + data,DBG_XML_PARSE)
|
||||
if self.last_is_data:
|
||||
self._ptr.data[-1] += data
|
||||
else:
|
||||
self._ptr.data.append(data)
|
||||
self.last_is_data = True
|
||||
|
||||
def dispatch(self,dom):
|
||||
pass
|
||||
|
||||
def DEBUG(self,dup1,dup2=None):
|
||||
pass
|
||||
|
||||
def getDom(self):
|
||||
return self._mini_dom
|
||||
|
||||
|
||||
class Stream(NodeBuilder):
|
||||
"""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
|
||||
"""
|
||||
def __init__(self, namespace,
|
||||
debug=[DBG_ALWAYS],
|
||||
log=None,
|
||||
id=None,
|
||||
timestampLog=True):
|
||||
|
||||
self._namespace = namespace
|
||||
|
||||
self._read , self._reader , self._write = None , None , None
|
||||
|
||||
self._incomingID = None
|
||||
self._outgoingID = id
|
||||
|
||||
self._debug = _debug.Debug(debug,encoding=ENCODING)
|
||||
self.DEBUG = self._debug.show # makes it backwards compatible with v0.4 code
|
||||
|
||||
self.DEBUG("stream init called",DBG_INIT)
|
||||
|
||||
if log:
|
||||
if type(log) is type(""):
|
||||
try:
|
||||
self._logFH = open(log,'w')
|
||||
except:
|
||||
print "ERROR: can open %s for writing" % log
|
||||
sys.exit(0)
|
||||
else: ## assume its a stream type object
|
||||
self._logFH = log
|
||||
else:
|
||||
self._logFH = None
|
||||
self._timestampLog = timestampLog
|
||||
|
||||
def connect(self):
|
||||
NodeBuilder.__init__(self)
|
||||
self._dispatch_depth = 2
|
||||
|
||||
def timestampLog(self,timestamp):
|
||||
""" Enable or disable the showing of a timestamp in the log.
|
||||
By default, timestamping is enabled.
|
||||
"""
|
||||
self._timestampLog = timestamp
|
||||
|
||||
def read(self):
|
||||
"""Reads incoming data. Blocks until done. Calls self.disconnected(self) if appropriate."""
|
||||
try: received = self._read(BLOCK_SIZE)
|
||||
except: received = ''
|
||||
|
||||
while select([self._reader],[],[],0)[0]:
|
||||
add = self._read(BLOCK_SIZE)
|
||||
received +=add
|
||||
if not add: break
|
||||
|
||||
if len(received): # length of 0 means disconnect
|
||||
self.DEBUG("got data " + received , DBG_XML_RAW )
|
||||
self.log(received, 'RECV:')
|
||||
else: self.disconnected(self)
|
||||
return received
|
||||
|
||||
def write(self,raw_data):
|
||||
"""Writes raw outgoing data. Blocks until done.
|
||||
If supplied data is not unicode string, ENCODING
|
||||
is used for convertion. Avoid this!
|
||||
Always send your data as a unicode string."""
|
||||
if type(raw_data) == type(''):
|
||||
self.DEBUG('Non-utf-8 string "%s" passed to Stream.write! Treating it as %s encoded.'%(raw_data,ENCODING))
|
||||
raw_data = unicode(raw_data,ENCODING)
|
||||
data_out = raw_data.encode('utf-8')
|
||||
try:
|
||||
self._write(data_out)
|
||||
self.log(data_out, 'SENT:')
|
||||
self.DEBUG("sent %s" % data_out,DBG_XML_RAW)
|
||||
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."""
|
||||
if select([self._reader],[],[],timeout)[0]:
|
||||
data = self.read()
|
||||
self._parser.Parse(data)
|
||||
return len(data)
|
||||
return '0' # Zero means that nothing received but link is alive.
|
||||
|
||||
def disconnect(self):
|
||||
"""Close the stream and socket"""
|
||||
self.write ( u"</stream:stream>" )
|
||||
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)
|
||||
except TypeError: self.disconnectHandler()
|
||||
|
||||
def disconnectHandler(self,conn): ## To be overidden ##
|
||||
"""Called when a Network Error or disconnection occurs.
|
||||
Designed to be overidden"""
|
||||
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
|
||||
and prefixed with inout"""
|
||||
if self._logFH is not None:
|
||||
if self._timestampLog:
|
||||
self._logFH.write("%s - %s - %s\n" % (time.asctime(), inout, data))
|
||||
else:
|
||||
self._logFH.write("%s - %s\n" % (inout, data ) )
|
||||
self._logFH.flush()
|
||||
|
||||
def getIncomingID(self):
|
||||
"""Returns the streams ID"""
|
||||
return self._incomingID
|
||||
|
||||
def getOutgoingID(self):
|
||||
"""Returns the streams ID"""
|
||||
return self._incomingID
|
||||
|
||||
|
||||
class Client(Stream):
|
||||
|
||||
def __init__(self, host, port, namespace,
|
||||
debug=[DBG_ALWAYS],
|
||||
log=None,
|
||||
sock=None,
|
||||
id=None,
|
||||
connection=TCP,
|
||||
hostIP=None,
|
||||
proxy=None):
|
||||
|
||||
Stream.__init__(self, namespace, debug, log, id)
|
||||
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._sock = sock
|
||||
self._connection = connection
|
||||
if hostIP: self._hostIP = hostIP
|
||||
else: self._hostIP = host
|
||||
self._proxy = proxy
|
||||
|
||||
self._sslObj = None
|
||||
self._sslIssuer = None
|
||||
self._sslServer = None
|
||||
|
||||
def getSocket(self):
|
||||
return self._sock
|
||||
|
||||
def connect(self):
|
||||
"""Attempt to connect to specified host"""
|
||||
|
||||
self.DEBUG("client connect called to %s %s type %i" % (self._host,
|
||||
self._port,
|
||||
self._connection), DBG_INIT )
|
||||
Stream.connect(self)
|
||||
|
||||
## TODO: check below that stdin/stdout are actually open
|
||||
if self._connection == STDIO:
|
||||
self._setupComms()
|
||||
return
|
||||
|
||||
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._sock.settimeout(10)
|
||||
try:
|
||||
if self._proxy: self._sock.connect((self._proxy['host'], self._proxy['port']))
|
||||
else: self._sock.connect((self._hostIP, self._port))
|
||||
except socket.error, e:
|
||||
self.DEBUG("socket error: "+str(e),DBG_CONN_ERROR)
|
||||
raise
|
||||
|
||||
if self._connection == TCP_SSL:
|
||||
try:
|
||||
self.DEBUG("Attempting to create ssl socket",DBG_INIT)
|
||||
self._sslObj = socket.ssl( self._sock, None, None )
|
||||
self._sslIssuer = self._sslObj.issuer()
|
||||
self._sslServer = self._sslObj.server()
|
||||
except:
|
||||
self.DEBUG("Socket Error: No SSL Support",DBG_CONN_ERROR)
|
||||
raise
|
||||
|
||||
self._setupComms()
|
||||
|
||||
if self._proxy:
|
||||
self.DEBUG("Proxy connected",DBG_INIT)
|
||||
if self._proxy.has_key('type'): type = self._proxy['type'].upper()
|
||||
else: type = 'CONNECT'
|
||||
connector = []
|
||||
if type == 'CONNECT':
|
||||
connector.append(u'CONNECT %s:%s HTTP/1.0'%(self._hostIP,self._port))
|
||||
elif type == 'PUT':
|
||||
connector.append(u'PUT http://%s:%s/ HTTP/1.0'%(self._hostIP,self._port))
|
||||
else:
|
||||
self.DEBUG("Proxy Error: unknown proxy type",DBG_CONN_ERROR)
|
||||
raise error('Unknown proxy type: '+type)
|
||||
connector.append('Proxy-Connection: Keep-Alive')
|
||||
connector.append('Pragma: no-cache')
|
||||
connector.append('Host: %s:%s'%(self._hostIP,self._port))
|
||||
connector.append('User-Agent: Jabberpy/'+VERSION)
|
||||
if self._proxy.has_key('user') and self._proxy.has_key('password'):
|
||||
credentials = '%s:%s'%(self._proxy['user'],self._proxy['password'])
|
||||
credentials = encodestring(credentials).strip()
|
||||
connector.append('Proxy-Authorization: Basic '+credentials)
|
||||
connector.append('\r\n')
|
||||
bak = self._read , self._write
|
||||
self.write('\r\n'.join(connector))
|
||||
reply = self.read().replace('\r','')
|
||||
self._read , self._write = bak
|
||||
try: proto,code,desc=reply.split('\n')[0].split(' ',2)
|
||||
except: raise error('Invalid proxy reply')
|
||||
if code<>'200': raise error('Invalid proxy reply: %s %s %s'%(proto,code,desc))
|
||||
while reply.find('\n\n') == -1: reply += self.read().replace('\r','')
|
||||
|
||||
self.DEBUG("Jabber server connected",DBG_INIT)
|
||||
self.header()
|
||||
|
||||
def _setupComms(self):
|
||||
if self._connection == TCP:
|
||||
self._read = self._sock.recv
|
||||
self._write = self._sock.sendall
|
||||
self._reader = self._sock
|
||||
elif self._connection == TCP_SSL:
|
||||
self._read = self._sslObj.read
|
||||
self._write = self._sslObj.write
|
||||
self._reader = self._sock
|
||||
elif self._connection == STDIO:
|
||||
self._read = self.stdin.read
|
||||
self._write = self.stdout.write
|
||||
self._reader = sys.stdin
|
||||
else:
|
||||
self.DEBUG('unknown connection type',DBG_CONN_ERROR)
|
||||
raise IOError('unknown connection type')
|
||||
|
||||
class Server:
|
||||
|
||||
def now(self): return time.ctime(time.time())
|
||||
|
||||
def __init__(self, maxclients=10):
|
||||
|
||||
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.mainsocks.append(self.portsock) # add to main list to identify
|
||||
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,
|
||||
self.writesocks, [])
|
||||
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)
|
||||
self.readsocks.append(newsock)
|
||||
self._makeNewStream(newsock)
|
||||
# add to select list, wait
|
||||
else:
|
||||
# client socket: read next line
|
||||
data = sockobj.recv(1024)
|
||||
# recv should not block
|
||||
print '\tgot', data, 'on', id(sockobj)
|
||||
if not data: # if closed by the clients
|
||||
sockobj.close() # close here and remv from
|
||||
self.readsocks.remove(sockobj)
|
||||
else:
|
||||
# this may block: should really select for writes too
|
||||
sockobj.send('Echo=>%s' % data)
|
||||
|
||||
def _makeNewStream(self, sckt):
|
||||
new_stream = Stream('localhost', 5222,
|
||||
'jabber:client',
|
||||
sock=sckt)
|
||||
self.streams.append(new_stream)
|
||||
## maybe overide for a 'server stream'
|
||||
new_stream.header()
|
||||
return new_stream
|
||||
|
||||
def _getStreamSockets(self):
|
||||
socks = [];
|
||||
for s in self.streams:
|
||||
socks.append(s.getSocket())
|
||||
return socks
|
||||
|
||||
def _getStreamFromSocket(self, sock):
|
||||
for s in self.streams:
|
||||
if s.getSocket() == sock:
|
||||
return s
|
||||
return None
|
||||
|
Loading…
Reference in New Issue