Пример #1
0
Файл: main.py Проект: m4tl0/mxpp
    def matrix_all_chat_message(self, room: MatrixRoom, event: Dict):
        """
        Handle a message sent to Matrix all-chat room.

        Allows manual sending of xmpp messages: "/m target_jid your message here".
        Sends a notice with the expected format if it isn't there by default.

        :param room: Matrix room object representing the all-chat room
        :param event: The Matrix event that was received. Assumed to be an m.room.message .
        """
        # Always ignore our own messages
        if event['sender'] == self.bot_id:
            return

        logger.debug('matrix_all_chat_message: {}  {}'.format(room.room_id, str(event)))

        if event['content']['msgtype'] == 'm.text':
            message_body = event['content']['body']
            message_parts = message_body.split()
            if message_parts[0] == '/m':
                jid = message_parts[1]
                payload = message_body[message_body.find(jid) + len(jid) + 1:]
                logger.info('sending manual message to '+ jid + ' : ' + payload)
                self.xmpp.send_message(mto=jid, mbody=payload, mtype='chat')
            else:
                room.send_notice('Expected message format: "/m DEST_JID your message here"')
Пример #2
0
 def is_room_member(self, room_id, user_id):
     try:
         r = Room(self.client, room_id)
         return user_id in list(r.get_joined_members().keys())
     except Exception as e:
         return False
     return False
Пример #3
0
    def set_room(self, room_id):
        try:
            from matrix_client.room import Room
        except ModuleNotFoundError:
            raise ModuleNotFoundError("No matrix client module found, please run j.clients.matrix.install() first")

        self.room = Room(client=self.client, room_id=room_id)
Пример #4
0
def bot_cmd_fug(room: Room, event: dict):
    """
    Send ``fug -_-``, even more useful than ``meh~``.

    :param Room room: Matrix room
    :param dict event: event content
    """
    room.send_notice("fug -_-")
Пример #5
0
def bot_cmd_meh(room: Room, event: dict):
    """
    Send ``meh~``, very useful.

    :param Room room: Matrix room
    :param dict event: event content
    """
    room.send_notice("meh~")
Пример #6
0
 def is_room_member(self, room_id, user_id):
     try:
         r = Room(self.client, room_id)
         room_members = [m.user_id for m in r.get_joined_members()]
         return user_id in room_members
     except Exception as e:
         self.logger.error("Error when fetching room members: %s" % e)
         return False
     return False
Пример #7
0
    def safe_send_message(self, room: Room, body: str, html: str):
        logger.debug(f"safe_send_message({room}, {body}, {html})")

        members = room.get_joined_members()
        logger.debug(f"room joined members: {members}")
        for u in members:
            if u.user_id not in self.allowed_users:
                body = "I'm sorry, but not everyone in this room has clearance, so I'm not going to respond."
                html = None
                break
        try:
            room.send_html(html, body)
        except MatrixError as me:
            logger.error(me)
Пример #8
0
def bot_cmd_bashorg(room: Room, event: dict):
    """
    Send ``смешнявка с башорга``!

    **BEWARE: dirty hacks!**

    :param Room room: Matrix room
    :param dict event: event content
    """
    log.debug("started dl")
    raw_text = requests.get("http://bash.im/forweb/?u").text
    without_spaces_text = raw_text.replace("\' + \'", "")
    log.debug(f"pre-parse ${without_spaces_text}")
    quote = REGEX_QUOTE_EXTRACT.search(without_spaces_text).group(1)
    log.debug(f"finally, quote \'${quote}\'")
    room.send_html(quote, quote)
Пример #9
0
def make_message(convert_to_hex: bool = False, overwrite_data=None):
    from matrix_client.room import Room
    room = Room(None, '!roomID:server')
    if not overwrite_data:
        message = SecretRequest(
            message_identifier=random.randint(0, UINT64_MAX),
            payment_identifier=1,
            secrethash=UNIT_SECRETHASH,
            amount=1,
            expiration=10,
        )
        message.sign(HOP1_KEY)
        data = message.encode()
        if convert_to_hex:
            data = '0x' + data.hex()
        else:
            data = json.dumps(message.to_dict())
    else:
        data = overwrite_data

    event = dict(
        type='m.room.message',
        sender=USERID1,
        content={
            'msgtype': 'm.text',
            'body': data,
        },
    )
    return room, event
Пример #10
0
    def matrix_all_chat_message(self, room: MatrixRoom, event: Dict):
        """
        Handle a message sent to Matrix all-chat room.

        Currently just sends a warning that nobody will hear your message.

        :param room: Matrix room object representing the all-chat room
        :param event: The Matrix event that was received. Assumed to be an m.room.message .
        """
        # Always ignore our own messages
        if event['sender'] == self.bot_id:
            return

        logging.debug('matrix_all_chat_message: {}  {}'.format(
            room.room_id, str(event)))

        room.send_notice('Don\'t talk in here! Nobody gets your messages.')
