def _broadcast_privacy_list_change(self, dest, src, node): # broadcast to all resolvers iq = domish.Element((None, 'iq')) iq['from'] = dest iq['type'] = 'set' iq['id'] = util.rand_str(8) nodeElement = iq.addElement((xmlstream2.NS_IQ_BLOCKING, node)) elem = nodeElement.addElement((None, 'item')) elem['jid'] = src from_component = util.component_jid(self.servername, util.COMPONENT_C2S) for server in self.keyring.hostlist(): if server != self.servername: iq['to'] = util.component_jid(server, util.COMPONENT_C2S) self.send_wrapped(iq, from_component)
def broadcast_public_key(self, userid, keydata): """Broadcasts to all remote c2s the given public key.""" # create vcard iq_vcard = domish.Element((None, 'iq')) iq_vcard['type'] = 'set' iq_vcard['from'] = util.userid_to_jid( userid, self.xmlstream.thisEntity.host).full() # add vcard vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard')) if keydata: vcard_key = vcard.addElement((None, 'key')) vcard_data = vcard_key.addElement((None, 'uri')) vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX + base64.b64encode(keydata)) # send vcard to remote c2s for server in self.keyring.hostlist(): if server != self.servername: iq_vcard['id'] = util.rand_str(8) # remote server iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver( "/iq[@id='%s']" % iq_vcard['id'], self.consume, 500) # send! self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
def forward_check(self, stanza, fn, componentfn): if not stanza.consumed: if stanza['to'] == util.component_jid(self.parent.servername, util.COMPONENT_C2S): return componentfn(stanza) else: return fn(stanza)
def handle(self, stanza): # enforce sender stanza['from'] = self.resolveJID(self.xmlstream.otherEntity).full() to = stanza.getAttribute('to') if to is not None: try: to = jid.JID(to) except: # invalid destination, consume stanza and return error stanza.consumed = True log.debug("invalid address: %s" % (to, )) e = error.StanzaError('jid-malformed', 'modify') self.send(e.toResponse(stanza)) return # stanza is for us if to.host == self.network: # sending to full JID, forward to router if to.user is not None and to.resource is not None: self.forward(stanza) # stanza is not intended to component either elif to.host != util.component_jid(self.servername, util.COMPONENT_C2S): self.forward(stanza)
def resolveJID(self, _jid): """Transform host attribute of JID from network name to component name.""" host = util.component_jid(self.servername, util.COMPONENT_C2S) if isinstance(_jid, jid.JID): return jid.JID(tuple=(_jid.user, host, _jid.resource)) else: _jid = jid.JID(_jid) _jid.host = host return _jid
def _presence_data(self, presence): log.debug("presence: %r" % (presence, )) if type(presence) == list and len(presence) > 0: for user in presence: # store fingerprint self.keyring.set_fingerprint(user['userid'], user['fingerprint']) host = util.component_jid(self.servername, util.COMPONENT_C2S) response_from = jid.JID(tuple=(user['userid'], host, None)).full() num_avail = 0 try: streams = self.sfactory.streams[user['userid']] for x in streams.itervalues(): presence = x._presence if presence and not presence.hasAttribute('type'): self.cache.user_available(presence) if self.logTraffic: log.debug("local presence: %s" % (presence.toXml().encode('utf-8'), )) else: log.debug("local presence: %s" % (presence['from'], )) num_avail += 1 except KeyError: pass # no available resources - send unavailable presence if not num_avail: response = domish.Element((None, 'presence')) response['from'] = response_from if user['status'] is not None: response.addElement((None, 'status'), content=user['status']) if user['show'] is not None: response.addElement((None, 'show'), content=user['show']) response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = user['timestamp'].strftime( xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) self.cache.user_unavailable(response) if self.logTraffic: log.debug("local presence: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("local presence: %s" % (response['from'], ))
def allow(self, stanza, sender=None): jid_from = jid.JID(stanza['from']) broadcast = (jid_from.host == util.component_jid(self.parent.servername, util.COMPONENT_C2S)) items = stanza.allow.elements(uri=xmlstream2.NS_IQ_BLOCKING, name='item') if items: self._whitelist(jid_from, items, broadcast=broadcast) if broadcast: self.parent.result(stanza)
def unblock(self, stanza): jid_from = jid.JID(stanza['from']) broadcast = (jid_from.host == util.component_jid(self.parent.servername, util.COMPONENT_C2S)) items = stanza.unblock.elements(uri=xmlstream2.NS_IQ_BLOCKING, name='item') if items: self._blacklist(jid_from, items, True, broadcast=broadcast) if broadcast: self.parent.result(stanza)
def _presence_data(self, presence): log.debug("presence: %r" % (presence, )) if type(presence) == list and len(presence) > 0: for user in presence: # store fingerprint self.keyring.set_fingerprint(user['userid'], user['fingerprint']) host = util.component_jid(self.servername, util.COMPONENT_C2S) response_from = jid.JID(tuple=(user['userid'], host, None)).full() num_avail = 0 try: streams = self.sfactory.streams[user['userid']] for x in streams.itervalues(): presence = x._presence if presence and not presence.hasAttribute('type'): self.cache.user_available(presence) if self.logTraffic: log.debug("local presence: %s" % (presence.toXml().encode('utf-8'), )) else: log.debug("local presence: %s" % (presence['from'], )) num_avail += 1 except KeyError: pass # no available resources - send unavailable presence if not num_avail: response = domish.Element((None, 'presence')) response['from'] = response_from if user['status'] is not None: response.addElement((None, 'status'), content=user['status']) if user['show'] is not None: response.addElement((None, 'show'), content=user['show']) response['type'] = 'unavailable' delay = domish.Element(('urn:xmpp:delay', 'delay')) delay['stamp'] = user['timestamp'].strftime(xmlstream2.XMPP_STAMP_FORMAT) response.addChild(delay) self.cache.user_unavailable(response) if self.logTraffic: log.debug("local presence: %s" % (response.toXml().encode('utf-8'), )) else: log.debug("local presence: %s" % (response['from'], ))
def send_privacy_lists(self, pname, plist, addr_from): sender = util.component_jid(self.parent.servername, util.COMPONENT_C2S) for user, wl in plist.iteritems(): iq = domish.Element((None, 'iq')) iq['from'] = '%s@%s' % (user, self.parent.network) iq['type'] = 'set' iq['id'] = util.rand_str(8) iq['to'] = addr_from allow = iq.addElement((xmlstream2.NS_IQ_BLOCKING, pname)) for item in wl: elem = allow.addElement((None, 'item')) elem['jid'] = item self.parent.send_wrapped(iq, sender)
def handle(self, stanza): to = stanza.getAttribute('to') if to is not None: try: to = jid.JID(to) except: # invalid destination, consume stanza and return error stanza.consumed = True log.debug("invalid address: %s" % (to, )) e = error.StanzaError('jid-malformed', 'modify') self.send(e.toResponse(stanza)) return # stanza is for us if to.host == self.network: # sending to full JID, forward to router if to.user is not None and to.resource is not None: self.forward(stanza) # stanza is not intended to component either elif to.host != util.component_jid(self.servername, util.COMPONENT_C2S): self.forward(stanza)
def broadcast_public_key(self, userid, keydata): """Broadcasts to all remote c2s the given public key.""" # create vcard iq_vcard = domish.Element((None, 'iq')) iq_vcard['type'] = 'set' iq_vcard['from'] = util.userid_to_jid(userid, self.xmlstream.thisEntity.host).full() # add vcard vcard = iq_vcard.addElement((xmlstream2.NS_XMPP_VCARD4, 'vcard')) if keydata: vcard_key = vcard.addElement((None, 'key')) vcard_data = vcard_key.addElement((None, 'uri')) vcard_data.addContent(xmlstream2.DATA_PGP_PREFIX + base64.b64encode(keydata)) # send vcard to remote c2s for server in self.keyring.hostlist(): if server != self.servername: iq_vcard['id'] = util.rand_str(8) # remote server iq_vcard['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % iq_vcard['id'], self.consume, 500) # send! self.send_wrapped(iq_vcard, self.xmlstream.thisEntity.full())
def bindNetRoute(self, host): name = util.component_jid(host, util.COMPONENT_NET) self.router.bind(name)
def send(self, stanza, force_delivery=False, force_bare=False, hold=False): """ Resolves stanza recipient and send the stanza to the router. @param stanza: the stanza to be sent @type stanza: domish.Element @param force_delivery: if true, deliver the stanza to the first available resource found if the intended one is not available @type force_delivery bool @param force_bare: if true, deliver the stanza to the bare JID despite of the indicated destination @type force_bare bool @param hold: this parameter is passed directly to the dispatch method, instructing it to not deliver the stanza and hold it in offline storage instead (e.g. delayed/unauthorized messages) @type hold bool """ # send raw xml if you really know what you are doing if not domish.IElement.providedBy(stanza): return component.Component.send(self, stanza) util.resetNamespace(stanza, component.NS_COMPONENT_ACCEPT) # force host in sender component_jid = util.component_jid(self.servername, util.COMPONENT_C2S) sender = jid.JID(stanza['from']) if sender.host == self.network: sender.host = component_jid stanza['from'] = sender.full() if not stanza.hasAttribute('to'): # no destination - send to router component.Component.send(self, stanza) return # save original recipient for later stanza['original-to'] = stanza['to'] to = jid.JID(stanza['to']) # stanza is intended to the network if to.full() == self.network: # TODO log.debug("stanza for the network: %s" % (stanza.toXml(), )) return # network JID - resolve and send to router elif to.host == self.network: rcpts = self.cache.lookup(to) log.debug("rcpts = %r" % (rcpts, )) if not rcpts: if not stanza.consumed: stanza.consumed = True log.debug("JID %s not found" % (to.full(), )) e = error.StanzaError('item-not-found', 'cancel') self.dispatch(e.toResponse(stanza)) else: log.debug("JID %s not found (stanza has been consumed)" % (to.full(), )) return else: """ Stanza delivery rules 1. deliver to all available resources 2. destination was a bare JID a. if all resources are unavailable, deliver to the first network bare JID (then return) 3. destination was a full JID: a. deliver to full JID """ log.debug("JID found: %r" % (rcpts, )) jids = rcpts.jids() # destination was a full JID if to.resource and not force_bare: # no available resources, deliver to bare JID if force delivery if len(jids) == 0 and force_delivery: stanza['to'] = rcpts.jid.userhost() self.dispatch(stanza, hold=hold) # deliver if resource is available else: sent = False for _to in jids: if _to.resource == to.resource: stanza['to'] = _to.full() self.dispatch(stanza, hold=hold) sent = True break # if sent=False it means that the intended resource has vanished # if force delivery is enabled, deliver to the first available resource if not sent and len(jids) > 0 and force_delivery: stanza['to'] = jids[0].full() self.dispatch(stanza, hold=hold) # destination was a bare JID else: log.debug("destination was a bare JID (force_bare=%s)" % (force_bare, )) log.debug("jids = %s, rcpts = %s" % (jids, rcpts)) # no available resources, send to first network bare JID if len(jids) == 0 or force_bare: stanza['to'] = rcpts.jid.userhost() self.dispatch(stanza, hold=hold) else: for _to in jids: stanza['to'] = _to.full() self.dispatch(stanza, hold=hold) # otherwise send to router else: self.dispatch(stanza, hold=hold)
def local_vcard(self, user, stanza): """ Called by SM when receiving a vCard from a local client. It checks vcard info (including public key) and if positive, sends the vCard to all remote c2s. @return: iq result or error stanza for the client """ if self.logTraffic: log.debug("client sent vcard: %s" % (stanza.toXml(), )) else: log.debug("client sent vcard: %s" % (stanza['to'], )) stanza.consumed = True stanza_to = stanza.getAttribute('to') if stanza_to: entity = jid.JID(stanza_to) else: entity = None # setting our own vcard if not entity or (entity.userhost() == user.userhost()): # TODO parse vcard for interesting sections if stanza.vcard.key is not None: # we do this because of the uri member in domish.Element keydata = stanza.vcard.key.firstChildElement() if keydata and keydata.name == 'uri': keydata = str(keydata) if keydata.startswith(xmlstream2.DATA_PGP_PREFIX): try: keydata = base64.b64decode(keydata[len(xmlstream2.DATA_PGP_PREFIX):]) except: log.debug("invalid base64 data") e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq # check key fp = self.keyring.check_user_key(keydata, user.user) if fp: # generate response beforing tampering with the stanza response = xmlstream.toResponse(stanza, 'result') # update presencedb self.presencedb.public_key(user.user, fp) # send vcard to all remote c2s for server in self.keyring.hostlist(): if server != self.servername: stanza['id'] = util.rand_str(8) stanza['to'] = util.component_jid(server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver("/iq[@id='%s']" % stanza['id'], self.consume, 500) # wrap stanza in an envelope because we want errors to return to us self.send_wrapped(stanza, self.xmlstream.thisEntity.full()) # send response return response else: log.debug("invalid key - authorization to vCard denied") e = xmlstream.error.StanzaError('bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq else: log.debug("authorization to vCard denied") e = xmlstream.error.StanzaError('not-allowed', text='Not authorized.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq
def local_vcard(self, user, stanza): """ Called by SM when receiving a vCard from a local client. It checks vcard info (including public key) and if positive, sends the vCard to all remote c2s. @return: iq result or error stanza for the client """ if self.logTraffic: log.debug("client sent vcard: %s" % (stanza.toXml(), )) else: log.debug("client sent vcard: %s" % (stanza['to'], )) stanza.consumed = True stanza_to = stanza.getAttribute('to') if stanza_to: entity = jid.JID(stanza_to) else: entity = None # setting our own vcard if not entity or (entity.userhost() == user.userhost()): # TODO parse vcard for interesting sections if stanza.vcard.key is not None: # we do this because of the uri member in domish.Element keydata = stanza.vcard.key.firstChildElement() if keydata and keydata.name == 'uri': keydata = str(keydata) if keydata.startswith(xmlstream2.DATA_PGP_PREFIX): try: keydata = base64.b64decode( keydata[len(xmlstream2.DATA_PGP_PREFIX):]) except: log.debug("invalid base64 data") e = xmlstream.error.StanzaError( 'bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq # check key fp = self.keyring.check_user_key(keydata, user.user) if fp: # generate response beforing tampering with the stanza response = xmlstream.toResponse(stanza, 'result') # update presencedb self.presencedb.public_key(user.user, fp) # send vcard to all remote c2s for server in self.keyring.hostlist(): if server != self.servername: stanza['id'] = util.rand_str(8) stanza['to'] = util.component_jid( server, util.COMPONENT_C2S) # consume any response (very high priority) self.xmlstream.addOnetimeObserver( "/iq[@id='%s']" % stanza['id'], self.consume, 500) # wrap stanza in an envelope because we want errors to return to us self.send_wrapped( stanza, self.xmlstream.thisEntity.full()) # send response return response else: log.debug( "invalid key - authorization to vCard denied") e = xmlstream.error.StanzaError( 'bad-request', text='Invalid public key.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq else: log.debug("authorization to vCard denied") e = xmlstream.error.StanzaError('not-allowed', text='Not authorized.') iq = xmlstream.toResponse(stanza, 'error') iq.addChild(e.getElement()) return iq
def test_component_jid(self): host = 'localhost.localdomain' component = 'c2s' data = util.component_jid(host, component) self.assertEqual(data, 'c2s.localhost.localdomain')