Package pyxmpp :: Package jabber :: Module disco
[hide private]

Source Code for Module pyxmpp.jabber.disco

  1  # 
  2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net> 
  3  # 
  4  # This program is free software; you can redistribute it and/or modify 
  5  # it under the terms of the GNU Lesser General Public License Version 
  6  # 2.1 as published by the Free Software Foundation. 
  7  # 
  8  # This program is distributed in the hope that it will be useful, 
  9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 11  # GNU Lesser General Public License for more details. 
 12  # 
 13  # You should have received a copy of the GNU Lesser General Public 
 14  # License along with this program; if not, write to the Free Software 
 15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 16  # 
 17  # pylint: disable-msg=W0201 
 18  """Jabber Service Discovery support. 
 19   
 20  Normative reference: 
 21    - `JEP 30 <http://www.jabber.org/jeps/jep-0030.html>`__ 
 22  """ 
 23   
 24  __docformat__="restructuredtext en" 
 25   
 26  import warnings 
 27   
 28  import libxml2 
 29   
 30  from pyxmpp.xmlextra import common_doc,common_root 
 31  from pyxmpp.jid import JID 
 32  from pyxmpp import cache 
 33   
 34  from pyxmpp.utils import to_utf8 
 35  from pyxmpp.objects import StanzaPayloadWrapperObject 
 36  from pyxmpp.exceptions import ProtocolError 
 37   
 38  DISCO_NS="http://jabber.org/protocol/disco" 
 39  DISCO_ITEMS_NS=DISCO_NS+"#items" 
 40  DISCO_INFO_NS=DISCO_NS+"#info" 
 41   