Пример #11
0
 def send_message(self,
                  room_id: str,
                  text: str,
                  name: str = None,
                  avatar_url: str = None,
                  file_url: str = None,
                  file_name: str = None,
                  file_mimetype: str = None,
                  file_authorization: str = None):
     room = Room(self._client, room_id)
     current_avatar_url = None
     current_name = None
     avatar_uri = None
     if room_id in self._cache['rooms']:
         current_name = self._cache['rooms'][room_id]['name']
         current_avatar_url = self._cache['rooms'][room_id]['avatar_url']
     else:
         self._cache['rooms'][room_id] = {}
     if avatar_url is not None and avatar_url != current_avatar_url:
         if avatar_url in self._cache['uploaded_avatars']:
             avatar_uri = self._cache['uploaded_avatars'][avatar_url]
             print("Use cache avatar for an user " + avatar_uri + " (" +
                   avatar_url + ")")
         else:
             avatar_content = request.urlopen(avatar_url).read()
             avatar_uri = self._client.upload(avatar_content, 'image/png')
             self._cache['uploaded_avatars'][avatar_url] = avatar_uri
             print("Uploaded a new avatar for an user " + avatar_uri +
                   " (" + avatar_url + ")")
     if (name is not None
             and name is not current_name) or avatar_uri is not None:
         room.set_user_profile(displayname=name, avatar_url=avatar_uri)
         self._cache['rooms'][room_id]['name'] = name
         self._cache['rooms'][room_id]['avatar_url'] = avatar_url
         self.__save_cache()
     if file_url is not None and file_mimetype is not None and file_name is not None:
         rq = Request(file_url)
         rq.add_header('Authorization', file_authorization)
         file_content = urlopen(rq).read()
         file_uri = self._client.upload(file_content, file_mimetype)
         if file_mimetype in ['image/png', 'image/jpeg']:
             room.send_image(file_uri, file_name)
         else:
             room.send_file(file_uri, file_name)
     if text is not None:
         room.send_text(text)
Пример #12
0
    def write_media(self, media_type: str, room: Room, url: str) -> None:
        """Get media, upload it and post to room
        """
        # image is the only media type supported right now
        if media_type != 'image':
            logger.error('%s as media type is not supported', media_type)
            return None

        # getting image and analyze it
        logger.info('download %s', url)
        image_data = get_image(url)
        logger.debug('got image_data: %s', image_data)

        if not image_data:
            logger.error('got no image_data')
            return

        # analyze image file and create image info dict
        media_info = {}  # type: Dict[str, Union[str, int, BytesIO, None]]

        # getting mimetype
        media_info['mimetype'] = image_data.get('content-type')
        if not media_info['mimetype']:
            media_info['mimetype'] = mimetypes.guess_type(url)[0]

        # getting name
        name = urlsplit(url).path.split('/')[-1]

        # image size
        media_info['h'] = image_data.get('height')
        media_info['w'] = image_data.get('width')

        logger.debug('media_info content: %s', media_info)

        # upload it to homeserver
        logger.info('upload file')
        uploaded = self.client.upload(image_data['image'],
                                      media_info['mimetype'])
        logger.debug('upload: %s', uploaded)

        # send image to room
        logger.info('send media: %s', name)
        room.send_image(uploaded, name, **media_info)
Пример #13
0
 def eventCallback(self, event):
     if 'type' in event and 'room_id' in event and 'content' in event and event[
             'type'] == 'm.room.message':
         room = Room(self.client, event['room_id'])
         self.messageReceived.emit(room, event['sender'], event['content'],
                                   time.time() - event['unsigned']['age'])
     if 'type' in event and 'room_id' in event and 'content' in event and event[
             'type'] == 'm.room.canonical_alias':
         self.roomUpdated.emit(event['room_id'], 'canonical_alias',
                               event['content']['alias'])
Пример #14
0
 def __init__(self):
     self.BOTUSERNAME = "******"
     self.BOTPASSWORD = "******"
     self.BOTSERVO = "matrix.org"
     self.RID = "!RnpiUpFIsfzZfHdHQf:matrix.org"
     self.realRID = "!obQcCWaLRAUgiGBvMg:postmarketos.org"
     self.MainClient = MatrixClient("https://" + self.BOTSERVO)
     self.token = self.MainClient.login_with_password(
         username=self.BOTUSERNAME, password=self.BOTPASSWORD)
     self.APIWrapper = MatrixHttpApi("https://" + self.BOTSERVO,
                                     token=self.token)
     self.target_room = Room(self.MainClient, self.RID)
     print("ready")
