Changeset 660
- Timestamp:
- 05/12/08 10:26:46 (7 months ago)
- Files:
-
- oss/headstock/headstock/api/client (deleted)
- oss/headstock/headstock/api/dataform.py (modified) (9 diffs)
- oss/headstock/headstock/api/discovery.py (modified) (1 diff)
- oss/headstock/headstock/api/registration.py (modified) (1 diff)
- oss/headstock/headstock/api/storage (deleted)
- oss/headstock/headstock/api/stream.py (added)
- oss/headstock/headstock/example/simplechat/simplechat.py (modified) (12 diffs)
- oss/headstock/headstock/protocol/core/stream.py (modified) (8 diffs)
- oss/headstock/headstock/protocol/extension/discovery.py (modified) (1 diff)
- oss/headstock/headstock/protocol/extension/register.py (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
oss/headstock/headstock/api/dataform.py
r450 r660 11 11 self.type = form_type 12 12 self.reported = reported 13 self.instructions = None 13 14 self.title = None 14 15 self.fields = [] … … 23 24 return field 24 25 25 def from_element(cls, e): 26 d = Data(unicode(e.get_attribute('type'))) 26 @staticmethod 27 def from_element(e): 28 d = Data(e.get_attribute_value('type')) 27 29 title = e.get_child('title', e.xml_ns) 28 30 if title: 29 31 d.title = title.xml_text 32 33 instructions = e.get_child('instructions', e.xml_ns) 34 if instructions: 35 d.instructions = instructions.xml_text 30 36 31 37 fields = e.get_children('field', e.xml_ns) … … 38 44 39 45 return d 40 from_element = classmethod(from_element)41 46 42 def to_element(cls, d, parent=None): 47 @staticmethod 48 def to_element(d, parent=None): 43 49 x = E(u'x', attributes={u'type': d.type}, 44 50 namespace=XMPP_DATA_FORM_NS, parent=parent) 45 51 for field in d.fields: 46 52 Field.to_element(field, parent=x) 47 to_element = classmethod(to_element) 53 for item in d.items: 54 Item.to_element(item, parent=x) 48 55 49 56 class Field(object): … … 60 67 return '<Field "%s" (%s) at %s>' % (self.type or '', self.var or '', hex(id(self))) 61 68 62 def from_element(cls, e): 63 f = Field() 64 f.type = e.get_attribute('type') 65 if f.type: f.type = unicode(f.type) 66 f.var = e.get_attribute('var') 67 if f.var: f.var = unicode(f.var) 68 f.label = e.get_attribute('label') 69 if f.label: f.label = unicode(f.label) 70 required = e.get_child('required') 69 @staticmethod 70 def from_element(e): 71 f = Field(field_type=e.get_attribute_value('type'), 72 var=e.get_attribute_value('var')) 73 f.label = e.get_attribute_value('label') 74 required = e.get_child('required', e.xml_ns) 71 75 if required and required == 'true': 72 76 f.required = True 73 desc = e.get_child('desc' )77 desc = e.get_child('desc', e.xml_ns) 74 78 if desc: 75 79 f.desc = desc.xml_text … … 84 88 85 89 return f 86 from_element = classmethod(from_element)87 90 88 def to_element(cls, field, parent=None): 91 @staticmethod 92 def to_element(field, parent=None): 89 93 attrs = {} 90 94 if field.var: attrs[u'var'] = field.var … … 95 99 E(u'value', content=value, 96 100 namespace=XMPP_DATA_FORM_NS, parent=f) 97 to_element = classmethod(to_element)98 101 99 102 class Option(object): … … 105 108 return '<Option "%s" at %s>' % (self.value, hex(id(self))) 106 109 107 def from_element(cls, e): 108 o = Option() 109 o.value = e.get_child('value').xml_text 110 label = e.get_child('label') 111 if label: 112 o.label = label.xml_text 110 @staticmethod 111 def from_element(e): 112 o = Option(e.get_child('value', e.xml_ns).xml_text, 113 e.get_attribute_value('label')) 113 114 return o 114 from_element = classmethod(from_element)115 115 116 @staticmethod 117 def to_element(e, parent=None): 118 option = E(u'option', namespace=XMPP_DATA_FORM_NS, 119 attributes={u'label': e.label}, parent=parent) 120 E(u'value', namespace=XMPP_DATA_FORM_NS, 121 content=e.value, parent=option) 122 return option 123 124 116 125 class Item(object): 117 126 def __init__(self): … … 121 130 return '<Item at %s>' % (hex(id(self)),) 122 131 123 def from_element(cls, e): 132 @staticmethod 133 def from_element(e): 124 134 i = Item() 125 135 fields = e.get_children('field', e.xml_ns) … … 128 138 129 139 return i 130 from_element = classmethod(from_element) 140 141 @staticmethod 142 def to_element(e, parent=None): 143 item = E(u'item', namespace=XMPP_DATA_FORM_NS, parent=parent) 144 for field in e.fields: 145 Field.to_element(field, parent=item) 146 return item oss/headstock/headstock/api/discovery.py
r654 r660 1 #!/usr/bin/env python2 1 # -*- coding: utf-8 -*- 3 4 2 5 3 from headstock.lib.utils import generate_unique oss/headstock/headstock/api/registration.py
r450 r660 1 #!/usr/bin/env python2 1 # -*- coding: utf-8 -*- 3 2 4 from headstock.protocol.extension.inbandregistration import InBandRegistration5 3 from headstock.lib.utils import generate_unique 4 from headstock.protocol.core.jid import JID 6 5 from headstock.api.dataform import Data, Field 7 from headstock.api.storage import Entity 8 from bridge.common import XMPP_IBR_NS, XMPP_DATA_FORM_NS 6 from headstock.api import Entity 7 from headstock.api.error import Error 8 from bridge.common import XMPP_IBR_NS, XMPP_CLIENT_NS, XMPP_DATA_FORM_NS 9 9 from bridge import Element as E 10 10 11 __all__ = ['Registration Info', 'RegistrationInfoDataForm', 'Registration']11 __all__ = ['Registration'] 12 12 13 class RegistrationInfo(object): 14 def __init__(self): 15 self.stanza_id = None 13 class Registration(Entity): 14 def __init__(self, from_jid=None, to_jid=None, type=u'get', stanza_id=None): 15 Entity.__init__(self, from_jid, to_jid, type, stanza_id) 16 self.x = None 16 17 self.registered = False 17 self.fields = {} 18 19 def __repr__(self): 20 return '<RegistrationInfo at %s>' % (hex(id(self))) 18 self.remove = False 19 self.infos = {} 21 20 22 class RegistrationInfoDataForm(object): 23 def __init__(self):24 self.stanza_id = None25 self.registered = False26 self.form = Data(form_type=u'form')27 self.form.fields.append(Field(field_type=u'hidden',28 var=u'FORM_TYPE', values=[XMPP_IBR_NS]))29 30 def __repr__(self):31 return '<RegistrationInfoDataForm at %s>' % (hex(id(self)))21 @staticmethod 22 def from_element(e): 23 registration = Registration(JID.parse(e.get_attribute_value('from')), 24 JID.parse(e.get_attribute_value('to')), 25 type=e.get_attribute_value('type'), 26 stanza_id=e.get_attribute_value('id')) 27 28 error = e.get_child('error', XMPP_CLIENT_NS) 29 if error: 30 registration.error = Error.from_element(error) 32 31 33 class Registration(object): 34 def __init__(self, session): 35 self.session = session 32 query = e.get_child('query', XMPP_IBR_NS) 36 33 37 @classmethod 38 def from_element(cls, e): 39 instructions = e.get_child('instructions', XMPP_IBR_NS) 40 if instructions: 41 instructions = instructions.xml_text 34 for c in query.xml_children: 35 if not isinstance(c, E): 36 continue 37 38 if c.xml_ns == XMPP_DATA_FORM_NS: 39 registration.x = Date.from_element(c) 40 elif c.xml_ns == XMPP_IBR_NS: 41 if c.xml_name == 'remove': 42 registration.remove = True 43 elif c.xml_name == 'registered': 44 registration.registered = True 45 else: 46 registration.infos[c.xml_name] = c.xml_text 42 47 43 stanza_id = e.xml_parent.get_attribute('id') 44 use_data_form = e.get_child('x', XMPP_DATA_FORM_NS) 45 if use_data_form: 46 ri = RegistrationInfoDataForm() 47 ri.stanza_id = unicode(stanza_id) 48 ri.form = Data.from_element(use_data_form) 49 if e.has_child('registered', XMPP_IBR_NS): 50 ri.registered = True 51 else: 52 ri = RegistrationInfo() 53 ri.stanza_id = unicode(stanza_id) 54 for child in e.xml_children: 55 if child.xml_ns == XMPP_IBR_NS: 56 ri.fields[child.xml_name] = child.xml_text 48 return registration 49 57 50 58 if 'instructions' in ri.fields: 59 del ri.fields['instructions'] 51 @staticmethod 52 def to_element(e): 53 iq = Entity.to_element(e) 54 query = E(u'query', namespace=XMPP_IBR_NS, parent=iq) 55 56 if e.remove: 57 E(u'remove', namespace=XMPP_IBR_NS, parent=query) 58 59 if e.registered: 60 E(u'registered', namespace=XMPP_IBR_NS, parent=query) 60 61 61 if 'registered' in ri.fields:62 ri.registered = True63 del ri.fields['registered']62 for info in e.infos: 63 E(info, namespace=XMPP_IBR_NS, parent=query, 64 content=e.infos[info]) 64 65 65 return ri, instructions 66 if e.x: 67 Data.to_element(x, parent=query) 66 68 67 def ask_registration_fields(self): 68 iq = InBandRegistration.create_ibr_query(stanza_id=generate_unique()) 69 self.session.stream.propagate(element=iq) 70 71 def registration_fields_requested(self, ibr, e): 72 children = [E(u'instructions', namespace=XMPP_IBR_NS, 73 content=u'Choose a username and password to register with this server'), 74 E(u'username', namespace=XMPP_IBR_NS), 75 E(u'password', namespace=XMPP_IBR_NS), 76 E(u'email', namespace=XMPP_IBR_NS)] 77 iq = InBandRegistration.create_ibr_result(elements=children, stanza_id=generate_unique()) 78 self.stream.propagate(element=iq) 79 80 def registration_submitted(self, ibr, e): 81 ri, instructions = self._from_element(e) 82 e = Entity.lookup_by_username(ri.fields.get('username', None)) 83 if e != None: 84 query = E(u'query', namespace=XMPP_IBR_NS) 85 E(u'username', content=ri.fields.get('username', u''), namespace=XMPP_IBR_NS, parent=query) 86 E(u'password', content=ri.fields.get('password', u''), namespace=XMPP_IBR_NS, parent=query) 87 E(u'email', content=ri.fields.get('email', u''), namespace=XMPP_IBR_NS, parent=query) 88 89 iq = StanzaError.create_conflict(from_jid=self.stream.node_name, stanza_id=ri.stanza_id, 90 children = [query]) 91 self.stream.propagate(element=iq) 92 return 93 94 e = Entity() 95 e.username = ri.fields.get('username', u'') 96 e.password = ri.fields.get('password', u'') 97 e.email = ri.fields.get('email', u'') 98 self.storage.save(e) 69 return iq 99 70 100 iq = Iq.create_result_iq(stanza_id=generate_unique())101 self.stream.propagate(element=iq)102 103 def registration_success(self, ibr, e):104 if callable(self.registered_successfully):105 self.registered_successfully()106 107 def unregistration_success(self, ibr, e):108 pass109 110 def registration_fields_received(self, ibr, e):111 ri, instructions = self._from_element(e)112 113 def send_registration_details(self, ri):114 iq = InBandRegistration.create_ibr_registration(stanza_id=generate_unique())115 query = iq.get_child('query', XMPP_IBR_NS)116 117 if isinstance(ri, RegistrationInfo):118 for field in ri.fields:119 E(field, content=ri.fields[field], namespace=XMPP_IBR_NS,120 prefix=query.xml_prefix, parent=query)121 self.session.stream.propagate(element=iq)122 elif isinstance(ri, RegistrationInfoDataForm):123 Data.to_element(ri.form, parent=query)124 self.session.stream.propagate(element=iq)125 126 def cancel_registration(self, from_jid):127 iq = InBandRegistration.create_ibr_unregistration(from_jid=from_jid,128 stanza_id=generate_unique())129 self.session.stream.propagate(element=iq)130 131 def change_password(self, to_jid, username, password):132 iq = InBandRegistration.create_ibr_change_password(to_jid=to_jid,133 stanza_id=generate_unique(),134 username=username,135 password=password)136 self.session.stream.propagate(element=iq)137 138 def conflict_detected(self, error, e):139 ri, instructions = Registration.from_element(e)oss/headstock/headstock/example/simplechat/simplechat.py
r659 r660 38 38 from headstock.protocol.core.roster import RosterDispatcher, RosterNull 39 39 from headstock.protocol.core.message import MessageDispatcher, MessageEchoer 40 from headstock.protocol.extension.register import RegisterDispatcher 40 41 from headstock.protocol.extension.activity import ActivityDispatcher 41 42 from headstock.protocol.extension.discovery import DiscoveryDispatcher … … 48 49 from headstock.api import Entity 49 50 from headstock.api.activity import Activity 51 from headstock.api.registration import Registration 50 52 from headstock.lib.utils import generate_unique 51 53 52 54 from bridge import Element as E 53 55 from bridge.common import XMPP_CLIENT_NS, XMPP_ROSTER_NS, \ 54 XMPP_LAST_NS, XMPP_DISCO_INFO_NS 56 XMPP_LAST_NS, XMPP_DISCO_INFO_NS, XMPP_IBR_NS 55 57 56 58 __all__ = ['Client'] … … 387 389 388 390 391 class RegistrationHandler(component): 392 Inboxes = {"inbox" : "headstock.api.registration.Registration", 393 "error" : "headstock.api.registration.Registration", 394 "control" : "Shutdown the client stream",} 395 396 Outboxes = {"outbox" : "headstock.api.registration.Registration", 397 "signal" : "Shutdown signal", 398 "log" : "log",} 399 400 def __init__(self, username, password): 401 super(RegistrationHandler, self).__init__() 402 self.username = username 403 self.password = password 404 self.registration_id = None 405 406 def main(self): 407 while 1: 408 if self.dataReady("control"): 409 mes = self.recv("control") 410 411 if isinstance(mes, shutdownMicroprocess) or isinstance(mes, producerFinished): 412 self.send(producerFinished(), "signal") 413 break 414 415 if self.dataReady("inbox"): 416 r = self.recv('inbox') 417 if r.registered: 418 print "'%s' is already a registered username." % self.username 419 elif self.registration_id == r.stanza_id: 420 print "'%s' is now a registered user."\ 421 "Please restart the client without the register flag." % self.username 422 else: 423 if 'username' in r.infos and 'password' in r.infos: 424 self.registration_id = generate_unique() 425 r = Registration(type=u'set', stanza_id=self.registration_id) 426 r.infos[u'username'] = self.username 427 r.infos[u'password'] = self.password 428 self.send(r, 'outbox') 429 430 if self.dataReady("error"): 431 r = self.recv('error') 432 print r.error 433 434 if not self.anyReady(): 435 self.pause() 436 437 yield 1 438 389 439 class Client(component): 390 Inboxes = {"inbox" : "",391 "jid" : "",392 " control" : "Shutdown the client stream",393 }440 Inboxes = {"inbox" : "", 441 "jid" : "", 442 "streamfeat" : "", 443 "control" : "Shutdown the client stream"} 394 444 395 445 Outboxes = {"outbox" : "", 396 446 "forward" : "", 397 447 "log" : "", 448 "doauth" : "", 398 449 "signal" : "Shutdown signal", 399 }450 "doregistration" : ""} 400 451 401 452 def __init__(self, username, password, domain, resource=u"headstock-client1", 402 server=u'localhost', port=5222, usetls=False ):453 server=u'localhost', port=5222, usetls=False, register=False): 403 454 super(Client, self).__init__() 404 455 self.jid = JID(username, domain, resource) 456 self.username = username 405 457 self.password = password 406 458 self.server = server … … 410 462 self.domain = domain 411 463 self.usetls = usetls 464 self.register = register 412 465 413 466 def passwordLookup(self, jid): … … 416 469 def shutdown(self): 417 470 self.send(Presence.to_element(Presence(self.jid, type=u'unavailable')), 'forward') 471 self.send('OUTGOING : </stream:stream>', 'log') 472 self.send('</stream:stream>', 'outbox') 473 474 def abort(self): 418 475 self.send('OUTGOING : </stream:stream>', 'log') 419 476 self.send('</stream:stream>', 'outbox') … … 454 511 Pipeline(ConsoleReader(), PublishTo('CONSOLE')).activate() 455 512 456 # Add two outboxes ro the ClientSteam to support specific features. 513 # Add two outboxes ro the ClientSteam to support specific extensions. 514 ClientStream.Outboxes["%s.query" % XMPP_IBR_NS] = "Registration" 457 515 ClientStream.Outboxes["%s.query" % XMPP_LAST_NS] = "Activity" 458 516 ClientStream.Outboxes["%s.query" % XMPP_DISCO_INFO_NS] = "Discovery" … … 471 529 activityhandler = ActivityHandler(), 472 530 rosterhandler = RosterHandler(self.jid), 531 registerhandler = RegistrationHandler(self.username, self.password), 473 532 msgdummyhandler = DummyMessageHandler(), 474 533 presencehandler = PresenceHandler(), … … 478 537 discodisp = DiscoveryDispatcher(), 479 538 activitydisp = ActivityDispatcher(), 539 registerdisp = RegisterDispatcher(), 480 540 pjid = PublishTo("JID"), 481 541 pbound = PublishTo("BOUND"), … … 496 556 ("xmpp", "jid"): ("pjid", "inbox"), 497 557 ("xmpp", "bound"): ("pbound", "inbox"), 498 558 ("xmpp", "features"): ("client", "streamfeat"), 559 ("client", "doauth"): ("xmpp", "auth"), 560 561 # Registration 562 ("xmpp", "%s.query" % XMPP_IBR_NS): ("registerdisp", "inbox"), 563 ("registerdisp", "log"): ('logger', "inbox"), 564 ("registerdisp", "xmpp.error"): ("registerhandler", "error"), 565 ("registerdisp", "xmpp.result"): ("registerhandler", "inbox"), 566 ("registerhandler", "outbox"): ("registerdisp", "forward"), 567 ("client", "doregistration"): ("registerdisp", "forward"), 568 ("registerdisp", "outbox"): ("xmpp", "forward"), 569 499 570 # Presence 500 571 ("xmpp", "%s.presence" % XMPP_CLIENT_NS): ("presencedisp", "inbox"), … … 562 633 break 563 634 635 if self.dataReady("streamfeat"): 636 feat = self.recv('streamfeat') 637 if feat.register and self.register: 638 self.send(Registration(), 'doregistration') 639 elif self.register and not feat.register: 640 print "The server does not support in-band registration. Closing connection." 641 self.abort() 642 else: 643 self.send(feat, 'doauth') 644 564 645 if self.dataReady("jid"): 565 646 self.jid = self.recv('jid') … … 587 668 parser.add_option("-u", "--username", dest="username", 588 669 help="XMPP username", action="store") 670 parser.set_defaults(username=None) 589 671 parser.add_option("-p", "--password", action="store", dest="password", 590 672 help="XMPP password") 673 parser.set_defaults(password=None) 674 parser.add_option("-r", "--register", action="store_true", dest="register", 675 help="Register the user if the server supports it") 676 parser.set_defaults(register=False) 591 677 parser.add_option("-t", "--usetls", dest="usetls", action="store_true", 592 678 help="Use TLS") … … 603 689 unicode(options.domain), 604 690 server=host, port=int(port), 605 usetls=options.usetls) 691 usetls=options.usetls, 692 register=options.register) 606 693 client.run() 607 694 oss/headstock/headstock/protocol/core/stream.py
r654 r660 14 14 from bridge import Attribute as A 15 15 from bridge.common import XML_NS, XML_PREFIX, XMPP_CLIENT_NS, XMPP_STREAM_NS, XMPP_STREAM_PREFIX,\ 16 XMPP_SASL_NS, XMPP_SASL_PREFIX, XMPP_AUTH_NS, XMPP_TLS_NS, XMPP_CLIENT_NS, \16 XMPP_SASL_NS, XMPP_SASL_PREFIX, XMPP_AUTH_NS, XMPP_TLS_NS, XMPP_CLIENT_NS, XMPP_IBR_NS, \ 17 17 XMPP_BIND_NS, XMPP_SESSION_NS, XMPP_DISCO_ITEMS_NS, XMPP_ROSTER_NS, xmpp_bind_as_attr 18 18 from bridge.common import ANY_NAMESPACE … … 26 26 from headstock.lib.auth.digest import challenge_to_dict, compute_digest_response 27 27 from headstock.lib.utils import generate_unique, extract_from_stanza 28 from headstock.api.stream import StreamFeatures 28 29 29 30 __all__ = ['ClientStream', 'StreamError', 'SaslError'] … … 113 114 114 115 class ClientStream(component): 115 Inboxes = {"inbox" : "bridge.Element instance", 116 "control" : "Shutdown the client stream", 117 "forward": "bridge.Element instance to be sent out to the client", 116 Inboxes = {"inbox" : "bridge.Element instance", 117 "control" : "Shutdown the client stream", 118 "forward" : "bridge.Element instance to be sent out to the client", 119 "auth" : "Perform authentication", 118 120 "proceedtls": "", 119 121 "tlssuccess": "", … … 127 129 "starttls": "", 128 130 "jid" : "", 131 "features": "", 129 132 "bound" : "indicates the client has been successfully bound to an XMPP server", 130 133 "terminated": "Indicates the stream has been terminated by the peer", … … 195 198 self.log(e) 196 199 self.status = CONNECTED 197 mechanisms = e.get_child('mechanisms', ns=XMPP_SASL_NS) 198 support_tls = e.get_child('starttls', ns=XMPP_TLS_NS) 199 if support_tls and self.use_tls: 200 feat = StreamFeatures.from_element(e) 201 if feat.tls and self.use_tls: 200 202 tls = E(u'starttls', namespace=XMPP_TLS_NS) 201 203 self.propagate(element=tls) 202 elif mechanisms:203 self. _handle_mechanisms(mechanisms)204 204 elif feat.mechanisms: 205 self.send(feat, 'features') 206 205 207 def _handle_tls(self): 206 208 self._reset_stream_header(omit_decl=True) … … 225 227 self.propagate(element=response) 226 228 227 def _handle_mechanisms(self, e): 228 self.log(e) 229 mechanisms = [m.xml_text for m in e.xml_children if m.xml_name == 'mechanism'] 229 def _handle_auth(self, feat): 230 230 mechanism = None 231 231 232 232 # Always favour DIGEST-MD5 if supported by receiving entity 233 if u'DIGEST-MD5' in mechanisms:233 if u'DIGEST-MD5' in feat.mechanisms: 234 234 mechanism = u'DIGEST-MD5' 235 235 token = None 236 elif u'PLAIN' in mechanisms:236 elif u'PLAIN' in feat.mechanisms: 237 237 mechanism = u'PLAIN' 238 238 email = '%s@%s' % (self.jid.node, self.jid.domain) 239 239 password = self.password_lookup(self.jid) 240 240 token = generate_credential(email, self.jid.node, password) 241 elif u'X-GOOGLE-TOKEN' in mechanisms:241 elif u'X-GOOGLE-TOKEN' in feat.mechanisms: 242 242 mechanism = u'X-GOOGLE-TOKEN' 243 243 password = self.password_lookup(self.jid) 244 244 token = perform_authentication(self.jid.node, password) 245 elif u'ANONYMOUS' in mechanisms:245 elif u'ANONYMOUS' in feat.mechanisms: 246 246 mechanism = u'ANONYMOUS' 247 247 token = None … … 320 320 self._handle_tls() 321 321 yield 1 322 323 if self.dataReady("auth"): 324 feat = self.recv('auth') 325 self._handle_auth(feat) 322 326 323 327 if self.dataReady("forward"): … … 354 358 self._handle_jid(e) 355 359 self.send('', 'bound') 360 elif (e.xml_ns == XMPP_IBR_NS) and (e.xml_name == 'query'): 361 self.send(e, "%s.%s" % (e.xml_ns, e.xml_name)) 356 362 elif (e.xml_ns == XMPP_STREAM_NS) and (e.xml_name == 'error'): 357 363 self.send(e, "error") oss/headstock/headstock/protocol/extension/discovery.py
r654 r660 1 #!/usr/bin/env python2 1 # -*- coding: utf-8 -*- 3 2
