pubsubclient: 627c06f737e51a059036b7bf2c941c2218a9f8ee

     1: #    PubSubClient, a Python library for interacting with XMPP PubSub
     2: #    Copyright (C) 2008  Chris Warburton
     3: #
     4: #    This program is free software: you can redistribute it and/or modify
     5: #    it under the terms of the GNU Affero General Public License as
     6: #    published by the Free Software Foundation, either version 3 of the
     7: #    License, or (at your option) any later version.
     8: #
     9: #    This program is distributed in the hope that it will be useful,
    10: #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    11: #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12: #    GNU Affero General Public License for more details.
    13: #
    14: #    You should have received a copy of the GNU Affero General Public License
    15: #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16: 
    17: ### WARNING: This library is still early in development. Expected changes
    18: ### include:
    19: ### * Consistent naming of PubSubClient methods
    20: ### * Any usage of a server URL should accept a string AND a Server
    21: ### * Combine some of PubSubClient's methods into fewer, more generic ones
    22: ### * Make some of PubSubClient's methods private after API stabilises
    23: ### * Possible new objects: Entity? Affiliate? Option?
    24: 
    25: import xmpp
    26: import string
    27: from random import Random
    28: from StringIO import StringIO
    29: from lxml.etree import ElementTree, Element, SubElement
    30: import lxml.etree as etree
    31: 
    32: class PubSubClient(object):
    33: 
    34: 	def __init__(self, jid, password, resource='subscriptions'):
    35: 		"""Creates a new PubSubClient. jid can be a string or a JID,
    36: 		password and resource are strings."""
    37: 		# Store all of the information we need to connect to Jabber
    38: 		# The user and server can be deduced from the JabberID
    39: 		# FIXME: This is probably really insecure password storage?
    40: 		self.password = password
    41: 		self.resource = resource
    42: 		if type(jid) == type("string"):
    43: 			self.jid = JID(jid)
    44: 		elif type(jid) == type(JID()):
    45: 			self.jid = jid
    46: 		self.user = self.jid.getNode()
    47: 		self.server = self.jid.getDomain()
    48: 		self.connection = xmpp.Client(self.server,debug=[])
    49: 
    50: 		# self.pending uses the stanza id of any requests which are
    51: 		# awaiting a response as keys, with assigned values of the
    52: 		# predefined functions which should handle these responses.
    53: 		# IMPORTANT: The functions are stored in a list since functions
    54: 		# cannot be directly stored in dictionaries. Each list contains
    55: 		# just one function, ie. {'id1':[function1], 'id2':[function2]}
    56: 		# To call a function use self.pending[id][0](stanza)
    57: 		self.pending = {}
    58: 
    59: 		# self.callbacks works in a similar way to self.pending, except
    60: 		# that it maps stanza ids to functions given by the programmer
    61: 		# using the library. These functions are passed to the
    62: 		# appropriate handler function from self.pending, and are
    63: 		# executed by those functions when they have finished
    64: 		# processing the replies
    65: 		self.callbacks = {}
    66: 
    67: 		# This stores the stanza ids used for this session, since ids
    68: 		# must be unique for their session
    69: 		self.used_ids = []
    70: 
    71: 		# Assign a default printing handler for messages
    72: 		self.assign_message_handler(self.default_handler)
    73: 
    74: 	def default_handler(self, message):
    75: 		print etree.tostring(message)
    76: 
    77: 	def connect(self):
    78: 		"""Turn on the connection. Returns 1 for error, 0 for success."""
    79: 		# First try connecting to the server
    80: 		connection_result = self.connection.connect()
    81: 		if not connection_result:
    82: 			print "Could not connect to server " + str(self.server)
    83: 			return 1
    84: 		if connection_result != 'tls':
    85: 			print "Warning: Could not use TLS"
    86: 
    87: 		# Then try to log in
    88: 		authorisation_result = self.connection.auth(self.user, \
    89: 													self.password, \
    90: 													self.resource)
    91: 		if not authorisation_result:
    92: 			print "Could not get authorized. Username/password problem?"
    93: 			return 1
    94: 		if authorisation_result != 'sasl':
    95: 			print "Warning: Could not use SASL"
    96: 
    97: 		# Here we specify which methods to run to process messages and
    98: 		# queries
    99: 		self.connection.RegisterHandler('message', self.message_handler)
   100: 		self.connection.RegisterHandler('iq', self.iq_handler)
   101: 
   102: 		# Tell the network we are online but don't ask for the roster
   103: 		self.connection.sendInitPresence(1)
   104: 
   105: 		print "Connected."
   106: 		return 0
   107: 
   108: 	def process(self):
   109: 		"""This looks for any new messages and passes those it finds to
   110: 		the assigned handling method."""
   111: 		self.connection.Process()
   112: 
   113: 	def get_jid(self, jid=None, use=False):
   114: 		"""This is a convenience method which returns the given JID if
   115: 		it is not None, or else returns the object's assigned JID."""
   116: 		if jid == None or use == False:
   117: 			return str(self.jid)
   118: 		else:
   119: 			return str(jid)
   120: 
   121: 	def assign_message_handler(self, handler_function):
   122: 		"""This causes the function handler_function to run whenever a
   123: 		message of type "message" is received."""
   124: 		self.message_handler_function = handler_function
   125: 
   126: 	def message_handler(self, connection, message):
   127: 		"""Passes incoming stanzas of type "message" to the function
   128: 		assigned to handle messages."""
   129: 		## FIXME: Doesn't do anything at the moment
   130: 		print message.__str__(1)
   131: 		message = ElementTree(file=StringIO(message))
   132: 		message_root = message.getroot()
   133: 		self.message_handler_function(message_root)
   134: 
   135: 	def iq_handler(self, connection, iq):
   136: 		"""Looks at every incoming Jabber iq stanza and handles them."""
   137: 		# This creates an XML object out of the stanza, making it more
   138: 		# manageable
   139: 		stanza = ElementTree(file=StringIO(iq))
   140: 		# Gets the top-level XML element of the stanza (the 'iq' one)
   141: 		stanza_root = stanza.getroot()
   142: 
   143: 		# See if there is a stanza id and if so whether it is in the
   144: 		# dictionary of stanzas awaiting replies.
   145: 		if 'id' in stanza_root.attrib.keys() and stanza_root.get('id') in self.pending.keys():
   146: 			# This stanza must be a reply, therefore run the function
   147: 			# which is assigned to handle it
   148: 			self.pending[stanza_root.get('id')][0](stanza_root, self.callbacks[stanza_root.get('id')][0])
   149: 			# These won't be run again, so collect the garbage
   150: 			del(self.pending[stanza_root.get('id')])
   151: 			del(self.callbacks[stanza_root.get('id')])
   152: 
   153: 	def send(self, stanza, reply_handler=None, callback=None):
   154: 		"""Sends the given stanza through the connection, giving it a
   155: 		random stanza id if it doesn't have one or if the current one
   156: 		is not unique. Also assigns the optional functions
   157: 		'reply_handler' and 'callback' to handle replies to this stanza."""
   158: 		# Get the id of this stanza if it has one,
   159: 		# or else make a new random one
   160: 		if 'id' in stanza.attrib.keys() and stanza.get('id') not in self.used_ids:
   161: 			id = stanza.get('id')
   162: 		else:
   163: 			# Make a random ID which is not already used
   164: 			while True:
   165: 				id = ''.join(Random().sample(string.digits+string.ascii_letters, 8))
   166: 				if id not in self.used_ids: break
   167: 			stanza.set('id', id)
   168: 		self.used_ids.append(id)
   169: 		self.pending[id] = [reply_handler]
   170: 		self.callbacks[id] = [callback]
   171: 		self.connection.send(etree.tostring(stanza))
   172: 
   173: 	def get_features(self, server, return_function=None, stanza_id=None):		#FIXME IDENTITY NOT HANDLED
   174: 		"""Queries server (string or Server) for the XMPP features it
   175: 		supports."""
   176: 		# This is the kind of XML we want to send
   177: 		#<iq type='get' from='us' to='them'>
   178: 		#  <query xmlns='http://jabber.org/protocol/disco#info' />
   179: 		#</iq>
   180: 
   181: 		# Make it as XML
   182: 		contents = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   183: 		contents.append(Element('query', attrib={'xmlns':'http://jabber.org/protocol/disco#info'}))
   184: 
   185: 		# This function is run when any replies are received with the
   186: 		# same stanza id as a get_features stanza. The
   187: 		# stanza argument is the reply stanza which has been received
   188: 		def handler(stanza, to_run):
   189: 			## FIXME: This handles <feature> but not <identity>
   190: 			if to_run is not None:
   191: 				# See if the server is not in our server_properties tree
   192: 				reply = Element('reply', attrib={'id':stanza.get('id')})
   193: 				# If this is an error report then say so
   194: 				if stanza.attrib.get('type') == 'error':
   195: 					error = SubElement(reply, 'error')
   196: 				# If this is a successful reply then handle it
   197: 				elif stanza.attrib.get('type') == 'result':
   198: 					identities = []
   199: 					features = []
   200: 					for query in stanza.xpath(".//{http://jabber.org/protocol/disco#info}query"):
   201: 						for identity in query.xpath("{http://jabber.org/protocol/disco#info}identity"):
   202: 							# Handle identity children
   203: 							## FIXME: Doesn't do anything yet
   204: 							pass
   205: 						for feature in query.xpath("{http://jabber.org/protocol/disco#info}feature"):
   206: 							# Handle feature children, adding features to
   207: 							# the server's entry in server_properties
   208: 							features.append(feature.get('var'))
   209: 					to_run(reply)
   210: 
   211: 		# Send the message and set the handler function above to deal with the reply
   212: 		self.send(contents, handler, return_function)
   213: 
   214: 	def get_nodes(self, server, node, return_function=None, stanza_id=None):
   215: 		"""Queries server (string or Server) for the top-level nodes it
   216: 		contains. If node is a string or Node then its child nodes are
   217: 		requested instead.
   218: 
   219: 		Upon reply, return_function is called with a list of Nodes which
   220: 		were returned."""
   221: 		# This is the kind of XML we want to send
   222: 		# <iq type='get' from='us' to='them'>
   223: 		#  <query xmlns='http://jabber.org/protocol/disco#items'/>
   224: 		#</iq>
   225: 
   226: 		# Make it as XML elements
   227: 		contents = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   228: 		query = SubElement(contents, 'query', attrib={'xmlns':'http://jabber.org/protocol/disco#items'})
   229: 		if node is not None:
   230: 			query.set('node', node.name)
   231: 
   232: 		# This is run on any replies that are received (identified by
   233: 		# their stanza id)
   234: 		def handler(stanza, callback):
   235: 			#<iq type='result'
   236: 			#    from='pubsub.shakespeare.lit'
   237: 			#    to='francisco@denmark.lit/barracks'
   238: 			#    id='nodes2'>
   239: 			#  <query xmlns='http://jabber.org/protocol/disco#items'
   240: 			#         node='blogs'>
   241: 			#    <item jid='pubsub.shakespeare.lit'
   242: 			#          node='princely_musings'/>
   243: 			#    <item jid='pubsub.shakespeare.lit'
   244: 			#          node='kingly_ravings'/>
   245: 			#    <item jid='pubsub.shakespeare.lit'
   246: 			#          node='starcrossed_stories'/>
   247: 			#    <item jid='pubsub.shakespeare.lit'
   248: 			#          node='moorish_meanderings'/>
   249: 			#  </query>
   250: 			#</iq>
   251: 			if callback is not None:
   252: 				#reply = Element('reply')
   253: 				reply = []
   254: 				if stanza.attrib.get('type') == 'error':
   255: 					# FIXME: Make this handle errors in a meaningful way
   256: 					#error = SubElement(reply, 'error')
   257: 					print "Error"
   258: 					callback("error")
   259: 				elif stanza.attrib.get('type') == 'result':
   260: 					# This is run if the request has been successful
   261: 					if stanza.find('.//{http://jabber.org/protocol/disco#items}query').get('node') is not None:
   262: 						node_parent = Node(name=stanza.find('.//{http://jabber.org/protocol/disco#items}query').get('node'), server=Server(name=stanza.get('from')))
   263: 					else:
   264: 						node_parent = Server(name=stanza.get('from'))
   265: 					# Go through all of the 'item' elements in the stanza
   266: 					for item in stanza.findall('.//{http://jabber.org/protocol/disco#items}item'):
   267: 						reply.append(Node(name=item.get('node'), jid=item.get('jid'), server=Server(name=stanza.get('from')), parent=node_parent))
   268: 				callback(reply)
   269: 
   270: 		self.send(contents, handler, return_function)
   271: 
   272: 	def get_node_information(self, server, node, return_function=None, stanza_id=None):		#FIXME NEEDS MOAR INFO
   273: 		"""Queries node (string or Node) on server (string or Server)
   274: 		for its metadata."""
   275: 		#<iq type='get' from='us' to='server'>
   276: 		#  <query xmlns='http://jabber.org/protocol/disco#info' node='node_name'/>
   277: 		#</iq>
   278: 
   279: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   280: 		stanza.append(Element('query', attrib={'xmlns':'http://jabber.org/protocol/disco#info', 'node':str(node)}))
   281: 
   282: 		def handler(stanza, callback):
   283: 			#print etree.tostring(stanza)
   284: 			## FIXME: Much more information available
   285: 			if callback is not None:
   286: 				#<iq type='result'
   287: 				#    from='pubsub.shakespeare.lit'
   288: 				#    to='francisco@denmark.lit/barracks'
   289: 				#    id='meta1'>
   290: 				#  <query xmlns='http://jabber.org/protocol/disco#info'
   291: 				#         node='blogs'>
   292: 				#    ...
   293: 				#    <identity category='pubsub' type='collection'/>
   294: 				#    ...
   295: 				#  </query>
   296: 				#</iq>
   297: 				if stanza.get('type') == 'result':
   298: 					node = Node(server=stanza.get('from'), name=stanza.find('{http://jabber.org/protocol/disco#info}query').get('node'))
   299: 					#for element in stanza.xpath("//query"):
   300: 					for element in stanza.find("{http://jabber.org/protocol/disco#info}query"):
   301: 						try:
   302: 							if element.get('type') == 'collection':
   303: 								node.set_type('collection')
   304: 							elif element.get('type') == 'leaf':
   305: 								node.set_type('leaf')
   306: 						except:
   307: 							pass
   308: 					callback(node)
   309: 				#etree.tostring()
   310: 
   311: 		self.send(stanza, handler, return_function)
   312: 
   313: 	def get_items(self, server, node, return_function=None, stanza_id=None):
   314: 		"""Requests the items of node (string or Node) on server (string
   315: 		or Server)."""
   316: 		#<iq type='get'
   317: 		#    from='jid'
   318: 		#    to='server'>
   319: 		#  <query xmlns='http://jabber.org/protocol/disco#items'
   320: 		#         node='node_name'/>
   321: 		#</iq>
   322: 
   323: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   324: 		query = SubElement(stanza, 'query', attrib={'xmlns':'http://jabber.org/protocol/disco#items', 'node':str(node)})
   325: 
   326: 		def handler(stanza, callback):
   327: 			#<iq type='result'
   328: 			#    from='pubsub.shakespeare.lit'
   329: 			#    to='francisco@denmark.lit/barracks'
   330: 			#    id='items1'>
   331: 			#  <query xmlns='http://jabber.org/protocol/disco#items'
   332: 			#         node='princely_musings'>
   333: 			#    <item jid='pubsub.shakespeare.lit' name='368866411b877c30064a5f62b917cffe'/>
   334: 			#    <item jid='pubsub.shakespeare.lit' name='3300659945416e274474e469a1f0154c'/>
   335: 			#    <item jid='pubsub.shakespeare.lit' name='4e30f35051b7b8b42abe083742187228'/>
   336: 			#    <item jid='pubsub.shakespeare.lit' name='ae890ac52d0df67ed7cfdf51b644e901'/>
   337: 			#  </query>
   338: 			#</iq>
   339: 			if callback is not None:
   340: 				if stanza.get('type') == 'error':
   341: 					items = False
   342: 				elif stanza.get('type') == 'result':
   343: 					# Make an empty list to store discovered items in
   344: 					items = []
   345: 					# Find every 'item' element
   346: 					for item in stanza.findall('.//{http://jabber.org/protocol/disco#items}item'):
   347: 						# Add new Items to the items list for each
   348: 						items.append(Item(jid=item.get('jid'), name=item.get('name')))
   349: 				# Give the items found to the callback function
   350: 				callback(items)
   351: 
   352: 		self.send(stanza, handler, return_function)
   353: 
   354: 	def get_subscriptions(self, server, node, return_function=None, stanza_id=None):
   355: 		"""Redundant."""
   356: 		#<iq type='get' from='us' to='them'>
   357: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   358: 		#    <subscriptions/>
   359: 		#  </pubsub>
   360: 		#</iq>
   361: 		self.retrieve_subscriptions(server, node, return_function, stanza_id)
   362: 
   363: 	def get_affiliations(self, server, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   364: 		"""Requests all afilliations on server (string or Server)."""
   365: 		#<iq type='get' from='us' to='them'>
   366: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   367: 		#    <affiliations/>
   368: 		#  </pubsub>
   369: 		#</iq>
   370: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   371: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   372: 		affiliations = SubElement(pubsub, 'affiliations')
   373: 
   374: 		def handler(stanza, callback):
   375: 			print etree.tostring(stanza)
   376: 
   377: 		self.send(stanza, handler, return_function)
   378: 
   379: 	def subscribe(self, server, node, jid=None, return_function=None, stanza_id=None):
   380: 		"""Subscribe the current JID to node on server. If supplied, jid
   381: 		will be subscribed rather than the logged-in JID.
   382: 
   383: 		return_function is given a single argument. This is False if there
   384: 		was an error, or if it was successful it is given a list of
   385: 		dictionaries of the form:
   386: 
   387: 		[{'server':server_URL, 'jid':subscribed_jid, 'subid':subscription_ID}, {...}, ...]"""
   388: 
   389: 		#<iq type='set' from='francisco@denmark.lit/barracks' to='pubsub.shakespeare.lit'>
   390: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   391: 		#    <subscribe node='princely_musings' jid='francisco@denmark.lit'/>
   392: 		#  </pubsub>
   393: 		#</iq>
   394: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   395: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   396: 		subscribe = SubElement(pubsub, 'subscribe', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
   397: 
   398: 		def handler(stanza, callback):
   399: 			#<iq xmlns="jabber:client" to="test2@localhost/subscriptions" from="pubsub.localhost" id="9r4LiyWpTOhI7z0j" type="result">
   400: 			#  <pubsub xmlns="http://jabber.org/protocol/pubsub">
   401: 			#    <subscription subid="4C1430B5BE841" node="/home" jid="test2@localhost/subscriptions" subscription="subscribed"/>
   402: 			#  </pubsub>
   403: 			#</iq
   404: 
   405: 			#<iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks'>
   406: 			#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   407: 			#    <subscription node='princely_musings' jid='francisco@denmark.lit' subid='ba49252aaa4f5d320c24d3766f0bdcade78c78d3' subscription='subscribed'/>
   408: 			#  </pubsub>
   409: 			#</iq>
   410: 
   411: 			if callback is not None:
   412: 				if stanza.get('type') == 'error':
   413: 					reply = False
   414: 				elif stanza.get('type') == 'result':
   415: 					reply = []
   416: 					for subscription_element in stanza.xpath(".//subscription"):
   417: 						reply.append({'node':subscription_element.get('node'), 'subid':subscription_element.get('subid'), 'server':stanza.get('from'), 'jid':subscription_element.get('jid')}))
   418: 				callback(reply)
   419: 
   420: 		self.send(stanza, handler, return_function)
   421: 
   422: 	def unsubscribe(self, server, node, jid=None, return_function=None, stanza_id=None):
   423: 		"""Unsubscribe the given jid (or if not supplied, the currently
   424: 		logged-in JID) from node on server.
   425: 
   426: 		No reply handling yet."""		#FIXME: Add description of return_function arguments
   427: 		#<iq type='set'
   428: 		#    from='us'
   429: 		#    to='them'>
   430: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   431: 		#     <unsubscribe
   432: 		#         node='node_name'
   433: 		#         jid='jid'/>
   434: 		#  </pubsub>
   435: 		#</iq>
   436: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   437: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   438: 		unsubscribe = SubElement(pubsub, 'unsubscribe', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
   439: 
   440: 		def handler(stanza, callback):
   441: 			#<iq type='result'
   442: 			#    from='pubsub.shakespeare.lit'
   443: 			#    to='francisco@denmark.lit/barracks'
   444: 			#    id='unsub1'/>
   445: 			if callback is not None:
   446: 				if stanza.get('type') == 'result':
   447: 					reply = True
   448: 				else:
   449: 					reply = False
   450: 				callback(reply)
   451: 
   452: 		self.send(stanza, handler, return_function)
   453: 
   454: 	def get_subscription_options(self, server, node, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
   455: 		"""Request the subscription options of jid (or if not supplied,
   456: 		the currently logged-in JID) for node on server.
   457: 
   458: 		No reply handling yet."""
   459: 		#<iq type='get'
   460: 		#    from='us'
   461: 		#    to='them'>
   462: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   463: 		#    <options node='node_name' jid='jid'/>
   464: 		#  </pubsub>
   465: 		#</iq>
   466: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   467: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   468: 		options = SubElement(pubsub, 'options', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
   469: 
   470: 		def handler(stanza, callback):
   471: 			print etree.tostring(stanza)
   472: 
   473: 		self.send(stanza, handler, return_function)
   474: 
   475: 	def subscription_options_form_submission(self, server, node, options, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
   476: 		"""Not yet implemented sanely."""
   477: 		# options is the "x" Element (which should contain all of the SubElements needed)
   478: 		#<iq type='set'
   479: 		#    from='us'
   480: 		#    to='them'>
   481: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   482: 		#    <options node='node_name' jid='jid'>
   483: 		#        <x xmlns='jabber:x:data' type='submit'>
   484: 		#          <field var='FORM_TYPE' type='hidden'>
   485: 		#            <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
   486: 		#          </field>
   487: 		#          <field var='pubsub#deliver'><value>1</value></field>
   488: 		#          <field var='pubsub#digest'><value>0</value></field>
   489: 		#          <field var='pubsub#include_body'><value>false</value></field>
   490: 		#          <field var='pubsub#show-values'>
   491: 		#            <value>chat</value>
   492: 		#            <value>online</value>
   493: 		#            <value>away</value>
   494: 		#          </field>
   495: 		#        </x>
   496: 		#     </options>
   497: 		#  </pubsub>
   498: 		#</iq>
   499: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   500: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   501: 		options_stanza = SubElement(pubsub, 'options', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
   502: 		options.append(options)
   503: 
   504: 		def handler(stanza, callback):
   505: 			print etree.tostring(stanza)
   506: 
   507: 		self.send(stanza, handler, return_function)
   508: 
   509: 	def subscribe_to_and_configure_a_node(self, server, node, options, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
   510: 		"""Not yet implemented sanely."""
   511: 		#<iq type='set'
   512: 		#    from='us'
   513: 		#    to='them'>
   514: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   515: 		#    <subscribe node='node_name' jid='jid'/>
   516: 		#    <options>
   517: 		#      <x xmlns='jabber:x:data' type='submit'>
   518: 		#        <field var='FORM_TYPE' type='hidden'>
   519: 		#          <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
   520: 		#        </field>
   521: 		#        <field var='pubsub#deliver'><value>1</value></field>
   522: 		#        <field var='pubsub#digest'><value>0</value></field>
   523: 		#        <field var='pubsub#include_body'><value>false</value></field>
   524: 		#        <field var='pubsub#show-values'>
   525: 		#          <value>chat</value>
   526: 		#          <value>online</value>
   527: 		#          <value>away</value>
   528: 		#        </field>
   529: 		#      </x>
   530: 		#    </options>
   531: 		#  </pubsub>
   532: 		#</iq>
   533: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   534: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   535: 		subscribe = SubElement(pubsub, 'subscribe', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
   536: 		options_stanza = SubElement(pubsub, 'options')
   537: 		options_stanza.append(options)
   538: 
   539: 		def handler(stanza, callback):
   540: 			print etree.tostring(stanza)
   541: 
   542: 		self.send(stanza, handler, return_function)
   543: 
   544: 	def request_items_generic(self, server, node, specific=None, some=None, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   545: 		"""General method used to implement item requests.
   546: 
   547: 		Retrieve items which have been published to node on server.
   548: 
   549: 		If the optional argument specific is given as a list of item IDs
   550: 		then those items are retrieved.
   551: 
   552: 		Replies are not yet handled.
   553: 
   554: 		If a number is supplied as the optional argument some then it is
   555: 		used as an upper limit the the number of items retrieved."""
   556: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   557: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   558: 		items = SubElement(pubsub, 'items', attrib={'node':str(node)})
   559: 		if some is not None:
   560: 			items.set('max_items', str(some))
   561: 		if specific is not None:
   562: 			for item in specific:
   563: 				items.append(Element('item', attrib={'id':item}))
   564: 
   565: 		def handler(stanza, callback):
   566: 			print etree.tostring(stanza)
   567: 
   568: 		self.send(stanza, handler, return_function)
   569: 
   570: 	def request_all_items(self, server, node, return_function=None, stanza_id=None):
   571: 		"""Retrieve all of the items published to node on server.
   572: 
   573: 		Replies are not yet handled."""
   574: 		#<iq type='get' from='us' to='them'>
   575: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   576: 		#    <items node='my_blog'/>
   577: 		#  </pubsub>
   578: 		#</iq>
   579: 		self.request_items_generic(server, node, return_function=return_function, stanza_id)
   580: 
   581: 	def request_specific_items(self, server, node, items, jid=None, return_function=None, stanza_id=None):
   582: 		"""Retrieves certain items which have been published to node on
   583: 		server. items is a list of item IDs.
   584: 
   585: 		Replies are not yet handled."""
   586: 		#<iq type='get' from='us' to='them'>
   587: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   588: 		#    <items node='my_blog'>
   589: 		#      <item id='an_id'/>
   590: 		#    </items>
   591: 		#  </pubsub>
   592: 		#</iq>
   593: 		self.request_items_generic(server, node, specific=items, return_function=return_function, stanza_id)
   594: 
   595: 	def request_some_items(self, server, node, item_count, return_function=None, stanza_id=None):
   596: 		"""Retrieves (at most) the last item_count items which have been
   597: 		published to node at server.
   598: 
   599: 		Replies are not yet handled."""
   600: 		#<iq type='get' from='us' to='them'>
   601: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   602: 		#    <items node='my_blog' max_items='5'/>
   603: 		#  </pubsub>
   604: 		#</iq>
   605: 		self.request_items_generic(server, node, some=item_count, return_function=return_function, stanza_id)
   606: 
   607: 	def publish(self, server, node, body, item_id=None, jid=None, return_function=None, stanza_id=None):
   608: 		"""Publish body to the node node on server. If item_id is
   609: 		specified then request that it be used as the item's ID.
   610: 
   611: 		Replies are not yet handled."""
   612: 		#<iq type='set'
   613: 		#    from='us'
   614: 		#    to='them'>
   615: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   616: 		#    <publish node='node'>
   617: 		#      <item id='item_id'>
   618: 		#.........body.............
   619: 		#        </entry>
   620: 		#      </item>
   621: 		#    </publish>
   622: 		#  </pubsub>
   623: 		#</iq>
   624: 
   625: 		self.publish_with_options(server, node, body, item_id=item_id, jid=jid, return_function=return_function, stanza_id)
   626: 
   627: 	def publish_with_options(self, server, node, body, options=None, item_id=None, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
   628: 		"""Generic method to implement all publishing requests.
   629: 
   630: 		Publishes body as an item at node on server. If item_id is
   631: 		given then requests that it be used as the item's ID.
   632: 
   633: 		options is not implemented sanely yet.
   634: 
   635: 		Replies are not yet handled."""
   636: 		#<iq type='set'
   637: 		#    from='us'
   638: 		#    to='them'>
   639: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   640: 		#    <publish node='node'>
   641: 		#      <item id='item_id'>
   642: 		#			...body...
   643: 		#		</item>
   644: 		#    </publish>
   645: 		#    <publish-options>
   646: 		#      <x xmlns='jabber:x:data' type='submit'>
   647: 		#        <field var='FORM_TYPE' type='hidden'>
   648: 		#          <value>http://jabber.org/protocol/pubsub#publish-options</value>
   649: 		#        </field>
   650: 		#        <field var='pubsub#access_model'>
   651: 		#          <value>presence</value>
   652: 		#        </field>
   653: 		#      </x>
   654: 		#    </publish-options>
   655: 		#  </pubsub>
   656: 		#</iq>
   657: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   658: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   659: 		publish = SubElement(pubsub, 'publish', attrib={'node':str(node)})
   660: 		item = SubElement(publish, 'item')
   661: 		if item_id is not None:
   662: 			item.set('id', item_id)
   663: 		if type(body) == type(Element):
   664: 			item.append(body)
   665: 		elif type(body) == type("string"):
   666: 			item.text = body
   667: 		if options is not None:
   668: 			publish_options = SubElement(pubsub, 'publish-options')
   669: 			publish_options.append(options)
   670: 
   671: 		def handler(stanza, callback):
   672: 			#<iq type='result'
   673: 			#    from='pubsub.shakespeare.lit'
   674: 			#    to='hamlet@denmark.lit/blogbot'
   675: 			#    id='publish1'>
   676: 			#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   677: 			#    <publish node='princely_musings'>
   678: 			#      <item id='ae890ac52d0df67ed7cfdf51b644e901'/>
   679: 			#    </publish>
   680: 			#  </pubsub>
   681: 			print etree.tostring(stanza)
   682: 			if callback is not None:
   683: 				if stanza.get("type") == "result":
   684: 					callback(0)
   685: 				else:
   686: 					callback(stanza)
   687: 
   688: 		print "Sending"
   689: 		self.send(stanza, handler, return_function)
   690: 
   691: 	def delete_an_item_from_a_node(self, server, node, item_id, jid=None, return_function=None, stanza_id=None):
   692: 		"""Removes the item with ID item_id from node at server."""
   693: 		#<iq type='set'
   694: 		#    from='us'
   695: 		#    to='them'>
   696: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   697: 		#    <retract node='node'>
   698: 		#      <item id='item_id'/>
   699: 		#    </retract>
   700: 		#  </pubsub>
   701: 		#</iq>
   702: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   703: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   704: 		retract = SubElement(pubsub, 'retract', attrib={'node':str(node)})
   705: 		item = SubElement(retract, 'item', attrib={'id':item_id})
   706: 
   707: 		def handler(stanza, callback):
   708: 			#<iq type='result'
   709: 			#    from='pubsub.shakespeare.lit'
   710: 			#    to='hamlet@denmark.lit/elsinore'
   711: 			#    id='retract1'/>
   712: 			if callback is not None:
   713: 				if stanza.get('type') == 'result':
   714: 					result = True
   715: 				else:
   716: 					result = False
   717: 				callback(result)
   718: 
   719: 		self.send(stanza, handler, return_function)
   720: 
   721: 	def request_node(self, server, node, type, parent, options=None, return_function=None, stanza_id=None):		#FIXME A LOT
   722: 		"""Asks the given server for a pubsub node. If node is None then
   723: 		an instant node is made, if it is a string or Node then that is
   724: 		used as the new node's ID. type can be 'collection' or 'leaf'
   725: 		the type type ('collection' or 'leaf').
   726: 
   727: 		Not implemented completely sanely yet."""
   728: 		##FIXME: Explain the other options
   729: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
   730: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
   731: 		create = SubElement(pubsub, 'create')
   732: 
   733: 		# Instant nodes do not need a name
   734: 		if node is not None:
   735: 			create.set('node', str(node))
   736: 
   737: 		configure = SubElement(pubsub, 'configure')
   738: 
   739: 		# Nodes must have an option set to show that they are collections
   740: 		if type == 'collection':
   741: 			#      <x xmlns='jabber:x:data' type='submit'>
   742: 			#        <field var='FORM_TYPE' type='hidden'>
   743: 			#          <value>http://jabber.org/protocol/pubsub#node_config</value>
   744: 			#        </field>
   745: 			#        <field var='pubsub#node_type'><value>collection</value></field>
   746: 			#      </x>
   747: 			x = SubElement(configure, "x", attrib={"xmlns":"jabber:x:data", "type":"submit"})
   748: 			formtype_field = SubElement(x, "field", attrib={"var":"FORM_TYPE", "type":"hidden"})
   749: 			formtype_value = SubElement(formtype_field, "value")
   750: 			formtype_value.text = "http://jabber.org/protocol/pubsub#node_config"
   751: 			nodetype_field = SubElement(x, "field", attrib={"var":"pubsub#node_type"})
   752: 			nodetype_value = SubElement(nodetype_field, "value")
   753: 			nodetype_value.text = "collection"
   754: 
   755: 		if options is not None:
   756: 			configure.append(options)
   757: 
   758: 		def handler(stanza, callback):
   759: 			#<iq type='result'
   760: 			#	from='pubsub.shakespeare.lit'
   761: 			#	to='hamlet@denmark.lit/elsinore'
   762: 			#	id='create1'/>
   763: 			if callback is not None:
   764: 				if stanza.attrib.get("type") == "error":
   765: 					callback(False)
   766: 				elif stanza.attrib.get("type") == "result":
   767: 					callback(True)
   768: 
   769: 		self.send(stanza, handler, return_function)
   770: 
   771: 	def entity_request_instant_node(self, server, return_function=None, stanza_id=None):
   772: 		"""Asks the given server for an instant node (ie. one without
   773: 		a predetermined name/id)."""
   774: 		#<iq type='set' from='us' to='them'>
   775: 		#    <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   776: 		#      <create/>
   777: 		#      <configure/>
   778: 		#    </pubsub>
   779: 		#</iq>
   780: 		self.request_node(server, None, "leaf", None, None, return_function, stanza_id)
   781: 
   782: 	def get_new_leaf_node(self, server, node, parent, options, return_function=None, stanza_id=None):
   783: 		"""Requests a new leaf node (which can store items) with name
   784: 		node, as a sub-node of the node parent on server.
   785: 
   786: 		options is not yet implemented sanely.
   787: 
   788: 		Replies are not yet handled."""
   789: 		#<iq type='set' from='us' to='them'>
   790: 		#    <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   791: 		#      <create node='my_blog'/>
   792: 		#      <configure/>
   793: 		#    </pubsub>
   794: 		#</iq>
   795: 		self.request_node(server, node, "leaf", parent, options, return_function, stanza_id)
   796: 
   797: 	def get_new_collection_node(self, server, node, parent, options, return_function=None, stanza_id=None):
   798: 		"""Requests a new collection node (which can store nodes) with
   799: 		name node, as a sub-node of the node parent on server.
   800: 
   801: 		options is not yet implemented sanely.
   802: 
   803: 		Replies are not yet handled."""
   804: 		#<iq type='set'
   805: 		#    from='us'
   806: 		#    to='them'>
   807: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   808: 		#    <create node='node_name'/>
   809: 		#    <configure>
   810: 		#      <x xmlns='jabber:x:data' type='submit'>
   811: 		#        <field var='FORM_TYPE' type='hidden'>
   812: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
   813: 		#        </field>
   814: 		#        <field var='pubsub#node_type'><value>collection</value></field>
   815: 		#      </x>
   816: 		#    </configure>
   817: 		#  </pubsub>
   818: 		#</iq>
   819: 		self.request_node(server, node, "collection", parent, options, return_function, stanza_id)
   820: 
   821: 	def get_new_leaf_node_nondefault_access(self, server, node, access_model, return_function=None, stanza_id=None):		#FIXME A LOT
   822: 		"""Not yet implemented sanely."""
   823: 		#<iq type='set' from='us' to='them'>
   824: 		#    <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   825: 		#      <create node='my_blog'/>
   826: 		#      <configure>
   827: 		#        <x xmlns='jabber:x:data' type='submit'>
   828: 		#          <field var='FORM_TYPE' type='hidden'>
   829: 		#            <value>http://jabber.org/protocol/pubsub#node_config</value>
   830: 		#          </field>
   831: 		#          <field var='pubsub#access_model'>
   832: 		#            <value>open</value>
   833: 		#          </field>
   834: 		#        </x>
   835: 		#      </configure>
   836: 		#    </pubsub>
   837: 		#</iq>"""
   838: 
   839: 		x = Element('x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
   840: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
   841: 		value1 = SubElement(field1, 'value')
   842: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
   843: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#access_model'})
   844: 		value2 = SubElement(field2, 'value')
   845: 		## FIXME: Add a check here
   846: 		value2.text = access_model
   847: 
   848: 		self.entity_request_new_node_nondefault_configuration(server, node, x, stanza_id)
   849: 
   850: 	def entity_request_new_node_nondefault_configuration(self, server, node, options, return_function=None, stanza_id=None):		#FIXME A LOT
   851: 		"""Not yet implemented sanely."""
   852: 		#<iq type='set' from='us' to='them'>
   853: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
   854: 		#    <create node='my_blog'/>
   855: 		#    <configure>
   856: 		#      <x xmlns='jabber:x:data' type='submit'>
   857: 		#        <field var='FORM_TYPE' type='hidden'>
   858: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
   859: 		#        </field>
   860: 		#        <field var='pubsub#title'>
   861: 		#          <value>My Blog</value>
   862: 		#        </field>
   863: 		#      </x>
   864: 		#    </configure>
   865: 		#  </pubsub>
   866: 		#</iq>
   867: 
   868: 		self.request_node(server, node, options, return_function, stanza_id)
   869: 
   870: 	def node_configuration_generic(self, server, node, options, return_function=None, stanza_id=None):		#FIXME A LOT
   871: 		"""Not yet implemented sanely."""
   872: 		stanza = Element('iq', attrib={'from':self.get_jid(), 'to':str(server)})
   873: 		if options is not None:
   874: 			stanza.set('type', 'set')
   875: 		else:
   876: 			stanza.set('type', 'get')
   877: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
   878: 		configure = SubElement(pubsub, 'configure', attrib={'node':str(node)})
   879: 		if options is not None:
   880: 			configure.append(options)
   881: 
   882: 		def handler(stanza, callback):
   883: 			print etree.tostring(stanza)
   884: 
   885: 		self.send(stanza, handler, return_function)
   886: 
   887: 	def request_node_configuration_form(self, server, node, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   888: 		"""Request a form with which to configure node on server.
   889: 
   890: 		Replies are not yet handled."""
   891: 		#<iq type='get' from='us' to='them'>
   892: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
   893: 		#    <configure node='my_blog'/>
   894: 		#  </pubsub>
   895: 		#</iq>
   896: 
   897: 		self.node_configuration_generic(server, node, None, stanza_id)
   898: 
   899: 	def submit_node_configuration_form(self, server, node, options, return_function=None, stanza_id=None):		#FIXME A LOT
   900: 		"""Not yet implemented sanely."""
   901: 		#<iq type='set' from='us' to='them'>
   902: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
   903: 		#    <configure node='my_blog'>
   904: 		#      <x xmlns='jabber:x:data' type='submit'>
   905: 		#        <field var='FORM_TYPE' type='hidden'>
   906: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
   907: 		#        </field>
   908: 		#        <field var='pubsub#title'>
   909: 		#          <value>Princely Musings (Atom)</value>
   910: 		#        </field>
   911: 		#      </x>
   912: 		#    </configure>
   913: 		#  </pubsub>
   914: 		#</iq>
   915: 
   916: 		self.node_configuration_generic(server, node, options, stanza_id)
   917: 
   918: 	def cancel_node_configuration(self, server, node, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   919: 		"""Retracts a request to reconfigure node on server.
   920: 
   921: 		Replies are not yet handled."""
   922: 		#<iq type='set' from='us' to='them'>
   923: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
   924: 		#    <configure node='my_blog'>
   925: 		#      <x xmlns='jabber:x:data' type='cancel'/>
   926: 		#    </configure>
   927: 		#  </pubsub>
   928: 		#</iq>
   929: 		x = Element(configure, 'x', attrib={'xmlns':'jabber:x:data', 'type':'cancel'})
   930: 
   931: 		self.submit_node_configuration_form(server, node, x, stanza_id)
   932: 
   933: 	def request_default_configuration_options(self, server, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   934: 		"""Asks server for the configuration which is used as default
   935: 		for new nodes.
   936: 
   937: 		Replies are not yet handled."""
   938: 		#<iq type='get'
   939: 		#    from='us'
   940: 		#    to='them'>
   941: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
   942: 		#    <default/>
   943: 		#  </pubsub>
   944: 		#</iq>
   945: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   946: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
   947: 		default = SubElement(pubsub, 'default')
   948: 
   949: 		def handler(stanza, callback):
   950: 			print etree.tostring(stanza)
   951: 
   952: 		self.send(stanza, handler, return_function)
   953: 
   954: 	def request_default_collection_configuration(self, server, return_function=None, stanza_id=None):		#FIXME NO HANDLER
   955: 		"""Ask server for the options which are applied by default to
   956: 		new collection nodes.
   957: 
   958: 		Replies are not yet handled."""
   959: 		#<iq type='get'
   960: 		#    from='us'
   961: 		#    to='them'>
   962: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
   963: 		#    <default>
   964: 		#      <x xmlns='jabber:x:data' type='submit'>
   965: 		#        <field var='FORM_TYPE' type='hidden'>
   966: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
   967: 		#        </field>
   968: 		#        <field var='pubsub#node_type'><value>collection</value></field>
   969: 		#      </x>
   970: 		#    </default>
   971: 		#  </pubsub>
   972: 		#</iq>
   973: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
   974: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
   975: 		default = SubElement(pubsub, 'default')
   976: 		x = SubElement(default, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
   977: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
   978: 		value1 = SubElement(field1, 'value')
   979: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
   980: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#node_type'})
   981: 		value2 = SubElement(field2, 'value')
   982: 		value2.text = 'collection'
   983: 
   984: 		def handler(stanza, callback):
   985: 			print etree.tostring(stanza)
   986: 
   987: 		self.send(stanza, handler, return_function)
   988: 
   989: 	def delete_a_node(self, server, node, return_function=None, stanza_id=None):
   990: 		"""Delete the given node from the given server.
   991: 
   992: 		If given, return_function is run upon reply with a value of
   993: 		True if the deletion was successful, or False if there was an
   994: 		error."""
   995: 		## FIXME: Give different results for different types of error.
   996: 		#<iq type='set'
   997: 		#    from='us'
   998: 		#    to='them'>
   999: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1000: 		#    <delete node='node_name'/>
  1001: 		#  </pubsub>
  1002: 		#</iq>
  1003: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1004: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1005: 		delete = SubElement(pubsub, 'delete', attrib={'node':str(node)})
  1006: 
  1007: 		def handler(stanza, callback):
  1008: 			#<iq type='result'
  1009: 			#    from='pubsub.shakespeare.lit'
  1010: 			#    id='delete1'/>
  1011: 			if callback is not None:
  1012: 				if stanza.get("type") == "result":
  1013: 					callback(True)
  1014: 				elif stanza.get("type") == "error":
  1015: 					callback(False)
  1016: 
  1017: 
  1018: 		self.send(stanza, handler, return_function)
  1019: 
  1020: 	def purge_all_items_from_a_node(self, server, node, return_function=None, stanza_id=None):
  1021: 		"""Remove every item which has been published to node on server."""
  1022: 		#<iq type='set'
  1023: 		#    from='us'
  1024: 		#    to='them'>
  1025: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1026: 		#    <purge node='node_name'/>
  1027: 		#  </pubsub>
  1028: 		#</iq>
  1029: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1030: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1031: 		purge = SubElement(pubsub, 'purge', attrib={'node':str(node)})
  1032: 
  1033: 		def handler(stanza, callback):
  1034: 			#<iq type='result'
  1035: 			#    from='pubsub.shakespeare.lit'
  1036: 			#    id='purge1'/>
  1037: 			if callback is not None:
  1038: 				if stanza.get('type') == 'result':
  1039: 					reply = True
  1040: 				else:
  1041: 					reply = False
  1042: 				callback(reply)
  1043: 
  1044: 		self.send(stanza, handler, return_function)
  1045: 
  1046: 	def request_pending_subscription_requests(self, server, return_function=None, stanza_id=None):		#FIXME NO HANDLER
  1047: 		"""Get every relevant request for subscription for nodes at
  1048: 		server.
  1049: 
  1050: 		Replies are not yet handled."""
  1051: 		#<iq type='set'
  1052: 		#    from='us'
  1053: 		#    to='them'>
  1054: 		#  <command xmlns='http://jabber.org/protocol/commands'
  1055: 		#           node='http://jabber.org/protocol/pubsub#get-pending'
  1056: 		#           action='execute'/>
  1057: 		#</iq>"""
  1058: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1059: 		command = SubElement(stanza, 'command', attrib={'xmlns':'http://jabber.org/protocol/commands', 'node':'http://jabber.org/protocol/pubsub#get-pending', 'action':'execute'})
  1060: 
  1061: 		def handler(stanza, callback):
  1062: 			#<iq type='result'
  1063: 			#    from='pubsub.shakespeare.lit'
  1064: 			#    to='hamlet@denmark.lit/elsinore'
  1065: 			#    id='pending1'>
  1066: 			#  <command xmlns='http://jabber.org/protocol/commands'
  1067: 			#           sessionid='pubsub-get-pending:20031021T150901Z-600'
  1068: 			#           node='http://jabber.org/protocol/pubsub#get-pending'
  1069: 			#           status='executing'
  1070: 			#           action='execute'>
  1071: 			#    <x xmlns='jabber:x:data' type='form'>
  1072: 			#      <field type='list-single' var='pubsub#node'>
  1073: 			#        <option><value>princely_musings</value></option>
  1074: 			#        <option><value>news_from_elsinore</value></option>
  1075: 			#      </field>
  1076: 			#    </x>
  1077: 			#  </command>
  1078: 			#</iq>
  1079: 			print etree.tostring(stanza)
  1080: 
  1081: 		self.send(stanza, handler, return_function)
  1082: 
  1083: 	def request_pending_subscription_requests_for_a_node(self, server, node, time, return_function=None, stanza_id=None):		#FIXME A LOT
  1084: 		"""Ask server for every subscription request which has not yet
  1085: 		been handled for node.
  1086: 
  1087: 		Not yet implemented sanely."""
  1088: 		## FIXME: Check spec, no "from"??!
  1089: 		#<iq type='set' to='them'>
  1090: 		#  <command xmlns='http://jabber.org/protocol/commands'
  1091: 		#           sessionid='pubsub-get-pending:20031021T150901Z-600'
  1092: 		#           node='http://jabber.org/protocol/pubsub#get-pending'
  1093: 		#           action='execute'>
  1094: 		#    <x xmlns='jabber:x:data' type='submit'>
  1095: 		#      <field var='pubsub#node'>
  1096: 		#        <value>node_name</value>
  1097: 		#      </field>
  1098: 		#    </x>
  1099: 		#  </command>
  1100: 		#</iq>
  1101: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1102: 		command = SubElement(stanza, 'command', attrib={'xmlns':'http://jabber.org/protocol/commands', 'sessionid':'pubsub-get-pending:' + time, 'node':'http://jabber.org/protocol/pubsub#get-pending', 'action':'execute'})
  1103: 		x = SubElement(command, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1104: 		field1 = SubElement(x, 'field', attrib={'var':'pubsub#node'})
  1105: 		value1 = SubElement(field1, 'value')
  1106: 		value1.text = str(node)
  1107: 
  1108: 		def handler(stanza, callback):
  1109: 			print etree.tostring(stanza)
  1110: 
  1111: 		self.send(stanza, handler, return_function)
  1112: 
  1113: 	def request_all_subscriptions(self, server, node, return_function=None, stanza_id=None):
  1114: 		"""Redundant"""
  1115: 		#<iq type='get'
  1116: 		#    from='us'
  1117: 		#    to='them'>
  1118: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1119: 		#    <subscriptions node='node_name'/>
  1120: 		#  </pubsub>
  1121: 		#</iq>
  1122: 		self.retrieve_subscriptions(server, node, return_function, stanza_id)
  1123: 
  1124: 	def modify_subscriptions(self, server, node, subscriptions, return_function=None, stanza_id=None):		#FIXME A LOT
  1125: 		"""Not yet implemented in a sane way."""
  1126: 		#<iq type='set'
  1127: 		#    from='them'
  1128: 		#    to='us'>
  1129: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1130: 		#    <subscriptions node='node_name'>
  1131: 		#      <subscription jid='current_jid' subscription='subscribed'/>
  1132: 		#    </subscriptions>
  1133: 		#  </pubsub>
  1134: 		#</iq>
  1135: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
  1136: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1137: 		subscriptions = SubElement(pubsub, 'subscriptions', attrib={'node':str(node)})
  1138: 		for current_jid in subscriptions.keys():
  1139: 			subscriptions.append(Element('subscription', attrib={'jid':current_jid, 'subscription':subscriptions[current_jid]}))
  1140: 
  1141: 		def handler(stanza, callback):
  1142: 			print etree.tostring(stanza)
  1143: 
  1144: 		self.send(stanza, handler, return_function)
  1145: 
  1146: 	def multiple_simultaneous_modifications(self, server, node, subscriptions, return_function=None, stanza_id=None):		#FIXME A LOT
  1147: 		"""Not yet implemented in a sane way."""
  1148: 		#<iq type='set'
  1149: 		#    from='us'
  1150: 		#    to='them'>
  1151: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1152: 		#    <subscriptions node='node_name'>
  1153: 		#      <subscription jid='current_subscription' subscription='subscribed'/>
  1154: 		#    </subscriptions>
  1155: 		#  </pubsub>
  1156: 		#</iq>
  1157: 		stanza = ElementTree('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1158: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1159: 		subscriptions = SubElement(pubsub, 'subscriptions', attrib={'node':str(node)})
  1160: 		for current_subscription in subscriptions.keys():
  1161: 			subscriptions.append(Element('subscription', attrib={'jid':current_subscription, 'subscription':subscriptions[current_subscription]}))
  1162: 
  1163: 		def handler(stanza, callback):
  1164: 			print etree.tostring(stanza)
  1165: 
  1166: 		self.send(stanza, handler, return_function)
  1167: 
  1168: 	def request_all_affiliated_entities(self, server, node, return_function=None, stanza_id=None):
  1169: 		"""Asks server for all entities (JIDs) affiliated in some way to
  1170: 		node.
  1171: 
  1172: 		An affiliation is owner, publisher or outcast. It does not
  1173: 		include subscribers.
  1174: 
  1175: 		If return_function is given, it is called upon reply with one
  1176: 		argument of False if there was an error, or if it was successful
  1177: 		a dictionary of the form:
  1178: 
  1179: 		{'publisher':[JID1, JID2, ...], 'owner':[JID3, JID4, ...], ...}"""
  1180: 		#<iq type='get'
  1181: 		#    from='us'
  1182: 		#    to='them'>
  1183: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1184: 		#    <affiliations node='node'/>
  1185: 		#  </pubsub>
  1186: 		#</iq>
  1187: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})
  1188: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1189: 		affiliations = SubElement(pubsub, 'affiliations', attrib={'node':str(node)})
  1190: 
  1191: 		def handler(stanza, callback):
  1192: 			#<iq xmlns="jabber:client"
  1193: 			#    to="test1@localhost/subscriptions"
  1194: 			#    from="pubsub.localhost"
  1195: 			#    type="result">
  1196: 			#    <pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
  1197: 			#        <affiliations node="/home/localhost/test1">
  1198: 			#            <affiliation affiliation="owner" jid="test1@localhost"/>
  1199: 			#        </affiliations>
  1200: 			#    </pubsub>
  1201: 			#</iq>
  1202: 			if callback is not None:
  1203: 				if stanza.get('type') == 'error':
  1204: 					affiliation_dictionary = False
  1205: 				elif stanza.get('type') == 'result':
  1206: 					affiliations = stanza.find('.//{http://jabber.org/protocol/pubsub#owner}affiliations')
  1207: 					affiliation_dictionary = {}
  1208: 					if affiliations is not None:
  1209: 						for affiliation in affiliations:
  1210: 							if not affiliation.get("affiliation") in affiliation_dictionary.keys():
  1211: 								affiliation_dictionary[affiliation.get("affiliation")] = []
  1212: 							affiliation_dictionary[affiliation.get("affiliation")].append(JID(affiliation.get("jid")))
  1213: 				callback(affiliation_dictionary)
  1214: 
  1215: 		self.send(stanza, handler, return_function)
  1216: 
  1217: 	def modify_affiliation(self, server, node, affiliations, return_function=None, stanza_id=None):
  1218: 		"""Tells server to change the affiliation of some entities to
  1219: 		the node node. affiliations is a dictionary of the form:
  1220: 
  1221: 		{JID1:affiliation_type, JID2:affiliation_type, ...}
  1222: 		"""
  1223: 		#<iq type='set'
  1224: 		#    from='us'
  1225: 		#    to='them'>
  1226: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1227: 		#    <affiliations node='node'/>
  1228: 		#      <affiliation jid='current_affiliation' affiliation='affiliations'/>
  1229: 		#    </affiliations>
  1230: 		#  </pubsub>
  1231: 		#</iq>
  1232: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1233: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1234: 		affiliations_element = SubElement(pubsub, 'affiliations', attrib={'node':str(node)})
  1235: 		for current_affiliation in affiliations.keys():
  1236: 			affiliations_element.append(Element('affiliation', attrib={'jid':str(current_affiliation), 'affiliation':affiliations[current_affiliation]}))
  1237: 
  1238: 		def handler(stanza, callback):
  1239: 			if callback is not None:
  1240: 				if stanza.get("type") == "result":
  1241: 					reply = True
  1242: 				else:
  1243: 					reply = False
  1244: 				callback(reply)
  1245: 
  1246: 		self.send(stanza, handler, return_function)
  1247: 
  1248: 	def subscribe_to_a_collection_node(self, server, node, jid, return_function=None, stanza_id=None):		#FIXME REDUNDANT?
  1249: 		"""Not yet implemented in a sane way"""
  1250: 		#<iq type='set'
  1251: 		#    from='us'
  1252: 		#    to='them'>
  1253: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1254: 		#    <subscribe jid='jid'
  1255: 		#               node='node_name'/>
  1256: 		#   </pubsub>
  1257: 		#</iq>
  1258: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1259: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1260: 		subscribe = SubElement(pubsub, 'subscribe', attrib={'jid':self.get_jid(jid, True), 'node':str(node)})
  1261: 
  1262: 		def handler(stanza, callback):
  1263: 			print etree.tostring(stanza)
  1264: 
  1265: 		self.send(stanza, handler, return_function)
  1266: 
  1267: 	def subscribe_to_collection_node_with_configuration(self, server, node, options, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
  1268: 		"""Not yet implemented in a sane way."""
  1269: 		#<iq type='set'
  1270: 		#    from='us'
  1271: 		#    to='them'>
  1272: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1273: 		#    <subscribe jid='jid'
  1274: 		#               node='node_name'/>
  1275: 		#    <options>
  1276: 		#      <x xmlns='jabber:x:data' type='submit'>
  1277: 		#        <field var='FORM_TYPE' type='hidden'>
  1278: 		#          <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
  1279: 		#        </field>
  1280: 		#        <field var='pubsub#subscription_type'>
  1281: 		#          <value>items</value>
  1282: 		#        </field>
  1283: 		#        <field var='pubsub#subscription_depth'>
  1284: 		#          <value>all</value>
  1285: 		#        </field>
  1286: 		#      </x>
  1287: 		#   </options>
  1288: 		# </pubsub>
  1289: 		#</iq>
  1290: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1291: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1292: 		subscribe = SubElement(pubsub, 'subscribe', attrib={'jid':self.get_jid(jid, True), 'node':str(node)})
  1293: 		options_element = SubElement(pubsub, 'options')
  1294: 		options_element.append(options)
  1295: 
  1296: 		def handler(stanza, callback):
  1297: 			print etree.tostring(stanza)
  1298: 
  1299: 		self.send(stanza, handler, return_function)
  1300: 
  1301: 	def subscribe_to_root_collection_node(self, server, jid=None, return_function=None, stanza_id=None):		#FIXME NO HANDLER XEP-0248?
  1302: 		"""Subscribe jid (or, if not supplied, the currently logged-in
  1303: 		JID) to the root collection node (the node which contains all
  1304: 		of the nodes) on server.
  1305: 
  1306: 		Replies are not yet handled."""
  1307: 		#<iq type='set'
  1308: 		#    from='us'
  1309: 		#    to='them'>
  1310: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1311: 		#    <subscribe jid='jid'/>
  1312: 		# </pubsub>
  1313: 		#</iq>
  1314: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1315: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1316: 		subscribe = SubElement(pubsub, 'subscribe', attrib={'jid':self.get_jid(jid, True)})
  1317: 
  1318: 		def handler(stanza, callback):
  1319: 			print etree.tostring(stanza)
  1320: 
  1321: 		self.send(stanza, handler, return_function)
  1322: 
  1323: 	def create_a_new_node_associated_with_a_collection(self, server, node, collection, return_function=None, stanza_id=None):		#FIXME NO HANDLER
  1324: 		"""Request a new node with name node on server server. The new
  1325: 		node will be a sub-node of the node collection.
  1326: 
  1327: 		Results are not yet handled."""
  1328: 		#<iq type='set'
  1329: 		#    from='us'
  1330: 		#    to='them'>
  1331: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1332: 		#    <create node='node_name'/>
  1333: 		#    <configure>
  1334: 		#      <x xmlns='jabber:x:data' type='submit'>
  1335: 		#        <field var='pubsub#collection'><value>collection_name</value></field>
  1336: 		#      </x>
  1337: 		#    </configure>
  1338: 		#  </pubsub>
  1339: 		#</iq>
  1340: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1341: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1342: 		create = SubElement(pubsub, 'create', attrib={'node':str(node)})
  1343: 		configure = SubElement(pubsub, 'configure')
  1344: 		x = SubElement(configure, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1345: 		field1 = SubElement(x, 'field', attrib={'var':'pubsub#collection'})
  1346: 		value1 = SubElement(field1, 'value')
  1347: 		value1.text = str(collection)
  1348: 
  1349: 		def handler(stanza, callback):
  1350: 			print etree.tostring(stanza)
  1351: 
  1352: 		self.send(stanza, handler, return_function)
  1353: 
  1354: 	def modify_node_configuration(self, server, node, collection, return_function=None, stanza_id=None):		#FIXME A LOT
  1355: 		"""Not yet implemented sanely."""
  1356: 		# Sets node_name as member of collection_name
  1357: 		#<iq type='set'
  1358: 		#    from='us'
  1359: 		#    to='them'>
  1360: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1361: 		#    <configure node='node'>
  1362: 		#      <x xmlns='jabber:x:data' type='submit'>
  1363: 		#        <field var='FORM_TYPE' type='hidden'>
  1364: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
  1365: 		#        </field>
  1366: 		#        <field var='pubsub#collection'><value>collection</value></field>
  1367: 		#      </x>
  1368: 		#    </configure>
  1369: 		#  </pubsub>
  1370: 		#</iq>
  1371: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1372: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1373: 		configure = SubElement(pubsub, 'configure', attrib={'node':str(node)})
  1374: 		x = SubElement(configure, 'x', attrib={'xmlns':'jaber:x:data', 'type':'submit'})
  1375: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1376: 		value1 = SubElement(field1, 'value')
  1377: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
  1378: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#collection'})
  1379: 		value2 = SubElement(field2, 'value')
  1380: 		value2.text = str(collection)
  1381: 
  1382: 		def handler(stanza, callback):
  1383: 			print etree.tostring(stanza)
  1384: 
  1385: 		self.send(stanza, handler, return_function)
  1386: 
  1387: 	def modify_collection_configuration(self, server, node, collection, return_function=None, stanza_id=None):		#FIXME A LOT
  1388: 		"""Not yet implemented sanely."""
  1389: 		# Make node_name a child of collection_name
  1390: 		## FIXME: This MUST include the current children too, but doesn't at the mo'
  1391: 		#<iq type='set'
  1392: 		#    from='us'
  1393: 		#    to='them'>
  1394: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1395: 		#    <configure node='collection'>
  1396: 		#      <x xmlns='jabber:x:data' type='submit'>
  1397: 		#        <field var='FORM_TYPE' type='hidden'>
  1398: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
  1399: 		#        </field>
  1400: 		#        <field var='pubsub#children'>
  1401: 		#          <value>node</value>
  1402: 		#        </field>
  1403: 		#      </x>
  1404: 		#    </configure>
  1405: 		#  </pubsub>
  1406: 		#</iq>
  1407: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1408: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1409: 		configure = SubElement(pubsub, 'configure', attrib={'node':str(collection)})
  1410: 		x = SubElement(configure, 'x', attrib={'xmlns':'jaber:x:data', 'type':'submit'})
  1411: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1412: 		value1 = SubElement(field1, 'value')
  1413: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
  1414: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#children'})
  1415: 		value2 = SubElement(field2, 'value')
  1416: 		value2.text = str(node)
  1417: 
  1418: 		def handler(stanza, callback):
  1419: 			print etree.tostring(stanza)
  1420: 
  1421: 		self.send(stanza, handler, return_function)
  1422: 
  1423: 	def disassociate_collection_from_a_node(self, server, node, return_function=None, stanza_id=None):		#FIXME A LOT
  1424: 		"""Not yet implemented sanely"""
  1425: 		## FIXME: This disassociates from EVERY collection. Should be able to specify collections
  1426: 		#<iq type='set'
  1427: 		#    from='us'
  1428: 		#    to='them'>
  1429: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1430: 		#    <configure node='node'>
  1431: 		#      <x xmlns='jabber:x:data' type='submit'>
  1432: 		#        <field var='FORM_TYPE' type='hidden'>
  1433: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
  1434: 		#        </field>
  1435: 		#        <field var='pubsub#collection'><value></value></field>
  1436: 		#      </x>
  1437: 		#    </configure>
  1438: 		#  </pubsub>
  1439: 		#</iq>
  1440: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1441: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1442: 		configure = SubElement(pubsub, 'configure', attrib={'node':str(node)})
  1443: 		x = SubElement(configure, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1444: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1445: 		value1 = SubElement(field1, 'value')
  1446: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
  1447: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#collection'})
  1448: 		value2 = SubElement(field2, 'value')
  1449: 		value2.text = ''
  1450: 
  1451: 		def handler(stanza, callback):
  1452: 			print etree.tostring(stanza)
  1453: 
  1454: 		self.send(stanza, handler, return_function)
  1455: 
  1456: 	def disassociate_node_from_a_collection(self, server, node, collection, return_function=None, stanza_id=None):		#FIXME A LOT
  1457: 		"""Not implemented sanely yet"""
  1458: 		## FIXME: Needs fixing badly. This clears all children from the node
  1459: 		#<iq type='set'
  1460: 		#    from='us'
  1461: 		#    to='them'>
  1462: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
  1463: 		#    <configure node='collection'>
  1464: 		#      <x xmlns='jabber:x:data' type='submit'>
  1465: 		#        <field var='FORM_TYPE' type='hidden'>
  1466: 		#          <value>http://jabber.org/protocol/pubsub#node_config</value>
  1467: 		#        </field>
  1468: 		#        <field var='pubsub#children'>
  1469: 		#          <value>add children here, minus node</value>
  1470: 		#        </field>
  1471: 		#      </x>
  1472: 		#    </configure>
  1473: 		#  </pubsub>
  1474: 		#</iq>
  1475: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1476: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#owner'})
  1477: 		configure = SubElement(pubsub, 'configure', attrib={'node':str(collection)})
  1478: 		x = SubElement(configure, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1479: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1480: 		value1 = SubElement(field1, 'value')
  1481: 		value1.text = 'http://jabber.org/protocol/pubsub#node_config'
  1482: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#children'})
  1483: 		value2 = SubElement(field2, 'value')
  1484: 		value2.text = ''		# sould be all current children, except for node
  1485: 
  1486: 		def handler(stanza, callback):
  1487: 			print etree.tostring(stanza)
  1488: 
  1489: 		self.send(stanza, handler, return_function)
  1490: 
  1491: 	def time_based_subscribe(self, server, node, expire_time, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
  1492: 		"""Not yet implemented sanely."""
  1493: 		#<iq type='set'
  1494: 		#    from='us'
  1495: 		#    to='them'>
  1496: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1497: 		#    <options node='node_name' jid='jid'>
  1498: 		#        <x xmlns='jabber:x:data' type='submit'>
  1499: 		#          <field var='FORM_TYPE' type='hidden'>
  1500: 		#            <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
  1501: 		#          </field>
  1502: 		#          <field var='pubsub#expire'><value>expire_time</value></field>
  1503: 		#        </x>
  1504: 		#     </options>
  1505: 		#  </pubsub>
  1506: 		#</iq>
  1507: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1508: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub#subscription_options'})
  1509: 		options = SubElement(pubsub, 'options', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
  1510: 		x = SubElement(options, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1511: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1512: 		value1 = SubElement(field1, 'value')
  1513: 		value1.text = 'http://jabber.org/protocol/pubsub#subscribe_options'
  1514: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#expire'})
  1515: 		value2 = SubElement(field2, 'value')
  1516: 		value2.text = expire_time
  1517: 
  1518: 		def handler(stanza, callback):
  1519: 			print etree.tostring(stanza)
  1520: 
  1521: 		self.send(stanza, handler, return_function)
  1522: 
  1523: 	def renew_lease(self, server, node, new_expire, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
  1524: 		"""Not yet implemented sanely."""
  1525: 		#<iq type='set'
  1526: 		#    from='us'
  1527: 		#    to='them'>
  1528: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1529: 		#    <options node='node_name' jid='jid'>
  1530: 		#        <x xmlns='jabber:x:data' type='submit'>
  1531: 		#          <field var='FORM_TYPE' type='hidden'>
  1532: 		#            <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
  1533: 		#          </field>
  1534: 		#          <field var='pubsub#expire'><value>new_expire</value></field>
  1535: 		#        </x>
  1536: 		#     </options>
  1537: 		#  </pubsub>
  1538: 		#</iq>
  1539: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1540: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1541: 		options = SubElement(pubsub, 'options', attrib={'node':str(node), 'jid':self.get_jid(jid, True)})
  1542: 		x = SubElement(options, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1543: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1544: 		value1 = SubElement(field1, 'value')
  1545: 		value1.text = 'http://jabber.org/protocol/pubsub#subscribe_options'
  1546: 		field2 = SubElement(x, 'field', attrib={'var':'pubsub#expire'})
  1547: 		value2 = SubElement(field2, 'value')
  1548: 		value2.text = new_expire
  1549: 
  1550: 		def handler(stanza, callback):
  1551: 			print etree.tostring(stanza)
  1552: 
  1553: 		self.send(stanza, handler, return_function)
  1554: 
  1555: 	def keyword_filtered_subscription(self, server, node, subid, filters, jid=None, return_function=None, stanza_id=None):		#FIXME A LOT
  1556: 		"""Not yet implemented sanely."""
  1557: 		#<iq type='set'
  1558: 		#    from='bard@shakespeare.lit/globe'
  1559: 		#    to='pubsub.shakespeare.lit'
  1560: 		#    id='filter3'>
  1561: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1562: 		#    <options node='node'
  1563: 		#             jid='ourjid'
  1564: 		#             subid='subid'>
  1565: 		#        <x xmlns='jabber:x:data' type='submit'>
  1566: 		#          <field var='FORM_TYPE' type='hidden'>
  1567: 		#            <value>http://jabber.org/protocol/pubsub#subscribe_options</value>
  1568: 		#          </field>
  1569: 		#          <field var='http://shakespeare.lit/search#keyword'><value>filters</value></field>
  1570: 		#        </x>
  1571: 		#     </options>
  1572: 		#  </pubsub>
  1573: 		#</iq>
  1574: 		stanza = Element('iq', attrib={'type':'set', 'from':self.get_jid(), 'to':str(server)})
  1575: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1576: 		options = SubElement(pubsub, 'options', attrib={'node':str(node), 'jid':self.get_jid(jid, True), 'subid':subid})
  1577: 		x = SubElement(options, 'x', attrib={'xmlns':'jabber:x:data', 'type':'submit'})
  1578: 		field1 = SubElement(x, 'field', attrib={'var':'FORM_TYPE', 'type':'hidden'})
  1579: 		value1 = SubElement(field1, 'value')
  1580: 		value1.text = 'http://jabber.org/protocol/pubsub#subscribe_options'
  1581: 		filter_field = SubElement(x, 'field', attrib={'var':'http://shakespeare.lit/search#keyword'})
  1582: 		filter_value = SubElement(filter_field, "value")
  1583: 		filter_value.text = filters		## FIXME: Desperately :P
  1584: 
  1585: 		def handler(stanza, callback):
  1586: 			print etree.tostring(stanza)
  1587: 
  1588: 		self.send(stanza, handler, return_function)
  1589: 
  1590: 	def retrieve_subscriptions(self, server, node=None, return_function=None, stanza_id=None):
  1591: 		"""Retrieve any subscriptions which the current account has on
  1592: 		the given server. If a node is given, retrieve any subscriptions
  1593: 		which the current account has on the given node on the given
  1594: 		server.
  1595: 
  1596: 		If given, return_function is run upon a reply being received with
  1597: 		a dictionary in the format
  1598: 		{server_name:{node_name:{'jid':subscribed_jid, 'state':subscription_state, 'subid':subscription_id}}}
  1599: 		where 'jid', 'state' and 'subid' are literally those strings,
  1600: 		whilst server_name is the address of the server, node_name is a
  1601: 		string of the node ID, subscribed_jid is a string of the JID
  1602: 		used to subscribe to this node (which may include a resource,
  1603: 		depending on how the subscription was made), subscription_state
  1604: 		is a string of 'subscribed', 'pending' or 'unconfigured' and
  1605: 		subscription_id is an optional string of this subscription's
  1606: 		unique ID (depending whether the server gives subscriptions an
  1607: 		ID or not).
  1608: 
  1609: 		If an error is received, the return_function is run with False."""
  1610: 		# Server
  1611: 		#<iq type='get'
  1612: 		#    from='francisco@denmark.lit/barracks'
  1613: 		#    to='pubsub.shakespeare.lit'
  1614: 		#    id='subscriptions1'>
  1615: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1616: 		#    <subscriptions/>
  1617: 		#  </pubsub>
  1618: 		#</iq>
  1619: 		#Node
  1620: 		#<iq type='get'
  1621: 		#    from='francisco@denmark.lit/barracks'
  1622: 		#    to='pubsub.shakespeare.lit'
  1623: 		#    id='subscriptions2'>
  1624: 		#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1625: 		#    <subscriptions node='princely_musings'/>
  1626: 		#  </pubsub>
  1627: 		#</iq>
  1628: 		stanza = Element('iq', attrib={'type':'get', 'from':self.get_jid(), 'to':str(server)})		# Is it correct to use self.server here?
  1629: 		pubsub = SubElement(stanza, 'pubsub', attrib={'xmlns':'http://jabber.org/protocol/pubsub'})
  1630: 		if node is not None:
  1631: 			subscriptions = SubElement(pubsub, 'subscriptions', attrib={'node':str(node)})
  1632: 		else:
  1633: 			subscriptions = SubElement(pubsub, 'subscriptions')
  1634: 		def handler(stanza, callback):
  1635: 			#Subscriptions
  1636: 			#<iq type='result'
  1637: 			#    from='pubsub.shakespeare.lit'
  1638: 			#    to='francisco@denmark.lit'
  1639: 			#    id='subscriptions1'>
  1640: 			#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1641: 			#    <subscriptions>
  1642: 			#      <subscription node='node1' jid='francisco@denmark.lit' subscription='subscribed'/>
  1643: 			#      <subscription node='node2' jid='francisco@denmark.lit' subscription='subscribed'/>
  1644: 			#      <subscription node='node5' jid='francisco@denmark.lit' subscription='unconfigured'/>
  1645: 			#      <subscription node='node6' jid='francisco@denmark.lit' subscription='pending'/>
  1646: 			#    </subscriptions>
  1647: 			#  </pubsub>
  1648: 			#</iq>
  1649: 			#No Subscriptions
  1650: 			#<iq type='result'
  1651: 			#    from='pubsub.shakespeare.lit'
  1652: 			#    to='francisco@denmark.lit/barracks'
  1653: 			#    id='subscriptions1'>
  1654: 			#  <pubsub xmlns='http://jabber.org/protocol/pubsub'>
  1655: 			#    <subscriptions/>
  1656: 			#  </pubsub>
  1657: 			#</iq>
  1658: 
  1659: 			# Don't bother doing anything if we aren't going to act anyway
  1660: 			if callback is not None:
  1661: 				# Report errors as False
  1662: 				if stanza.get('type') == 'error':
  1663: 					callback(False)
  1664: 				# If we get a successful reply, there's a bit more work to do
  1665: 				elif stanza.get('type') == 'result':
  1666: 					# Get server name
  1667: 					reply_server = stanza.get('from')
  1668: 					# Initialise an empty dictionary
  1669: 					subscriptions_dict = {reply_server:{}}
  1670: 					# Look for any <subscriptions> elements
  1671: 					for subscriptions in stanza.xpath(".//{http://jabber.org/protocol/pubsub}subscriptions"):
  1672: 						# Look inside the <subscriptions> element for actual subscriptions
  1673: 						for subscription in subscriptions.xpath(".//{http://jabber.org/protocol/pubsub}subscription"):
  1674: 							# The subid attribute is optional, so check for it
  1675: 							if subscription.get('subid') is not None:
  1676: 								# If found then construct the reply using it
  1677: 								subscriptions_dict[reply_server][subscription.get('node')] = {\
  1678: 									'jid':subscription.get('jid'), \
  1679: 									'state':subscription.get('subscription'), \
  1680: 									'subid':subscription.get('subid')}
  1681: 							else:
  1682: 								# If not found then construct the reply without it
  1683: 								subscriptions_dict[reply_server][subscription.get('node')] = {\
  1684: 									'jid':subscription.get('jid'), \
  1685: 									'state':subscription.get('subscription')}
  1686: 					# Run the callback with the derived reply
  1687: 					callback(subscriptions_dict)
  1688: 
  1689: 		self.send(stanza, handler, return_function)
  1690: 
  1691: class Node(object):
  1692: 	"""Pointer to a PubSub Node."""
  1693: 
  1694: 	def __init__(self, server=None, name=None, jid=None, type=None, parent=None):
  1695: 		self.set_server(server)
  1696: 		self.set_name(name)
  1697: 		self.set_jid(jid)
  1698: 		self.set_type(type)
  1699: 		self.set_parent(parent)
  1700: 
  1701: 	def __str__(self):
  1702: 		return self.name
  1703: 
  1704: 	def set_server(self, server):
  1705: 		"""Sets the server which this Node object points to (does NOT
  1706: 		edit any actual nodes, only this pointer!)"""
  1707: 		if type(server) == type("string"):
  1708: 			self.server = Server(server)
  1709: 		elif type(server) == type(Server()):
  1710: 			self.server = server
  1711: 		else:
  1712: 			print "Error: server must be a string or a Server."
  1713: 
  1714: 	def set_name(self, name):
  1715: 		"""Sets the node name which this Node object points to (does NOT
  1716: 		edit any actual nodes, only this pointer!)"""
  1717: 		self.name = str(name)
  1718: 
  1719: 	def set_jid(self, jid):
  1720: 		self.jid = jid
  1721: 
  1722: 	def set_type(self, type):
  1723: 		"""Sets the type of this Node object. Does not edit the actual
  1724: 		node."""
  1725: 		self.type = type
  1726: 
  1727: 	def set_parent(self, parent):
  1728: 		"""Sets the parent collection node of this Node object. Does
  1729: 		not edit the actual node."""
  1730: 		self.parent = parent
  1731: 
  1732: 	def get_sub_nodes(self, client, callback=None):
  1733: 		"""Queries this node for its children. Passes a list of Nodes
  1734: 		it finds to the return_function when a reply is received."""
  1735: 		client.get_nodes(self.server, self, return_function=callback)
  1736: 
  1737: 	def get_items(self, client, callback=None):
  1738: 		"""TODO: Queries this node for the items it contains. Returns a list
  1739: 		of the strings contained in the items."""
  1740: 		client.get_items(self.server, self.name, return_function=callback)
  1741: 
  1742: 	def get_information(self, client, callback=None):
  1743: 		client.get_node_information(self.server, self, return_function=callback)
  1744: 
  1745: 	def make_sub_node(self, client, name, type, callback=None):
  1746: 		if self.type is "leaf":
  1747: 			raise TypeError('Leaf nodes cannot contain child nodes')
  1748: 		else:
  1749: 			if self.type is None:
  1750: 				print "Warning: Node type is not known, yet child node requested. This will fail for leaf nodes."
  1751: 			if type == 'leaf':
  1752: 				client.get_new_leaf_node()
  1753: 			elif type == 'collection':
  1754: 				client.get_new_collection_node()
  1755: 
  1756: 	def request_all_affiliated_entities(self, client, return_function=None):
  1757: 		client.request_all_affiliated_entities(self.server, self, return_function)
  1758: 
  1759: 	def modify_affiliations(self, client, affiliation_dictionary, return_function=None):
  1760: 		client.modify_affiliation(self.server, self, affiliation_dictionary, return_function)
  1761: 
  1762: 	def publish(self, client, body, id=None, return_function=None):
  1763: 		client.publish(self.server, self, body, id, None, return_function)
  1764: 
  1765: 	def subscribe(self, client, jid, return_function=None):
  1766: 		client.subscribe(self.server, self, jid, return_function)
  1767: 
  1768: class Server(object):
  1769: 
  1770: 	def __init__(self, name=None):
  1771: 		if name is not None:
  1772: 			self.set_name(name)
  1773: 
  1774: 	def set_name(self, name):
  1775: 		self.name = name
  1776: 
  1777: 	def __str__(self):
  1778: 		return self.name
  1779: 
  1780: 	def add_node(self, client, name, callback=None):
  1781: 		client.request_node(self, name, None, None, return_function=callback)
  1782: 
  1783: class JID(xmpp.JID, object):
  1784: 
  1785: 	def __init__(self, string='none@none'):
  1786: 		super(JID, self).__init__(string)
  1787: 		self.name = str(self)
  1788: 
  1789: class Item(object):
  1790: 	"""Something which has been, or can be, published to a Node."""
  1791: 
  1792: 	def __init__(name=None, jid=None, node=None):
  1793: 		self.name = name
  1794: 		self.jid = jid
  1795: 		self.node = node
  1796: 
  1797: class Form(object):
  1798: 	"""A form stores a list of fields, which can contain a list of
  1799: 	values. Each field and value is accessed with

Generated by git2html.