Пример #15
0
    def test_megolm_outbound_persistence(self, device):
        session = MegolmOutboundSession(max_messages=2, max_age=100000)
        session.message_count = 1
        session.add_device(self.device_id)
        sessions = {}

        self.store.load_outbound_sessions(sessions)
        assert not sessions
        assert not self.store.get_outbound_session(self.room_id)

        self.store.save_outbound_session(self.room_id, session)
        self.store.save_megolm_outbound_devices(self.room_id, {self.device_id})
        self.store.load_outbound_sessions(sessions)
        assert sessions[self.room_id].id == session.id
        assert sessions[self.room_id].devices == session.devices
        assert sessions[self.room_id].creation_time == session.creation_time
        assert sessions[self.room_id].max_messages == session.max_messages
        assert sessions[self.room_id].message_count == session.message_count
        assert sessions[self.room_id].max_age == session.max_age

        saved_session = self.store.get_outbound_session(self.room_id)
        assert saved_session.id == session.id
        assert saved_session.devices == session.devices
        assert saved_session.creation_time == session.creation_time
        assert saved_session.max_messages == session.max_messages
        assert saved_session.message_count == session.message_count
        assert saved_session.max_age == session.max_age

        sessions.clear()
        saved_session = self.store.get_outbound_session(self.room_id, sessions)
        assert sessions[self.room_id].id == session.id

        self.store.remove_outbound_session(self.room_id)
        assert not self.store.get_outbound_session(self.room_id)

        self.store.save_outbound_session(self.room_id, session)
        saved_session = self.store.get_outbound_session(self.room_id)
        # Verify the saved devices have been erased with the session
        assert not saved_session.devices

        room = Room(None, self.room_id)
        with pytest.raises(AttributeError):
            device.megolm_build_encrypted_event(room, {})
        assert device.megolm_outbound_sessions[self.room_id].id == session.id

        self.store.remove_olm_account()
        assert not self.store.get_outbound_session(self.room_id)
Пример #16
0
    def on_message(self, room: Room, event: Dict) -> None:
        """Callback for recieved messages

        Gets events and checks if something can be triggered.
        """
        logger.debug(event)

        logger.info('stores msg in db')
        self.store_msg(event)

        if event['content'].get('msgtype') == 'm.text' and event['sender'] != \
                self.uid:

            # add config to event
            event['config'] = self.config

            # gives event to mossbot and watching out for a return message
            msg = MOSS.serve(event)

            if msg and msg.data:

                if msg.type == 'text':
                    logger.info('sending text msg...')
                    room.send_text(msg.data)

                elif msg.type == 'notice':
                    logger.info('sending notice msg...')
                    room.send_notice(msg.data)

                elif msg.type == 'html':
                    logger.info('sending html msg...')
                    room.send_html(msg.data)

                elif msg.type == 'image':
                    logger.info('sending image msg...')
                    self.write_media('image', room, msg.data)

                else:
                    logger.error('could not recognize msg type "%s"', msg[0])

            elif msg and msg.type == 'skip':
                logger.info('skipping msg...')

            else:
                logger.debug('no matching in event')
