Changeset 49:50a84c44cbf1 for wokkel/disco.py
- Timestamp:
- Jan 12, 2009, 9:00:30 PM (13 years ago)
- Branch:
- default
- Convert:
- svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@157
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
wokkel/disco.py
r46 r49 1 1 # -*- test-case-name: wokkel.test.test_disco -*- 2 2 # 3 # Copyright (c) 2003-200 8Ralph Meijer3 # Copyright (c) 2003-2009 Ralph Meijer 4 4 # See LICENSE for details. 5 5 … … 12 12 13 13 from twisted.internet import defer 14 from twisted.words.protocols.jabber import error, jid 14 from twisted.words.protocols.jabber import error, jid, xmlstream 15 15 from twisted.words.xish import domish 16 16 17 from wokkel import data_form 17 18 from wokkel.iwokkel import IDisco 18 19 from wokkel.subprotocols import IQHandlerMixin, XMPPHandler … … 26 27 DISCO_ITEMS = IQ_GET + '/query[@xmlns="' + NS_DISCO_ITEMS + '"]' 27 28 28 class DiscoFeature(domish.Element): 29 """ 30 Element representing an XMPP service discovery feature. 31 """ 32 33 def __init__(self, feature): 34 domish.Element.__init__(self, (NS_DISCO_INFO, 'feature'), 35 attribs={'var': feature}) 36 37 38 class DiscoIdentity(domish.Element): 39 """ 40 Element representing an XMPP service discovery identity. 41 """ 42 43 def __init__(self, category, type, name = None): 44 domish.Element.__init__(self, (NS_DISCO_INFO, 'identity'), 45 attribs={'category': category, 46 'type': type}) 47 if name: 48 self['name'] = name 49 50 51 class DiscoItem(domish.Element): 52 """ 53 Element representing an XMPP service discovery item. 54 """ 55 56 def __init__(self, jid, node='', name=None): 57 domish.Element.__init__(self, (NS_DISCO_ITEMS, 'item'), 58 attribs={'jid': jid.full()}) 59 if node: 60 self['node'] = node 61 62 if name: 63 self['name'] = name 29 class DiscoFeature(unicode): 30 """ 31 XMPP service discovery feature. 32 33 This extends C{unicode} to convert to and from L{domish.Element}, but 34 further behaves identically. 35 """ 36 37 def toElement(self): 38 """ 39 Render to a DOM representation. 40 41 @rtype: L{domish.Element}. 42 """ 43 element = domish.Element((NS_DISCO_INFO, 'feature')) 44 element['var'] = unicode(self) 45 return element 46 47 48 @staticmethod 49 def fromElement(element): 50 """ 51 Parse a DOM representation into a L{DiscoFeature} instance. 52 53 @param element: Element that represents the disco feature. 54 @type element: L{domish.Element}. 55 @rtype L{DiscoFeature}. 56 """ 57 featureURI = element.getAttribute('var', u'') 58 feature = DiscoFeature(featureURI) 59 return feature 60 61 62 63 class DiscoIdentity(object): 64 """ 65 XMPP service discovery identity. 66 67 @ivar category: The identity category. 68 @type category: C{unicode} 69 @ivar type: The identity type. 70 @type type: C{unicode} 71 @ivar name: The optional natural language name for this entity. 72 @type name: C{unicode} 73 """ 74 75 def __init__(self, category, idType, name=None): 76 self.category = category 77 self.type = idType 78 self.name = name 79 80 81 def toElement(self): 82 """ 83 Generate a DOM representation. 84 85 @rtype: L{domish.Element}. 86 """ 87 element = domish.Element((NS_DISCO_INFO, 'identity')) 88 if self.category: 89 element['category'] = self.category 90 if self.type: 91 element['type'] = self.type 92 if self.name: 93 element['name'] = self.name 94 return element 95 96 97 @staticmethod 98 def fromElement(element): 99 """ 100 Parse a DOM representation into a L{DiscoIdentity} instance. 101 102 @param element: Element that represents the disco identity. 103 @type element: L{domish.Element}. 104 @rtype L{DiscoIdentity}. 105 """ 106 category = element.getAttribute('category') 107 idType = element.getAttribute('type') 108 name = element.getAttribute('name') 109 feature = DiscoIdentity(category, idType, name) 110 return feature 111 112 113 114 class DiscoInfo(object): 115 """ 116 XMPP service discovery info. 117 118 @ivar nodeIdentifier: The optional node this info applies to. 119 @type nodeIdentifier: C{unicode} 120 @ivar features: Features as L{DiscoFeature}. 121 @type features: C{set) 122 @ivar identities: Identities as a mapping from (category, type) to name, 123 all C{unicode}. 124 @type identities: C{dict} 125 @ivar extensions: Service discovery extensions as a mapping from the 126 extension form's C{FORM_TYPE} (C{unicode}) to 127 L{data_form.Form}. Forms with no C{FORM_TYPE} field 128 are mapped as C{None}. Note that multiple forms 129 with the same C{FORM_TYPE} have the last in sequence 130 prevail. 131 @type extensions: C{dict} 132 @ivar _items: Sequence of added items. 133 @type _items: C{list} 134 """ 135 136 def __init__(self): 137 self.nodeIdentifier = '' 138 self.features = set() 139 self.identities = {} 140 self.extensions = {} 141 self._items = [] 142 143 144 def __iter__(self): 145 """ 146 Iterator over sequence of items in the order added. 147 """ 148 return iter(self._items) 149 150 151 def append(self, item): 152 """ 153 Add a piece of service discovery info. 154 155 @param item: A feature, identity or extension form. 156 @type item: L{DiscoFeature}, L{DiscoIdentity} or L{data_form.Form} 157 """ 158 self._items.append(item) 159 160 if isinstance(item, DiscoFeature): 161 self.features.add(item) 162 elif isinstance(item, DiscoIdentity): 163 self.identities[(item.category, item.type)] = item.name 164 elif isinstance(item, data_form.Form): 165 self.extensions[item.formNamespace] = item 166 167 168 def toElement(self): 169 """ 170 Generate a DOM representation. 171 172 This takes the items added with C{append} to create a DOM 173 representation of service discovery information. 174 175 @rtype: L{domish.Element}. 176 """ 177 element = domish.Element((NS_DISCO_INFO, 'query')) 178 179 if self.nodeIdentifier: 180 element['node'] = self.nodeIdentifier 181 182 for item in self: 183 element.addChild(item.toElement()) 184 185 return element 186 187 188 @staticmethod 189 def fromElement(element): 190 """ 191 Parse a DOM representation into a L{DiscoInfo} instance. 192 193 @param element: Element that represents the disco info. 194 @type element: L{domish.Element}. 195 @rtype L{DiscoInfo}. 196 """ 197 198 info = DiscoInfo() 199 200 info.nodeIdentifier = element.getAttribute('node', '') 201 202 for child in element.elements(): 203 item = None 204 205 if (child.uri, child.name) == (NS_DISCO_INFO, 'feature'): 206 item = DiscoFeature.fromElement(child) 207 elif (child.uri, child.name) == (NS_DISCO_INFO, 'identity'): 208 item = DiscoIdentity.fromElement(child) 209 elif (child.uri, child.name) == (data_form.NS_X_DATA, 'x'): 210 item = data_form.Form.fromElement(child) 211 212 if item: 213 info.append(item) 214 215 return info 216 217 218 219 class DiscoItem(object): 220 """ 221 XMPP service discovery item. 222 223 @ivar entity: The entity holding the item. 224 @type entity: L{jid.JID} 225 @ivar nodeIdentifier: The optional node identifier for the item. 226 @type nodeIdentifier: C{unicode} 227 @ivar name: The optional natural language name for this entity. 228 @type name: C{unicode} 229 """ 230 231 def __init__(self, entity, nodeIdentifier='', name=None): 232 self.entity = entity 233 self.nodeIdentifier = nodeIdentifier 234 self.name = name 235 236 237 def toElement(self): 238 """ 239 Generate a DOM representation. 240 241 @rtype: L{domish.Element}. 242 """ 243 element = domish.Element((NS_DISCO_ITEMS, 'item')) 244 if self.entity: 245 element['jid'] = self.entity.full() 246 if self.nodeIdentifier: 247 element['node'] = self.nodeIdentifier 248 if self.name: 249 element['name'] = self.name 250 return element 251 252 253 @staticmethod 254 def fromElement(element): 255 """ 256 Parse a DOM representation into a L{DiscoItem} instance. 257 258 @param element: Element that represents the disco iitem. 259 @type element: L{domish.Element}. 260 @rtype L{DiscoItem}. 261 """ 262 try: 263 entity = jid.JID(element.getAttribute('jid', ' ')) 264 except jid.InvalidFormat: 265 entity = None 266 nodeIdentifier = element.getAttribute('node', '') 267 name = element.getAttribute('name') 268 feature = DiscoItem(entity, nodeIdentifier, name) 269 return feature 270 271 272 273 class DiscoItems(object): 274 """ 275 XMPP service discovery items. 276 277 @ivar nodeIdentifier: The optional node this info applies to. 278 @type nodeIdentifier: C{unicode} 279 @ivar _items: Sequence of added items. 280 @type _items: C{list} 281 """ 282 283 def __init__(self): 284 self.nodeIdentifier = '' 285 self._items = [] 286 287 288 def __iter__(self): 289 """ 290 Iterator over sequence of items in the order added. 291 """ 292 return iter(self._items) 293 294 295 def append(self, item): 296 """ 297 Append item to the sequence of items. 298 299 @param item: Item to be added. 300 @type item: L{DiscoItem} 301 """ 302 self._items.append(item) 303 304 305 def toElement(self): 306 """ 307 Generate a DOM representation. 308 309 This takes the items added with C{append} to create a DOM 310 representation of service discovery items. 311 312 @rtype: L{domish.Element}. 313 """ 314 element = domish.Element((NS_DISCO_ITEMS, 'query')) 315 316 if self.nodeIdentifier: 317 element['node'] = self.nodeIdentifier 318 319 for item in self: 320 element.addChild(item.toElement()) 321 322 return element 323 324 325 @staticmethod 326 def fromElement(element): 327 """ 328 Parse a DOM representation into a L{DiscoItems} instance. 329 330 @param element: Element that represents the disco items. 331 @type element: L{domish.Element}. 332 @rtype L{DiscoItems}. 333 """ 334 335 info = DiscoItems() 336 337 info.nodeIdentifier = element.getAttribute('node', '') 338 339 for child in element.elements(): 340 if (child.uri, child.name) == (NS_DISCO_ITEMS, 'item'): 341 item = DiscoItem.fromElement(child) 342 info.append(item) 343 344 return info 345 346 347 348 class _DiscoRequest(xmlstream.IQ): 349 """ 350 Element representing an XMPP service discovery request. 351 """ 352 353 def __init__(self, xs, namespace, nodeIdentifier=''): 354 """ 355 Initialize the request. 356 357 @param xs: XML Stream the request should go out on. 358 @type xs: L{xmlstream.XmlStream} 359 @param namespace: Request namespace. 360 @type namespace: C{str} 361 @param nodeIdentifier: Node to request info from. 362 @type nodeIdentifier: C{unicode} 363 """ 364 xmlstream.IQ.__init__(self, xs, "get") 365 query = self.addElement((namespace, 'query')) 366 if nodeIdentifier: 367 query['node'] = nodeIdentifier 368 369 370 371 class DiscoClientProtocol(XMPPHandler): 372 """ 373 XMPP Service Discovery client protocol. 374 """ 375 376 def requestInfo(self, entity, nodeIdentifier=''): 377 """ 378 Request information discovery from a node. 379 380 @param entity: Entity to send the request to. 381 @type entity: L{jid.JID} 382 @param nodeIdentifier: Optional node to request info from. 383 @type nodeIdentifier: C{unicode} 384 """ 385 386 request = _DiscoRequest(self.xmlstream, NS_DISCO_INFO, nodeIdentifier) 387 388 d = request.send(entity.full()) 389 d.addCallback(lambda iq: DiscoInfo.fromElement(iq.query)) 390 return d 391 392 393 def requestItems(self, entity, nodeIdentifier=''): 394 """ 395 Request items discovery from a node. 396 397 @param entity: Entity to send the request to. 398 @type entity: L{jid.JID} 399 @param nodeIdentifier: Optional node to request info from. 400 @type nodeIdentifier: C{unicode} 401 """ 402 403 request = _DiscoRequest(self.xmlstream, NS_DISCO_ITEMS, nodeIdentifier) 404 405 d = request.send(entity.full()) 406 d.addCallback(lambda iq: DiscoItems.fromElement(iq.query)) 407 return d 64 408 65 409 … … 97 441 raise error.StanzaError('item-not-found') 98 442 else: 99 response = domish.Element((NS_DISCO_INFO, 'query')) 100 if nodeIdentifier: 101 response['node'] = nodeIdentifier 443 response = DiscoInfo() 444 response.nodeIdentifier = nodeIdentifier 102 445 103 446 for item in info: 104 response.a ddChild(item)105 106 return response 447 response.append(item) 448 449 return response.toElement() 107 450 108 451 d = self.info(requestor, target, nodeIdentifier) … … 123 466 124 467 def toResponse(items): 125 response = domish.Element((NS_DISCO_ITEMS, 'query')) 126 if nodeIdentifier: 127 response['node'] = nodeIdentifier 468 response = DiscoItems() 469 response.nodeIdentifier = nodeIdentifier 128 470 129 471 for item in items: 130 response.a ddChild(item)131 132 return response 472 response.append(item) 473 474 return response.toElement() 133 475 134 476 d = self.items(requestor, target, nodeIdentifier)
Note: See TracChangeset
for help on using the changeset viewer.