<commit_before># Copyright 2011-2012 <NAME> - All Rights Reserved

"""Definition of the buddycloud channel server."""

import logging
import select
import time
import uuid
import xmpp

from datetime import datetime

from buddycloud.channel_server.storage import init_storage


NS_PUBSUB_OWNER = '%s#%s' % (xmpp.protocol.NS_PUBSUB, 'owner')
NS_RSM = 'http://jabber.org/protocol/rsm'
NS_ATOM = 'http://www.w3.org/2005/Atom'
NS_THREADS = 'http://purl.org/syndication/thread/1.0'
NS_ACTIVITY_STREAMS = 'http://activitystrea.ms/spec/1.0/'


class ChannelServer:
    """XMPP component for buddycloud channel server."""

    def __init__(self, config):
        self.config = config
        self.connection = None
        self.is_online = False
        self.logger = logging.getLogger('ChannelServer')
        handler = logging.StreamHandler()
        formatter = logging.Formatter(
            config.get('Logging', 'log_format', raw=True))
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(
            logging.__getattribute__(self.config.get('Logging', 'log_level')))
        # Component config section
        self.jid = None
        self.allow_register = False
        self.component_binding = False
        self.use_route_wrap = False
        # MainServer config section
        self.main_server = None
        # Auth config section
        self.sasl_username = None
        self.secret = None
        # Storage section
        self.storage = init_storage(config)
        # Do the set-up
        self._parse_config(config)

    def _parse_config(self, config):
        """Parse the configuration and set up the component."""
        self.jid = config.get('Component', 'jid')
        self.allow_register = config.getboolean('Component', 'allow_register')
        self.component_binding = config.getboolean(
            'Component', 'component_binding')
        self.route_wrap = config.getboolean('Component', 'route_wrap')
        self.main_server = (
            config.get('MainServer', 'host'), config.get('MainServer', 'port'))
        self.sasl_username = config.get('Auth', 'sasl_username')
        self.secret = config.get('Auth', 'secret')
        self.logger.debug('Configuration: %s',
            dict(((prop, self.__dict__.get(prop)) for prop in (
                'jid', 'allow_register', 'component_binding', 'route_wrap',
                'main_server', 'sasl_username', 'secret'))))

    def register_handlers(self):
        """Register handlers for the various XMPP stanzas."""
        self.connection.RegisterHandler('message', self.xmpp_message)
        self.connection.RegisterHandler('presence', self.xmpp_presence)
        self.connection.RegisterHandler(
            'iq', self.xmpp_pubsub_get, typ='get', ns=xmpp.protocol.NS_PUBSUB)
        self.connection.RegisterHandler(
            'iq', self.xmpp_pubsub_set, typ='set', ns=xmpp.protocol.NS_PUBSUB)
        self.connection.RegisterHandler(
            'iq', self.xmpp_register_set, typ='set',
            ns=xmpp.protocol.NS_REGISTER)
        self.disco = xmpp.browser.Browser()
        self.disco.PlugIn(self.connection)
        self.disco.setDiscoHandler(self.xmpp_base_disco, node='', jid=self.jid)

    def xmpp_message(self, conn, event):
        """Callback to handle XMPP message stanzas."""
        self.logger.debug(event)

    def xmpp_presence(self, conn, event):
        """Callback to handle XMPP presence stanzas."""
        self.logger.debug(event)

    def xmpp_pubsub_get(self, conn, event):
        """Callback to handle XMPP PubSub queries."""
        self.logger.debug('Pubsub request: %s', event)
        tag = event.getTag('pubsub')
        if tag and tag.getNamespace() == xmpp.protocol.NS_PUBSUB:
            node = tag.getTagAttr('items', 'node')
            rsm = tag.getTag('set', namespace=NS_RSM)
            set_size = int(rsm.getTagData('max'))
            channel = self.storage.get_node(node)
            self.logger.debug(
                'Got channel entries for node %s: %s', node, channel)
            if channel is None:
                # TODO Channel does not exist - return 404
                return
            reply = event.buildReply('result')
            pubsub = reply.setTag('pubsub',
                    namespace=xmpp.protocol.NS_PUBSUB)
            items = pubsub.setTag('items', attrs={'node': node})
            for channel_item in sorted(channel.items(), key=lambda x: x[1][0],
                    reverse=True)[:set_size]:
                item = items.setTag('item', attrs={'id': channel_item[0]})
                item.addChild(node=channel_item[1][1])
            rsm = pubsub.setTag('set', namespace=NS_RSM)
            if len(channel.items()) > 0:
                rsm.setTagData(
                    'first', channel.items()[0][0], attrs={'index': 0})
                rsm.setTagData('last', channel.items()[-1][0])
            rsm.setTagData('count', len(channel.items()))
            conn.send(reply)
            raise xmpp.protocol.NodeProcessed

    def xmpp_pubsub_set(self, conn, event):
        """Callback to handle XMPP PubSub commands."""
        self.logger.debug('Pubsub command: %s', event)
        tag = event.getTag('pubsub')
        if tag and tag.getNamespace() == xmpp.protocol.NS_PUBSUB:
            publish = tag.getTag('publish')
            node = publish.getAttr('node')
            jid = publish.getAttr('jid')
            # TODO Check the sending JID can post to the JID/node
            entry = publish.getTag('item').getTag('entry')
            entry_id = str(uuid.uuid4())
            author = entry.getTag('author')
            author.setTagData('uri', 'acct:%s' % author.getTagData('name'))
            entry.setTagData('id', entry_id)
            entry.setTagData('published', entry.getTagData('updated'))
            entry.setTag('link', attrs={'rel': 'self', 'href':
                'xmpp:%s?pubsub;action=retrieve;node=%s;item=%s' % (self.jid,
                    node, entry_id)})
            items = self.storage.get_node(node)
            items[entry_id] = (datetime.utcnow(), entry)
            self.storage.set_node(node, items)
            reply = event.buildReply('result')
            pubsub = reply.setTag('pubsub', namespace=xmpp.protocol.NS_PUBSUB)
            publish = pubsub.setTag('publish', attrs={'node': node})
            publish.setTag('item', attrs={'id': entry_id})
            conn.send(reply)
            raise xmpp.protocol.NodeProcessed

    def xmpp_register_set(self, conn, event):
        """Callback to handle XMPP register commands."""
        self.logger.debug('Register command: %s', event)
        tag = event.getTag('query')
        if tag and tag.getNamespace() == xmpp.protocol.NS_REGISTER:
            fromjid = event.getFrom().getStripped().__str__()
            # Create /user/<jid>/posts
            # Create /user/<jid>/geo/previous
            # Create /user/<jid>/geo/current
            # Create /user/<jid>/geo/next
            # Create /user/<jid>/subscriptions
            # Create /user/<jid>/status<commit_msg>Better, more protocol-compliant handling of some error conditions.<commit_after># Copyright 2011-2012 <NAME> - All Rights Reserved

