<commit_before># Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from activitystreams.models.activity import Activity
from activitystreams.models.defobject import DefObject
from activitystreams.models.actor import Actor
from activitystreams.models.target import Target

import logging
import traceback

from dino import environ
from dino import utils
from dino.config import SessionKeys
from dino.config import ApiActions
from dino.config import ApiTargets
from dino.config import ConfigKeys
from dino.config import ErrorCodes as ECodes
from dino.validation.base import BaseValidator
from dino.exceptions import NoSuchRoomException
from dino.exceptions import NoSuchUserException
from dino.exceptions import NoSuchChannelException
from dino.exceptions import NoChannelFoundException
from dino import validation
from dino.validation.duration import DurationValidator

__author__ = '<NAME> <<EMAIL>>'

logger = logging.getLogger(__name__)


class RequestValidator(BaseValidator):
    def on_message(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        user_id = activity.actor.id
        object_type = activity.target.object_type
        message = activity.object.content

        from_room_id = None
        if hasattr(activity.actor, 'url'):
            from_room_id = activity.actor.url

        if message is None or len(message.strip()) == 0:
            return False, ECodes.EMPTY_MESSAGE, 'empty message body'

        if not utils.is_base64(message):
            return False, ECodes.NOT_BASE64, 'invalid message content, not base64 encoded'

        if room_id is None or room_id == '':
            return False, ECodes.MISSING_TARGET_ID, 'no room id specified when sending message'

        if object_type not in ['room', 'private']:
            return False, ECodes.INVALID_TARGET_TYPE, 'invalid object_type "%s", must be one of [room, private]' % object_type

        if object_type == 'room':
            channel_id = None
            if hasattr(activity, 'object') and hasattr(activity.object, 'url'):
                channel_id = activity.object.url
            if channel_id is None or len(channel_id.strip()) == 0:
                channel_id = utils.get_channel_for_room(room_id)

            if channel_id is None or channel_id == '':
                return False, ECodes.MISSING_OBJECT_URL, 'no channel id specified when sending message'

            activity.object.url = channel_id
            activity.object.display_name = utils.get_channel_name(channel_id)

            if not utils.channel_exists(channel_id):
                return False, ECodes.NO_SUCH_CHANNEL, 'channel %s does not exists' % channel_id

            if not utils.room_exists(channel_id, room_id):
                return False, ECodes.NO_SUCH_ROOM, 'target room %s does not exist' % room_id

            if from_room_id is not None:
                if from_room_id != room_id and not utils.room_exists(channel_id, from_room_id):
                    return False, ECodes.NO_SUCH_ROOM, 'origin room %s does not exist' % from_room_id

            if not utils.is_user_in_room(user_id, room_id):
                if from_room_id is None:
                    return False, ECodes.USER_NOT_IN_ROOM, 'user is not in target room'
                if not utils.is_user_in_room(user_id, from_room_id):
                    return False, ECodes.USER_NOT_IN_ROOM, 'user is not in origin room, cannot send message from there'
                if not utils.can_send_cross_room(activity, from_room_id, room_id):
                    return False, ECodes.NOT_ALLOWED, 'user not allowed to send cross-room msg from %s to %s' % (from_room_id, room_id)

        elif object_type == 'private':
            try:
                utils.get_user_name_for(room_id)
            except NoSuchUserException:
                return False, ECodes.INVALID_TARGET_TYPE, 'target.id is not a known user id'

        return True, None, None

    def on_delete(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id
        room_id = activity.target.id

        if not utils.user_is_allowed_to_delete_message(room_id, user_id):
            return False, ECodes.NOT_ALLOWED, 'not allowed to remove message in room %s' % room_id
        return True, None, None

    def on_login(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id

        is_banned, duration = utils.is_banned_globally(user_id)
        if is_banned:
            environ.env.join_room(user_id)
            reason = utils.reason_for_ban(user_id)
            json_act = utils.activity_for_already_banned(duration, reason)
            environ.env.emit('gn_banned', json_act, json=True, room=user_id, broadcast=False, include_self=True)
            environ.env.disconnect()
            logger.info('user %s is banned from chatting for: %ss' % (user_id, duration))
            return False, ECodes.USER_IS_BANNED, json_act

        if hasattr(activity.actor, 'attachments') and activity.actor.attachments is not None:
            for attachment in activity.actor.attachments:
                environ.env.session[attachment.object_type] = attachment.content

        if SessionKeys.token.value not in environ.env.session:
            environ.env.disconnect()
            return False, ECodes.NO_USER_IN_SESSION, 'no token in session'

        token = environ.env.session.get(SessionKeys.token.value)
        is_valid, error_msg, session = self.validate_login(user_id, token)

        if not is_valid:
            environ.env.stats.incr('on_login.failed')
            environ.env.disconnect()
            return False, ECodes.NOT_ALLOWED, error_msg

        for session_key, session_value in session.items():
            environ.env.session[session_key] = session_value

        return True, None, None

    def on_remove_room(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id
        room_id = activity.target.id

        if utils.is_owner(room_id, user_id):
            return True, None, None
        if utils.is_super_user(user_id):
            return True, None, None
        if utils.is_global_moderator(user_id) and utils.is_room_ephemeral(room_id):
            return True, None, None
        if utils.is_moderator(room_id, user_id) and utils.is_room_ephemeral(room_id):
            return True, None, None

        channel_id = utils.get_channel_for_room(room_id)
        if utils.is_admin(channel_id, user_id):
            return True, None, None
        if utils.is_owner_channel(channel_id, user_id):
            return True, None, None

        return False, ECodes.NOT_ALLOWED, 'user %s is not allowed to remove the room' % str(user_id)

    def on_disconnect(self, activity: Activity) -> (bool, int, str):
        user_id = environ.env.session.get(SessionKeys.user_id.value)
        user_name = environ.env.session.get(SessionKeys.user_name.value)
        if user_id is None or not isinstance(user_id, str) or user_name is None:
            return False, ECodes.NO_USER_IN_SESSION, 'no user in session, not connected'
        return True, None, None

    def on_report(self, activity: Activity) -> (bool, int, str):
        if not hasattr(activity, 'object') or not hasattr(activity.object, 'content'):
            return False, ECodes.MISSING_OBJECT_CONTENT, 'need object.content (reported message)'
        if not hasattr(activity.object, 'id'):
            return False, ECodes.MISSING_OBJECT_ID, 'need object.id (id of reported message)'

        if not hasattr(activity, 'target') or not hasattr(activity.target, 'id'):
            return False, ECodes.MISSING_TARGET_ID, 'need target.id (id of user reported)'

        try:
            activity.target.display_name = utils.get_user_name_for(activity.target.id)
        except Exception as e:
            logger.warning('could not get username for id %s: %s' % (str(activity.target.id), str(e)))
            logger.exception(traceback.format_exc(e))
            return False, ECodes.NO_SUCH_USER, 'no such user %s' % activity.target.id

        if not utils.is_base64(activity.object.content):
            return False, ECodes.NOT_BASE64, 'object.content is not base64 encoded'

        if hasattr(activity.object, 'summary') and len(activity.object.summary.trim()) > 0:
            if not utils.is_base64(activity.object.summary):
                return False, ECodes.NOT_BASE64, 'object.summary is not base64 encoded'

        return True, None, None

    def on_ban(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        user_id = activity.actor.id
        kicked_id = activity.object.id
        ban_duration = activity.object.summary

        channel_id = None
        if hasattr(activity, 'object') and hasattr(activity.object, 'url'):
            channel_id = activity.object.url
        if channel_id is None or len(channel_id.strip()) == 0:
            channel_id = utils.get_channel_for_room(room_id)

        try:
            DurationValidator(ban_duration)
        except ValueError as e:
            return False, ECodes.INVALID_BAN_DURATION, 'invalid ban duration: %s' % str(e)

        if room_id is not None and len(room_id.strip()) > 0:
            try:
                utils.get_room_name(room_id)
            except NoSuchRoomException as e:
                return False, ECodes.NO_SUCH_ROOM, 'no room found for uuid: %s' % str(e)

        is_global_ban = room_id is None or room_id == ''

        if kicked_id is None or kicked_id.strip() == '':
            return False, ECodes.MISSING_OBJECT_ID, 'got blank user id, can not ban'

        if not is_global_ban and not utils.room_exists(channel_id, room_id):
            return False, ECodes.NO_SUCH_ROOM, 'no room with id "%s" exists' % room_id

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            return True, None, None

        if not is_global_ban:
            if not utils.is_owner(room_id, user_id):
                return False, ECodes.NOT_ALLOWED, 'only owners can ban'
        elif not utils.is_admin(channel_id, user_id) and not utils.is_super_user(user_id):
            return False, ECodes.NOT_ALLOWED, 'only admins and super users can do global bans'

        return True, None, None

    def on_request_admin(self, activity: Activity) -> (bool, int, str):
        activity.actor = Actor({
            'id': str(environ.env.session.get(SessionKeys.user_id.value)),
            'displayName': environ.env.session.get(SessionKeys.user_name.value)
        })

        room_id = activity.target.id
        channel_id = utils.get_channel_for_room(room_id)
        admin_room_id = utils.get_admin_room()

        if admin_room_id is None or len(admin_room_id.strip()) == 0:
            logger.error('no admin room found for channel "%s"' % channel_id)
            return False, ECodes.NO_ADMIN_ROOM_FOUND, 'no admin room for this channel'<commit_msg>don't try to get room name for global ban<commit_after># Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from activitystreams.models.activity import Activity
from activitystreams.models.defobject import DefObject
from activitystreams.models.actor import Actor
from activitystreams.models.target import Target

import logging
import traceback

from dino import environ
from dino import utils
from dino.config import SessionKeys
from dino.config import ApiActions
from dino.config import ApiTargets
from dino.config import ConfigKeys
from dino.config import ErrorCodes as ECodes
from dino.validation.base import BaseValidator
from dino.exceptions import NoSuchRoomException
from dino.exceptions import NoSuchUserException
from dino.exceptions import NoSuchChannelException
from dino.exceptions import NoChannelFoundException
from dino import validation
from dino.validation.duration import DurationValidator

__author__ = '<NAME> <<EMAIL>>'

logger = logging.getLogger(__name__)


class RequestValidator(BaseValidator):
    def on_message(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        user_id = activity.actor.id
        object_type = activity.target.object_type
        message = activity.object.content

        from_room_id = None
        if hasattr(activity.actor, 'url'):
            from_room_id = activity.actor.url

        if message is None or len(message.strip()) == 0:
            return False, ECodes.EMPTY_MESSAGE, 'empty message body'

        if not utils.is_base64(message):
            return False, ECodes.NOT_BASE64, 'invalid message content, not base64 encoded'

        if room_id is None or room_id == '':
            return False, ECodes.MISSING_TARGET_ID, 'no room id specified when sending message'

        if object_type not in ['room', 'private']:
            return False, ECodes.INVALID_TARGET_TYPE, 'invalid object_type "%s", must be one of [room, private]' % object_type

        if object_type == 'room':
            channel_id = None
            if hasattr(activity, 'object') and hasattr(activity.object, 'url'):
                channel_id = activity.object.url
            if channel_id is None or len(channel_id.strip()) == 0:
                channel_id = utils.get_channel_for_room(room_id)

            if channel_id is None or channel_id == '':
                return False, ECodes.MISSING_OBJECT_URL, 'no channel id specified when sending message'

            activity.object.url = channel_id
            activity.object.display_name = utils.get_channel_name(channel_id)

            if not utils.channel_exists(channel_id):
                return False, ECodes.NO_SUCH_CHANNEL, 'channel %s does not exists' % channel_id

            if not utils.room_exists(channel_id, room_id):
                return False, ECodes.NO_SUCH_ROOM, 'target room %s does not exist' % room_id

            if from_room_id is not None:
                if from_room_id != room_id and not utils.room_exists(channel_id, from_room_id):
                    return False, ECodes.NO_SUCH_ROOM, 'origin room %s does not exist' % from_room_id

            if not utils.is_user_in_room(user_id, room_id):
                if from_room_id is None:
                    return False, ECodes.USER_NOT_IN_ROOM, 'user is not in target room'
                if not utils.is_user_in_room(user_id, from_room_id):
                    return False, ECodes.USER_NOT_IN_ROOM, 'user is not in origin room, cannot send message from there'
                if not utils.can_send_cross_room(activity, from_room_id, room_id):
                    return False, ECodes.NOT_ALLOWED, 'user not allowed to send cross-room msg from %s to %s' % (from_room_id, room_id)

        elif object_type == 'private':
            try:
                utils.get_user_name_for(room_id)
            except NoSuchUserException:
                return False, ECodes.INVALID_TARGET_TYPE, 'target.id is not a known user id'

        return True, None, None

    def on_delete(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id
        room_id = activity.target.id

        if not utils.user_is_allowed_to_delete_message(room_id, user_id):
            return False, ECodes.NOT_ALLOWED, 'not allowed to remove message in room %s' % room_id
        return True, None, None

    def on_login(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id

        is_banned, duration = utils.is_banned_globally(user_id)
        if is_banned:
            environ.env.join_room(user_id)
            reason = utils.reason_for_ban(user_id)
            json_act = utils.activity_for_already_banned(duration, reason)
            environ.env.emit('gn_banned', json_act, json=True, room=user_id, broadcast=False, include_self=True)
            environ.env.disconnect()
            logger.info('user %s is banned from chatting for: %ss' % (user_id, duration))
            return False, ECodes.USER_IS_BANNED, json_act

        if hasattr(activity.actor, 'attachments') and activity.actor.attachments is not None:
            for attachment in activity.actor.attachments:
                environ.env.session[attachment.object_type] = attachment.content

        if SessionKeys.token.value not in environ.env.session:
            environ.env.disconnect()
            return False, ECodes.NO_USER_IN_SESSION, 'no token in session'

        token = environ.env.session.get(SessionKeys.token.value)
        is_valid, error_msg, session = self.validate_login(user_id, token)

        if not is_valid:
            environ.env.stats.incr('on_login.failed')
            environ.env.disconnect()
            return False, ECodes.NOT_ALLOWED, error_msg

        for session_key, session_value in session.items():
            environ.env.session[session_key] = session_value

        return True, None, None

    def on_remove_room(self, activity: Activity) -> (bool, int, str):
        user_id = activity.actor.id
        room_id = activity.target.id

        if utils.is_owner(room_id, user_id):
            return True, None, None
        if utils.is_super_user(user_id):
            return True, None, None
        if utils.is_global_moderator(user_id) and utils.is_room_ephemeral(room_id):
            return True, None, None
        if utils.is_moderator(room_id, user_id) and utils.is_room_ephemeral(room_id):
            return True, None, None

        channel_id = utils.get_channel_for_room(room_id)
        if utils.is_admin(channel_id, user_id):
            return True, None, None
        if utils.is_owner_channel(channel_id, user_id):
            return True, None, None

        return False, ECodes.NOT_ALLOWED, 'user %s is not allowed to remove the room' % str(user_id)

    def on_disconnect(self, activity: Activity) -> (bool, int, str):
        user_id = environ.env.session.get(SessionKeys.user_id.value)
        user_name = environ.env.session.get(SessionKeys.user_name.value)
        if user_id is None or not isinstance(user_id, str) or user_name is None:
            return False, ECodes.NO_USER_IN_SESSION, 'no user in session, not connected'
        return True, None, None

    def on_report(self, activity: Activity) -> (bool, int, str):
        if not hasattr(activity, 'object') or not hasattr(activity.object, 'content'):
            return False, ECodes.MISSING_OBJECT_CONTENT, 'need object.content (reported message)'
        if not hasattr(activity.object, 'id'):
            return False, ECodes.MISSING_OBJECT_ID, 'need object.id (id of reported message)'

        if not hasattr(activity, 'target') or not hasattr(activity.target, 'id'):
            return False, ECodes.MISSING_TARGET_ID, 'need target.id (id of user reported)'

        try:
            activity.target.display_name = utils.get_user_name_for(activity.target.id)
        except Exception as e:
            logger.warning('could not get username for id %s: %s' % (str(activity.target.id), str(e)))
            logger.exception(traceback.format_exc(e))
            return False, ECodes.NO_SUCH_USER, 'no such user %s' % activity.target.id

        if not utils.is_base64(activity.object.content):
            return False, ECodes.NOT_BASE64, 'object.content is not base64 encoded'

        if hasattr(activity.object, 'summary') and len(activity.object.summary.trim()) > 0:
            if not utils.is_base64(activity.object.summary):
                return False, ECodes.NOT_BASE64, 'object.summary is not base64 encoded'

        return True, None, None

    def on_ban(self, activity: Activity) -> (bool, int, str):
        room_id = activity.target.id
        target_type = activity.target.object_type
        user_id = activity.actor.id
        kicked_id = activity.object.id
        ban_duration = activity.object.summary

        channel_id = None
        if hasattr(activity, 'object') and hasattr(activity.object, 'url'):
            channel_id = activity.object.url
        if channel_id is None or len(channel_id.strip()) == 0:
            channel_id = utils.get_channel_for_room(room_id)

        try:
            DurationValidator(ban_duration)
        except ValueError as e:
            return False, ECodes.INVALID_BAN_DURATION, 'invalid ban duration: %s' % str(e)

        is_global_ban = target_type == 'global' or room_id is None or room_id == ''

        if not is_global_ban and room_id is not None and len(room_id.strip()) > 0:
            try:
                utils.get_room_name(room_id)
            except NoSuchRoomException as e:
                return False, ECodes.NO_SUCH_ROOM, 'no room found for uuid: %s' % str(e)

        if kicked_id is None or kicked_id.strip() == '':
            return False, ECodes.MISSING_OBJECT_ID, 'got blank user id, can not ban'

        if not is_global_ban and not utils.room_exists(channel_id, room_id):
            return False, ECodes.NO_SUCH_ROOM, 'no room with id "%s" exists' % room_id

        if utils.is_super_user(user_id) or utils.is_global_moderator(user_id):
            return True, None, None

        if not is_global_ban:
            if not utils.is_owner(room_id, user_id):
                return False, ECodes.NOT_ALLOWED, 'only owners can ban'
        elif not utils.is_admin(channel_id, user_id) and not utils.is_super_user(user_id):
            return False, ECodes.NOT_ALLOWED, 'only admins and super users can do global bans'

        return True, None, None

    def on_request_admin(self, activity: Activity) -> (bool, int, str):
        activity.actor = Actor({
            'id': str(environ.env.session.get(SessionKeys.user_id.value)),
            'displayName': environ.env.session.get(SessionKeys.user_name.value)
        })

        room_id = activity.target.id
        channel_id = utils.get_channel_for_room(room_id)
        admin_room_id = utils.get_admin_room()

        if admin_room_id is None or len(admin_room_id.strip()) == 0:
            logger.error('no admin room found for channel "%s"' % channel_id)
            return False, ECodes.NO_ADMIN_ROOM_FOUND, 'no admin room for this channel'
{'fraction_non_alphanumeric': 0.0856407796624951, 'fraction_numerical': 0.002441895957790084, 'mean_word_length': 2.750449713818479, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 29, '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': '892872', 'n_tokens_mistral': 6805, 'n_tokens_neox': 6711, 'n_words': 1925}