Example #1
0
    def post(self):
        message_obj = json.loads(self.request.body)
        from_client_id = message_obj['fromClientId']
        assert (self.session.user_id == int(from_client_id.split('|')[0]))
        room_id = message_obj['chatRoomId']
        message_obj['fromUsernameAsWritten'] = self.session.username_as_written

        try:
            chat_room_obj = chat_room_module.ChatRoomModel.get_by_id(room_id)
            if chat_room_obj:
                self.handle_message_room(chat_room_obj, from_client_id,
                                         message_obj)
            else:
                logging.error('Unknown room_id %d' % room_id)
                raise Exception('unknownRoomId')

            http_helpers.set_http_ok_json_response(self.response, {})

        except:
            status_string = 'Server error'
            http_status_code = 500
            logging_function = logging.error

            http_helpers.set_error_json_response_and_write_log(
                self.response, status_string, logging_function,
                http_status_code, self.request)
    def handle_message_client(self, from_client_id, message_obj):
        # This function passes a message from one user in a given "room" to the other user in the same room.
        # It is used for exchanging sdp (session description protocol) data for setting up sessions, as well
        # as for passing video and other information from one user to the other.

        to_client_id = message_obj['toClientId']
        message_type = message_obj['messageType']
        message_payload = message_obj['messagePayload']
        message_obj['fromUsernameAsWritten'] = self.session.username_as_written

        if message_type == 'videoExchangeStatusMsg':

            logging.info('clientId %s videoElementsEnabledAndCameraAccessRequested is: %s ' %
                         (from_client_id, message_payload['videoElementsEnabledAndCameraAccessRequested']))

            if message_payload['videoElementsEnabledAndCameraAccessRequested'] == 'doVideoExchange':

                video_setup.VideoSetup.txn_add_user_id_to_video_elements_enabled_client_ids(from_client_id, to_client_id )
                send_video_call_settings_to_participants(from_client_id, to_client_id)
            else:
                assert message_payload['videoElementsEnabledAndCameraAccessRequested'] == 'hangupVideoExchange' or \
                    message_payload['videoElementsEnabledAndCameraAccessRequested'] == 'denyVideoExchange'

                video_setup.VideoSetup.txn_remove_user_id_from_video_elements_enabled_client_ids(from_client_id, to_client_id )

            if message_type == 'sdp' and message_payload['type'] == 'offer':
                # This is just for debugging
                logging.info('sdp offer. Payload: %s' % repr(message_payload))

        logging.info('\n***\nSending message to client %s: %s\n' % (to_client_id,  json.dumps(message_obj)))
        channel.send_message(to_client_id, json.dumps(message_obj))
        http_helpers.set_http_ok_json_response(self.response, {})
