2 NEPI, a framework to manage network experiments
3 Copyright (C) 2013 INRIA
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from nepi.util.logger import Logger
23 from sleekxmpp.exceptions import IqError, IqTimeout
25 import xml.etree.ElementTree as ET
27 # inherit from BaseXmpp and XMLStream classes
28 class OMFClient(sleekxmpp.ClientXMPP, Logger):
30 .. class:: Class Args :
32 :param jid: Jabber Id (= Xmpp Slice + Date)
34 :param password: Jabber Password (= Xmpp Password)
39 This class is an XMPP Client with customized method
43 def __init__(self, jid, password):
46 :param jid: Jabber Id (= Xmpp Slice + Date)
48 :param password: Jabber Password (= Xmpp Password)
53 Logger.__init__(self, "OMFClient")
55 sleekxmpp.ClientXMPP.__init__(self, jid, password)
57 self._registered = False
60 self.register_plugin('xep_0077') # In-band registration
61 self.register_plugin('xep_0030')
62 self.register_plugin('xep_0059')
63 self.register_plugin('xep_0060') # PubSub
65 self.add_event_handler("session_start", self.start)
66 self.add_event_handler("register", self.register)
67 self.add_event_handler("pubsub_publish", self.handle_omf_message)
71 """ Check if the client is ready
76 def start(self, event):
77 """ Send presence to the Xmppp Server. This function is called directly by the sleekXmpp library
82 self._server = "pubsub.%s" % self.boundjid.domain
84 def register(self, iq):
85 """ Register to the Xmppp Server. This function is called directly by the sleekXmpp library
89 msg = " %s already registered!" % self.boundjid
95 resp['register']['username'] = self.boundjid.user
96 resp['register']['password'] = self.password
100 msg = " Account created for %s!" % self.boundjid
102 self._registered = True
104 msg = " Could not register account: %s" % e.iq['error']['text']
107 msg = " No response from server."
110 def unregister(self):
111 """ Unregister from the Xmppp Server.
115 self.plugin['xep_0077'].cancel_registration(
116 ifrom=self.boundjid.full)
117 msg = " Account unregistered for %s!" % self.boundjid
120 msg = " Could not unregister account: %s" % e.iq['error']['text']
123 msg = " No response from server."
127 """ Get all the nodes of the Xmppp Server.
131 result = self['xep_0060'].get_nodes(self._server)
132 for item in result['disco_items']['items']:
133 msg = ' - %s' % str(item)
137 error = traceback.format_exc()
138 msg = 'Could not retrieve node list.\ntraceback:\n%s' % error
141 def subscriptions(self):
142 """ Get all the subscriptions of the Xmppp Server.
146 result = self['xep_0060'].get_subscriptions(self._server)
148 for node in result['node']:
149 msg = ' - %s' % str(node)
153 error = traceback.format_exc()
154 msg = ' Could not retrieve subscriptions.\ntraceback:\n%s' % error
157 def create(self, node):
158 """ Create the topic corresponding to the node
160 :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
164 self.debug(" Create Topic : " + node)
166 config = self['xep_0004'].makeForm('submit')
167 config.add_field(var='pubsub#node_type', value='leaf')
168 config.add_field(var='pubsub#notify_retract', value='0')
169 config.add_field(var='pubsub#publish_model', value='open')
170 config.add_field(var='pubsub#persist_items', value='1')
171 config.add_field(var='pubsub#max_items', value='1')
172 config.add_field(var='pubsub#title', value=node)
175 self['xep_0060'].create_node(self._server, node, config = config)
177 error = traceback.format_exc()
178 msg = ' Could not create topic: %s\ntraceback:\n%s' % (node, error)
181 def delete(self, node):
182 """ Delete the topic corresponding to the node
184 :param node: Name of the topic, corresponding to the node (ex : omf.plexus.wlab17)
188 # To check if the queue are well empty at the end
189 #print " length of the queue : " + str(self.send_queue.qsize())
190 #print " length of the queue : " + str(self.event_queue.qsize())
192 self['xep_0060'].delete_node(self._server, node)
193 msg = ' Deleted node: %s' % node
196 error = traceback.format_exc()
197 msg = ' Could not delete topic: %s\ntraceback:\n%s' % (node, error)
200 def publish(self, data, node):
201 """ Publish the data to the corresponding topic
203 :param data: Data that will be published
205 :param node: Name of the topic
210 msg = " Publish to Topic : " + node
213 result = self['xep_0060'].publish(self._server,node,payload=data)
214 # id = result['pubsub']['publish']['item']['id']
215 # print('Published at item id: %s' % id)
217 error = traceback.format_exc()
218 msg = ' Could not publish to: %s\ntraceback:\n%s' % (node, error)
224 :param data: data from which the items will be get back
230 result = self['xep_0060'].get_item(self._server, self.boundjid,
232 for item in result['pubsub']['items']['substanzas']:
233 msg = 'Retrieved item %s: %s' % (item['id'], tostring(item['payload']))
236 error = traceback.format_exc()
237 msg = ' Could not retrieve item %s from topic %s\ntraceback:\n%s' \
238 % (data, self.boundjid, error)
241 def retract(self, data):
244 :param data: data from which the item will be retracted
249 result = self['xep_0060'].retract(self._server, self.boundjid, data)
250 msg = ' Retracted item %s from topic %s' % (data, self.boundjid)
253 error = traceback.format_exc()
254 msg = 'Could not retract item %s from topic %s\ntraceback:\n%s' \
255 % (data, self.boundjid, error)
259 """ Purge the information in the server
263 result = self['xep_0060'].purge(self._server, self.boundjid)
264 msg = ' Purged all items from topic %s' % self.boundjid
267 error = traceback.format_exc()
268 msg = ' Could not purge items from topic %s\ntraceback:\n%s' \
269 % (self.boundjid, error)
272 def subscribe(self, node):
273 """ Subscribe to a topic
275 :param node: Name of the topic
280 result = self['xep_0060'].subscribe(self._server, node)
281 msg = ' Subscribed %s to topic %s' \
282 % (self.boundjid.user, node)
286 error = traceback.format_exc()
287 msg = ' Could not subscribe %s to topic %s\ntraceback:\n%s' \
288 % (self.boundjid.bare, node, error)
291 def unsubscribe(self, node):
292 """ Unsubscribe to a topic
294 :param node: Name of the topic
299 result = self['xep_0060'].unsubscribe(self._server, node)
300 msg = ' Unsubscribed %s from topic %s' % (self.boundjid.bare, node)
303 error = traceback.format_exc()
304 msg = ' Could not unsubscribe %s from topic %s\ntraceback:\n%s' \
305 % (self.boundjid.bare, node, error)
308 def _check_for_tag(self, root, namespaces, tag):
309 """ Check if an element markup is in the ElementTree
311 :param root: Root of the tree
312 :type root: ElementTree Element
313 :param namespaces: Namespaces of the element
314 :type namespaces: str
315 :param tag: Tag that will search in the tree
319 for element in root.iter(namespaces+tag):
325 def _check_output(self, root, namespaces):
326 """ Check the significative element in the answer and display it
328 :param root: Root of the tree
329 :type root: ElementTree Element
330 :param namespaces: Namespaces of the tree
331 :type namespaces: str
334 fields = ["TARGET", "REASON", "PATH", "APPID", "VALUE"]
337 msg = self._check_for_tag(root, namespaces, elt)
339 response = response + " " + msg.text + " :"
340 deb = self._check_for_tag(root, namespaces, "MESSAGE")
342 msg = response + " " + deb.text
347 def handle_omf_message(self, iq):
348 """ Handle published/received message
350 :param iq: Stanzas that is currently published/received
354 namespaces = "{http://jabber.org/protocol/pubsub}"
355 for i in iq['pubsub_event']['items']:
356 root = ET.fromstring(str(i))
357 self._check_output(root, namespaces)