Пример #17
0
class MatrixClient(JSBaseConfigClient):
    def __init__(self, instance, data=None, parent=None, interactive=False):
        if not data:
            data = {}
        JSBaseConfigClient.__init__(self,
                                    instance=instance,
                                    data=data,
                                    parent=parent,
                                    template=TEMPLATE,
                                    interactive=interactive)
        self._client = None
        self.room = None

    @property
    def client(self):
        if self._client is None:
            try:
                from matrix_client.client import MatrixClient
            except ModuleNotFoundError:
                raise ModuleNotFoundError(
                    "No matrix client module found, please run j.clients.matrix.install() first"
                )

            self._client = MatrixClient(base_url=self.config.data['base_url'])
            if self.config.data['user'] and self.config.data['password_']:
                self._client.login_with_password(
                    username=self.config.data['user'],
                    password=self.config.data['password_'])
            else:
                jwt = j.clients.itsyouonline.get().jwt
                response = self._client.api.login(login_type="m.login.jwt",
                                                  token=jwt)
                self._client.user_id = response["user_id"]
                self._client.token = response["access_token"]
                self._client.hs = response["home_server"]
                self._client.api.token = self._client.token
        return self._client

    def create_user(self, username, password):
        return self.client.register_with_password(username, password)

    def create_room(self, alias=None, is_public=False, invitees=()):
        """ Create a new room on the homeserver.

        Args:
            alias (str): The canonical_alias of the room.
            is_public (bool):  The public/private visibility of the room.
            invitees (str[]): A set of user ids to invite into the room.

        Returns:
            Room

        Raises:
            MatrixRequestError
        """
        return self.client.create_room(alias=alias,
                                       is_public=is_public,
                                       invitees=invitees)

    def join_room(self, room_id_or_alias):
        """ Join a room.

        Args:
            room_id_or_alias (str): Room ID or an alias.

        Returns:
            Room

        Raises:
            MatrixRequestError
        """
        return self.client.join_room(room_id_or_alias)

    def get_rooms(self):
        """ Return a dict of {room_id: Room objects} that the user has joined.

        Returns:
            Room{}: Rooms the user has joined.

        """
        return self.client.get_rooms()

    def get_user(self, user_id):
        """ Return a User by their id.

        NOTE: This function only returns a user object, it does not verify
            the user with the Home Server.

        Args:
            user_id (str): The matrix user id of a user.
        """

        return self.client.get_user(user_id=user_id)

    def upload(self, content, content_type):
        """ Upload content to the home server and receive a MXC url.

        Args:
            content (bytes): The data of the content.
            content_type (str): The mime-type of the content.
        """
        return self.client.upload(content, content_type)

    def set_room(self, room_id):
        try:
            from matrix_client.room import Room
        except ModuleNotFoundError:
            raise ModuleNotFoundError(
                "No matrix client module found, please run j.clients.matrix.install() first"
            )

        self.room = Room(client=self.client, room_id=room_id)

    @room_check
    def send_text(self, text):
        """ Send a plain text message to the room.

        Args:
            text (str): The message to send
        """
        return self.room.send_text(text)

    @room_check
    def send_html(self, html, body=None, msg_type="m.text"):
        """Send an html formatted message."""
        return self.room.send_html(html, body, msg_type)

    @room_check
    def set_account_data(self, account_type, account_data):
        return self.room.set_account_data(account_type, account_data)

    @room_check
    def get_tags(self):
        return self.room.get_tags()

    @room_check
    def remove_tag(self, tag):
        return self.room.remove_tag(tag)

    @room_check
    def add_tag(self, tag, order=None, content=None):
        return self.room.add_tag(tag, order, content)

    @room_check
    def send_emote(self, text):
        """ Send a emote (/me style) message to the room.

        Args:
            text (str): The message to send
        """
        return self.room.send_emote(text)

    @room_check
    def send_notice(self, text):
        return self.client.api.send_notice(self.room.room_id, text)

    @room_check
    def send_image(self, url, name, **imageinfo):
        """ Send a pre-uploaded image to the room.
        See http://matrix.org/docs/spec/r0.0.1/client_server.html#m-image
        for imageinfo

        Args:
            url (str): The mxc url of the image.
            name (str): The filename of the image.
            imageinfo (): Extra information about the image.
        """
        return self.room.send_image(url, name, **imageinfo)

    @room_check
    def send_location(self, geo_uri, name, thumb_url=None, **thumb_info):
        """ Send a location to the room.
        See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location
        for thumb_info

        Args:
            geo_uri (str): The geo uri representing the location.
            name (str): Description for the location.
            thumb_url (str): URL to the thumbnail of the location.
            thumb_info (): Metadata about the thumbnail, type ImageInfo.
        """
        return self.room.send_location(geo_uri, name, thumb_url, **thumb_info)

    @room_check
    def send_video(self, url, name, **videoinfo):
        """ Send a pre-uploaded video to the room.
        See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
        for videoinfo

        Args:
            url (str): The mxc url of the video.
            name (str): The filename of the video.
            videoinfo (): Extra information about the video.
        """
        return self.room.send_video(url, name, **videoinfo)

    @room_check
    def send_audio(self, url, name, **audioinfo):
        """ Send a pre-uploaded audio to the room.
        See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
        for audioinfo

        Args:
            url (str): The mxc url of the audio.
            name (str): The filename of the audio.
            audioinfo (): Extra information about the audio.
        """
        return self.room.send_audio(url, name, **audioinfo)
Пример #18
0
    def matrix_control_message(self, room: MatrixRoom, event: Dict):
        """
        Handle a message sent to the control room.

        Does nothing unless a valid command is received:
          refresh  Probes the presence of all XMPP contacts, and updates the roster.
          purge    Leaves any ((un-mapped and non-special) or empty) Matrix rooms.
          joinmuc [email protected]   Joins a muc
          leavemuc [email protected]  Leaves a muc

        :param room: Matrix room object representing the control room
        :param event: The Matrix event that was received. Assumed to be an m.room.message .
        """
        # Always ignore our own messages
        if event['sender'] == self.bot_id:
            return

        logging.debug('matrix_control_message: {}  {}'.format(
            room.room_id, str(event)))

        if event['content']['msgtype'] == 'm.text':
            message_body = event['content']['body']
            logging.info('Matrix received control message: ' + message_body)

            message_parts = message_body.split()
            if len(message_parts) > 0:
                message_parts[0] = message_parts[0].lower()
                # what about a empty body?
                if message_parts[0] == 'refresh':
                    for jid in self.topic_room_id_map.keys():
                        self.xmpp.send_presence(pto=jid, ptype='probe')

                    self.xmpp.send_presence()
                    self.xmpp.get_roster()

                elif message_parts[0] == 'purge':
                    self.special_rooms['control'].send_text(
                        'Purging unused rooms')

                    # Leave from unwanted rooms
                    for room in self.get_unmapped_rooms(
                    ) + self.get_empty_rooms():
                        logging.info(
                            'Leaving room {r.room_id} ({r.name}) [{r.topic}]'.
                            format(r=room))
                        if room.topic.startswith(self.groupchat_flag):
                            room_jid = room.topic[len(self.groupchat_flag):]
                            self.xmpp.plugin['xep_0045'].leaveMUC(room_jid)
                        room.leave()

                elif len(message_parts) > 1:
                    if message_parts[0] == 'joinmuc':
                        room_jid = message_parts[1]
                        logging.info('XMPP MUC join: {}'.format(room_jid))
                        self.create_groupchat_room(room_jid)
                        self.xmpp.plugin['xep_0045'].joinMUC(
                            room_jid, self.xmpp_groupchat_nick)
                    elif message_parts[0] == 'leavemuc':
                        room_jid = message_parts[1]
                        logging.info('XMPP MUC leave: {}'.format(room_jid))
                        self.xmpp.plugin['xep_0045'].leaveMUC(
                            room_jid, self.xmpp_groupchat_nick)
                        room = self.get_room_for_jid(self.groupchat_flag +
                                                     room_jid)
                        room.leave()