Example #3
0
    def post(self):
        client_id = self.session.client_obj.key.id()

        expire_dt = datetime.datetime.utcnow() + datetime.timedelta(
            minutes=constants.turn_relay_timeout_minutes)
        expire_ts = int(time.mktime((expire_dt).timetuple()))

        expire_datetime = time.strftime('%Y-%m-%d %H:%M:%S',
                                        time.localtime(expire_ts))
        logging.debug('turn rest expires at %s (epoch value %d)' %
                      (expire_datetime, expire_ts))

        uris = []
        for option in turn_uri_combinations:
            uris.append("turn:%s:%s?transport=%s" %
                        (constants.turn_ip, option[0], option[1]))

        turn_username = "******" % (expire_ts, client_id)
        turn_password = b64encode(
            hmac.new(constants.turn_shared_secret, turn_username,
                     hashlib.sha1).digest())

        response_dict = {
            'turn_username': turn_username,
            'turn_password': turn_password,
            'uris': uris
        }

        http_helpers.set_http_ok_json_response(self.response, response_dict)
    def get(self, chat_room_name_from_url=None):
        chat_room_name_from_url = chat_room_name_from_url.decode('utf8')
        chat_room_name_normalized = chat_room_name_from_url.lower()

        if chat_room_name_normalized:
            logging.info('Query for room name: ' + chat_room_name_normalized)
            chat_room_obj = ChatRoomModel.query(ChatRoomModel.chat_room_name_normalized == chat_room_name_normalized).get()

            if chat_room_obj:
                response_dict = {
                    'chatRoomName': chat_room_name_normalized,
                    'roomIsRegistered': True,
                    'numInRoom': chat_room_obj.get_occupancy(),
                }
                logging.info('Found room: ' + repr(chat_room_obj))

            else:
                response_dict = {
                    'chatRoomName': chat_room_name_normalized,
                    'roomIsRegistered' : False,
                    'numInRoom': 0
                }
                logging.info('Room name is available: ' + chat_room_name_normalized)

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        else:
            err_status = 'ErrorChatRoomNameRequired'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_error_json_response(self.response, err_status)
    def get(self, username_from_url=None):
        username_from_url = username_from_url.decode('utf8')
        username_normalized = username_from_url.lower()

        if username_normalized:
            logging.info('Query for username: '******'usernameNormalized': username_normalized,
                    'usernameAvailable': False,
                }
                logging.info('Username taken: ' + repr(user_obj))

            else:
                response_dict = {
                    'usernameNormalized': username_normalized,
                    'usernameAvailable': True,
                }
                logging.info('Username is available: ' + username_normalized)

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        else:
            err_status = 'ErrorUsernameRequired'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_error_json_response(self.response, err_status)
Example #6
0
    def post(self):

        client_id = self.request.get('from')
        client_obj = clients.ClientModel.get_by_id(client_id)

        logging.warning('client_id: %s has been disconnected' % client_id)

        if client_obj:
            video_setup.VideoSetup.remove_video_setup_objects_containing_client_id(client_id)

            for chat_room_obj_key in client_obj.list_of_open_chat_rooms_keys:
                chat_room_obj = chat_room_obj_key.get()

                if chat_room_obj.has_client(client_id):

                    # Remove the client from the room. However, notice that we don't remove the room from the client's
                    # list_of_open_chat_rooms_keys, because if the channel comes back, we want the client to automatically
                    # join the rooms that he previously had open.
                    chat_room_obj = chat_room_obj.txn_remove_client_from_room(client_id)

                    # This client has disconnected from the room, so we want to send an update to the remote
                    # clients informing them of the new room status.
                    messaging.send_room_occupancy_to_clients(chat_room_obj, chat_room_obj.room_members_client_ids,
                                                             recompute_members_from_scratch=True)

                else:
                    logging.info('Room %s (%d) does not have client %s - probably already removed' % (chat_room_obj.chat_room_name_normalized, chat_room_obj.key.id(), client_id))

        http_helpers.set_http_ok_json_response(self.response, {})
    def get(self):
        num_clients_removed = self.cleanup_expired_clients()
        if num_clients_removed >= NUM_OBJECTS_TO_REMOVE_AT_A_TIME:
            time.sleep(5.0) # just in case it takes a few milliseconds for the DB to get updated
            taskqueue.add(queue_name = 'cleanup-sessions-queue', url='/_lx/admin/cleanup_expired_clients/')

        http_helpers.set_http_ok_json_response(self.response, {'CleanupExpiredClients': 'OK'})
Example #8
0
    def post(self):
        data_object = json.loads(self.request.body)
        client_id = data_object['clientId']
        assert self.session.user_id == int(client_id.split('|')[0])
        room_id = data_object['chatRoomId']

        chat_room_obj = chat_room_module.ChatRoomModel.get_by_id(room_id)
        self.add_client_to_room(chat_room_obj, client_id)

        http_helpers.set_http_ok_json_response(self.response, {})
Example #9
0
    def post(self):
        data_object = json.loads(self.request.body)
        client_id = data_object['clientId']
        assert self.session.user_id == int(client_id.split('|')[0])
        logging.debug('CreateClientOnServer called for client_id: %s' % client_id)

        client_obj = clients.ClientModel.get_by_id(client_id)
        if not client_obj:
            clients.ClientModel.txn_create_new_client_object(client_id)

        http_helpers.set_http_ok_json_response(self.response, {})
