def get_body(self, lang=None): """Return the contents of the HTML body.""" if lang is None: lang = self.get_lang() bodies = self.xml.findall('{%s}body' % XHTML_NS) if lang == '*': result = OrderedDict() for body in bodies: body_lang = body.attrib.get('{%s}lang' % self.xml_ns, '') body_result = [] body_result.append(body.text if body.text else '') for child in body: body_result.append(tostring(child, xmlns=XHTML_NS)) body_result.append(body.tail if body.tail else '') result[body_lang] = ''.join(body_result) return result else: for body in bodies: if body.attrib.get('{%s}lang' % self.xml_ns, self.get_lang()) == lang: result = [] result.append(body.text if body.text else '') for child in body: result.append(tostring(child, xmlns=XHTML_NS)) result.append(body.tail if body.tail else '') return ''.join(result) return ''
def send_xml(self, data): """Send an XML object on the stream :param data: The :class:`~xml.etree.ElementTree.Element` XML object to send on the stream. """ return self.send(tostring(data))
def send(self, data, use_filters=True): """A wrapper for :meth:`send_raw()` for sending stanza objects. May optionally block until an expected response is received. :param data: The :class:`~slixmpp.xmlstream.stanzabase.ElementBase` stanza to send on the stream. :param bool use_filters: Indicates if outgoing filters should be applied to the given stanza data. Disabling filters is useful when resending stanzas. Defaults to ``True``. """ if isinstance(data, ElementBase): if use_filters: for filter in self.__filters['out']: data = filter(data) if data is None: return if isinstance(data, ElementBase): if use_filters: for filter in self.__filters['out_sync']: data = filter(data) if data is None: return str_data = tostring(data.xml, xmlns=self.default_ns, stream=self, top_level=True) self.send_raw(str_data) else: self.send_raw(data)
async def _continue_slow_send( self, task: asyncio.Task, already_used: Set[Callable[[ElementBase], Optional[StanzaBase]]] ) -> None: """ Used when an item in the send queue has taken too long to process. This is away from the send queue and can take as much time as needed. :param asyncio.Task task: the Task wrapping the coroutine :param set already_used: Filters already used on this outgoing stanza """ data = await task self.__slow_tasks.remove(task) for filter in self.__filters['out']: if filter in already_used: continue if iscoroutinefunction(filter): data = await task else: data = filter(data) if data is None: return if isinstance(data, ElementBase): for filter in self.__filters['out_sync']: data = filter(data) if data is None: return str_data = tostring(data.xml, xmlns=self.default_ns, stream=self, top_level=True) self.send_raw(str_data) else: self.send_raw(data)
async def get(self): try: result = await self['xep_0060'].get_item(self.pubsub_server, self.node, self.data) for item in result['pubsub']['items']['substanzas']: logging.info('Retrieved item %s: %s', item['id'], tostring(item['payload'])) except XMPPError as error: logging.error('Could not retrieve item %s from node %s: %s', self.data, self.node, error.format())
def data_received(self, data): """Called when incoming data is received on the socket. We feed that data to the parser and the see if this produced any XML event. This could trigger one or more event (a stanza is received, the stream is opened, etc). """ self.parser.feed(data) for event, xml in self.parser.read_events(): if event == 'start': if self.xml_depth == 0: # We have received the start of the root element. self.xml_root = xml log.debug('[33;1mRECV[0m: %s', highlight(tostring(self.xml_root, xmlns=self.default_ns, stream=self, top_level=True, open_only=True))) self.start_stream_handler(self.xml_root) self.xml_depth += 1 if event == 'end': self.xml_depth -= 1 if self.xml_depth == 0: # The stream's root element has closed, # terminating the stream. log.debug("End of stream received") self.abort() elif self.xml_depth == 1: # A stanza is an XML element that is a direct child of # the root element, hence the check of depth == 1 self.loop.idle_call(functools.partial(self.__spawn_event, xml)) if self.xml_root is not None: # Keep the root element empty of children to # save on memory use. self.xml_root.clear()
def get(self): future, callback = make_callback() try: self["xep_0060"].get_item(self.pubsub_server, self.node, self.data, callback=callback) result = yield from future for item in result["pubsub"]["items"]["substanzas"]: print("Retrieved item %s: %s" % (item["id"], tostring(item["payload"]))) except: logging.error("Could not retrieve item %s from node %s" % (self.data, self.node))
def send_body_to(self, consumer): if self.bosh_wait: consumer.wait_handle.cancel() xml = self.current_body.xml self.current_body = None ack = self.rid_in - 1 if ack != consumer.rid: xml.attrib['ack'] = str(ack) data = tostring(xml, top_level=True) self.send_to_consumer(consumer, data)
def _publish(self, msg): """Handle receiving a publish item event.""" print('Published item %s to %s:' % ( msg['pubsub_event']['items']['item']['id'], msg['pubsub_event']['items']['node'])) data = msg['pubsub_event']['items']['item']['payload'] if data is not None: print(tostring(data)) else: print('No item content')
def _publish(self, msg): """Handle receiving a publish item event.""" print('Published item %s to %s:' % (msg['pubsub_event']['items']['item']['id'], msg['pubsub_event']['items']['node'])) data = msg['pubsub_event']['items']['item']['payload'] if data is not None: print(tostring(data)) else: print('No item content')
def data_received(self, data): """Called when incoming data is received on the socket. We feed that data to the parser and the see if this produced any XML event. This could trigger one or more event (a stanza is received, the stream is opened, etc). """ if self.parser is None: log.warning( 'Received data before the connection is established: %r', data) return self.parser.feed(data) try: for event, xml in self.parser.read_events(): if event == 'start': if self.xml_depth == 0: # We have received the start of the root element. self.xml_root = xml log.debug( 'RECV: %s', tostring(self.xml_root, xmlns=self.default_ns, stream=self, top_level=True, open_only=True)) self.start_stream_handler(self.xml_root) self.xml_depth += 1 if event == 'end': self.xml_depth -= 1 if self.xml_depth == 0: # The stream's root element has closed, # terminating the stream. self.end_session_on_disconnect = True log.debug("End of stream received") self.disconnect_reason = "End of stream" self.abort() elif self.xml_depth == 1: # A stanza is an XML element that is a direct child of # the root element, hence the check of depth == 1 self._spawn_event(xml) if self.xml_root is not None: # Keep the root element empty of children to # save on memory use. self.xml_root.clear() except ET.ParseError: log.error('Parse error: %r', data) # Due to cyclic dependencies, this can’t be imported at the module # level. from slixmpp.stanza.stream_error import StreamError error = StreamError() error['condition'] = 'not-well-formed' error['text'] = 'Server sent: %r' % data self.send(error) self.disconnect()
def data_received(self, data): """Called when incoming data is received on the socket. We feed that data to the parser and the see if this produced any XML event. This could trigger one or more event (a stanza is received, the stream is opened, etc). """ if self.parser is None: log.warning('Received data before the connection is established: %r', data) return self.parser.feed(data) try: for event, xml in self.parser.read_events(): if event == 'start': if self.xml_depth == 0: # We have received the start of the root element. self.xml_root = xml log.debug('RECV: %s', tostring(self.xml_root, xmlns=self.default_ns, stream=self, top_level=True, open_only=True)) self.start_stream_handler(self.xml_root) self.xml_depth += 1 if event == 'end': self.xml_depth -= 1 if self.xml_depth == 0: # The stream's root element has closed, # terminating the stream. self.end_session_on_disconnect = True log.debug("End of stream received") self.disconnect_reason = "End of stream" self.abort() elif self.xml_depth == 1: # A stanza is an XML element that is a direct child of # the root element, hence the check of depth == 1 self._spawn_event(xml) if self.xml_root is not None: # Keep the root element empty of children to # save on memory use. self.xml_root.clear() except ET.ParseError: log.error('Parse error: %r', data) # Due to cyclic dependencies, this can’t be imported at the module # level. from slixmpp.stanza.stream_error import StreamError error = StreamError() error['condition'] = 'not-well-formed' error['text'] = 'Server sent: %r' % data self.send(error) self.disconnect()
async def ipc_reply(self, type, channel, xml): if self.ipc_logger.isEnabledFor(logging.DEBUG): self.ipc_logger.debug("IPC-Reply type %s from %s to [%s]: %s", type, self.boundjid, channel, tostring(xml)) await self.channel_layer.send( channel, { 'type': type, 'origin': self.channel_name, 'from': self.boundjid.full, 'xml': xml, })
def send_message(self, mto, mbody, msubject=None, mtype=None, mhtml=None, mfrom=None, mnick=None): # wild hack that is necessary body = mbody.replace("\r", " \n") message = self.make_message(mto=mto, mfrom=mfrom, mbody=body) message['lang'] = None str_data = tostring(message.xml, xmlns=message.stream.default_ns, stream=message.stream, top_level=True) str_data = str_data.replace("
", " ") message.stream.send_raw(str_data)
async def ipc_send(self, type, target, xml): group_name = self.group_for_user(target) if self.ipc_logger.isEnabledFor(logging.DEBUG): self.ipc_logger.debug("IPC-Send type %s from %s [%s] to %s: %s", type, self.boundjid, self.channel_name, target.bare, tostring(xml)) await self.channel_layer.group_send( group_name, { 'type': type, 'origin': self.channel_name, 'from': self.boundjid.full, 'xml': xml, })
async def run_filters(self): """ Background loop that processes stanzas to send. """ while True: (data, use_filters) = await self.waiting_queue.get() try: if isinstance(data, ElementBase): if use_filters: already_run_filters = set() for filter in self.__filters['out']: already_run_filters.add(filter) if iscoroutinefunction(filter): task = asyncio.create_task(filter(data)) completed, pending = await wait( {task}, timeout=1, ) if pending: self.slow_tasks.append(task) asyncio.ensure_future( self._continue_slow_send( task, already_run_filters ), loop=self.loop, ) raise Exception("Slow coro, rescheduling") data = task.result() else: data = filter(data) if data is None: raise ContinueQueue('Empty stanza') if isinstance(data, ElementBase): if use_filters: for filter in self.__filters['out_sync']: data = filter(data) if data is None: raise ContinueQueue('Empty stanza') str_data = tostring(data.xml, xmlns=self.default_ns, stream=self, top_level=True) self.send_raw(str_data) else: self.send_raw(data) except ContinueQueue as exc: log.debug('Stanza in send queue not sent: %s', exc) except Exception: log.error('Exception raised in send queue:', exc_info=True) self.waiting_queue.task_done()
async def _set_presence(self, msg): msg['from'] = self.stream.boundjid.full initial = not self.available self.last_presence = msg self.available = True await self.stream.session_hook.set_presence(msg['priority'], tostring(msg.xml)) # clients usually get the roster before becoming # available, so use cached roster if possible roster = self.stream.roster.cached if roster is None: roster = await self.stream.roster_hook.get_contacts(self.stream.boundjid) await self._broadcast_presence(msg, roster, initial) if initial: await self._remind_pending()
async def _ipc_received(self, msg): type = msg['type'] xml = msg['xml'] origin = msg['origin'] ifrom = msg['from'] if self.ipc_logger.isEnabledFor(logging.DEBUG): self.ipc_logger.debug("IPC-Receive type %s from %s [%s]: %s", type, ifrom, origin, tostring(xml)) try: attrs = type.split('.') target = self for attr in attrs[:-1]: target = getattr(target, attr) target = getattr(target, 'ipc_recv_' + attrs[-1]) await target(origin, ifrom, xml) except Exception as e: self.exception(e)
async def _outbound_subscribe(self, msg): try: if self.stream.kicked: raise XMPPError('forbidden') user = self.stream.boundjid contact = msg['to'] msg['from'] = user.bare if contact.resource: # 'to' field must be bare contact.resource = '' msg['to'] = contact if contact.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. contact.user = contact.domain contact.domain = self.stream.host msg['to'] = contact stanza_out = tostring(msg.xml) values = (await self.stream.roster_hook. outbound_subscribe(user, contact, stanza_out)) # Even if we're already subscribed, the RFC doesn't say not to # forward the stanza (maybe in case the remote server didn't receive # a previous subscribe?), so we're only going to suppress the # roster push (after all, the roster wouldn't have changed). if self.stream.is_local(contact.domain): if not await self.stream.auth_hook.valid_contact(contact.user): reply = msg.reply() reply['error']['condition'] = 'item-not-found' reply.send() return await self._inbound_subscribe(msg.xml, contact, user, stanza_out) elif not self._remote_server(msg): return if values is not None: await self.stream.roster.send_push(user, contact, values) except Exception as e: msg.exception(e) return
async def handle_bosh(consumer, xml): if 'sid' not in xml.attrib: stream = BOSHStream() stream.http_host = consumer.http_host stream.http_origin = consumer.http_origin stream.trust_origin = consumer.is_trusted() if stream.trust_origin: stream.web_user = await consumer.get_user() else: stream = get_local_stream(xml.attrib['sid']) if not stream: # stream is gone reply = BOSHBody() reply['type'] = 'terminate' reply['condition'] = 'remote-connection-failed' await consumer.send_data(tostring(reply.xml, top_level=True), build_headers(origin=consumer.http_origin, trust=consumer.is_trusted())) return if stream.http_host != consumer.http_host: # We're going to consider an unexpected host fishy, # perhaps there's a man-in-the-middle attack or something. await consumer.send_response(status=403) return elif not stream.bosh_started and not consumer.http_origin: # On the first BOSH request, if it seems the browser # don't think it's necessary to send us origin headers, # don't expect them for future requests either. stream.http_origin = None elif stream.http_origin != consumer.http_origin: # Possible stream hijacking attempt. We'll just tell # the browser what origin we accept, it'll do the rest. await consumer.send_response(stream.http_headers) return consumer.stream = stream stream.add_consumer_threadsafe(consumer, xml)
def send_element(self, xml): self.send_raw(tostring(xml, xmlns=self.default_ns, stream=self, top_level=True))
def get_empty_body(): return tostring(BOSHBody().xml, top_level=True)
def get_recoverable_body(): body = BOSHBody() body['type'] = 'error' return tostring(body.xml, top_level=True)
def to_xhtml_im(self, body, markup_elem): chunks = self._split_first_level(body, markup_elem) final = [] stack = [] for chunk in chunks: if isinstance(chunk, str): chunk = (chunk.replace("&", '&') .replace('<', '<') .replace('>', '>') .replace('"', '"') .replace("'", ''') .replace('\n', '<br/>')) final.append(chunk) continue num_end = 0 for elem in chunk: if isinstance(elem, End): num_end += 1 for i in range(num_end): stack_top = stack.pop() for elem in chunk: if not isinstance(elem, End): continue elem = elem.elem if elem is stack_top: if isinstance(elem, Span): final.append('</span>') elif isinstance(elem, BlockCode): final.append('</code></pre>') elif isinstance(elem, List): final.append('</ul>') elif isinstance(elem, Li): final.append('</li>') elif isinstance(elem, BlockQuote): final.append('</blockquote>') break else: assert False for elem in chunk: if not isinstance(elem, Start): continue elem = elem.elem stack.append(elem) if isinstance(elem, Span): style = [] for type_ in elem['types']: if type_ == 'emphasis': style.append('font-style: italic;') if type_ == 'code': style.append('font-family: monospace;') if type_ == 'deleted': style.append('text-decoration: line-through;') final.append("<span style='%s'>" % ' '.join(style)) elif isinstance(elem, BlockCode): final.append('<pre><code>') elif isinstance(elem, List): final.append('<ul>') elif isinstance(elem, Li): final.append('<li>') elif isinstance(elem, BlockQuote): final.append('<blockquote>') p = "<p xmlns='http://www.w3.org/1999/xhtml'>%s</p>" % ''.join(final) p2 = ET.fromstring(p) print('coucou', p, tostring(p2)) xhtml_im = XHTML_IM() xhtml_im['body'] = p2 return xhtml_im
def to_xhtml_im(self, body, markup_elem): chunks = self._split_first_level(body, markup_elem) final = [] stack = [] for chunk in chunks: if isinstance(chunk, str): chunk = (chunk.replace("&", '&').replace( '<', '<').replace('>', '>').replace('"', '"').replace( "'", ''').replace('\n', '<br/>')) final.append(chunk) continue num_end = 0 for elem in chunk: if isinstance(elem, End): num_end += 1 for i in range(num_end): stack_top = stack.pop() for elem in chunk: if not isinstance(elem, End): continue elem = elem.elem if elem is stack_top: if isinstance(elem, Span): final.append('</span>') elif isinstance(elem, BlockCode): final.append('</code></pre>') elif isinstance(elem, List): final.append('</ul>') elif isinstance(elem, Li): final.append('</li>') elif isinstance(elem, BlockQuote): final.append('</blockquote>') break else: assert False for elem in chunk: if not isinstance(elem, Start): continue elem = elem.elem stack.append(elem) if isinstance(elem, Span): style = [] for type_ in elem['types']: if type_ == 'emphasis': style.append('font-style: italic;') if type_ == 'code': style.append('font-family: monospace;') if type_ == 'deleted': style.append('text-decoration: line-through;') final.append("<span style='%s'>" % ' '.join(style)) elif isinstance(elem, BlockCode): final.append('<pre><code>') elif isinstance(elem, List): final.append('<ul>') elif isinstance(elem, Li): final.append('<li>') elif isinstance(elem, BlockQuote): final.append('<blockquote>') p = "<p xmlns='http://www.w3.org/1999/xhtml'>%s</p>" % ''.join(final) p2 = ET.fromstring(p) print('coucou', p, tostring(p2)) xhtml_im = XHTML_IM() xhtml_im['body'] = p2 return xhtml_im
def send_element(self, xml): self.send_raw(tostring(xml, top_level=True))