Пример #19
0
 def create_room(alias, is_public=False, invitees=None):  # pylint: disable=unused-argument
     room = Room(client, f"!room_id:ownserver.com")
     room.canonical_alias = alias
     return room
Пример #20
0
def main():
    module = AnsibleModule(
        argument_spec={
            "username": {
                "required": True,
                "type": "str"
            },
            "password": {
                "required": True,
                "type": "str",
                "no_log": True
            },
            "domain": {
                "required": False,
                "type": "str",
                "default": "http://localhost:8008"
            },
            "join_rule": {
                "required": False,
                "type": "str",
                "choices": ["public", "invite"]
            },
            "history_visibility": {
                "required": False,
                "type": "str",
                "choices": ["invited", "joined", "shared", "world_readable"]
            },
            "invites": {
                "required": False,
                "type": "list",
                "default": []
            },
            "power_levels": {
                "required": False,
                "type": "list",
                "default": []
            },
            "room_alias": {
                "required": False,
                "type": "str"
            },
        })

    client = MatrixClient(module.params['domain'])
    client.login(module.params["username"], module.params["password"])
    room = None
    changed = False
    try:
        room = client.create_room(module.params["room_alias"],
                                  invitees=module.params["invites"])
        changed = True
    except MatrixRequestError as e:
        if json.loads(e.content)["errcode"] == "M_ROOM_IN_USE":
            room_id = client.api.get_room_id("#" +
                                             module.params["room_alias"] +
                                             ":" + client.hs)
            room = Room(client, room_id)
            for user_id in module.params["invites"]:
                # Failure can be due to many reasons, one of which is that the user is
                # already in the room. We interpret failure as lack of changing.
                changed |= room.invite_user(user_id)

        else:
            raise

    if module.params["power_levels"] != []:
        content = client.api.get_power_levels(room.room_id)
        content_changed = False
        for [user, level] in module.params["power_levels"]:
            if content["users"].get(user, -1) != level:
                content["users"][user] = level
                content_changed = True

        if content_changed:
            changed = True
            client.api.set_power_levels(room.room_id, content)

    if module.params["join_rule"] is not None:
        current = get_state(room, "m.room.join_rules")
        if current is None or current["join_rule"] != module.params[
                "join_rule"]:
            client.api.set_join_rule(room.room_id, module.params["join_rule"])

    if module.params["history_visibility"] is not None:
        current = get_state(room, "m.room.history_visibility")
        if current is None or current["history_visibility"] != module.params[
                "history_visibility"]:
            changed = True
            room.send_state_event(
                "m.room.history_visibility", {
                    "history_visibility": module.params["history_visibility"],
                })

    module.exit_json(changed=changed, meta={"room_id": room.room_id})
Пример #21
0
 def create_room(alias, is_public=False, invitees=None):
     room = Room(client, f'!room_id:ownserver.com')
     room.canonical_alias = alias
     return room
Пример #22
0
    service=args.e,
    host=args.l,
    host_display=args.n,
    host_output=args.o,
)

if args.ip4:
    message += 'IPv4: {ip4}; '.format(ip4=args.ip4)
if args.ip6:
    message += 'IPv6: {ip6}; '.format(ip6=args.ip6)
if args.c:
    message += 'Comment: {c}; '.format(c=args.c)
if args.b:
    message += 'Comment by: {b}; '.format(b=args.b)
if args.so:
    message += 'Service Output: {so}; '.format(so=args.so)

print(message)

# MESSAGE SENDING
if PASSWORD:
    client = MatrixClient(SERVER)
    token = client.login_with_password(USERNAME, PASSWORD)
elif TOKEN:
    client = MatrixClient(SERVER, token=TOKEN, user_id=USERNAME)
else:
    sys.exit(0)

if ROOMID:
    room = Room(client, ROOMID)
    room.send_text(message)