Example #10
0
    def get(self):
        num_clients_removed = self.cleanup_expired_clients()
        if num_clients_removed >= NUM_OBJECTS_TO_REMOVE_AT_A_TIME:
            time.sleep(
                5.0
            )  # just in case it takes a few milliseconds for the DB to get updated
            taskqueue.add(queue_name='cleanup-sessions-queue',
                          url='/_lx/admin/cleanup_expired_clients/')

        http_helpers.set_http_ok_json_response(self.response,
                                               {'CleanupExpiredClients': 'OK'})
Example #11
0
    def post(self):
        message_obj = self.session.post_body_json
        message_type = message_obj['messageType']
        presence_state_name = message_obj['messagePayload']['presenceStateName']
        currently_open_chat_room_id = message_obj['messagePayload']['currentlyOpenChatRoomId']

        client_obj = self.session.client_obj
        client_id = client_obj.key.id()

        # We only update the user presence in the case that this is posted to as an acknowledgement
        # of a heartbeat. If we were to update presence state in other cases, then the memcache and other timeouts
        # that are synchronized to the heartbeat timing would be incorrect (eg. This function is also called when
        # the user changes rooms so that they receive an updated list of room members, but that doesn't mean
        # that their channel is currently up an running -- the channel is only proven to be up if we have received
        # an ackHeartBeat message from the client, as the client sends ackHeartBeat as a response to a
        # synAckHeartBeat message that is sent on the channel)
        if message_type == 'ackHeartbeat':

            # Get the previous presence state, so that we can detect if a user was offline, which would mean
            # that they need to be added back to all of the rooms that they previously had open.
            previous_presence_state = client_obj.get_current_presence_state()

            # Important to update the presence state before adding the client to the rooms, so that they will not
            # be immediately removed from the rooms in the event that we generate a new room members list (which
            # would remove offline clients) at the same time that we are attempting to add the client back to all of the
            # rooms that he previously had open.
            client_obj.store_current_presence_state(presence_state_name)

            if previous_presence_state == 'PRESENCE_OFFLINE':
                # Since the client was offline, they (should) have already been removed from all rooms that they
                # previously had open. Add them back to all rooms since we now know that they are alive.
                logging.info('Client %s had state %s, and is now getting added back to all previously '
                             'open rooms. New client state is %s' %
                             (client_id, previous_presence_state, presence_state_name))
                AddClientToRoom.add_client_to_all_previously_open_rooms(client_obj)

        # Chat room that the client is currently looking at needs an up-to-date view of
        # clients and their activity. Other rooms do not need to be updated as often since the client is not looking
        # at these other rooms right now. Send the client an updated list of the currently viewed room members.
        if currently_open_chat_room_id is not None:
            chat_room_obj = chat_room_module.ChatRoomModel.get_by_id(currently_open_chat_room_id)

            if message_type == 'ackHeartbeat' and client_id not in chat_room_obj.room_members_client_ids:
                # This should not happen, as the above code should have added the client back to any rooms
                # that they opened as soon as he sent a message during a time when his state was
                # considered PRESENCE_OFFLINE.
                logging.error("client_id %s not in chat room %s even though he is requesting an update for that "
                              "room." % (client_id, currently_open_chat_room_id))


            messaging.send_room_occupancy_to_clients(chat_room_obj, [client_id,], recompute_members_from_scratch=False)

        http_helpers.set_http_ok_json_response(self.response, {})
    def handle_message_room(self, chat_room_obj, from_client_id, message_obj):
        # This function passes a message from one user in a given "room" to the other user in the same room.
        # It is used for exchanging sdp (session description protocol) data for setting up sessions, as well
        # as for passing video and other information from one user to the other.

        # If to_client_id is "sendMsgToEveryoneInTheChatRoom", then the message will be sent to all room members, otherwise it will be sent
        # only to the indicated client.
        to_client_ids_list = chat_room_obj.get_list_of_other_client_ids(from_client_id)

        for to_client_id in to_client_ids_list:
            channel.send_message(to_client_id, json.dumps(message_obj))

        http_helpers.set_http_ok_json_response(self.response, {})
