Exemple #1
0
    def add_node(self, jid=None, node=None, ifrom=None):
        """
        Create a new set of stanzas for the provided
        JID and node combination.

        Arguments:
            jid  -- The JID that will own the new stanzas.
            node -- The node that will own the new stanzas.
        """
        with self.lock:
            if jid is None:
                jid = self.xmpp.boundjid.full
            if node is None:
                node = ''
            if ifrom is None:
                ifrom = ''
            if isinstance(ifrom, JID):
                ifrom = ifrom.full
            if (jid, node, ifrom) not in self.nodes:
                self.nodes[(jid, node, ifrom)] = {
                    'info': DiscoInfo(),
                    'items': DiscoItems()
                }
                self.nodes[(jid, node, ifrom)]['info']['node'] = node
                self.nodes[(jid, node, ifrom)]['items']['node'] = node
Exemple #2
0
    def del_info(self, jid, node, ifrom, data):
        """
        Reset the info stanza for a given JID/node combination.

        The data parameter is not used.
        """
        if self.node_exists(jid, node):
            self.get_node(jid, node)['info'] = DiscoInfo()
Exemple #3
0
    def _fix_default_info(self, info: DiscoInfo):
        """
        Disco#info results for a JID are required to include at least
        one identity and feature. As a default, if no other identity is
        provided, Slixmpp will use either the generic component or the
        bot client identity. A the standard disco#info feature will also be
        added if no features are provided.

        :param info: The disco#info quest (not the full Iq stanza) to modify.
        """
        result = info
        if isinstance(info, Iq):
            info = info['disco_info']
        if not info['node']:
            if not info['identities']:
                if self.xmpp.is_component:
                    log.debug("No identity found for this entity. "
                              "Using default component identity.")
                    info.add_identity('component', 'generic')
                else:
                    log.debug("No identity found for this entity. "
                              "Using default client identity.")
                    info.add_identity('client', 'bot')
            if not info['features']:
                log.debug("No features found for this entity. "
                          "Using default disco#info feature.")
                info.add_feature(info.namespace)
        return result
Exemple #4
0
    def get_info(self, jid, node, ifrom, data):
        """
        Return the stored info data for the requested JID/node combination.

        The data parameter is not used.
        """
        if not self.node_exists(jid, node):
            if not node:
                return DiscoInfo()
            else:
                raise XMPPError(condition='item-not-found')
        else:
            return self.get_node(jid, node)['info']