Пример #23
0
Файл: main.py Проект: m4tl0/mxpp
    def matrix_control_message(self, room: MatrixRoom, event: Dict):
        """
        Handle a message sent to the control room.

        Does nothing unless a valid command is received:
          refresh  Probes the presence of all XMPP contacts, and updates the roster.
          purge    Leaves any ((un-mapped and non-special) or empty) Matrix rooms.
          joinmuc [email protected]   Joins a muc
          leavemuc [email protected]  Leaves a muc

        :param room: Matrix room object representing the control room
        :param event: The Matrix event that was received. Assumed to be an m.room.message .
        """
        # Always ignore our own messages
        if event['sender'] == self.bot_id:
            return

        logger.debug('matrix_control_message: {}  {}'.format(room.room_id, str(event)))

        if event['content']['msgtype'] == 'm.text':
            message_body = event['content']['body']
            logger.info('Matrix received control message: ' + message_body)

            message_parts = message_body.split()
            if len(message_parts) < 1:
                logger.warning('Received empty control message, ignoring')
                return

            if message_parts[0] == 'refresh':
                for jid in self.topic_room_id_map.keys():
                    self.xmpp.send_presence(pto=jid, ptype='probe')
                self.xmpp.send_presence()
                self.xmpp.get_roster()

            elif message_parts[0] == 'purge':
                self.special_rooms['control'].send_text('Purging unused rooms')

                # Leave from unwanted rooms
                for room in self.get_unmapped_rooms() + self.get_empty_rooms():
                    logger.info('Leaving room {r.room_id} ({r.name}) [{r.topic}]'.format(r=room))

                    if room.topic in self.topic_room_id_map.keys():
                        self.leave_mapped_room(room.topic)
                    else:
                        room.leave()

            elif message_parts[0] == 'joinmuc':
                if len(message_parts) < 2:
                    logger.warning('joinmuc command didn\'t specify a room, ignoring')
                    return

                room_jid = message_parts[1]
                logger.info('XMPP MUC join: {}'.format(room_jid))
                self.create_groupchat_room(room_jid)
                self.xmpp.plugin['xep_0045'].joinMUC(room_jid, self.xmpp_groupchat_nick)

            elif message_parts[0] == 'leavemuc':
                if len(message_parts) < 2:
                    logger.warning('leavemuc command didn\'t specify a room, ignoring')
                    return

                room_jid = message_parts[1]
                room_topic = self.groupchat_flag + room_jid

                success = self.leave_mapped_room(room_topic)
                if not success:
                    msg = 'Groupchat {} isn\'t mapped or doesn\'t exist'.format(room_jid)
                else:
                    msg = 'Left groupchat {}'.format(room_jid)
                self.special_rooms['control'].send_notice(msg)