Example #13
0
    def post(self):
        duration_minutes = constants.channel_duration_minutes
        client_id = self.session.client_obj.key.id()
        channel_token = channel.create_channel(str(client_id), duration_minutes)

        logging.debug('New channel token created for client_id: %s' % client_id)

        response_dict = {
            'channelToken': channel_token,
        }

        # Finally, send the http response.
        http_helpers.set_http_ok_json_response(self.response, response_dict)
Example #14
0
    def handle_message_room(self, chat_room_obj, from_client_id, message_obj):
        # This function passes a message from one user in a given "room" to the other user in the same room.
        # It is used for exchanging sdp (session description protocol) data for setting up sessions, as well
        # as for passing video and other information from one user to the other.

        # If to_client_id is "sendMsgToEveryoneInTheChatRoom", then the message will be sent to all room members, otherwise it will be sent
        # only to the indicated client.
        to_client_ids_list = chat_room_obj.get_list_of_other_client_ids(
            from_client_id)

        for to_client_id in to_client_ids_list:
            channel.send_message(to_client_id, json.dumps(message_obj))

        http_helpers.set_http_ok_json_response(self.response, {})
Example #15
0
    def post(self):
        client_id = self.session.client_obj.key.id()

        # Just send a short simple response so that the client can verify if the channel is up.
        response_message_obj = {
            'fromClientId': client_id, # channel-services expects fromClientId to be specified.
            'messageType': 'synAckHeartBeat' # use handshaking terminology for naming
        }

        logging.debug('Heartbeat synchronization received from client_id %s. '
                     'Synchronization acknowledgement returned to same client on channel api' % (client_id))

        channel.send_message(client_id, json.dumps(response_message_obj))

        http_helpers.set_http_ok_json_response(self.response, {})
    def post(self):
        message_obj = json.loads(self.request.body)
        from_client_id = message_obj['fromClientId']
        assert(self.session.user_id == int(from_client_id.split('|')[0]))

        try:
            self.handle_message_client(from_client_id, message_obj)
            http_helpers.set_http_ok_json_response(self.response, {})

        except:
            status_string = 'Unknown server error'
            http_status_code = 500
            logging_function = logging.error

            http_helpers.set_error_json_response_and_write_log(self.response, status_string, logging_function,
                                                               http_status_code, self.request)
    def post(self):
        try:
            room_dict = json.loads(self.request.body)

            # Need to get the URL encoded data from utf8. Note that json encoded data appears to already be decoded.

            # Convert camel case keys to underscore (standard python) keys
            room_dict = utils.convert_dict(room_dict, utils.camel_to_underscore)

            chat_room_name_as_written = room_dict['chat_room_name_as_written']
            chat_room_name_normalized = chat_room_name_as_written.lower()
            room_dict['chat_room_name_normalized'] = chat_room_name_normalized


            # Make sure that the room name is valid before continuing.
            # These errors should be rare since these values are
            # already formatted to be within bounds and characters checked by the client-side javascript.
            # Only if the user has somehow bypassed the javascript checks should we receive values that don't
            # conform to the constraints that we have placed.
            if not constants.valid_chat_room_name_regex_compiled.match(chat_room_name_as_written):
                raise Exception('Room name regexp did not match')
            if len(chat_room_name_as_written) > constants.room_max_chars or len(chat_room_name_as_written) < constants.room_min_chars:
                raise Exception('Room name length of %s is out of range' % len(chat_room_name_as_written))

            response_dict = {}
            user_id = int(room_dict['user_id'])

            assert self.session.user_id == user_id

            # If this is a new room, then the room_creator_user_key will be stored in the room
            # object as the "creator" of the room
            room_creator_user_key = ndb.Key('UserModel', user_id)
            chat_room_obj = ChatRoomModel.create_or_get_room(chat_room_name_normalized, room_dict,
                                                            room_creator_user_key)

            response_dict['chatRoomNameNormalized'] = chat_room_name_normalized
            response_dict['chatRoomId'] = chat_room_obj.key.id()
            response_dict['statusString'] = 'roomJoined'

            http_helpers.set_http_ok_json_response(self.response, response_dict)

        except:
            # Unknown exception - provide feedback to the user to indicate that the room was not created or entered into
            err_status = 'ErrorUnableToCreateOrEnterRoom'
            # log this error for further analysis
            status_reporting.log_call_stack_and_traceback(logging.error, extra_info = err_status)
            http_helpers.set_http_ok_json_response(self.response, {'statusString': err_status})