42 -class DiscoItem(StanzaPayloadWrapperObject):
43 """An item of disco#items reply. 44 45 :Ivariables: 46 - `jid`: the JID of the item. 47 - `node`: node name of the item. 48 - `name`: name of the item. 49 - `action`: action of the item. 50 - `disco`: the disco reply this is the part of. 51 - `xmlnode`: XML element describing the item. 52 :Types: 53 - `jid`: `JID` 54 - `node`: `unicode` 55 - `name`: `unicode` 56 - `action`: `unicode` 57 - `disco`: `DiscoItems` 58 - `xmlnode`: `libxml2.xmlNode` 59 """
60 - def __init__(self,disco,xmlnode_or_jid,node=None,name=None,action=None):
61 """Initialize an `DiscoItem` object. 62 63 :Parameters: 64 - `disco`: the disco#items reply `self` is a part of. 65 - `xmlnode_or_jid`: XML element describing the item or the JID of 66 the item. 67 - `node`: disco node of the item. 68 - `name`: name of the item. 69 - `action`: 'action' attribute of the item. 70 :Types: 71 - `disco`: `DiscoItems` 72 - `xmlnode_or_jid`: `libxml2.xmlNode` or `JID` 73 - `node`: `unicode` 74 - `name`: `unicode` 75 - `action`: `unicode` 76 """ 77 self.disco=disco 78 if isinstance(xmlnode_or_jid,JID): 79 if disco: 80 self.xmlnode=disco.xmlnode.newChild(None,"item",None) 81 else: 82 self.xmlnode=common_root.newChild(None,"item",None) 83 ns=self.xmlnode.newNs(DISCO_ITEMS_NS,None) 84 self.xmlnode.setNs(ns) 85 self.set_jid(xmlnode_or_jid) 86 self.set_name(name) 87 self.set_node(node) 88 self.set_action(action) 89 else: 90 if disco is None: 91 self.xmlnode=xmlnode_or_jid.copyNode(1) 92 else: 93 self.xmlnode=xmlnode_or_jid 94 if name: 95 self.set_name(name) 96 if node: 97 self.set_node(node) 98 if action: 99 self.set_action(action) 100 self.xpath_ctxt=common_doc.xpathNewContext() 101 self.xpath_ctxt.setContextNode(self.xmlnode) 102 self.xpath_ctxt.xpathRegisterNs("d",DISCO_ITEMS_NS)
103
104 - def __del__(self):
105 if self.disco is None: 106 if self.xmlnode: 107 self.xmlnode.unlinkNode() 108 self.xmlnode.freeNode() 109 self.xmlnode=None 110 if self.xpath_ctxt: 111 self.xpath_ctxt.xpathFreeContext()
112
113 - def __str__(self):
114 return self.xmlnode.serialize()
115
116 - def remove(self):
117 """Remove `self` from the containing `DiscoItems` object.""" 118 if self.disco is None: 119 return 120 self.xmlnode.unlinkNode() 121 oldns=self.xmlnode.ns() 122 ns=self.xmlnode.newNs(oldns.getContent(),None) 123 self.xmlnode.replaceNs(oldns,ns) 124 common_root.addChild(self.xmlnode()) 125 self.disco=None
126
127 - def get_name(self):
128 """Get the name of the item. 129 130 :return: the name of the item or `None`. 131 :returntype: `unicode`""" 132 name = self.xmlnode.prop("name") 133 if name is None: 134 return None 135 return name.decode("utf-8")
136
137 - def set_name(self, name):
138 """Set the name of the item. 139 140 :Parameters: 141 - `name`: the new name or `None`. 142 :Types: 143 - `name`: `unicode` """ 144 if name is None: 145 if self.xmlnode.hasProp("name"): 146 self.xmlnode.unsetProp("name") 147 return 148 name = unicode(name) 149 self.xmlnode.setProp("name", name.encode("utf-8"))
150 151 name = property(get_name, set_name) 152
153 - def get_node(self):
154 """Get the node of the item. 155 156 :return: the node of the item or `None`. 157 :returntype: `unicode`""" 158 node = self.xmlnode.prop("node") 159 if node is None: 160 return None 161 return node.decode("utf-8")
162
163 - def set_node(self,node):
164 """Set the node of the item. 165 166 :Parameters: 167 - `node`: the new node or `None`. 168 :Types: 169 - `node`: `unicode` 170 """ 171 if node is None: 172 if self.xmlnode.hasProp("node"): 173 self.xmlnode.unsetProp("node") 174 return 175 node = unicode(node) 176 self.xmlnode.setProp("node", node.encode("utf-8"))
177 178 node = property(get_node, set_node) 179
180 - def get_action(self):
181 """Get the action attribute of the item. 182 183 :return: the action of the item or `None`. 184 :returntype: `unicode`""" 185 action=self.xmlnode.prop("action") 186 if action is None: 187 return None 188 return action.decode("utf-8")
189
190 - def set_action(self,action):
191 """Set the action of the item. 192 193 :Parameters: 194 - `action`: the new action or `None`. 195 :Types: 196 - `action`: `unicode` 197 """ 198 if action is None: 199 if self.xmlnode.hasProp("action"): 200 self.xmlnode.unsetProp("action") 201 return 202 if action not in ("remove","update"): 203 raise ValueError, "Action must be 'update' or 'remove'" 204 action = unicode(action) 205 self.xmlnode.setProp("action", action.encode("utf-8"))
206 207 action = property(get_action, set_action) 208
209 - def get_jid(self):
210 """Get the JID of the item. 211 212 :return: the JID of the item. 213 :returntype: `JID`""" 214 jid = self.xmlnode.prop("jid") 215 return JID( jid.decode("utf-8") )
216
217 - def set_jid(self,jid):
218 """Set the JID of the item. 219 220 :Parameters: 221 - `jid`: the new jid. 222 :Types: 223 - `jid`: `JID` 224 """ 225 self.xmlnode.setProp("jid", jid.as_unicode().encode("utf-8"))
226 227 jid = property(get_jid, set_jid)
228
229 -class DiscoIdentity(StanzaPayloadWrapperObject):
230 """An <identity/> element of disco#info reply. 231 232 Identifies an item by its name, category and type. 233 234 :Ivariables: 235 - `disco`: the disco reply this is the part of. 236 - `xmlnode`: XML element describing the identity. 237 :Types: 238 - `disco`: `DiscoInfo` 239 - `xmlnode`: `libxml2.xmlNode` 240 """
241 - def __init__(self, disco, xmlnode_or_name, item_category=None, item_type=None, replace=False):
242 """Initialize an `DiscoIdentity` object. 243 244 :Parameters: 245 - `disco`: the disco#info reply `self` is a part of. 246 - `xmlnode_or_name`: XML element describing the identity or the 247 name of the item described. 248 - `item_category`: category of the item described. 249 - `item_type`: type of the item described. 250 - `replace`: if `True` than all other <identity/> elements in 251 `disco` will be removed. 252 :Types: 253 - `disco`: `DiscoItems` 254 - `xmlnode_or_name`: `libxml2.xmlNode` or `unicode` 255 - `item_category`: `unicode` 256 - `item_type`: `unicode` 257 - `replace`: `bool` 258 """ 259 self.disco=disco 260 if disco and replace: 261 old=disco.xpath_ctxt.xpathEval("d:identity") 262 if old: 263 for n in old: 264 n.unlinkNode() 265 n.freeNode() 266 if isinstance(xmlnode_or_name,libxml2.xmlNode): 267 if disco is None: 268 self.xmlnode=xmlnode_or_name.copyNode(1) 269 else: 270 self.xmlnode=xmlnode_or_name 271 elif not item_category: 272 raise ValueError,"DiscoInfo requires category" 273 elif not item_type: 274 raise ValueError,"DiscoInfo requires type" 275 else: 276 if disco: 277 self.xmlnode=disco.xmlnode.newChild(None,"identity",None) 278 else: 279 self.xmlnode=common_root.newChild(None,"identity",None) 280 ns=self.xmlnode.newNs(DISCO_INFO_NS,None) 281 self.xmlnode.setNs(ns) 282 self.set_name(xmlnode_or_name) 283 self.set_category(item_category) 284 self.set_type(item_type) 285 self.xpath_ctxt=common_doc.xpathNewContext() 286 self.xpath_ctxt.setContextNode(self.xmlnode) 287 self.xpath_ctxt.xpathRegisterNs("d",DISCO_INFO_NS)
288
289 - def __del__(self):
290 if self.disco is None: 291 if self.xmlnode: 292 self.xmlnode.unlinkNode() 293 self.xmlnode.freeNode() 294 self.xmlnode=None 295 if self.xpath_ctxt: 296 self.xpath_ctxt.xpathFreeContext()
297
298 - def __str__(self):
299 return self.xmlnode.serialize()
300
301 - def remove(self):
302 """Remove `self` from the containing `DiscoInfo` object.""" 303 if self.disco is None: 304 return 305 self.xmlnode.unlinkNode() 306 oldns=self.xmlnode.ns() 307 ns=self.xmlnode.newNs(oldns.getContent(),None) 308 self.xmlnode.replaceNs(oldns,ns) 309 common_root.addChild(self.xmlnode()) 310 self.disco=None
311
312 - def get_name(self):
313 """Get the name of the item. 314 315 :return: the name of the item or `None`. 316 :returntype: `unicode`""" 317 var = self.xmlnode.prop("name") 318 if not var: 319 var = "" 320 return var.decode("utf-8")
321
322 - def set_name(self,name):
323 """Set the name of the item. 324 325 :Parameters: 326 - `name`: the new name or `None`. 327 :Types: 328 - `name`: `unicode` """ 329 if not name: 330 raise ValueError, "name is required in DiscoIdentity" 331 name = unicode(name) 332 self.xmlnode.setProp("name", name.encode("utf-8"))
333 334 name = property(get_name, set_name) 335
336 - def get_category(self):
337 """Get the category of the item. 338 339 :return: the category of the item. 340 :returntype: `unicode`""" 341 var = self.xmlnode.prop("category") 342 if not var: 343 var = "?" 344 return var.decode("utf-8")
345
346 - def set_category(self, category):
347 """Set the category of the item. 348 349 :Parameters: 350 - `category`: the new category. 351 :Types: 352 - `category`: `unicode` """ 353 if not category: 354 raise ValueError, "Category is required in DiscoIdentity" 355 category = unicode(category) 356 self.xmlnode.setProp("category", category.encode("utf-8"))
357 358 category = property(get_category, set_category) 359
360 - def get_type(self):
361 """Get the type of the item. 362 363 :return: the type of the item. 364 :returntype: `unicode`""" 365 item_type = self.xmlnode.prop("type") 366 if not item_type: 367 item_type = "?" 368 return item_type.decode("utf-8")
369
370 - def set_type(self, item_type):
371 """Set the type of the item. 372 373 :Parameters: 374 - `item_type`: the new type. 375 :Types: 376 - `item_type`: `unicode` """ 377 if not item_type: 378 raise ValueError,"Type is required in DiscoIdentity" 379 item_type = unicode(item_type) 380 self.xmlnode.setProp("type", item_type.encode("utf-8"))
381 382 type = property(get_type, set_type)
383
384 -class DiscoItems(StanzaPayloadWrapperObject):
385 """A disco#items response or publish-request object. 386 387 :Ivariables: 388 - `node`: node name of the disco#items element. 389 - `items`: items in the disco#items element. 390 - `xmlnode`: XML element listing the items. 391 :Types: 392 - `node`: `unicode` 393 - `items`: `tuple` of `DiscoItem` 394 - `xmlnode`: `libxml2.xmlNode` 395 """
396 - def __init__(self,xmlnode_or_node=None):
397 """Initialize an `DiscoItems` object. 398 399 Wrap an existing disco#items XML element or create a new one. 400 401 :Parameters: 402 - `xmlnode_or_node`: XML node to be wrapped into `self` or an item 403 node name. 404 :Types: 405 - `xmlnode_or_node`: `libxml2.xmlNode` or `unicode`""" 406 self.xmlnode=None 407 self.xpath_ctxt=None 408 if isinstance(xmlnode_or_node,libxml2.xmlNode): 409 ns=xmlnode_or_node.ns() 410 if ns.getContent() != DISCO_ITEMS_NS: 411 raise ValueError, "Bad disco-items namespace" 412 self.xmlnode=xmlnode_or_node.docCopyNode(common_doc,1) 413 common_root.addChild(self.xmlnode) 414 self.ns=self.xmlnode.ns() 415 else: 416 self.xmlnode=common_root.newChild(None,"query",None) 417 self.ns=self.xmlnode.newNs(DISCO_ITEMS_NS,None) 418 self.xmlnode.setNs(self.ns) 419 self.set_node(xmlnode_or_node) 420 self.xpath_ctxt=common_doc.xpathNewContext() 421 self.xpath_ctxt.setContextNode(self.xmlnode) 422 self.xpath_ctxt.xpathRegisterNs("d",DISCO_ITEMS_NS)
423
424 - def __del__(self):
425 if self.xmlnode: 426 self.xmlnode.unlinkNode() 427 self.xmlnode.freeNode() 428 self.xmlnode=None 429 if self.xpath_ctxt: 430 self.xpath_ctxt.xpathFreeContext() 431 self.xpath_ctxt=None
432
433 - def get_node(self):
434 """Get the node address of the `DiscoItems` object. 435 436 :return: the node name. 437 :returntype: `unicode`""" 438 node = self.xmlnode.prop("node") 439 if not node: 440 return None 441 return node.decode("utf-8")
442
443 - def set_node(self, node):
444 """Set the node of the disco#item element. 445 446 :Parameters: 447 - `node`: the new node or `None`. 448 :Types: 449 - `node`: `unicode` 450 """ 451 if node is None: 452 if self.xmlnode.hasProp("node"): 453 self.xmlnode.unsetProp("node") 454 return 455 node = unicode(node) 456 self.xmlnode.setProp("node", node.encode("utf-8"))
457 458 node = property(get_node, set_node) 459
460 - def get_items(self):
461 """Get the items contained in `self`. 462 463 :return: the items contained. 464 :returntype: `list` of `DiscoItem`""" 465 ret=[] 466 l=self.xpath_ctxt.xpathEval("d:item") 467 if l is not None: 468 for i in l: 469 ret.append(DiscoItem(self, i)) 470 return ret
471
472 - def set_items(self, item_list):
473 """Set items in the disco#items object. 474 475 All previous items are removed. 476 477 :Parameters: 478 - `item_list`: list of items or item properties 479 (jid,node,name,action). 480 :Types: 481 - `item_list`: sequence of `DiscoItem` or sequence of sequences 482 """ 483 for item in self.items: 484 item.remove() 485 for item in item_list: 486 try: 487 self.add_item(item.jid,item.node,item.name,item.action) 488 except AttributeError: 489 self.add_item(*item)
490 491 items = property(get_items, set_items, doc = "List of `DiscoItems`") 492
493 - def invalidate_items(self):
494 """Clear cached item list.""" 495 warnings.warn("DiscoItems.invalidate_items() is deprecated and not needed any more.", DeprecationWarning, stacklevel=1)
496
497 - def add_item(self,jid,node=None,name=None,action=None):
498 """Add a new item to the `DiscoItems` object. 499 500 :Parameters: 501 - `jid`: item JID. 502 - `node`: item node name. 503 - `name`: item name. 504 - `action`: action for a "disco push". 505 :Types: 506 - `jid`: `pyxmpp.JID` 507 - `node`: `unicode` 508 - `name`: `unicode` 509 - `action`: `unicode` 510 511 :returns: the item created. 512 :returntype: `DiscoItem`.""" 513 return DiscoItem(self,jid,node,name,action)
514
515 - def has_item(self,jid,node=None):
516 """Check if `self` contains an item. 517 518 :Parameters: 519 - `jid`: JID of the item. 520 - `node`: node name of the item. 521 :Types: 522 - `jid`: `JID` 523 - `node`: `libxml2.xmlNode` 524 525 :return: `True` if the item is found in `self`. 526 :returntype: `bool`""" 527 l=self.xpath_ctxt.xpathEval("d:item") 528 if l is None: 529 return False 530 for it in l: 531 di=DiscoItem(self,it) 532 if di.jid==jid and di.node==node: 533 return True 534 return False
535
536 -class DiscoInfo(StanzaPayloadWrapperObject):
537 """A disco#info response object. 538 539 :Ivariables: 540 - `node`: node name of the disco#info element (cached). 541 - `identities`: identities in the disco#info object. 542 - `features`: features in the disco#info object. 543 - `xmlnode`: XML element listing the items. 544 :Types: 545 - `node`: `unicode` 546 - `identities`: `tuple` of `DiscoIdentity` 547 - `features`: `tuple` of `unicode` 548 - `xmlnode`: `libxml2.xmlNode` 549 """
550 - def __init__(self,xmlnode_or_node=None, parent=None, doc=None):
551 """Initialize an `DiscoInfo` object. 552 553 Wrap an existing disco#info XML element or create a new one. 554 555 :Parameters: 556 - `xmlnode_or_node`: XML node to be wrapped into `self` or an item 557 node name. 558 - `parent`: parent node for the `DiscoInfo` element. 559 - `doc`: document for the `DiscoInfo` element. 560 :Types: 561 - `xmlnode_or_node`: `libxml2.xmlNode` or `unicode` 562 - `parent`: `libxml2.xmlNode` 563 - `doc`: `libxml2.xmlDoc` 564 """ 565 self.xmlnode=None 566 self.xpath_ctxt=None 567 if not doc: 568 doc=common_doc 569 if not parent: 570 parent=common_root 571 if isinstance(xmlnode_or_node,libxml2.xmlNode): 572 ns=xmlnode_or_node.ns() 573 if ns.getContent() != DISCO_INFO_NS: 574 raise ValueError, "Bad disco-info namespace" 575 self.xmlnode=xmlnode_or_node.docCopyNode(doc,1) 576 parent.addChild(self.xmlnode) 577 else: 578 self.xmlnode=parent.newChild(None,"query",None) 579 self.ns=self.xmlnode.newNs(DISCO_INFO_NS,None) 580 self.xmlnode.setNs(self.ns) 581 self.set_node(xmlnode_or_node) 582 583 self.xpath_ctxt=doc.xpathNewContext() 584 self.xpath_ctxt.setContextNode(self.xmlnode) 585 self.xpath_ctxt.xpathRegisterNs("d",DISCO_INFO_NS)
586
587 - def __del__(self):
588 if self.xmlnode: 589 self.xmlnode.unlinkNode() 590 self.xmlnode.freeNode() 591 self.xmlnode=None 592 if self.xpath_ctxt: 593 self.xpath_ctxt.xpathFreeContext() 594 self.xpath_ctxt=None
595
596 - def get_node(self):
597 """Get the node address of the `DiscoInfo` object. 598 599 :return: the node name. 600 :returntype: `unicode`""" 601 602 node=self.xmlnode.prop("node") 603 if not node: 604 return None 605 return node.decode("utf-8")
606
607 - def set_node(self,node):
608 """Set the node of the disco#info element. 609 610 :Parameters: 611 - `node`: the new node or `None`. 612 :Types: 613 - `node`: `unicode` 614 """ 615 if node is None: 616 if self.xmlnode.hasProp("node"): 617 self.xmlnode.unsetProp("node") 618 return 619 node = unicode(node) 620 self.xmlnode.setProp("node", node.encode("utf-8"))
621 622 node = property(get_node, set_node) 623
624 - def get_features(self):
625 """Get the features contained in `self`. 626 627 :return: the list of features. 628 :returntype: `list` of `unicode`""" 629 l = self.xpath_ctxt.xpathEval("d:feature") 630 ret = [] 631 for f in l: 632 if f.hasProp("var"): 633 ret.append( f.prop("var").decode("utf-8") ) 634 return ret
635
636 - def set_features(self, features):
637 """Set features in the disco#info object. 638 639 All existing features are removed from `self`. 640 641 :Parameters: 642 - `features`: list of features. 643 :Types: 644 - `features`: sequence of `unicode` 645 """ 646 for var in self.features: 647 self.remove_feature(var) 648 649 for var in features: 650 self.add_feature(var)
651 652 features = property(get_features, set_features) 653
654 - def has_feature(self,var):
655 """Check if `self` contains the named feature. 656 657 :Parameters: 658 - `var`: the feature name. 659 :Types: 660 - `var`: `unicode` 661 662 :return: `True` if the feature is found in `self`. 663 :returntype: `bool`""" 664 if not var: 665 raise ValueError,"var is None" 666 if '"' not in var: 667 expr=u'd:feature[@var="%s"]' % (var,) 668 elif "'" not in var: 669 expr=u"d:feature[@var='%s']" % (var,) 670 else: 671 raise ValueError, "Invalid feature name" 672 673 l=self.xpath_ctxt.xpathEval(to_utf8(expr)) 674 if l: 675 return True 676 else: 677 return False
678
679 - def invalidate_features(self):
680 """Clear cached feature list.""" 681 warnings.warn("DiscoInfo.invalidate_features() is deprecated and not needed any more.", DeprecationWarning, stacklevel=1)
682
683 - def add_feature(self,var):
684 """Add a feature to `self`. 685 686 :Parameters: 687 - `var`: the feature name. 688 :Types: 689 - `var`: `unicode`""" 690 if self.has_feature(var): 691 return 692 n=self.xmlnode.newChild(None, "feature", None) 693 n.setProp("var", to_utf8(var))
694
695 - def remove_feature(self,var):
696 """Remove a feature from `self`. 697 698 :Parameters: 699 - `var`: the feature name. 700 :Types: 701 - `var`: `unicode`""" 702 if not var: 703 raise ValueError,"var is None" 704 if '"' not in var: 705 expr='d:feature[@var="%s"]' % (var,) 706 elif "'" not in var: 707 expr="d:feature[@var='%s']" % (var,) 708 else: 709 raise ValueError, "Invalid feature name" 710 711 l=self.xpath_ctxt.xpathEval(expr) 712 if not l: 713 return 714 715 for f in l: 716 f.unlinkNode() 717 f.freeNode()
718
719 - def get_identities(self):
720 """List the identity objects contained in `self`. 721 722 :return: the list of identities. 723 :returntype: `list` of `DiscoIdentity`""" 724 ret=[] 725 l=self.xpath_ctxt.xpathEval("d:identity") 726 if l is not None: 727 for i in l: 728 ret.append(DiscoIdentity(self,i)) 729 return ret
730
731 - def set_identities(self,identities):
732 """Set identities in the disco#info object. 733 734 Remove all existing identities from `self`. 735 736 :Parameters: 737 - `identities`: list of identities or identity properties 738 (jid,node,category,type,name). 739 :Types: 740 - `identities`: sequence of `DiscoIdentity` or sequence of sequences 741 """ 742 for identity in self.identities: 743 identity.remove() 744 for identity in identities: 745 try: 746 self.add_identity(identity.name,identity.category,identity.type) 747 except AttributeError: 748 self.add_identity(*identity)
749 750 identities = property(get_identities, set_identities) 751
752 - def identity_is(self,item_category,item_type=None):
753 """Check if the item described by `self` belongs to the given category 754 and type. 755 756 :Parameters: 757 - `item_category`: the category name. 758 - `item_type`: the type name. If `None` then only the category is 759 checked. 760 :Types: 761 - `item_category`: unicode 762 - `item_type`: unicode 763 764 :return: `True` if `self` contains at least one <identity/> object with 765 given type and category. 766 :returntype: `bool`""" 767 if not item_category: 768 raise ValueError, "bad category" 769 if not item_type: 770 type_expr=u"" 771 elif '"' not in item_type: 772 type_expr=u' and @type="%s"' % (item_type,) 773 elif "'" not in type: 774 type_expr=u" and @type='%s'" % (item_type,) 775 else: 776 raise ValueError, "Invalid type name" 777 if '"' not in item_category: 778 expr=u'd:identity[@category="%s"%s]' % (item_category,type_expr) 779 elif "'" not in item_category: 780 expr=u"d:identity[@category='%s'%s]" % (item_category,type_expr) 781 else: 782 raise ValueError, "Invalid category name" 783 784 l=self.xpath_ctxt.xpathEval(to_utf8(expr)) 785 if l: 786 return True 787 else: 788 return False
789
790 - def invalidate_identities(self):
791 """Clear cached identity list.""" 792 warnings.warn("DiscoInfo.invalidate_identities() is deprecated and not needed any more.", DeprecationWarning, stacklevel=1)
793
794 - def add_identity(self,item_name,item_category=None,item_type=None):
795 """Add an identity to the `DiscoInfo` object. 796 797 :Parameters: 798 - `item_name`: name of the item. 799 - `item_category`: category of the item. 800 - `item_type`: type of the item. 801 :Types: 802 - `item_name`: `unicode` 803 - `item_category`: `unicode` 804 - `item_type`: `unicode` 805 806 :returns: the identity created. 807 :returntype: `DiscoIdentity`""" 808 return DiscoIdentity(self,item_name,item_category,item_type)
809
810 -class DiscoCacheFetcherBase(cache.CacheFetcher):
811 """Base class for disco cache fetchers. 812 813 :Cvariables: 814 - `stream`: stream used by the fetcher. 815 - `disco_class`: disco class to be used (`DiscoInfo` or `DiscoItems`). 816 :Types: 817 - `stream`: `pyxmpp.stream.Stream` 818 - `disco_class`: `classobj` 819 """ 820 stream=None 821 disco_class=None
822 - def fetch(self):
823 """Initialize the Service Discovery process.""" 824 from pyxmpp.iq import Iq 825 jid,node = self.address 826 iq = Iq(to_jid = jid, stanza_type = "get") 827 disco = self.disco_class(node) 828 iq.add_content(disco.xmlnode) 829 self.stream.set_response_handlers(iq,self.__response, self.__error, 830 self.__timeout) 831 self.stream.send(iq)
832
833 - def __response(self,stanza):
834 """Handle successful disco response. 835 836 :Parameters: 837 - `stanza`: the stanza received. 838 :Types: 839 - `stanza`: `pyxmpp.stanza.Stanza`""" 840 try: 841 d=self.disco_class(stanza.get_query()) 842 self.got_it(d) 843 except ValueError,e: 844 self.error(e)
845
846 - def __error(self,stanza):
847 """Handle disco error response. 848 849 :Parameters: 850 - `stanza`: the stanza received. 851 :Types: 852 - `stanza`: `pyxmpp.stanza.Stanza`""" 853 try: 854 self.error(stanza.get_error()) 855 except ProtocolError: 856 from pyxmpp.error import StanzaErrorNode 857 self.error(StanzaErrorNode("undefined-condition"))
858
859 - def __timeout(self,stanza):
860 """Handle disco timeout.""" 861 pass
862
863 -def register_disco_cache_fetchers(cache_suite,stream):
864 """Register Service Discovery cache fetchers into given 865 cache suite and using the stream provided. 866 867 :Parameters: 868 - `cache_suite`: the cache suite where the fetchers are to be 869 registered. 870 - `stream`: the stream to be used by the fetchers. 871 :Types: 872 - `cache_suite`: `cache.CacheSuite` 873 - `stream`: `pyxmpp.stream.Stream` 874 """ 875 tmp=stream 876 class DiscoInfoCacheFetcher(DiscoCacheFetcherBase): 877 """Cache fetcher for DiscoInfo.""" 878 stream=tmp 879 disco_class=DiscoInfo
880 class DiscoItemsCacheFetcher(DiscoCacheFetcherBase): 881 """Cache fetcher for DiscoItems.""" 882 stream=tmp 883 disco_class=DiscoItems 884 cache_suite.register_fetcher(DiscoInfo,DiscoInfoCacheFetcher) 885 cache_suite.register_fetcher(DiscoItems,DiscoItemsCacheFetcher) 886 887 # vi: sts=4 et sw=4 888