Пример #24
0
class TestCryptoStore(object):

    # Initialise a store and test some init code
    device_id = 'AUIETSRN'
    user_id = '@user:matrix.org'
    room_id = '!test:example.com'
    room = Room(None, room_id)
    user = User(None, user_id, '')
    room._members[user_id] = user
    db_name = 'test.db'
    db_path = mkdtemp()
    store_conf = {
        'db_name': db_name,
        'db_path': db_path
    }
    store = CryptoStore(
        user_id, device_id=device_id, db_path=db_path, db_name=db_name)
    db_filepath = os.path.join(db_path, db_name)
    assert os.path.exists(db_filepath)
    store.close()
    store = CryptoStore(
        user_id, device_id=device_id, db_path=db_path, db_name=db_name)

    @pytest.fixture(autouse=True, scope='class')
    def cleanup(self):
        yield
        os.remove(self.db_filepath)

    @pytest.fixture()
    def account(self):
        account = self.store.get_olm_account()
        if account is None:
            account = olm.Account()
            self.store.save_olm_account(account)
        return account

    @pytest.fixture()
    def curve_key(self, account):
        return account.identity_keys['curve25519']

    @pytest.fixture()
    def ed_key(self, account):
        return account.identity_keys['ed25519']

    @pytest.fixture()
    def device(self):
        return OlmDevice(None, self.user_id, self.device_id, store_conf=self.store_conf)

    def test_olm_account_persistence(self):
        account = olm.Account()
        identity_keys = account.identity_keys
        self.store.remove_olm_account()

        # Try to load inexisting account
        saved_account = self.store.get_olm_account()
        assert saved_account is None

        # Try to load inexisting account without device_id
        self.store.device_id = None
        with pytest.raises(ValueError):
            self.store.get_olm_account()
        self.store.device_id = self.device_id

        # Save and load
        self.store.save_olm_account(account)
        saved_account = self.store.get_olm_account()
        assert saved_account.identity_keys == identity_keys

        # Save and load without device_id
        self.store.save_olm_account(account)
        self.store.device_id = None
        saved_account = self.store.get_olm_account()
        assert saved_account.identity_keys == identity_keys
        assert self.store.device_id == self.device_id

        # Replace the account, causing foreign keys to be deleted
        self.store.save_sync_token('test')
        self.store.replace_olm_account(account)
        assert self.store.get_sync_token() is None

        # Load the account from an OlmDevice
        device = OlmDevice(None, self.user_id, self.device_id, store_conf=self.store_conf)
        assert device.olm_account.identity_keys == account.identity_keys

        # Load the account from an OlmDevice, without device_id
        device = OlmDevice(None, self.user_id, store_conf=self.store_conf)
        assert device.device_id == self.device_id

    def test_olm_sessions_persistence(self, account, curve_key, device):
        session = olm.OutboundSession(account, curve_key, curve_key)
        sessions = defaultdict(list)

        self.store.load_olm_sessions(sessions)
        assert not sessions
        assert not self.store.get_olm_sessions(curve_key)

        self.store.save_olm_session(curve_key, session)
        self.store.load_olm_sessions(sessions)
        assert sessions[curve_key][0].id == session.id

        saved_sessions = self.store.get_olm_sessions(curve_key)
        assert saved_sessions[0].id == session.id

        sessions.clear()
        saved_sessions = self.store.get_olm_sessions(curve_key, sessions)
        assert sessions[curve_key][0].id == session.id

        # Replace the session when its internal state has changed
        pickle = session.pickle()
        session.encrypt('test')
        self.store.save_olm_session(curve_key, session)
        saved_sessions = self.store.get_olm_sessions(curve_key)
        assert saved_sessions[0].pickle != pickle

        # Load sessions dynamically
        assert not device.olm_sessions
        with pytest.raises(AttributeError):
            device._olm_decrypt(None, curve_key)
        assert device.olm_sessions[curve_key][0].id == session.id

        device.olm_sessions.clear()
        device.device_keys[self.user_id][self.device_id] = device
        device.olm_ensure_sessions({self.user_id: [self.device_id]})
        assert device.olm_sessions[curve_key][0].id == session.id

        # Test cascade deletion
        self.store.remove_olm_account()
        assert not self.store.get_olm_sessions(curve_key)

    def test_megolm_inbound_persistence(self, curve_key, ed_key, device):
        out_session = olm.OutboundGroupSession()
        session = MegolmInboundSession(out_session.session_key, ed_key)
        session.forwarding_chain.append(curve_key)
        sessions = defaultdict(lambda: defaultdict(dict))

        self.store.load_inbound_sessions(sessions)
        assert not sessions
        assert not self.store.get_inbound_session(self.room_id, curve_key, session.id)

        self.store.save_inbound_session(self.room_id, curve_key, session)
        self.store.load_inbound_sessions(sessions)
        assert sessions[self.room_id][curve_key][session.id].id == session.id

        saved_session = self.store.get_inbound_session(self.room_id, curve_key,
                                                       session.id)
        assert saved_session.id == session.id
        assert saved_session.forwarding_chain == [curve_key]

        sessions = {}
        saved_session = self.store.get_inbound_session(self.room_id, curve_key,
                                                       session.id, sessions)
        assert sessions[session.id].id == session.id

        assert not device.megolm_inbound_sessions
        created = device.megolm_add_inbound_session(
            self.room_id, curve_key, ed_key, session.id, out_session.session_key)
        assert not created
        assert device.megolm_inbound_sessions[self.room_id][curve_key][session.id].id == \
            session.id

        device.megolm_inbound_sessions.clear()
        content = {
            'sender_key': curve_key,
            'session_id': session.id,
            'algorithm': device._megolm_algorithm,
            'device_id': ''
        }
        event = {
            'sender': '',
            'room_id': self.room_id,
            'content': content
        }
        with pytest.raises(KeyError):
            device.megolm_decrypt_event(event)
        assert device.megolm_inbound_sessions[self.room_id][curve_key][session.id].id == \
            session.id

        self.store.remove_olm_account()
        assert not self.store.get_inbound_session(self.room_id, curve_key, session.id)

    @pytest.mark.usefixtures('account')
    def test_megolm_outbound_persistence(self, device):
        session = MegolmOutboundSession(max_messages=2, max_age=100000)
        session.message_count = 1
        session.add_device(self.device_id)
        sessions = {}

        self.store.load_outbound_sessions(sessions)
        assert not sessions
        assert not self.store.get_outbound_session(self.room_id)

        self.store.save_outbound_session(self.room_id, session)
        self.store.save_megolm_outbound_devices(self.room_id, {self.device_id})
        self.store.load_outbound_sessions(sessions)
        assert sessions[self.room_id].id == session.id
        assert sessions[self.room_id].devices == session.devices
        assert sessions[self.room_id].creation_time == session.creation_time
        assert sessions[self.room_id].max_messages == session.max_messages
        assert sessions[self.room_id].message_count == session.message_count
        assert sessions[self.room_id].max_age == session.max_age

        saved_session = self.store.get_outbound_session(self.room_id)
        assert saved_session.id == session.id
        assert saved_session.devices == session.devices
        assert saved_session.creation_time == session.creation_time
        assert saved_session.max_messages == session.max_messages
        assert saved_session.message_count == session.message_count
        assert saved_session.max_age == session.max_age

        sessions.clear()
        saved_session = self.store.get_outbound_session(self.room_id, sessions)
        assert sessions[self.room_id].id == session.id

        self.store.remove_outbound_session(self.room_id)
        assert not self.store.get_outbound_session(self.room_id)

        self.store.save_outbound_session(self.room_id, session)
        saved_session = self.store.get_outbound_session(self.room_id)
        # Verify the saved devices have been erased with the session
        assert not saved_session.devices

        room = Room(None, self.room_id)
        with pytest.raises(AttributeError):
            device.megolm_build_encrypted_event(room, {})
        assert device.megolm_outbound_sessions[self.room_id].id == session.id

        self.store.remove_olm_account()
        assert not self.store.get_outbound_session(self.room_id)

    @pytest.mark.usefixtures('account')
    def test_device_keys_persistence(self, device):
        user_devices = {self.user_id: [self.device_id]}
        device_keys = defaultdict(dict)
        device._verified = True

        self.store.load_device_keys(None, device_keys)
        assert not device_keys
        assert not self.store.get_device_keys(None, user_devices, device_keys)
        assert not device_keys

        device_keys_to_save = {self.user_id: {self.device_id: device}}
        self.store.save_device_keys(device_keys_to_save)
        self.store.load_device_keys(None, device_keys)
        assert device_keys[self.user_id][self.device_id].curve25519 == \
            device.curve25519
        assert device_keys[self.user_id][self.device_id].verified

        device_keys.clear()
        devices = self.store.get_device_keys(None, user_devices)[self.user_id]
        assert devices[self.device_id].curve25519 == device.curve25519
        assert self.store.get_device_keys(None, user_devices, device_keys)
        assert device_keys[self.user_id][self.device_id].curve25519 == \
            device.curve25519
        assert device_keys[self.user_id][self.device_id].verified

        # Test device verification persistence
        device.verified = False
        device.ignored = True
        devices = self.store.get_device_keys(None, user_devices)[self.user_id]
        assert not devices[self.device_id].verified
        assert devices[self.device_id].ignored

        # Test [] wildcard
        devices = self.store.get_device_keys(None, {self.user_id: []})[self.user_id]
        assert devices[self.device_id].curve25519 == device.curve25519

        device.device_list.tracked_user_ids = {self.user_id}
        device.device_list.get_room_device_keys(self.room)
        assert device_keys[self.user_id][self.device_id].curve25519 == \
            device.curve25519

        # Test multiples []
        device_keys.clear()
        user_id = 'test'
        device_id = 'test'
        device_keys_to_save[user_id] = {device_id: device}
        self.store.save_device_keys(device_keys_to_save)
        user_devices[user_id] = []
        user_devices[self.user_id] = []
        device_keys = self.store.get_device_keys(None, user_devices)
        assert device_keys[self.user_id][self.device_id].curve25519 == device.curve25519
        assert device_keys[user_id][device_id].curve25519 == device.curve25519

        # Try to verify a device that has no keys
        device._ed25519 = None
        with pytest.raises(ValueError):
            device.verified = False

        self.store.remove_olm_account()
        assert not self.store.get_device_keys(None, user_devices)

    @pytest.mark.usefixtures('account')
    def test_tracked_users_persistence(self):
        tracked_user_ids = set()
        tracked_user_ids_to_save = {self.user_id}

        self.store.load_tracked_users(tracked_user_ids)
        assert not tracked_user_ids

        self.store.save_tracked_users(tracked_user_ids_to_save)
        self.store.load_tracked_users(tracked_user_ids)
        assert tracked_user_ids == tracked_user_ids_to_save

        self.store.remove_tracked_users({self.user_id})
        tracked_user_ids.clear()
        self.store.load_tracked_users(tracked_user_ids)
        assert not tracked_user_ids

    @pytest.mark.usefixtures('account')
    def test_sync_token_persistence(self):
        sync_token = 'test'

        assert not self.store.get_sync_token()

        self.store.save_sync_token(sync_token)
        assert self.store.get_sync_token() == sync_token

        sync_token = 'new'
        self.store.save_sync_token(sync_token)
        assert self.store.get_sync_token() == sync_token

    @pytest.mark.usefixtures('account')
    def test_key_requests(self):
        session_id = 'test'
        session_ids = set()

        self.store.load_outgoing_key_requests(session_ids)
        assert not session_ids

        self.store.add_outgoing_key_request(session_id)
        self.store.load_outgoing_key_requests(session_ids)
        assert session_id in session_ids

        session_ids.clear()
        self.store.remove_outgoing_key_request(session_id)
        self.store.load_outgoing_key_requests(session_ids)
        assert not session_ids

    def test_load_all(self, account, curve_key, ed_key, device):
        curve_key = account.identity_keys['curve25519']
        session = olm.OutboundSession(account, curve_key, curve_key)
        out_session = MegolmOutboundSession()
        out_session.add_device(self.device_id)
        in_session = MegolmInboundSession(out_session.session_key, ed_key)
        device_keys_to_save = {self.user_id: {self.device_id: device}}

        self.store.save_inbound_session(self.room_id, curve_key, in_session)
        self.store.save_olm_session(curve_key, session)
        self.store.save_outbound_session(self.room_id, out_session)
        self.store.save_megolm_outbound_devices(self.room_id, {self.device_id})
        self.store.save_device_keys(device_keys_to_save)

        device = OlmDevice(
            None, self.user_id, self.device_id, store_conf=self.store_conf, load_all=True)

        assert session.id in {s.id for s in device.olm_sessions[curve_key]}
        saved_in_session = \
            device.megolm_inbound_sessions[self.room_id][curve_key][in_session.id]
        assert saved_in_session.id == in_session.id
        saved_out_session = device.megolm_outbound_sessions[self.room_id]
        assert saved_out_session.id == out_session.id
        assert saved_out_session.devices == out_session.devices
        assert device.device_keys[self.user_id][self.device_id].curve25519 == \
            device.curve25519