Example #18
0
    def post(self):

        post_body_json = json.loads(self.request.body)
        client_id = post_body_json['clientId']
        room_id = post_body_json['chatRoomId']

        chat_room_obj = chat_room_module.ChatRoomModel.get_by_id(room_id)
        chat_room_obj = chat_room_obj.txn_remove_client_from_room(client_id)

        client_obj = clients.ClientModel.get_by_id(client_id)
        if client_obj:
            client_obj.txn_remove_room_from_client_status_tracker(chat_room_obj.key)

        messaging.send_room_occupancy_to_clients(chat_room_obj, chat_room_obj.room_members_client_ids,
                                                 recompute_members_from_scratch=True)

        http_helpers.set_http_ok_json_response(self.response, {})
Example #19
0
    def post(self):
        message_obj = json.loads(self.request.body)
        from_client_id = message_obj['fromClientId']
        assert (self.session.user_id == int(from_client_id.split('|')[0]))

        try:
            self.handle_message_client(from_client_id, message_obj)
            http_helpers.set_http_ok_json_response(self.response, {})

        except:
            status_string = 'Unknown server error'
            http_status_code = 500
            logging_function = logging.error

            http_helpers.set_error_json_response_and_write_log(
                self.response, status_string, logging_function,
                http_status_code, self.request)
    def post(self):

        data_object = json.loads(self.request.body)

        username_as_written = data_object['usernameAsWritten']
        username_normalized = username_as_written.lower()

        auth_id = 'username:'******'t rely on auth_id because it is a list
        # containing various logins that the user may wish to use - such as email address, user name, perhaps
        # a facebook login token, etc.

        # Not necessary to include username in the unique_properties, since it will be included
        # in the "auth_id" in the create_user function, which will ensure that it is unique.
        unique_properties = None
        expiration_datetime = datetime.datetime.utcnow() + datetime.timedelta(minutes=constants.unregistered_user_token_session_expiry_minutes)
        user_created_bool, user_obj = users.UserModel.create_user(auth_id, unique_properties,
                                                                  username_normalized=username_normalized,
                                                                  username_as_written=username_as_written,
                                                                  registered_user_bool=False,
                                                                  expiration_datetime=expiration_datetime)

        if user_created_bool:
            user_id = user_obj.key.id()
            logging.info('New user object created. user_id: %d' % user_id)
            # close any active session the user has since he is trying to login
            # if self.session.is_active():
            #     self.session.terminate()
            #
            # # Writing a value to the session causes a new session to be created.
            # self.session.user_id = user_obj.key.id()
            #
            # self.redirect(self.uri_for('main'))
            jwt_token = token_sessions.generate_jwt_token(user_obj.key.id(), user_obj.username_as_written, expiration_datetime)
            response_dict = {
                'token': jwt_token,
            }
            http_helpers.set_http_ok_json_response(self.response, response_dict)

        else:
            err_msg = 'Failed to create username %s', username_normalized
            logging.error(err_msg)
            http_helpers.set_http_error_json_response(self.response, err_msg, http_status_code=403)