"""Definition of the buddycloud channel server."""

import logging
import select
import time
import uuid
import xmpp

from datetime import datetime

from buddycloud.channel_server.storage import init_storage


NS_PUBSUB_OWNER = '%s#%s' % (xmpp.protocol.NS_PUBSUB, 'owner')
NS_RSM = 'http://jabber.org/protocol/rsm'
NS_ATOM = 'http://www.w3.org/2005/Atom'
NS_THREADS = 'http://purl.org/syndication/thread/1.0'
NS_ACTIVITY_STREAMS = 'http://activitystrea.ms/spec/1.0/'


class ChannelServer(object):
    """XMPP component for buddycloud channel server."""

    def __init__(self, config):
        self.config = config
        self.connection = None
        self.is_online = False
        self.logger = logging.getLogger('ChannelServer')
        handler = logging.StreamHandler()
        formatter = logging.Formatter(
            config.get('Logging', 'log_format', raw=True))
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(
            logging.__getattribute__(self.config.get('Logging', 'log_level')))
        # Component config section
        self.jid = None
        self.allow_register = False
        self.component_binding = False
        self.use_route_wrap = False
        # MainServer config section
        self.main_server = None
        # Auth config section
        self.sasl_username = None
        self.secret = None
        # Storage section
        self.storage = init_storage(config)
        # Do the set-up
        self._parse_config(config)

    def _parse_config(self, config):
        """Parse the configuration and set up the component."""
        self.jid = config.get('Component', 'jid')
        self.allow_register = config.getboolean('Component', 'allow_register')
        self.component_binding = config.getboolean(
            'Component', 'component_binding')
        self.route_wrap = config.getboolean('Component', 'route_wrap')
        self.main_server = (
            config.get('MainServer', 'host'), config.get('MainServer', 'port'))
        self.sasl_username = config.get('Auth', 'sasl_username')
        self.secret = config.get('Auth', 'secret')
        self.logger.debug('Configuration: %s',
            dict(((prop, self.__dict__.get(prop)) for prop in (
                'jid', 'allow_register', 'component_binding', 'route_wrap',
                'main_server', 'sasl_username', 'secret'))))

    def register_handlers(self):
        """Register handlers for the various XMPP stanzas."""
        self.connection.RegisterHandler('message', self.xmpp_message)
        self.connection.RegisterHandler('presence', self.xmpp_presence)
        self.connection.RegisterHandler(
            'iq', self.xmpp_pubsub_get, typ='get', ns=xmpp.protocol.NS_PUBSUB)
        self.connection.RegisterHandler(
            'iq', self.xmpp_pubsub_set, typ='set', ns=xmpp.protocol.NS_PUBSUB)
        self.connection.RegisterHandler(
            'iq', self.xmpp_register_set, typ='set',
            ns=xmpp.protocol.NS_REGISTER)
        self.disco = xmpp.browser.Browser()
        self.disco.PlugIn(self.connection)
        self.disco.setDiscoHandler(self.xmpp_base_disco, node='', jid=self.jid)

    def xmpp_message(self, conn, event):
        """Callback to handle XMPP message stanzas."""
        self.logger.debug(event)

    def xmpp_presence(self, conn, event):
        """Callback to handle XMPP presence stanzas."""
        self.logger.debug(event)

    def xmpp_pubsub_get(self, conn, event):
        """Callback to handle XMPP PubSub queries."""
        self.logger.debug('Pubsub request: %s', event)
        tag = event.getTag('pubsub')
        if tag and tag.getNamespace() == xmpp.protocol.NS_PUBSUB:
            node = tag.getTagAttr('items', 'node')
            rsm = tag.getTag('set', namespace=NS_RSM)
            set_size = int(rsm.getTagData('max'))
            channel = self.storage.get_node(node)
            self.logger.debug(
                'Got channel entries for node %s: %s', node, channel)
            if channel is None:
                conn.send(xmpp.protocol.Error(event, xmpp.ERR_ITEM_NOT_FOUND)) 
                raise xmpp.protocol.NodeProcessed
            reply = event.buildReply('result')
            pubsub = reply.setTag('pubsub',
                    namespace=xmpp.protocol.NS_PUBSUB)
            items = pubsub.setTag('items', attrs={'node': node})
            for channel_item in sorted(channel.items(), key=lambda x: x[1][0],
                    reverse=True)[:set_size]:
                item = items.setTag('item', attrs={'id': channel_item[0]})
                item.addChild(node=channel_item[1][1])
            rsm = pubsub.setTag('set', namespace=NS_RSM)
            if len(channel.items()) > 0:
                rsm.setTagData(
                    'first', channel.items()[0][0], attrs={'index': 0})
                rsm.setTagData('last', channel.items()[-1][0])
            rsm.setTagData('count', len(channel.items()))
            conn.send(reply)
            raise xmpp.protocol.NodeProcessed

    def xmpp_pubsub_set(self, conn, event):
        """Callback to handle XMPP PubSub commands."""
        self.logger.debug('Pubsub command: %s', event)
        tag = event.getTag('pubsub')
        if tag and tag.getNamespace() == xmpp.protocol.NS_PUBSUB:
            publish = tag.getTag('publish')
            node = publish.getAttr('node')
            jid = publish.getAttr('jid')
            # TODO Check the sending JID can post to the JID/node
            entry = publish.getTag('item').getTag('entry')
            entry_id = str(uuid.uuid4())
            author = entry.getTag('author')
            author.setTagData('uri', 'acct:%s' % author.getTagData('name'))
            entry.setTagData('id', entry_id)
            entry.setTagData('published', entry.getTagData('updated'))
            entry.setTag('link', attrs={'rel': 'self', 'href':
                'xmpp:%s?pubsub;action=retrieve;node=%s;item=%s' % (self.jid,
                    node, entry_id)})
            items = self.storage.get_node(node)
            items[entry_id] = (datetime.utcnow(), entry)
            self.storage.set_node(node, items)
            reply = event.buildReply('result')
            pubsub = reply.setTag('pubsub', namespace=xmpp.protocol.NS_PUBSUB)
            publish = pubsub.setTag('publish', attrs={'node': node})
            publish.setTag('item', attrs={'id': entry_id})
            conn.send(reply)
            raise xmpp.protocol.NodeProcessed

    def xmpp_register_set(self, conn, event):
        """Callback to handle XMPP register commands."""
        self.logger.debug('Register command: %s', event)
        if event.getTo().getDomain() != self.jid:
            conn.send(xmpp.protocol.Error(event, xmpp.ERR_NOT_ALLOWED)) 
            raise xmpp.protocol.NodeProcessed
        tag = event.getTag('query')
        if tag and tag.getNamespace() == xmpp.protocol.NS_REGISTER:
            fromjid = event.getFrom().getStripped().__str__()
            # Create /user/<jid>/posts
            node = self.storage.get_node(u'/user/%s/posts' % fromjid)
            if node:
                error = xmpp.protocol.Error(event, xmpp.ERR_CONFLICT)
                error.addChild(node=tag)
                conn.send(error)
                raise xmpp.protocol.NodeProcessed
            # Create /user/<jid>/geo/previous
            # Create /user/<jid>/geo/current
            # Create /user/<jid>/geo/next
            # Create /user/<jid>/subscriptions
            # Create /user/<jid>/status
{'fraction_non_alphanumeric': 0.13184873379048137, 'fraction_numerical': 0.004141489578382783, 'mean_word_length': 2.4120917303683114, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6952785', 'n_tokens_mistral': 4469, 'n_tokens_neox': 4396, 'n_words': 857}