def leave_private_room(user_id, current_sid): all_sids = utils.get_sids_for_user_id(user_id) # only one of the user sessions disconnected if len(all_sids) > 1: return try: # todo: only broadcast 'offline' status if currently 'online' (i.e. don't broadcast if e.g. 'invisible') user_name = environ.env.session.get(SessionKeys.user_name.value) logger.debug('a user disconnected [id: "%s", name: "%s", sid: "%s"]' % (user_id, user_name, current_sid)) try: environ.env.leave_room(current_sid) environ.env.db.remove_sid_for_user(user_id, current_sid) except Exception as e: logger.warning('could not remove sid {} for user {}: {}'.format(current_sid, user_id, str(e))) if user_id is None or len(user_id.strip()) == 0: return all_sids = utils.get_sids_for_user_id(user_id) all_sids = all_sids.copy() if len(all_sids) == 0 or (current_sid in all_sids and len(all_sids) == 1): environ.env.leave_room(user_id) environ.env.db.reset_sids_for_user(user_id) for key in SessionKeys.temporary_keys.value: environ.env.auth.update_session_for_key(activity.actor.id, key, False) except Exception as e: logger.error('could not leave private room: %s' % str(e)) logger.debug('request for failed leave_private_room(): %s' % str(data)) logger.exception(traceback.format_exc()) environ.env.capture_exception(sys.exc_info())
def set_user_offline(user_id, current_sid): try: if not utils.is_valid_id(user_id): logger.warning( 'got invalid id on disconnect for act: {}'.format( str(activity.id))) # TODO: sentry return environ.env.db.remove_sid_for_user(user_id, current_sid) all_sids = utils.get_sids_for_user_id(user_id) # if the user still has another session up we don't set the user as offline if all_sids is not None and len(all_sids) > 0: logger.debug( 'when setting user offline, found other sids: [%s]' % ','.join(all_sids)) return if utils.get_user_status(user_id) == UserKeys.STATUS_INVISIBLE: environ.env.cache.remove_from_multicast_on_disconnect( user_id) else: environ.env.db.set_user_offline(user_id) except Exception as e: logger.error('could not set user offline: %s' % str(e)) logger.debug('request for failed set_user_offline(): %s' % str(data)) logger.exception(traceback.format_exc())
def emit_disconnect_event(user_id, current_sid) -> None: try: if is_socket_disconnect: user_name = environ.env.session.get(SessionKeys.user_name.value) if user_name is None or len(user_name.strip()) == 0: try: user_name = utils.get_user_name_for(user_id) except NoSuchUserException: user_name = '<unknown>' else: try: user_name = utils.get_user_name_for(user_id) except NoSuchUserException: user_name = '<unknown>' all_sids = utils.get_sids_for_user_id(user_id) # race condition might lead to db cache saying it's still there or similar make_sure_current_sid_removed(all_sids, user_id, current_sid) if user_id is None or user_id == 'None': logger.warning('blank user_id on disconnect event, trying sid instead') if current_sid is None or current_sid == 'None' or current_sid == '': logger.error('blank sid as well as blank user id, ignoring disconnect event') return try: user_id = utils.get_user_for_sid(current_sid) except Exception as e: logger.warning('could not get user id from sid "{}": {}'.format(current_sid, str(e))) environ.env.capture_exception(sys.exc_info()) user_id = '-1' if user_id is None or len(user_id.strip()) == 0: logger.warning('blank user id for sid "{}"'.format(current_sid)) user_id = '-1' logger.debug( 'sid %s disconnected, all_sids: [%s] for user %s (%s)' % ( current_sid, ','.join(all_sids), user_id, user_name)) if user_id != '-1': sid_ended_event = utils.activity_for_sid_disconnect(user_id, user_name, current_sid) environ.env.publish(sid_ended_event, external=True) # if the user still has another session up we don't send disconnect event if all_sids is not None and len(all_sids) > 0: return if user_id != '-1': activity_json = utils.activity_for_disconnect(user_id, user_name) environ.env.publish(activity_json, external=True) except Exception as e: logger.error('could not emit disconnect event: %s' % str(e)) logger.debug('request for failed emit_disconnect_event(): %s' % str(data)) logger.exception(traceback.format_exc()) environ.env.capture_exception(sys.exc_info())
def handle_kick(self, activity: Activity): kicker_id = activity.actor.id if kicker_id == '0': kicker_name = 'admin' else: try: kicker_name = activity.actor.display_name or utils.get_user_name_for( kicker_id) except NoSuchUserException: # if kicking from rest api the user might not exist logger.error('no such user when kicking: %s' % kicker_id) return kicked_id = activity.object.id kicked_name = activity.object.display_name or utils.get_user_name_for( kicked_id) kicked_sids = utils.get_sids_for_user_id(kicked_id) room_id = activity.target.id if room_id is not None: room_name = utils.get_room_name(room_id) else: room_name = activity.target.display_name namespace = activity.target.url if kicked_sids is None or len(kicked_sids) == 0 or kicked_sids == [ None ] or kicked_sids[0] == '': logger.warning('no sid(s) found for user id %s' % kicked_id) return reason = None if hasattr(activity.object, 'content'): reason = activity.object.content activity_json = utils.activity_for_user_kicked(kicker_id, kicker_name, kicked_id, kicked_name, room_id, room_name, reason) try: # user just got banned globally, kick from all rooms if room_id is None or room_id == '': room_keys = self.env.db.rooms_for_user(kicked_id).copy().keys() for room_key in room_keys: self.kick(activity_json, activity, room_key, kicked_id, kicked_sids, namespace) else: self.kick(activity_json, activity, room_id, kicked_id, kicked_sids, namespace) except KeyError as e: logger.error('could not kick user %s: %s' % (kicked_id, str(e))) self.env.capture_exception(sys.exc_info())
def user_is_on_this_node(self, activity: Activity) -> bool: if self.env.node not in {'app', 'wio'}: return False room_id = activity.target.id namespace = activity.target.url or '/ws' user_id = activity.object.id or activity.target.id user_sids = utils.get_sids_for_user_id(user_id) users = list() try: if room_id is None: logger.debug('checking if we have user %s in namespace %s' % (user_id, namespace)) for user_sid in user_sids: if user_sid in self.socketio.server.manager.rooms[ namespace]: logger.debug('found user %s on this node' % user_id) return True logger.info( 'no user %s for namespace [%s] (or user not on this node)' % (user_id, namespace)) return False else: logger.debug('checking if we have room %s in namespace %s' % (room_id, namespace)) if room_id in self.socketio.server.manager.rooms[namespace]: users = self.socketio.server.manager.rooms[namespace][ room_id] logger.debug('found users for room %s: %s' % (room_id, str(users))) else: logger.warning( 'no room %s for namespace [%s] (or room is empty/removed)' % (room_id, namespace)) return any(user_sid in users for user_sid in user_sids) except KeyError as e: logger.warning( 'namespace %s does not exist (maybe this is web/rest node?): %s' % (namespace, str(e))) return False except Exception as e: logger.error( 'could not get users for namespace "%s" and room "%s": %s' % (namespace, room_id, str(e))) logger.exception(traceback.format_exc()) return False
def handle_ban(self, activity: Activity): banner_id = activity.actor.id if banner_id == '0' or banner_id is None: banner_id = '0' banner_name = 'admin' else: try: banner_name = utils.get_user_name_for(banner_id) except NoSuchUserException: # if banning from rest api the user might not exist logger.error('no such user when banning: %s' % banner_id) return banned_id = activity.object.id if not utils.is_valid_id(banned_id): logger.warning('got invalid id on ban activity: {}'.format( str(activity.id))) # TODO: sentry return banned_name = utils.get_user_name_for(banned_id) banned_sids = utils.get_sids_for_user_id(banned_id) namespace = activity.target.url or '/ws' target_type = activity.target.object_type if target_type == 'room': target_id = activity.target.id target_name = utils.get_room_name(target_id) elif target_type == 'channel': target_id = activity.target.id target_name = utils.get_channel_name(target_id) else: target_id = '' target_name = '' if len(banned_sids) == 0 or banned_sids == [None ] or banned_sids[0] == '': logger.warning('no sid(s) found for user id %s' % banned_id) return reason = None if hasattr(activity.object, 'content'): reason = activity.object.content activity_json = utils.activity_for_user_banned(banner_id, banner_name, banned_id, banned_name, target_id, target_name, reason) try: ban_activity = self.get_ban_activity(activity, target_type) self.env.out_of_scope_emit('gn_banned', ban_activity, json=True, namespace=namespace, room=banned_id) if target_id is None or target_id == '': rooms_for_user = self.env.db.rooms_for_user(banned_id) logger.info( 'user %s is in these rooms (will ban from all): %s' % (banned_id, str(rooms_for_user))) self.ban_globally(activity_json, activity, rooms_for_user, banned_id, banned_sids, namespace) if utils.get_user_status( banned_id) == UserKeys.STATUS_INVISIBLE: environ.env.cache.remove_from_multicast_on_disconnect( banned_id) else: environ.env.db.set_user_offline(banned_id) disconnect_activity = utils.activity_for_disconnect( banned_id, banned_name) self.env.publish(disconnect_activity, external=True) elif target_type == 'channel': rooms_in_channel = self.env.db.rooms_for_channel(target_id) self.ban_channel(activity_json, activity, rooms_in_channel, target_id, banned_id, banned_sids, namespace) else: self.ban_room(activity_json, activity, target_id, banned_id, banned_sids, namespace) except KeyError as ke: logger.error('could not ban: %s' % str(ke)) logger.exception(traceback.format_exc()) self.env.capture_exception(sys.exc_info())