Exemple #5
0
    def __init__(self):
        keyfile = config.get('keyfile')
        certfile = config.get('certfile')

        device_id = config.get('device_id')
        if not device_id:
            rng = random.SystemRandom()
            device_id = base64.urlsafe_b64encode(
                rng.getrandbits(24).to_bytes(3, 'little')).decode('ascii')
            config.set_and_save('device_id', device_id)

        if config.get('jid'):
            # Field used to know if we are anonymous or not.
            # many features will be handled differently
            # depending on this setting
            self.anon = False
            jid = config.get('jid')
            password = config.get('password')
            eval_password = config.get('eval_password')
            if not password and not eval_password and not (keyfile
                                                           and certfile):
                password = getpass.getpass()
            elif not password and not (keyfile and certfile):
                sys.stderr.write(
                    "No password or certificates provided, using the eval_password command.\n"
                )
                process = subprocess.Popen(
                    ['sh', '-c', eval_password],
                    stdin=subprocess.PIPE,
                    stdout=subprocess.PIPE,
                    close_fds=True)
                code = process.wait()
                if code != 0:
                    sys.stderr.write(
                        'The eval_password command (%s) returned a '
                        'nonzero status code: %s.\n' % (eval_password, code))
                    sys.stderr.write('Poezio will now exit\n')
                    sys.exit(code)
                password = process.stdout.readline().decode('utf-8').strip(
                    '\n')
        else:  # anonymous auth
            self.anon = True
            jid = config.get('server')
            password = None
        jid = safeJID(jid)
        jid.resource = '%s-%s' % (
            jid.resource,
            device_id) if jid.resource else 'poezio-%s' % device_id
        # TODO: use the system language
        slixmpp.ClientXMPP.__init__(
            self, jid, password, lang=config.get('lang'))

        force_encryption = config.get('force_encryption')
        if force_encryption:
            self['feature_mechanisms'].unencrypted_plain = False
            self['feature_mechanisms'].unencrypted_digest = False
            self['feature_mechanisms'].unencrypted_cram = False
            self['feature_mechanisms'].unencrypted_scram = False

        self.keyfile = config.get('keyfile')
        self.certfile = config.get('certfile')
        if keyfile and not certfile:
            log.error(
                'keyfile is present in configuration file without certfile')
        elif certfile and not keyfile:
            log.error(
                'certfile is present in configuration file without keyfile')

        self.core = None
        self.auto_reconnect = config.get('auto_reconnect')
        self.auto_authorize = None
        # prosody defaults, lowest is AES128-SHA, it should be a minimum
        # for anything that came out after 2002
        self.ciphers = config.get(
            'ciphers', 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK'
            ':!SRP:!3DES:!aNULL')
        self.ca_certs = config.get('ca_cert_path') or None
        interval = config.get('whitespace_interval')
        if int(interval) > 0:
            self.whitespace_keepalive_interval = int(interval)
        else:
            self.whitespace_keepalive = False
        self.register_plugin('xep_0004')
        self.register_plugin('xep_0012')
        # Must be loaded before 0030.
        self.register_plugin(
            'xep_0115', {
                'caps_node':
                'https://poez.io',
                'cache':
                FileSystemCache(
                    str(xdg.CACHE_HOME),
                    'caps',
                    encode=str,
                    decode=lambda x: DiscoInfo(ET.fromstring(x))),
            })
        self.register_plugin('xep_0030')
        self.register_plugin('xep_0045')
        self.register_plugin('xep_0048')
        self.register_plugin('xep_0050')
        self.register_plugin('xep_0054')
        self.register_plugin('xep_0060')
        self.register_plugin('xep_0066')
        self.register_plugin('xep_0070')
        self.register_plugin('xep_0071')
        self.register_plugin('xep_0077')
        self.plugin['xep_0077'].create_account = False
        self.register_plugin('xep_0084')
        self.register_plugin('xep_0085')
        self.register_plugin('xep_0153')

        # monkey-patch xep_0184 to avoid requesting receipts for messages
        # without a body
        XEP_0184._filter_add_receipt_request = fixes._filter_add_receipt_request
        self.register_plugin('xep_0184')
        self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts')
        self.plugin['xep_0184'].auto_request = config.get(
            'request_message_receipts')

        self.register_plugin('xep_0191')
        if config.get('enable_smacks'):
            self.register_plugin('xep_0198')
        self.register_plugin('xep_0199')

        if config.get('enable_user_tune'):
            self.register_plugin('xep_0118')

        if config.get('enable_user_nick'):
            self.register_plugin('xep_0172')

        if config.get('enable_user_mood'):
            self.register_plugin('xep_0107')

        if config.get('enable_user_activity'):
            self.register_plugin('xep_0108')

        if config.get('enable_user_gaming'):
            self.register_plugin('xep_0196')

        if config.get('send_poezio_info'):
            info = {'name': 'poezio', 'version': options.custom_version}
            if config.get('send_os_info'):
                info['os'] = common.get_os_info()
            self.plugin['xep_0030'].set_identities(identities={('client',
                                                                'console',
                                                                None,
                                                                'Poezio')})
        else:
            info = {'name': '', 'version': ''}
            self.plugin['xep_0030'].set_identities(identities={('client',
                                                                'console',
                                                                None, '')})
        self.register_plugin('xep_0092', pconfig=info)
        if config.get('send_time'):
            self.register_plugin('xep_0202')
        self.register_plugin('xep_0224')
        self.register_plugin('xep_0231')
        self.register_plugin('xep_0249')
        self.register_plugin('xep_0257')
        self.register_plugin('xep_0280')
        self.register_plugin('xep_0297')
        self.register_plugin('xep_0308')
        self.register_plugin('xep_0313')
        self.register_plugin('xep_0319')
        self.register_plugin('xep_0334')
        self.register_plugin('xep_0352')
        try:
            self.register_plugin('xep_0363')
        except SyntaxError:
            log.error('Failed to load HTTP File Upload plugin, it can only be '
                      'used on Python 3.5+')
        except slixmpp.plugins.base.PluginNotFound:
            log.error('Failed to load HTTP File Upload plugin, it can only be '
                      'used with aiohttp installed')
        self.register_plugin('xep_0380')
        self.init_plugins()