Example #21
0
    def handle_message_client(self, from_client_id, message_obj):
        # This function passes a message from one user in a given "room" to the other user in the same room.
        # It is used for exchanging sdp (session description protocol) data for setting up sessions, as well
        # as for passing video and other information from one user to the other.

        to_client_id = message_obj['toClientId']
        message_type = message_obj['messageType']
        message_payload = message_obj['messagePayload']
        message_obj['fromUsernameAsWritten'] = self.session.username_as_written

        if message_type == 'videoExchangeStatusMsg':

            logging.info(
                'clientId %s videoElementsEnabledAndCameraAccessRequested is: %s '
                % (from_client_id, message_payload[
                    'videoElementsEnabledAndCameraAccessRequested']))

            if message_payload[
                    'videoElementsEnabledAndCameraAccessRequested'] == 'doVideoExchange':

                video_setup.VideoSetup.txn_add_user_id_to_video_elements_enabled_client_ids(
                    from_client_id, to_client_id)
                send_video_call_settings_to_participants(
                    from_client_id, to_client_id)
            else:
                assert message_payload['videoElementsEnabledAndCameraAccessRequested'] == 'hangupVideoExchange' or \
                    message_payload['videoElementsEnabledAndCameraAccessRequested'] == 'denyVideoExchange'

                video_setup.VideoSetup.txn_remove_user_id_from_video_elements_enabled_client_ids(
                    from_client_id, to_client_id)

            if message_type == 'sdp' and message_payload['type'] == 'offer':
                # This is just for debugging
                logging.info('sdp offer. Payload: %s' % repr(message_payload))

        logging.info('\n***\nSending message to client %s: %s\n' %
                     (to_client_id, json.dumps(message_obj)))
        channel.send_message(to_client_id, json.dumps(message_obj))
        http_helpers.set_http_ok_json_response(self.response, {})
    def post(self):
        client_id = self.session.client_obj.key.id()

        expire_dt = datetime.datetime.utcnow() + datetime.timedelta(minutes=constants.turn_relay_timeout_minutes)
        expire_ts = int(time.mktime((expire_dt).timetuple()))

        expire_datetime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(expire_ts))
        logging.debug('turn rest expires at %s (epoch value %d)' % (expire_datetime, expire_ts))

        uris = []
        for option in turn_uri_combinations:
            uris.append("turn:%s:%s?transport=%s" % (constants.turn_ip, option[0], option[1]))

        turn_username = "******" % (expire_ts, client_id)
        turn_password = b64encode(hmac.new(constants.turn_shared_secret, turn_username, hashlib.sha1).digest())

        response_dict = {
            'turn_username': turn_username,
            'turn_password': turn_password,
            'uris': uris
        }

        http_helpers.set_http_ok_json_response(self.response, response_dict)
    def post(self):
        message_obj = json.loads(self.request.body)
        from_client_id = message_obj['fromClientId']
        assert(self.session.user_id == int(from_client_id.split('|')[0]))
        room_id = message_obj['chatRoomId']
        message_obj['fromUsernameAsWritten'] = self.session.username_as_written

        try:
            chat_room_obj = chat_room_module.ChatRoomModel.get_by_id(room_id)
            if chat_room_obj:
                self.handle_message_room(chat_room_obj, from_client_id, message_obj)
            else:
                logging.error('Unknown room_id %d' % room_id)
                raise Exception('unknownRoomId')

            http_helpers.set_http_ok_json_response(self.response, {})

        except:
            status_string = 'Server error'
            http_status_code = 500
            logging_function = logging.error

            http_helpers.set_error_json_response_and_write_log(self.response, status_string, logging_function,
                                                               http_status_code, self.request)
Example #24
0
    def post(self, logging_fn):

        client_error_string = self.format_main_response(logging_fn)
        logging_fn(client_error_string)
        http_helpers.set_http_ok_json_response(self.response, {})
    def post(self, logging_fn):

        client_error_string = self.format_main_response(logging_fn)
        logging_fn(client_error_string)
        http_helpers.set_http_ok_json_response(self.response, {})