def _default_get_last_activity(self, jid: JID, node: str, ifrom: JID, iq: Iq) -> Iq: if not isinstance(iq, Iq): reply = self.xmpp.Iq() else: reply = iq.reply() if jid not in self._last_activities: raise XMPPError('service-unavailable') bare = JID(jid).bare if bare != self.xmpp.boundjid.bare: if bare in self.xmpp.roster[jid]: sub = self.xmpp.roster[jid][bare]['subscription'] if sub not in ('from', 'both'): raise XMPPError('forbidden') td = datetime.now() - self._last_activities[jid]['seconds'] seconds = td.seconds + td.days * 24 * 3600 status = self._last_activities[jid]['status'] reply['last_activity']['seconds'] = seconds reply['last_activity']['status'] = status return reply
def get_data(self): text = self.xml.text if not text: raise XMPPError('not-acceptable', 'IBB data element is empty.') b64_data = text.strip() if VALID_B64.match(b64_data).group() == b64_data: return from_b64(b64_data) else: raise XMPPError('not-acceptable')
def _handle_streamhost(self, iq): """Handle incoming SOCKS5 session request.""" sid = iq['socks']['sid'] if not sid: raise XMPPError(etype='modify', condition='bad-request') if not self._accept_stream(iq): raise XMPPError(etype='modify', condition='not-acceptable') streamhosts = iq['socks']['streamhosts'] requester = iq['from'] target = iq['to'] dest = self._get_dest_sha1(sid, requester, target) proxy_futures = [] for streamhost in streamhosts: proxy_futures.append( self._connect_proxy(dest, streamhost['host'], streamhost['port'])) @asyncio.coroutine def gather(futures, iq, streamhosts): proxies = yield from asyncio.gather(*futures, return_exceptions=True) for streamhost, proxy in zip(streamhosts, proxies): if isinstance(proxy, ValueError): continue elif isinstance(proxy, socket.error): log.error('Socket error while connecting to the proxy.') continue proxy = proxy[1] # TODO: what if the future never happens? try: addr, port = yield from proxy.connected except socket.error: log.exception( 'Socket error while connecting to the proxy.') continue # TODO: make a better choice than just the first working one. used_streamhost = streamhost['jid'] conn = proxy break else: raise XMPPError(etype='cancel', condition='item-not-found') # TODO: close properly the connection to the other proxies. iq = iq.reply() self._sessions[sid] = conn iq['socks']['sid'] = sid iq['socks']['streamhost_used']['jid'] = used_streamhost iq.send() self.xmpp.event('socks5_stream', conn) self.xmpp.event('stream:%s:%s' % (sid, requester), conn) asyncio. async (gather(proxy_futures, iq, streamhosts))
async def process(self, request): if 'username' not in request or \ 'resource' not in request: raise XMPPError('not-acceptable') username = request['username'] if not await self.auth.check_password(username, request.get('password', '')): raise XMPPError('not-authorized') self.boundjid.user = username self.boundjid.resource = request['resource']
def _handle_roster(self, iq): """Update the roster after receiving a roster stanza. :param iq: The roster stanza. """ if iq['type'] == 'set': if iq['from'].bare and iq['from'].bare != self.boundjid.bare: raise XMPPError(condition='service-unavailable') roster = self.client_roster if iq['roster']['ver']: roster.version = iq['roster']['ver'] items = iq['roster']['items'] valid_subscriptions = ('to', 'from', 'both', 'none', 'remove') for jid, item in items.items(): if item['subscription'] in valid_subscriptions: roster[jid]['name'] = item['name'] roster[jid]['groups'] = item['groups'] roster[jid]['from'] = item['subscription'] in ('from', 'both') roster[jid]['to'] = item['subscription'] in ('to', 'both') roster[jid]['pending_out'] = (item['ask'] == 'subscribe') roster[jid].save(remove=(item['subscription'] == 'remove')) if iq['type'] == 'set': resp = self.Iq(stype='result', sto=iq['from'], sid=iq['id']) resp.enable('roster') resp.send()
def message(msg): raise XMPPError(condition='feature-not-implemented', text="We don't do things that way here.", etype='cancel', extension='foo', extension_ns='foo:error', extension_args={'test': 'true'})
async def _handle_command_next(self, iq): """ Process a request for the next step in the workflow for a command with multiple steps. :param iq: The command continuation request. """ sessionid = iq['command']['sessionid'] session = self.sessions.get(sessionid) if session: handler = session['next'] interfaces = session['interfaces'] results = [] for stanza in iq['command']['substanzas']: if stanza.plugin_attrib in interfaces: results.append(stanza) if len(results) == 1: results = results[0] session = await _await_if_needed(handler, results, session) self._process_command_response(iq, session) else: raise XMPPError('item-not-found')
def _start_voting(self, iq, session): if iq['from'].bare not in self.xmpp.xsf_members: self.xmpp['xep_0050'].terminate_command(session) raise XMPPError('forbidden') ballot = self.xmpp['xsf_voting'].get_ballot() self.xmpp['xsf_voting'].get_session(iq['from']) form = self.xmpp['xep_0004'].stanza.Form() form['type'] = 'form' form['title'] = 'XSF Elections: %s' % iq['command']['node'] form['instructions'] = ( 'By proceeding, you affirm that you wish to have your ' 'vote count as a proxy vote in the official meeting to ' 'be held on %s' % ballot['date']) session['ballot'] = ballot for section in ballot['sections']: if section['title'] == iq['command']['node']: session['ballot_section'] = section break session['payload'] = form session['has_next'] = True if session['ballot_section']['limit']: session['next'] = self._handle_limited_voting else: session['next'] = self._handle_voting return session
def gather(futures, iq, streamhosts): proxies = yield from asyncio.gather(*futures, return_exceptions=True) for streamhost, proxy in zip(streamhosts, proxies): if isinstance(proxy, ValueError): continue elif isinstance(proxy, socket.error): log.error('Socket error while connecting to the proxy.') continue proxy = proxy[1] # TODO: what if the future never happens? try: addr, port = yield from proxy.connected except socket.error: log.exception('Socket error while connecting to the proxy.') continue # TODO: make a better choice than just the first working one. used_streamhost = streamhost['jid'] conn = proxy break else: raise XMPPError(etype='cancel', condition='item-not-found') # TODO: close properly the connection to the other proxies. iq = iq.reply() self._sessions[sid] = conn iq['socks']['sid'] = sid iq['socks']['streamhost_used']['jid'] = used_streamhost iq.send() self.xmpp.event('socks5_stream', conn) self.xmpp.event('stream:%s:%s' % (sid, requester), conn)
def _handle_command_cancel(self, iq): """ Process a request to cancel a command's execution. Arguments: iq -- The command cancellation request. """ node = iq['command']['node'] sessionid = iq['command']['sessionid'] session = self.sessions.get(sessionid) if session: handler = session['cancel'] if handler: handler(iq, session) del self.sessions[sessionid] iq = iq.reply() iq['command']['node'] = node iq['command']['sessionid'] = sessionid iq['command']['status'] = 'canceled' iq['command']['notes'] = session['notes'] iq.send() else: raise XMPPError('item-not-found')
def _add_jid(self, iq, session): if iq['from'].bare not in self._admins: raise XMPPError('forbidden') form = self.xmpp['xep_0004'].stanza.Form() form['type'] = 'form' form['title'] = 'Add XSF Member JID' form['instructions'] = 'Enter a JID for an XSF Member' form.add_field(var='jid', ftype='jid-single', title='JID', desc='XSF Member JID', required=True) session['payload'] = form session['has_next'] = False def handle_result(form, session): jid = JID(form['values']['jid']) if jid.bare not in self._members: self._members.add(jid) self._save_data() self.xmpp.event('xsf_jid_added', jid) session['payload'] = None session['next'] = None return session session['next'] = handle_result return session
def _handle_data(self, stanza): sid = stanza['ibb_data']['sid'] stream = self.api['get_stream'](stanza['to'], sid, stanza['from']) if stream is not None and stanza['from'] == stream.peer_jid: stream._recv_data(stanza) else: raise XMPPError('item-not-found')
def _handle_command_prev(self, iq): """ Process a request for the prev step in the workflow for a command with multiple steps. Arguments: iq -- The command continuation request. """ sessionid = iq['command']['sessionid'] session = self.sessions.get(sessionid) if session: handler = session['prev'] interfaces = session['interfaces'] results = [] for stanza in iq['command']['substanzas']: if stanza.plugin_attrib in interfaces: results.append(stanza) if len(results) == 1: results = results[0] session = handler(results, session) self._process_command_response(iq, session) else: raise XMPPError('item-not-found')
async def _get_roster(self, iq): try: roster = await self.stream.roster_hook.get_contacts( self.stream.boundjid) if roster is None: raise XMPPError('item-not-found') except Exception as e: iq.exception(e) return # When clients connect, they usually get the roster first, # then set initial presence, which also needs the roster. # We could cache the roster here so that the initial presence # wouldn't have to retrieve it again. However, that is a risky # thing to do; the client might never send presence, meaning # we'd be wasting memory, or the client might wait a while, # meaning our cached roster might be outdated (unless we keep # it up-to-date, which is possible but not implemented). # So for now, we won't cache the roster, but maybe we can # implement a safe way to do this in the future. #if not self.stream.presence.available: # self.cached = roster reply = iq.reply() items = reply['roster'] for jid, values in roster: item = roster_stanza.RosterItem() item.values = values item.set_jid(jid) items.append(item) reply.send()
def _recv_data(self, stanza): new_seq = stanza['ibb_data']['seq'] if new_seq != (self.recv_seq + 1) % 65535: self.close() raise XMPPError('unexpected-request') self.recv_seq = new_seq data = stanza['ibb_data']['data'] if len(data) > self.block_size: self.close() raise XMPPError('not-acceptable') self.recv_queue.put_nowait(data) self.xmpp.event('ibb_stream_data', self) if isinstance(stanza, Iq): stanza.reply().send()
def _reload(self, iq, session): if iq['from'].bare not in self._admins: raise XMPPError('forbidden') self._load_data() session['has_next'] = False session['payload'] = None return session
async def process(self, request): if request.xml.text: value = request['value'] else: value = await self.challenge() toks = value.split(b'\0') if len(toks) != 3: raise XMPPError('malformed-request') toks = [x.decode('utf8') for x in toks] username = toks[1] if not await self.auth.check_password(username, toks[2]): raise XMPPError('not-authorized') authcid = "%s@%s" % (username, self.stream.host) if toks[0] != '' and toks[0] != authcid: # authzid not supported yet raise XMPPError('invalid-authzid') self.boundjid.user = username
async def _handle_close(self, iq: Iq): sid = iq['ibb_close']['sid'] stream = await self.api['get_stream'](iq['to'], sid, iq['from']) if stream is not None and iq['from'] == stream.peer_jid: stream._closed(iq) await self.api['del_stream'](stream.self_jid, stream.sid, stream.peer_jid) else: raise XMPPError('item-not-found')
async def _handle_request(self, iq): profile = iq['si']['profile'] sid = iq['si']['id'] if not sid: raise XMPPError(etype='modify', condition='bad-request') if profile not in self._profiles: raise XMPPError( etype='modify', condition='bad-request', extension='bad-profile', extension_ns=SI.namespace) neg = iq['si']['feature_neg']['form'].get_fields() options = neg['stream-method']['options'] or [] methods = [] for opt in options: methods.append(opt['value']) for method in methods: if method in self._methods: supported = True break else: raise XMPPError('bad-request', extension='no-valid-streams', extension_ns=SI.namespace) selected_method = None log.debug('Available: %s', methods) for order, method, plugin in self._methods_order: log.debug('Testing: %s', method) if method in methods: selected_method = method break receiver = iq['to'] sender = iq['from'] await self.api['add_pending'](receiver, sid, sender, { 'response_id': iq['id'], 'method': selected_method, 'profile': profile }) self.xmpp.event('si_request', iq)
async def _update_contact(self, jid, values): if self.stream.kicked: raise XMPPError('forbidden') new_values = await self.stream.roster_hook.update_contact( self.stream.boundjid, jid, values) if new_values is None: # If the roster hook won't add the contact, # tell the client that it's removed new_values = {'subscription': 'remove'} return new_values
def _default_handler(self, iq): """ As a safe default, don't actually download files. Register a new handler using self.register_url_handler to screen requests and download files. :param iq: The Iq stanza containing the OOB transfer request. """ raise XMPPError('service-unavailable')
async def _set_roster(self, iq): try: self.delay_pushes += 1 items = {} try: contacts = iq['roster'].get_items() if len(contacts) != 1: raise XMPPError('bad-request') for jid, values in contacts.items(): if jid.bare == self.stream.boundjid.bare: raise XMPPError('not-allowed') if values.get('subscription', 'none') != 'remove': if jid.user == '': # the request might be addressed to a nickname; # I haven't found any reference for that actually # being allowed, so for now, treat that as a # query for a username on the local server # (while also telling the client to remove the # original request, just in case). items[jid] = {'subscription': 'remove'} jid = JID(jid) jid.user = jid.domain jid.domain = self.stream.host items[jid] = await self._update_contact(jid, values) else: items[jid] = await self._remove_contact(jid) except Exception as e: iq.exception(e) return iq.reply().send() # push to other resources of the same user del iq['from'] iq['roster'].set_items(items) await self.stream.ipc_send('roster.push', self.stream.boundjid, iq.xml) finally: self.delay_pushes -= 1 if self.delay_pushes == 0: pushes = self.delayed_pushes self.delayed_pushes = [] for xml, checked in pushes: await self._relay_push(xml, checked)
def fail_with(message, code): args = {'issuer': 'enedis-data-connect'} # TODO directly use a xml ns? if code is not None: args['code'] = code raise XMPPError(extension="upstream-error", extension_ns="urn:quoalise:0", extension_args=args, text=message, etype="cancel")
def _handle_command_complete(self, iq): """ Process a request to finish the execution of command and terminate the workflow. All data related to the command session will be removed. Arguments: iq -- The command completion request. """ node = iq['command']['node'] sessionid = iq['command']['sessionid'] session = self.sessions.get(sessionid) if session: handler = session['next'] interfaces = session['interfaces'] results = [] for stanza in iq['command']['substanzas']: if stanza.plugin_attrib in interfaces: results.append(stanza) if len(results) == 1: results = results[0] if handler: handler(results, session) del self.sessions[sessionid] payload = session['payload'] if payload is None: payload = [] if not isinstance(payload, list): payload = [payload] for item in payload: register_stanza_plugin(Command, item.__class__, iterable=True) iq = iq.reply() iq['command']['node'] = node iq['command']['sessionid'] = sessionid iq['command']['actions'] = [] iq['command']['status'] = 'completed' iq['command']['notes'] = session['notes'] for item in payload: iq['command'].append(item) iq.send() else: raise XMPPError('item-not-found')
async def _outbound_subscribed(self, msg): try: if self.stream.kicked: raise XMPPError('forbidden') user = self.stream.boundjid contact = msg['to'] msg['from'] = user.bare values = (await self.stream.roster_hook. outbound_subscribed(user, contact)) await self._handle_subscribed(msg, user, contact, values) except Exception as e: msg.exception(e) return
def _run_url_handler(self, iq): """ Execute the appropriate handler for a transfer request. :param iq: The Iq stanza containing the OOB transfer request. """ if iq['to'] in self.url_handlers['jid']: return self.url_handlers['jid'][iq['to']](iq) else: if self.url_handlers['global']: self.url_handlers['global'](iq) else: raise XMPPError('service-unavailable')
def get_items(self, jid, node, ifrom, data): """ Return the stored items data for the requested JID/node combination. The data parameter is not used. """ if not self.node_exists(jid, node): if not node: return DiscoItems() else: raise XMPPError(condition='item-not-found') else: return self.get_node(jid, node)['items']
async def _set_register(self, iq): if not settings.ALLOW_REGISTRATION: reply = iq.reply() reply['error']['condition'] = 'not-allowed' reply.send() return fields = iq['register'] try: if fields.get_remove(): # delete registration if 'mechanisms' not in self.stream.features: raise XMPPError('forbidden') username = self.stream.boundjid.user await self.stream.roster_hook.delete_user() self.stream.user_deleted() elif 'mechanisms' in self.stream.features: # update registration username = fields['username'] if username != self.stream.boundjid.user: raise XMPPError('bad-request') password = fields['password'] if password != '': await self.stream.roster_hook.change_password(password) else: # create registration # TODO: try to avoid registration spam username = fields['username'] password = fields['password'] if username == '' or password == '': raise XMPPError('not-acceptable') if not await self.stream.roster_hook.create_user( username, password): raise XMPPError('conflict') except Exception as e: iq.exception(e) return iq.reply().send()
def _handle_get_vcard(self, iq): if iq['type'] == 'result': self.api['set_vcard'](jid=iq['from'], args=iq['vcard_temp']) return elif iq['type'] == 'get' and self.xmpp.is_component: vcard = self.api['get_vcard'](iq['from'].bare) if isinstance(vcard, Iq): vcard.send() else: iq = iq.reply() iq.append(vcard) iq.send() elif iq['type'] == 'set': raise XMPPError('service-unavailable')
async def _handle_open_request(self, iq: Iq): sid = iq['ibb_open']['sid'] size = iq['ibb_open']['block_size'] or self.block_size log.debug('Received IBB stream request from %s', iq['from']) if not sid: raise XMPPError(etype='modify', condition='bad-request') if not await self._accept_stream(iq): raise XMPPError(etype='cancel', condition='not-acceptable') if size > self.max_block_size: raise XMPPError('resource-constraint') stream = IBBytestream(self.xmpp, sid, size, iq['to'], iq['from']) stream.stream_started = True await self.api['set_stream'](stream.self_jid, stream.sid, stream.peer_jid, stream) iq.reply().send() self.xmpp.event('ibb_stream_start', stream) self.xmpp.event('stream:%s:%s' % (sid, stream.peer_jid), stream)