From a2f52d98db7052b79aa3e605830842d3b2a3580d Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Wed, 8 Apr 2020 03:23:21 +0200 Subject: [PATCH] Deleted the finally obsolete big handler file --- .../client/ConnectedClientCommandHandler.cpp | 8026 ----------------- 1 file changed, 8026 deletions(-) delete mode 100644 server/src/client/ConnectedClientCommandHandler.cpp diff --git a/server/src/client/ConnectedClientCommandHandler.cpp b/server/src/client/ConnectedClientCommandHandler.cpp deleted file mode 100644 index 4ab4011..0000000 --- a/server/src/client/ConnectedClientCommandHandler.cpp +++ /dev/null @@ -1,8026 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include "../build.h" -#include "ConnectedClient.h" -#include "InternalClient.h" -#include "../server/file/FileServer.h" -#include "../server/VoiceServer.h" -#include "voice/VoiceClient.h" -#include "PermissionManager.h" -#include "../InstanceHandler.h" -#include "../server/QueryServer.h" -#include "file/FileClient.h" -#include "music/MusicClient.h" -#include "query/QueryClient.h" -#include "../weblist/WebListManager.h" -#include "../manager/ConversationManager.h" -#include "../manager/PermissionNameMapper.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fs = std::experimental::filesystem; -using namespace std::chrono; -using namespace std; -using namespace ts; -using namespace ts::server; -using namespace ts::token; - -#define QUERY_PASSWORD_LENGTH 12 - -//TODO: Map permsid! -#define PARSE_PERMISSION(cmd) \ -permission::PermissionType permType = permission::PermissionType::unknown; \ -bool grant = false; \ -if (cmd[index].has("permid")) { \ - permType = cmd[index]["permid"].as(); \ - if ((permType & PERM_ID_GRANT) != 0) { \ - grant = true; \ - permType &= ~PERM_ID_GRANT; \ - } \ -} else if (cmd[index].has("permsid")) { \ - auto resv = permission::resolvePermissionData(cmd[index]["permsid"].as()); \ - permType = resv->type; \ - if (resv->grantName() == cmd[index]["permsid"].as()) grant = true; \ -} \ -if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) { \ - if (conOnError) continue; \ - return ts::command_result{error::parameter_invalid}; \ -} -//{findError("parameter_invalid"), "could not resolve permission " + (cmd[index].has("permid") ? cmd[index]["permid"].as() : cmd[index]["permsid"].as())}; \ -//TODO: Log missing permissions? - -#define CHANNEL_PERM_TEST_INIT \ -auto current_channel = channel == this->getChannel() - -#define CHANNEL_PERM_TEST(permission_type, required, enfore_required) \ -do { \ - if(current_channel) \ - PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ - else \ - PERM_CHECK_CHANNELR(permission_type, required, channel, enfore_required); \ -} while(0) - -#define RESOLVE_CHANNEL_R(command, force) \ -auto channel_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get();\ -shared_lock channel_tree_read_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock());\ -auto channel_id = command.as(); \ -auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as()) : nullptr; \ -if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \ - -#define RESOLVE_CHANNEL_W(command, force) \ -auto channel_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get();\ -unique_lock channel_tree_write_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock());\ -auto channel_id = command.as(); \ -auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as()) : nullptr; \ -if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \ - -/* the "newest" channel permission access test */ -#define CHANNEL_PERMISSION_TEST(permission_type, permission_needed_type, _channel, permission_required) \ -do { \ - auto permission_granted = this->calculate_permission(permission_type, _channel->channelId()); \ - if(!channel->permission_granted(permission_needed_type, permission_granted, permission_required)) \ - return command_result{permission_type}; \ -} while(0) - -/* the "newest" group permission access test */ -#define GROUP_PERMISSION_TEST(permission_type, permission_needed_type, _group, permission_required) \ -do { \ - auto permission_granted = this->calculate_permission(permission_type, 0); \ - if(!_group->permission_granted(permission_needed_type, permission_granted, permission_required)) \ - return command_result{permission_type}; \ -} while(0) - -inline bool permission_require_granted_value(permission::PermissionType type) { - switch (type) { - case permission::i_permission_modify_power: - - case permission::i_channel_group_member_add_power: - case permission::i_channel_group_member_remove_power: - case permission::i_channel_group_modify_power: - - case permission::i_channel_group_needed_member_add_power: - case permission::i_channel_group_needed_member_remove_power: - case permission::i_channel_group_needed_modify_power: - - - case permission::i_server_group_member_add_power: - case permission::i_server_group_member_remove_power: - case permission::i_server_group_modify_power: - - case permission::i_server_group_needed_member_add_power: - case permission::i_server_group_needed_member_remove_power: - case permission::i_server_group_needed_modify_power: - - case permission::i_displayed_group_member_add_power: - case permission::i_displayed_group_member_remove_power: - case permission::i_displayed_group_modify_power: - - case permission::i_displayed_group_needed_member_add_power: - case permission::i_displayed_group_needed_member_remove_power: - case permission::i_displayed_group_needed_modify_power: - - case permission::i_channel_permission_modify_power: - case permission::i_channel_needed_permission_modify_power: - case permission::i_client_permission_modify_power: - case permission::i_client_needed_permission_modify_power: - - case permission::i_client_needed_kick_from_server_power: - case permission::i_client_needed_kick_from_channel_power: - case permission::i_client_kick_from_channel_power: - case permission::i_client_kick_from_server_power: - return true; - default: - return false; - } -} - -inline bool permission_is_group_property(permission::PermissionType type) { - switch (type) { - case permission::i_icon_id: - case permission::i_group_show_name_in_tree: - case permission::i_group_sort_id: - case permission::b_group_is_permanent: - case permission::i_displayed_group_needed_modify_power: - case permission::i_displayed_group_needed_member_add_power: - case permission::i_displayed_group_needed_member_remove_power: - return true; - default: - return false; - } -} - -inline bool permission_is_client_property(permission::PermissionType type) { - switch (type) { - case permission::i_icon_id: - case permission::i_client_talk_power: - case permission::i_client_max_idletime: - case permission::i_group_sort_id: - case permission::i_channel_view_power: - case permission::b_channel_ignore_view_power: - case permission::b_client_is_priority_speaker: - return true; - default: - return false; - } -} - -command_result ConnectedClient::handleCommand(Command &cmd) { - threads::MutexLock l2(this->command_lock); - auto command = cmd.command(); - if (command == "servergetvariables") return this->handleCommandServerGetVariables(cmd); - else if (command == "serverrequestconnectioninfo") return this->handleCommandServerRequestConnectionInfo(cmd); - else if (command == "getconnectioninfo") return this->handleCommandGetConnectionInfo(cmd); - else if (command == "setconnectioninfo") return this->handleCommandSetConnectionInfo(cmd); - else if (command == "clientgetvariables") return this->handleCommandClientGetVariables(cmd); - else if (command == "serveredit") return this->handleCommandServerEdit(cmd); - else if (command == "clientedit") return this->handleCommandClientEdit(cmd); - else if (command == "channelgetdescription") return this->handleCommandChannelGetDescription(cmd); - else if (command == "connectioninfoautoupdate") return this->handleCommandConnectionInfoAutoUpdate(cmd); - else if (command == "permissionlist") return this->handleCommandPermissionList(cmd); - else if (command == "propertylist") return this->handleCommandPropertyList(cmd); - - //Server group - else if (command == "servergrouplist") return this->handleCommandServerGroupList(cmd); - else if (command == "servergroupadd") return this->handleCommandServerGroupAdd(cmd); - else if (command == "servergroupcopy") return this->handleCommandServerGroupCopy(cmd); - else if (command == "servergroupdel") return this->handleCommandServerGroupDel(cmd); - else if (command == "servergrouprename") return this->handleCommandServerGroupRename(cmd); - else if (command == "servergroupclientlist") return this->handleCommandServerGroupClientList(cmd); - else if (command == "servergroupaddclient" || command == "clientaddservergroup") return this->handleCommandServerGroupAddClient(cmd); - else if (command == "servergroupdelclient" || command == "clientdelservergroup") return this->handleCommandServerGroupDelClient(cmd); - else if (command == "servergrouppermlist") return this->handleCommandServerGroupPermList(cmd); - else if (command == "servergroupaddperm") return this->handleCommandServerGroupAddPerm(cmd); - else if (command == "servergroupdelperm") return this->handleCommandServerGroupDelPerm(cmd); - - else if (command == "setclientchannelgroup") return this->handleCommandSetClientChannelGroup(cmd); - - //Channel basic actions - else if (command == "channelcreate") return this->handleCommandChannelCreate(cmd); - else if (command == "channelmove") return this->handleCommandChannelMove(cmd); - else if (command == "channeledit") return this->handleCommandChannelEdit(cmd); - else if (command == "channeldelete") return this->handleCommandChannelDelete(cmd); - //Find a channel and get informations - else if (command == "channelfind") return this->handleCommandChannelFind(cmd); - else if (command == "channelinfo") return this->handleCommandChannelInfo(cmd); - //Channel perm actions - else if (command == "channelpermlist") return this->handleCommandChannelPermList(cmd); - else if (command == "channeladdperm") return this->handleCommandChannelAddPerm(cmd); - else if (command == "channeldelperm") return this->handleCommandChannelDelPerm(cmd); - //Channel group actions - else if (command == "channelgroupadd") return this->handleCommandChannelGroupAdd(cmd); - else if (command == "channelgroupcopy") return this->handleCommandChannelGroupCopy(cmd); - else if (command == "channelgrouprename") return this->handleCommandChannelGroupRename(cmd); - else if (command == "channelgroupdel") return this->handleCommandChannelGroupDel(cmd); - else if (command == "channelgrouplist") return this->handleCommandChannelGroupList(cmd); - else if (command == "channelgroupclientlist") return this->handleCommandChannelGroupClientList(cmd); - else if (command == "channelgrouppermlist") return this->handleCommandChannelGroupPermList(cmd); - else if (command == "channelgroupaddperm") return this->handleCommandChannelGroupAddPerm(cmd); - else if (command == "channelgroupdelperm") return this->handleCommandChannelGroupDelPerm(cmd); - //Channel sub/unsubscribe - else if (command == "channelsubscribe") return this->handleCommandChannelSubscribe(cmd); - else if (command == "channelsubscribeall") return this->handleCommandChannelSubscribeAll(cmd); - else if (command == "channelunsubscribe") return this->handleCommandChannelUnsubscribe(cmd); - else if (command == "channelunsubscribeall") return this->handleCommandChannelUnsubscribeAll(cmd); - //manager channel permissions - else if (command == "channelclientpermlist") return this->handleCommandChannelClientPermList(cmd); - else if (command == "channelclientaddperm") return this->handleCommandChannelClientAddPerm(cmd); - else if (command == "channelclientdelperm") return this->handleCommandChannelClientDelPerm(cmd); - //Client actions - else if (command == "clientupdate") return this->handleCommandClientUpdate(cmd); - else if (command == "clientmove") return this->handleCommandClientMove(cmd); - else if (command == "clientgetids") return this->handleCommandClientGetIds(cmd); - else if (command == "clientkick") return this->handleCommandClientKick(cmd); - else if (command == "clientpoke") return this->handleCommandClientPoke(cmd); - else if (command == "sendtextmessage") return this->handleCommandSendTextMessage(cmd); - else if (command == "clientchatcomposing") return this->handleCommandClientChatComposing(cmd); - else if (command == "clientchatclosed") return this->handleCommandClientChatClosed(cmd); - - else if (command == "clientfind") return this->handleCommandClientFind(cmd); - else if (command == "clientinfo") return this->handleCommandClientInfo(cmd); - - else if (command == "clientaddperm") return this->handleCommandClientAddPerm(cmd); - else if (command == "clientdelperm") return this->handleCommandClientDelPerm(cmd); - else if (command == "clientpermlist") return this->handleCommandClientPermList(cmd); - //File transfare - else if (command == "ftgetfilelist") return this->handleCommandFTGetFileList(cmd); - else if (command == "ftcreatedir") return this->handleCommandFTCreateDir(cmd); - else if (command == "ftdeletefile") return this->handleCommandFTDeleteFile(cmd); - else if (command == "ftinitupload") return this->handleCommandFTInitUpload(cmd); - else if (command == "ftinitdownload") return this->handleCommandFTInitDownload(cmd); - else if (command == "ftgetfileinfo") return this->handleCommandFTGetFileInfo(cmd); - //Banlist - else if (command == "banlist") return this->handleCommandBanList(cmd); - else if (command == "banadd") return this->handleCommandBanAdd(cmd); - else if (command == "banedit") return this->handleCommandBanEdit(cmd); - else if (command == "banclient") return this->handleCommandBanClient(cmd); - else if (command == "bandel") return this->handleCommandBanDel(cmd); - else if (command == "bandelall") return this->handleCommandBanDelAll(cmd); - else if (command == "bantriggerlist") return this->handleCommandBanTriggerList(cmd); - //Tokens - else if (command == "tokenlist" || command == "privilegekeylist") return this->handleCommandTokenList(cmd); - else if (command == "tokenadd" || command == "privilegekeyadd") return this->handleCommandTokenAdd(cmd); - else if (command == "tokenuse" || command == "privilegekeyuse") return this->handleCommandTokenUse(cmd); - else if (command == "tokendelete" || command == "privilegekeydelete") return this->handleCommandTokenDelete(cmd); - - //DB stuff - else if (command == "clientdblist") return this->handleCommandClientDbList(cmd); - else if (command == "clientdbinfo") return this->handleCommandClientDbInfo(cmd); - else if (command == "clientdbedit") return this->handleCommandClientDBEdit(cmd); - else if (command == "clientdbfind") return this->handleCommandClientDBFind(cmd); - else if (command == "clientdbdelete") return this->handleCommandClientDBDelete(cmd); - else if (command == "plugincmd") return this->handleCommandPluginCmd(cmd); - - else if (command == "clientmute") return this->handleCommandClientMute(cmd); - else if (command == "clientunmute") return this->handleCommandClientUnmute(cmd); - - else if (command == "clientlist") return this->handleCommandClientList(cmd); - else if (command == "whoami") return this->handleCommandWhoAmI(cmd); - else if (command == "servergroupsbyclientid") return this->handleCommandServerGroupsByClientId(cmd); - - else if (command == "clientgetdbidfromuid") return this->handleCommandClientGetDBIDfromUID(cmd); - else if (command == "clientgetnamefromdbid") return this->handleCommandClientGetNameFromDBID(cmd); - else if (command == "clientgetnamefromuid") return this->handleCommandClientGetNameFromUid(cmd); - else if (command == "clientgetuidfromclid") return this->handleCommandClientGetUidFromClid(cmd); - - - else if (command == "complainadd") return this->handleCommandComplainAdd(cmd); - else if (command == "complainlist") return this->handleCommandComplainList(cmd); - else if (command == "complaindel") return this->handleCommandComplainDel(cmd); - else if (command == "complaindelall") return this->handleCommandComplainDelAll(cmd); - - else if (command == "version") return this->handleCommandVersion(cmd); - - else if (command == "verifyserverpassword") return this->handleCommandVerifyServerPassword(cmd); - else if (command == "verifychannelpassword") return this->handleCommandVerifyChannelPassword(cmd); - - else if (command == "messagelist") return this->handleCommandMessageList(cmd); - else if (command == "messageadd") return this->handleCommandMessageAdd(cmd); - else if (command == "messageget") return this->handleCommandMessageGet(cmd); - else if (command == "messagedel") return this->handleCommandMessageDel(cmd); - else if (command == "messageupdateflag") return this->handleCommandMessageUpdateFlag(cmd); - - else if (command == "permget") return this->handleCommandPermGet(cmd); - else if (command == "permfind") return this->handleCommandPermFind(cmd); - else if (command == "permidgetbyname") return this->handleCommandPermIdGetByName(cmd); - else if (command == "permoverview") return this->handleCommandPermOverview(cmd); - else if (command == "permreset") return this->handleCommandPermReset(cmd); - - else if (command == "clientsetserverquerylogin") return this->handleCommandClientSetServerQueryLogin(cmd); - - //Music stuff - else if (command == "musicbotcreate") return this->handleCommandMusicBotCreate(cmd); - else if (command == "musicbotdelete") return this->handleCommandMusicBotDelete(cmd); - else if (command == "musicbotsetsubscription") return this->handleCommandMusicBotSetSubscription(cmd); - else if (command == "musicbotplayerinfo") return this->handleCommandMusicBotPlayerInfo(cmd); - else if (command == "musicbotplayeraction") return this->handleCommandMusicBotPlayerAction(cmd); - else if (command == "musicbotqueuelist") return this->handleCommandMusicBotQueueList(cmd); - else if (command == "musicbotqueueadd") return this->handleCommandMusicBotQueueAdd(cmd); - else if (command == "musicbotqueueremove") return this->handleCommandMusicBotQueueRemove(cmd); - else if (command == "musicbotqueuereorder") return this->handleCommandMusicBotQueueReorder(cmd); - else if (command == "musicbotplaylistassign") return this->handleCommandMusicBotPlaylistAssign(cmd); - - else if (command == "help") return this->handleCommandHelp(cmd); - - else if (command == "logview") return this->handleCommandLogView(cmd); - else if (command == "servergroupautoaddperm") return this->handleCommandServerGroupAutoAddPerm(cmd); - else if (command == "servergroupautodelperm") return this->handleCommandServerGroupAutoDelPerm(cmd); - - else if (command == "updatemytsid") return this->handleCommandUpdateMyTsId(cmd); - else if (command == "updatemytsdata") return this->handleCommandUpdateMyTsData(cmd); - - else if (command == "querycreate") return this->handleCommandQueryCreate(cmd); - else if (command == "querydelete") return this->handleCommandQueryDelete(cmd); - else if (command == "querylist") return this->handleCommandQueryList(cmd); - else if (command == "queryrename") return this->handleCommandQueryRename(cmd); - else if (command == "querychangepassword") return this->handleCommandQueryChangePassword(cmd); - - else if (command == "playlistlist") return this->handleCommandPlaylistList(cmd); - else if (command == "playlistcreate") return this->handleCommandPlaylistCreate(cmd); - else if (command == "playlistdelete") return this->handleCommandPlaylistDelete(cmd); - else if (command == "playlistpermlist") return this->handleCommandPlaylistPermList(cmd); - else if (command == "playlistaddperm") return this->handleCommandPlaylistAddPerm(cmd); - else if (command == "playlistdelperm") return this->handleCommandPlaylistDelPerm(cmd); - else if (command == "playlistinfo") return this->handleCommandPlaylistInfo(cmd); - else if (command == "playlistedit") return this->handleCommandPlaylistEdit(cmd); - - else if (command == "playlistsonglist") return this->handleCommandPlaylistSongList(cmd); - else if (command == "playlistsongadd") return this->handleCommandPlaylistSongAdd(cmd); - else if (command == "playlistsongreorder" || command == "playlistsongmove") return this->handleCommandPlaylistSongReorder(cmd); - else if (command == "playlistsongremove") return this->handleCommandPlaylistSongRemove(cmd); - - else if (command == "dummy_ipchange") return this->handleCommandDummy_IpChange(cmd); - else if (command == "conversationhistory") return this->handleCommandConversationHistory(cmd); - else if (command == "conversationfetch") return this->handleCommandConversationFetch(cmd); - else if (command == "conversationmessagedelete") return this->handleCommandConversationMessageDelete(cmd); - - if (this->getType() == ClientType::CLIENT_QUERY) return command_result{error::command_not_found}; //Dont log query invalid commands - if (this->getType() == ClientType::CLIENT_TEAMSPEAK) - if (command.empty() || command.find_first_not_of(' ') == -1) { - if (!this->permissionGranted(permission::b_client_allow_invalid_packet, 1, this->currentChannel)) - ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_command, this->server ? this->server->serverAdmin : static_pointer_cast(serverInstance->getInitialServerAdmin()), true); - } - - logError(this->getServerId(), "Missing command '{}'", command); - return command_result{error::command_not_found}; -}; - -command_result ConnectedClient::handleCommandServerGetVariables(Command &cmd) { - CMD_REQ_SERVER; - this->notifyServerUpdated(_this.lock()); - return command_result{error::ok}; -} - -#define SERVEREDIT_CHK_PROP(name, perm, type)\ -else if(key == name) { \ - if(!permissionGranted(perm, 1, nullptr, true, cache, target_server, true)) return command_result{perm}; \ - if(toApplay.count(key) == 0) toApplay[key] = cmd[key].as(); \ - if(!cmd[0][key].castable()) return command_result{error::parameter_invalid}; - -#define SERVEREDIT_CHK_PROP_CACHED(name, perm, type)\ -else if(key == name) { \ - if(!this->permission_granted(this->calculate_permission(perm, 0), 1)) return command_result{perm}; \ - if(toApplay.count(key) == 0) toApplay[key] = cmd[key].as(); \ - if(!cmd[0][key].castable()) return command_result{error::parameter_invalid}; - -#define SERVEREDIT_CHK_PROP2(name, perm, type_a, type_b)\ -else if(key == name) { \ - if(!permissionGranted(perm, 1, nullptr, true, cache, target_server, true)) return command_result{perm}; \ - if(toApplay.count(key) == 0) toApplay[key] = cmd[key].as(); \ - if(!cmd[0][key].castable() && !!cmd[0][key].castable()) return command_result{error::parameter_invalid}; - -command_result ConnectedClient::handleCommandServerEdit(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - if (cmd[0].has("sid") && this->getServerId() != cmd["sid"].as()) - return command_result{error::server_invalid_id}; - - auto target_server = this->server; - if(cmd[0].has("sid")) { - target_server = serverInstance->getVoiceServerManager()->findServerById(cmd["sid"]); - if(!target_server && cmd["sid"].as() != 0) return command_result{error::server_invalid_id}; - } - - auto cache = make_shared(); - map toApplay; - for (auto &key : cmd[0].keys()) { - if (key == "sid") continue; - SERVEREDIT_CHK_PROP_CACHED("virtualserver_name", permission::b_virtualserver_modify_name, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_name_phonetic", permission::b_virtualserver_modify_name, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_maxclients", permission::b_virtualserver_modify_maxclients, size_t) - if (cmd["virtualserver_maxclients"].as() > 1024) - return command_result{error::accounting_slot_limit_reached, "Do you really need more that 1024 slots?"}; - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_reserved_slots", permission::b_virtualserver_modify_reserved_slots, size_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_icon_id", permission::b_virtualserver_modify_icon_id, int64_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_channel_temp_delete_delay_default", permission::b_virtualserver_modify_channel_temp_delete_delay_default, ChannelId) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_codec_encryption_mode", permission::b_virtualserver_modify_codec_encryption_mode, int) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_server_group", permission::b_virtualserver_modify_default_servergroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_server_group"].as()); - if (!group || group->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; - } - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_group", permission::b_virtualserver_modify_default_channelgroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_group"].as()); - if (!group || group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid}; - } - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_admin_group", permission::b_virtualserver_modify_default_channeladmingroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_channel_admin_group"].as()); - if (!group || group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid}; - } - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_music_group", permission::b_virtualserver_modify_default_musicgroup, GroupId) - if(target_server) { - auto group = target_server->groups->findGroup(cmd["virtualserver_default_music_group"].as()); - if (!group || group->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; - } - } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_priority_speaker_dimm_modificator", permission::b_virtualserver_modify_priority_speaker_dimm_modificator, float) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_port", permission::b_virtualserver_modify_port, uint16_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_host", permission::b_virtualserver_modify_host, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_web_host", permission::b_virtualserver_modify_port, uint16_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_web_port", permission::b_virtualserver_modify_host, string) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_url", permission::b_virtualserver_modify_hostbanner, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_gfx_url", permission::b_virtualserver_modify_hostbanner, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_gfx_interval", permission::b_virtualserver_modify_hostbanner, int) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbanner_mode", permission::b_virtualserver_modify_hostbanner, int) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_tooltip", permission::b_virtualserver_modify_hostbutton, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_url", permission::b_virtualserver_modify_hostbutton, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostbutton_gfx_url", permission::b_virtualserver_modify_hostbutton, string) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostmessage", permission::b_virtualserver_modify_hostmessage, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_hostmessage_mode", permission::b_virtualserver_modify_hostmessage, int) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_welcomemessage", permission::b_virtualserver_modify_welcomemessage, string) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_weblist_enabled", permission::b_virtualserver_modify_weblist, bool) - if (target_server && target_server->running()) { - if (cmd["virtualserver_weblist_enabled"].as()) - serverInstance->getWebList()->enable_report(target_server); - else - serverInstance->getWebList()->disable_report(target_server); - debugMessage(target_server->getServerId(), "Changed weblist state to -> {}", - cmd["virtualserver_weblist_enabled"].as() ? "activated" : "disabled"); - } - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_needed_identity_security_level", permission::b_virtualserver_modify_needed_identity_security_level, int) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_tick_reduce", permission::b_virtualserver_modify_antiflood, uint64_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_needed_command_block", permission::b_virtualserver_modify_antiflood, uint64_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_antiflood_points_needed_ip_block", permission::b_virtualserver_modify_antiflood, uint64_t) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_autoban_count", permission::b_virtualserver_modify_complain, uint64_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_autoban_time", permission::b_virtualserver_modify_complain, uint64_t) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_complain_remove_time", permission::b_virtualserver_modify_complain, uint64_t) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_autostart", permission::b_virtualserver_modify_autostart, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_min_clients_in_channel_before_forced_silence", permission::b_virtualserver_modify_channel_forced_silence, int) } - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_client", permission::b_virtualserver_modify_log_settings, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_query", permission::b_virtualserver_modify_log_settings, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_channel", permission::b_virtualserver_modify_log_settings, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_permissions", permission::b_virtualserver_modify_log_settings, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_server", permission::b_virtualserver_modify_log_settings, bool) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_log_filetransfer", permission::b_virtualserver_modify_log_settings, bool) } - - SERVEREDIT_CHK_PROP("virtualserver_min_client_version", permission::b_virtualserver_modify_min_client_version, uint64_t) } - SERVEREDIT_CHK_PROP("virtualserver_min_android_version", permission::b_virtualserver_modify_min_client_version, uint64_t) } - SERVEREDIT_CHK_PROP("virtualserver_min_ios_version", permission::b_virtualserver_modify_min_client_version, uint64_t) } - - - SERVEREDIT_CHK_PROP_CACHED("virtualserver_music_bot_limit", permission::b_virtualserver_modify_music_bot_limit, int) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_flag_password", permission::b_virtualserver_modify_password, bool) - if (cmd["virtualserver_flag_password"].as() && !cmd[0].has("virtualserver_password")) - return command_result{error::parameter_invalid}; - } SERVEREDIT_CHK_PROP_CACHED("virtualserver_password", permission::b_virtualserver_modify_password, string) - if(cmd["virtualserver_password"].string().empty()) { - toApplay["virtualserver_flag_password"] = "0"; - } else { - toApplay["virtualserver_flag_password"] = "1"; - if(this->getType() == CLIENT_QUERY) - toApplay["virtualserver_password"] = base64::encode(digest::sha1(cmd["virtualserver_password"].string())); - } - } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_client_description", permission::b_virtualserver_modify_default_messages, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_description", permission::b_virtualserver_modify_default_messages, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_default_channel_topic", permission::b_virtualserver_modify_default_messages, string) } - SERVEREDIT_CHK_PROP_CACHED("virtualserver_country_code", permission::b_virtualserver_modify_country_code, string) } - SERVEREDIT_CHK_PROP2("virtualserver_max_download_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) - if(cmd["virtualserver_max_download_total_bandwidth"].string().find('-') == string::npos) - toApplay["virtualserver_max_download_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_download_total_bandwidth"].as()); - else - toApplay["virtualserver_max_download_total_bandwidth"] = to_string(cmd["virtualserver_max_download_total_bandwidth"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_max_upload_total_bandwidth", permission::b_virtualserver_modify_ft_settings, uint64_t, int64_t) - if(cmd["virtualserver_max_upload_total_bandwidth"].string().find('-') == string::npos) - toApplay["virtualserver_max_upload_total_bandwidth"] = to_string((int64_t) cmd["virtualserver_max_upload_total_bandwidth"].as()); - else - toApplay["virtualserver_max_upload_total_bandwidth"] = to_string(cmd["virtualserver_max_upload_total_bandwidth"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_download_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) - if(cmd["virtualserver_download_quota"].string().find('-') == string::npos) - toApplay["virtualserver_download_quota"] = to_string((int64_t) cmd["virtualserver_download_quota"].as()); - else - toApplay["virtualserver_download_quota"] = to_string(cmd["virtualserver_download_quota"].as()); - } - SERVEREDIT_CHK_PROP2("virtualserver_upload_quota", permission::b_virtualserver_modify_ft_quotas, uint64_t, int64_t) - if(cmd["virtualserver_upload_quota"].string().find('-') == string::npos) - toApplay["virtualserver_upload_quota"] = to_string((int64_t) cmd["virtualserver_upload_quota"].as()); - else - toApplay["virtualserver_upload_quota"] = to_string(cmd["virtualserver_upload_quota"].as()); - } - else { - logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a not existing server properties. (" + key + ")"); - //return command_result{error::not_implemented}; - } - } - - std::deque keys; - bool group_update = false; - for (const auto& elm : toApplay) { - auto info = property::impl::info(elm.first); - if(*info == property::VIRTUALSERVER_UNDEFINED) { - logCritical(target_server ? target_server->getServerId() : 0, "Missing server property " + elm.first); - continue; - } - - if(!info->validate_input(elm.second)) { - logError(target_server ? target_server->getServerId() : 0, "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + elm.second + "', Property: '" + info->name + "')"); - continue; - } - if(target_server) - target_server->properties()[info] = elm.second; - else - (*serverInstance->getDefaultServerProperties())[info] = elm.second; - keys.push_back(elm.first); - - group_update |= *info == property::VIRTUALSERVER_DEFAULT_SERVER_GROUP || *info == property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP || *info == property::VIRTUALSERVER_DEFAULT_MUSIC_GROUP; - } - - if(target_server) { - if (group_update) - target_server->forEachClient([&](const shared_ptr& client) { - if(target_server->notifyClientPropertyUpdates(client, target_server->groups->update_server_group_property(client, true, client->getChannel()))) { - if(client->update_cached_permissions()) /* update cached calculated permissions */ - client->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - }); - - if (!keys.empty()) - target_server->notifyServerEdited(_this.lock(), keys); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetVariables(Command &cmd) { - CMD_REQ_SERVER; - auto client = this->server->findClient(cmd["clid"].as()); - shared_lock tree_lock(this->channel_lock); - - if (!client || (client != this && !this->isClientVisible(client, false))) - return command_result{error::client_invalid_id, ""}; - - deque> props; - for (auto &prop : client->properties()->list_properties(property::FLAG_CLIENT_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) { - props.push_back(property::info((property::ClientProperties) prop.type().property_index)); - } - - this->notifyClientUpdated(client, props, false); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientKick(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - if (client->getType() == CLIENT_MUSIC) return command_result{error::client_invalid_type, "You cant kick a music bot!"}; - std::shared_ptr targetChannel = nullptr; - auto type = cmd["reasonid"].as(); - if (type == ViewReasonId::VREASON_CHANNEL_KICK) { - auto channel = client->currentChannel; - PERM_CHECK_CHANNELR(permission::i_client_kick_from_channel_power, client->permissionValue(permission::i_client_needed_kick_from_channel_power, channel), channel, true); - targetChannel = this->server->channelTree->getDefaultChannel(); - } else if (type == ViewReasonId::VREASON_SERVER_KICK) { - auto channel = client->currentChannel; - PERM_CHECK_CHANNELR(permission::i_client_kick_from_server_power, client->permissionValue(permission::i_client_needed_kick_from_server_power, channel), channel, true); - targetChannel = nullptr; - } else return command_result{error::not_implemented}; - - if (targetChannel) { - this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), targetChannel); - } else { - this->server->notify_client_kick(client, this->ref(), cmd["reasonmsg"].as(), nullptr); - client->closeConnection(system_clock::now() + seconds(1)); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGetDescription(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(0); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - - if(!this->permission_granted(this->calculate_permission(permission::b_channel_ignore_description_view_power, channel->channelId()), 1, true)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); - } - - this->sendChannelDescription(channel, true); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandGetConnectionInfo(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - - bool send_temp; - auto info = client->request_connection_info(_this.lock(), send_temp); - if (info) { - this->notifyConnectionInfo(client, info); - } else if(send_temp) { - return command_result{error::no_cached_connection_info}; - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandSetConnectionInfo(Command &cmd) { - auto info = std::make_shared(); - info->timestamp = chrono::system_clock::now(); - for (const auto &key : cmd[0].keys()) - info->properties.insert({key, cmd[key].string()}); - - /* - CONNECTION_FILETRANSFER_BANDWIDTH_SENT, //how many bytes per second are currently being sent by file transfers - CONNECTION_FILETRANSFER_BANDWIDTH_RECEIVED, //how many bytes per second are currently being received by file transfers - CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL, //how many bytes we received in total through file transfers - CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL, //how many bytes we sent in total through file transfers - */ - - deque> receivers; - { - lock_guard info_lock(this->connection_info.lock); - for(const auto& weak_receiver : this->connection_info.receiver) { - auto receiver = weak_receiver.lock(); - if(!receiver) continue; - - receivers.push_back(receiver); - } - this->connection_info.receiver.clear(); - this->connection_info.data = info; - this->connection_info.data_age = system_clock::now(); - } - for(const auto& receiver : receivers) - receiver->notifyConnectionInfo(_this.lock(), info); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerRequestConnectionInfo(Command &) { - CMD_REQ_SERVER; - CACHED_PERM_CHECK(permission::b_virtualserver_connectioninfo_view, 1, true); - - Command notify("notifyserverconnectioninfo"); - - auto statistics = this->server->getServerStatistics()->statistics(); - auto report = this->server->getServerStatistics()->dataReport(); - - notify[0]["connection_filetransfer_bandwidth_sent"] = report.file_send; - notify[0]["connection_filetransfer_bandwidth_received"] = report.file_recv; - - notify[0]["connection_filetransfer_bytes_sent_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_SENT_TOTAL].as(); - notify[0]["connection_filetransfer_bytes_received_total"] = (*statistics)[property::CONNECTION_FILETRANSFER_BYTES_RECEIVED_TOTAL].as(); - - notify[0]["connection_filetransfer_bytes_sent_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); - notify[0]["connection_filetransfer_bytes_received_month"] = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); - - notify[0]["connection_packets_sent_total"] = (*statistics)[property::CONNECTION_PACKETS_SENT_TOTAL].as(); - notify[0]["connection_bytes_sent_total"] = (*statistics)[property::CONNECTION_BYTES_SENT_TOTAL].as(); - notify[0]["connection_packets_received_total"] = (*statistics)[property::CONNECTION_PACKETS_RECEIVED_TOTAL].as(); - notify[0]["connection_bytes_received_total"] = (*statistics)[property::CONNECTION_BYTES_RECEIVED_TOTAL].as(); - - notify[0]["connection_bandwidth_sent_last_second_total"] = report.send_second; - notify[0]["connection_bandwidth_sent_last_minute_total"] = report.send_minute; - notify[0]["connection_bandwidth_received_last_second_total"] = report.recv_second; - notify[0]["connection_bandwidth_received_last_minute_total"] = report.recv_minute; - - notify[0]["connection_connected_time"] = this->server->properties()[property::VIRTUALSERVER_UPTIME].as(); - notify[0]["connection_packetloss_total"] = this->server->averagePacketLoss(); - notify[0]["connection_ping"] = this->server->averagePing(); - - this->sendCommand(notify); - return command_result{error::ok}; -} - -//connectioninfoautoupdate connection_server2client_packetloss_speech=0.0000 connection_server2client_packetloss_keepalive=0.0010 connection_server2client_packetloss_control=0.0000 connection_server2client_packetloss_total=0.0009 -command_result ConnectedClient::handleCommandConnectionInfoAutoUpdate(Command &cmd) { - this->properties()[property::CONNECTION_SERVER2CLIENT_PACKETLOSS_KEEPALIVE] = cmd["connection_server2client_packetloss_keepalive"].as(); - this->properties()[property::CONNECTION_SERVER2CLIENT_PACKETLOSS_CONTROL] = cmd["connection_server2client_packetloss_control"].as(); - this->properties()[property::CONNECTION_SERVER2CLIENT_PACKETLOSS_SPEECH] = cmd["connection_server2client_packetloss_speech"].as(); - this->properties()[property::CONNECTION_SERVER2CLIENT_PACKETLOSS_TOTAL] = cmd["connection_server2client_packetloss_total"].as(); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetIds(Command &cmd) { - CMD_REQ_SERVER; - - bool error = false; - bool found = false; - auto client_list = this->server->getClients(); - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientids" : ""); - int result_index = 0; - - for(int index = 0; index < cmd.bulkCount(); index++) { - auto unique_id = cmd[index]["cluid"].as(); - for(const auto& entry : client_list) { - if(entry->getUid() == unique_id) { - if(!config::server::show_invisible_clients_as_online && !this->channels->channel_visible(entry->currentChannel, nullptr)) - continue; - - notify[result_index]["name"] = entry->getDisplayName(); - notify[result_index]["clid"] = entry->getClientId(); - notify[result_index]["cluid"] = entry->getUid(); - result_index++; - found = true; - } - } - if(found) found = false; - else error = false; - } - string uid = cmd["cluid"]; - - if(result_index > 0) { - this->sendCommand(notify); - } - if(error) { - return command_result{error::database_empty_result}; - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelSubscribe(Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - - bool flood_points = false; - deque> channels; - - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - - for (int index = 0; index < cmd.bulkCount(); index++) { - auto local_channel = this->channel_view()->find_channel(cmd[index]["cid"].as()); - if(!local_channel) - return command_result{error::channel_invalid_id, "Cant resolve channel"}; - - auto channel = this->server->channelTree->findChannel(cmd[index]["cid"].as()); - if (!channel) - return command_result{error::channel_invalid_id, "Cant resolve channel"}; - - channels.push_back(channel); - if(!flood_points && system_clock::now() - local_channel->view_timestamp > seconds(5)) { - flood_points = true; - CMD_CHK_AND_INC_FLOOD_POINTS(15); - } - } - - if(!channels.empty()) - this->subscribeChannel(channels, false, false); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelSubscribeAll(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(20); - - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - this->subscribeChannel(this->server->channelTree->channels(), false, false); - this->subscribeToAll = true; - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelUnsubscribe(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - - deque> channels; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if(!channel) continue; - channels.push_front(channel); - } - - this->unsubscribeChannel(channels, false); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelUnsubscribeAll(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - { - shared_lock server_channel_lock(this->server->channel_tree_lock); - unique_lock client_channel_lock(this->channel_lock); - this->unsubscribeChannel(this->server->channelTree->channels(), false); - this->subscribeToAll = false; - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPermissionList(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - static std::string permission_list_string[ClientType::MAX]; - static std::mutex permission_list_string_lock; - - static auto build_permission_list = [](const std::string& command, const ClientType& type) { - Command list_builder(command); - int index = 0; - for (auto group : permission::availableGroups) - list_builder[index++]["group_id_end"] = group; - - auto avPerms = permission::availablePermissions; - std::sort(avPerms.begin(), avPerms.end(), [](const std::shared_ptr &a, const std::shared_ptr &b) { - return a->type < b->type; - }); - - auto mapper = serverInstance->getPermissionMapper(); - for (const auto& permission : avPerms) { - if (!permission->clientSupported) continue; - - auto &blk = list_builder[index++]; - - blk["permname"] = permission->name; - blk["permname"] = mapper->permission_name(type, permission->type); - blk["permdesc"] = permission->description; - blk["permid"] = permission->type; - } - return list_builder; - }; - - auto type = this->getType(); - if(type == CLIENT_TEASPEAK || type == CLIENT_TEAMSPEAK || type == CLIENT_QUERY) { - Command response(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : ""); - { - lock_guard lock(permission_list_string_lock); - if(permission_list_string[type].empty()) - permission_list_string[type] = build_permission_list("", type).build(); - response[0][""] = permission_list_string[type]; - } - this->sendCommand(response); - } else { - this->sendCommand(build_permission_list(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypermissionlist" : "", type)); - } - return command_result{error::ok}; -} - - -#define M(ptype) \ -do { \ - for(const auto& prop : property::impl::list()) { \ - if((prop->flags & property::FLAG_INTERNAL) > 0) continue; \ - response[index]["name"] = prop->name; \ - response[index]["flags"] = prop->flags; \ - response[index]["type"] = property::PropertyType_Names[prop->type_property]; \ - index++; \ - } \ -} while(0) - -command_result ConnectedClient::handleCommandPropertyList(ts::Command& cmd) { - Command response(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifypropertylist" : ""); - - { - string pattern; - for (auto flag_name : property::flag_names) - pattern = flag_name + string("|") + pattern; - pattern = pattern.substr(0, pattern.length() - 1); - response["flag_set"] = pattern; - } - - int index = 0; - if(cmd.hasParm("all") || cmd.hasParm("server")) - M(property::VirtualServerProperties); - if(cmd.hasParm("all") || cmd.hasParm("channel")) - M(property::ChannelProperties); - if(cmd.hasParm("all") || cmd.hasParm("client")) - M(property::ClientProperties); - if(cmd.hasParm("all") || cmd.hasParm("instance")) - M(property::InstanceProperties); - if(cmd.hasParm("all") || cmd.hasParm("group")) - M(property::GroupProperties); - if(cmd.hasParm("all") || cmd.hasParm("connection")) - M(property::ConnectionProperties); - - this->sendCommand(response); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupList(Command &) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_list, 1, true); - - this->notifyServerGroupList(); - this->command_times.servergrouplist = system_clock::now(); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupAdd(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_create, 1, true); - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - if(cmd["type"].as() == GroupType::GROUP_TYPE_NORMAL && !this->server) return command_result{error::parameter_invalid, "You cant create normal channel groups on the template server"}; - if(cmd["name"].string().empty()) return command_result{error::parameter_invalid, "invalid group name"}; - for(const auto& gr : group_manager->availableServerGroups(true)) - if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "Group already exists"}; - auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["type"].as(), cmd["name"].string()); - if (group) { - group->permissions()->set_permission(permission::b_group_is_permanent, {1, 0}, permission::v2::set_value, permission::v2::do_nothing); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - } else return command_result{error::group_invalid_id}; - return command_result{error::ok}; -} - -//name=Channel\sAdmin scgid=5 tcgid=4 type=1 -command_result ConnectedClient::handleCommandChannelGroupCopy(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto ref_server = this->server; - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_create, 1, true); - - auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); - - auto source_group_id = cmd["scgid"].as(); - auto source_group = group_manager->findGroup(source_group_id); - - if(!source_group || source_group->target() != GROUPTARGET_CHANNEL) - return command_result{error::group_invalid_id, "invalid source group"}; - - const auto group_type_modificable = [&](GroupType type) { - switch(type) { - case GroupType::GROUP_TYPE_TEMPLATE: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) - return permission::b_serverinstance_modify_templates; - break; - case GroupType::GROUP_TYPE_QUERY: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) - return permission::b_serverinstance_modify_querygroup; - break; - - default: - break; - } - return permission::undefined; - }; - - { - auto result = group_type_modificable(source_group->type()); - if(result != permission::undefined) - return command_result{result}; - } - - auto global_update = false; - if(cmd[0].has("tcgid") && cmd["tcgid"].as() != 0) { - //Copy an existing group - auto target_group = group_manager->findGroup(cmd["tcgid"]); - if(!target_group || target_group->target() != GROUPTARGET_CHANNEL) - return command_result{error::group_invalid_id, "invalid target group"}; - - { - auto result = group_type_modificable(target_group->type()); - if(result != permission::undefined) - return command_result{result}; - } - - if(!target_group->permission_granted(permission::i_channel_group_needed_modify_power, this->calculate_permission(permission::i_channel_group_modify_power, 0), true)) - return command_result{permission::i_channel_group_modify_power}; - - if(!group_manager->copyGroupPermissions(source_group, target_group)) - return command_result{error::vs_critical, "failed to copy group permissions"}; - - global_update = !this->server || !group_manager->isLocalGroup(target_group); - } else { - //Copy a new group - auto target_type = cmd["type"].as(); - - { - auto result = group_type_modificable(target_type); - if(result != permission::undefined) - return command_result{result}; - } - - if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) - return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"}; - - if(!group_manager->findGroup(GroupTarget::GROUPTARGET_CHANNEL, cmd["name"].string()).empty()) - return command_result{error::group_name_inuse, "You cant create normal groups on the template server!"}; - - auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); - if(target_group_id == 0) - return command_result{error::vs_critical, "failed to copy group"}; - - if(this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["cgid"] = target_group_id; - this->sendCommand(notify); - } - - global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); - } - - for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) - if(server) - server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupRename(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - auto serverGroup = group_manager->findGroup(cmd["cgid"].as()); - if (!serverGroup || serverGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, serverGroup, true); - - group_manager->renameGroup(serverGroup, cmd["name"].string()); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupDel(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - auto channel_group = group_manager->findGroup(cmd["cgid"].as()); - if (!channel_group || channel_group->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_delete, 1, true); - - if(this->server) { - if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_GROUP] == channel_group->groupId()) - return command_result{error::parameter_invalid, "Could not delete default channel group!"}; - if(this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] == channel_group->groupId()) - return command_result{error::parameter_invalid, "Could not delete default channel admin group!"}; - } - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELDEFAULT_GROUP] == channel_group->groupId()) - return command_result{error::parameter_invalid, "Could not delete instance default channel group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_CHANNELADMIN_GROUP] == channel_group->groupId()) - return command_result{error::parameter_invalid, "Could not delete instance default channel admin group!"}; - - if (!cmd["force"].as()) - if (!group_manager->listGroupMembers(channel_group, false).empty()) - return command_result{error::database_empty_result, "group not empty!"}; - - if (group_manager->deleteGroup(channel_group) && this->server) { - this->server->forEachClient([&](shared_ptr cl) { - if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - cl->notifyChannelGroupList(); - }); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupList(Command &) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_list, 1, true); - - this->notifyChannelGroupList(); - this->command_times.servergrouplist = system_clock::now(); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupClientList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_client_list, 1, true); - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifychannelgroupclientlist" : ""); - - deque variables{variable{":sid", this->getServerId()}}; - string query = "SELECT `groupId`, `cldbid`, `until`, `channelId` FROM `assignedGroups` WHERE `serverId` = :sid"; - - if(cmd[0].has("cgid") && cmd["cgid"].as() > 0) { - auto group = this->server->getGroupManager()->findGroup(cmd["cgid"]); - if(!group || group->target() != GroupTarget::GROUPTARGET_CHANNEL) - return command_result{error::parameter_invalid, "invalid channel group id"}; - query += " AND `groupId` = :groupId"; - variables.push_back({":groupId", cmd["cgid"].as()}); - } else { - query += " AND `groupId` IN (SELECT `groupId` FROM `groups` WHERE `serverId` = :sid AND `target` = :target)"; - variables.push_back({":target", GroupTarget::GROUPTARGET_CHANNEL}); - } - if(cmd[0].has("cldbid") && cmd["cldbid"].as() > 0) { - query += " AND `cldbid` = :cldbid"; - variables.push_back({":cldbid", cmd["cldbid"].as()}); - } - if(cmd[0].has("cid") && cmd["cid"].as() > 0) { - auto channel = this->server->getChannelTree()->findChannel(cmd["cid"]); - if(!channel) - return command_result{error::parameter_invalid, "invalid channel id"}; - query += " AND `channelId` = :cid"; - variables.push_back({":cid", cmd["cid"].as()}); - } - debugMessage(this->getServerId(), "Command channelgroupclientlist sql: {}", query); - - auto command = sql::command(this->sql, query); - for(const auto& variable : variables) - command.value(variable); - - int index = 0; - command.query([&](Command& command, int& index, int length, string* values, string* names) { - GroupId group_id = 0; - ChannelId channel_id = 0; - ClientDbId cldbid = 0; - for(int i = 0; i < length; i++) { - try { - if(names[i] == "groupId") - group_id = stoll(values[i]); - else if(names[i] == "cldbid") - cldbid = stoll(values[i]); - else if(names[i] == "channelId") - channel_id = stoll(values[i]); - } catch(std::exception& ex) { - logError(this->getServerId(), "Failed to parse db field {}", names[i]); - } - } - result[index]["cid"] = channel_id; - result[index]["cgid"] = group_id; - result[index]["cldbid"] = cldbid; - index++; - }, result, index); - if (index == 0) return command_result{error::database_empty_result}; - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupPermList(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_channelgroup_permission_list, 1, true); - - auto channelGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["cgid"].as()); - if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; - if (!this->notifyGroupPermList(channelGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result}; - - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupAddPerm(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); - if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool updateList = false; - bool conOnError = cmd[0].has("continueonerror"); - - auto permission_manager = channelGroup->permissions(); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return command_result{permission::i_permission_modify_power}; - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - permission_manager->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, - - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - updateList |= permission_is_group_property(permType); - } - } - - - if(updateList) - channelGroup->apply_properties_from_permissions(); - - if(this->server) { - if(updateList) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - this->server->forEachClient([channelGroup](shared_ptr cl) { - unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ - if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { - if(cl->update_cached_permissions()) - cl->sendNeededPermissions(false); /* update the needed permissions */ - cl->updateChannelClientProperties(false, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelGroupDelPerm(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - auto channelGroup = group_manager->findGroup(cmd["cgid"].as()); - if (!channelGroup || channelGroup->target() != GROUPTARGET_CHANNEL) return command_result{error::parameter_invalid, "invalid channel group id"}; - GROUP_PERMISSION_TEST(permission::i_channel_group_modify_power, permission::i_channel_group_needed_modify_power, channelGroup, true); - - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool updateList = false; - bool conOnError = cmd[0].has("continueonerror"); - - auto permission_manager = channelGroup->permissions(); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd) - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - if (grant) { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - permission_manager->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - updateList |= permission_is_group_property(permType); - } - } - - if(updateList) - channelGroup->apply_properties_from_permissions(); - - if(this->server) { - if(updateList) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyChannelGroupList(); - }); - this->server->forEachClient([channelGroup](shared_ptr cl) { - unique_lock client_channel_lock(cl->channel_lock); /* while we're updating groups we dont want to change anything! */ - if (cl->channelGroupAssigned(channelGroup, cl->getChannel())) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - cl->updateChannelClientProperties(false, false); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - } - return command_result{error::ok}; -} - -using namespace std::chrono; - -command_result ConnectedClient::handleCommandClientMove(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(10); - - shared_lock server_channel_r_lock(this->server->channel_tree_lock); - auto target_client_id = cmd["clid"].as(); - auto target_client = target_client_id == 0 ? this->ref() : this->server->findClient(target_client_id); - if(!target_client) { - return command_result{error::client_invalid_id, "Invalid target clid"}; - } - - if(!target_client->currentChannel) { - if(target_client != this) - return command_result{error::client_invalid_id, "Invalid target clid"}; - } - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) { - return command_result{error::channel_invalid_id}; - } - - auto permission_cache = make_shared(); - if(!cmd[0].has("cpw")) - cmd["cpw"] = ""; - if (!channel->passwordMatch(cmd["cpw"], true)) - if (!this->permissionGranted(permission::b_channel_join_ignore_password, 1, channel, true, permission_cache)) - return command_result{error::channel_invalid_password}; - - switch(channel->channelType()) { - case ChannelType::permanent: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_permanent, 1, channel, true, permission_cache); - break; - case ChannelType::semipermanent: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_semi_permanent, 1, channel, true, permission_cache); - break; - case ChannelType::temporary: - PERM_CHECK_CHANNEL_CR(permission::b_channel_join_temporary, 1, channel, true, permission_cache); - break; - } - if (!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as() || !channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as()) { - if(!this->permissionGranted(permission::b_channel_join_ignore_maxclients, 1, channel, true, permission_cache)) { - if(!channel->properties()[property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED].as()) { - auto maxClients = channel->properties()[property::CHANNEL_MAXCLIENTS].as(); - if (maxClients >= 0 && maxClients <= this->server->getClientsByChannel(channel).size()) - return command_result{error::channel_maxclients_reached}; - } - if(!channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED].as()) { - shared_ptr family_root; - - if(channel->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) { - family_root = channel; - while(family_root && family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED].as()) family_root = family_root->parent(); - } - if(family_root && !family_root->properties()[property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED]) { //Could not be CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED - auto maxClients = family_root->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as(); - auto clients = 0; - for(const auto& entry : this->server->getClientsByChannelRoot(channel, false)) if(entry.get() != this) clients++; //Dont count the client itself - if (maxClients >= 0 && maxClients <= clients) - return command_result{error::channel_maxfamily_reached}; - } - } - } - } - - if(!this->permissionGranted(permission::b_channel_ignore_join_power, 1, channel, true, permission_cache)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); - } - - if (target_client == this) { - auto permission_cache_current = make_shared(); - auto val = this->permissionValue(permission::b_client_is_sticky, this->currentChannel, permission_cache_current); - if (val != permNotGranted && val > 0) { - auto st = this->permissionValue(permission::b_client_ignore_sticky, this->currentChannel, permission_cache_current); - if (st != 1) - return command_result{permission::b_client_is_sticky}; - } - } - - if (target_client != this) { - PERM_CHECK_CHANNELR(permission::i_client_move_power, target_client->permissionValue(permission::i_client_needed_move_power, target_client->getChannel()), target_client->getChannel(), true); - PERM_CHECK_CHANNEL_CR(permission::i_client_move_power, target_client->permissionValue(permission::i_client_needed_move_power, channel), channel, true, permission_cache); - } - - server_channel_r_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); - auto oldChannel = target_client->getChannel(); - this->server->client_move( - target_client, - channel, - target_client == this ? nullptr : _this.lock(), - "", - target_client == this ? ViewReasonId::VREASON_USER_ACTION : ViewReasonId::VREASON_MOVED, - true, - server_channel_w_lock - ); - - if(oldChannel) { - if(!server_channel_w_lock.owns_lock()) - server_channel_w_lock.lock(); - if(oldChannel->channelType() == ChannelType::temporary && oldChannel->properties()[property::CHANNEL_DELETE_DELAY].as() == 0) - if(this->server->getClientsByChannelRoot(oldChannel, false).empty()) - this->server->delete_channel(dynamic_pointer_cast(oldChannel), this->ref(), "temporary auto delete", server_channel_w_lock); - if(server_channel_w_lock.owns_lock()) - server_channel_w_lock.unlock(); - } - return command_result{error::ok}; -} - -//TODO: Test if parent or previous is deleted! -command_result ConnectedClient::handleCommandChannelCreate(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CMD_CHK_PARM_COUNT(1); - - auto permission_cache = make_shared(); - if (cmd[0].has("cpid") && cmd["cpid"].as() != 0) CACHED_PERM_CHECK(permission::b_channel_create_child, 1); - if (cmd[0].has("channel_order")) CACHED_PERM_CHECK(permission::b_channel_create_with_sortorder, 1); - - if(!cmd[0].has("channel_flag_permanent")) cmd[0]["channel_flag_permanent"] = false; - if(!cmd[0].has("channel_flag_semi_permanent")) cmd[0]["channel_flag_semi_permanent"] = false; - if(!cmd[0].has("channel_flag_default")) cmd[0]["channel_flag_default"] = false; - if(!cmd[0].has("channel_flag_password")) cmd[0]["channel_flag_password"] = false; - - if (cmd[0]["channel_flag_permanent"].as()) CACHED_PERM_CHECK(permission::b_channel_create_permanent, 1); - else if (cmd[0]["channel_flag_semi_permanent"].as()) CACHED_PERM_CHECK(permission::b_channel_create_semi_permanent, 1); - else CACHED_PERM_CHECK(permission::b_channel_create_temporary, 1); - - if (!cmd[0]["channel_flag_permanent"].as() && !this->server) return command_result{error::parameter_invalid, "You can only create a permanent channel"}; - - if (cmd[0]["channel_flag_default"].as()) CACHED_PERM_CHECK(permission::b_channel_create_with_default, 1); - if (cmd[0]["channel_flag_password"].as()) CACHED_PERM_CHECK(permission::b_channel_create_with_password, 1); - else if(this->permission_granted(this->cached_permission_value(permission::b_channel_create_modify_with_force_password), 1)) return command_result{permission::b_channel_create_modify_with_force_password}; - - if(cmd[0].has("channel_password") && this->getType() == ClientType::CLIENT_QUERY) - cmd["channel_password"] = base64::decode(digest::sha1(cmd["channel_password"].string())); - if (cmd[0].has("channel_description")) CACHED_PERM_CHECK(permission::b_channel_create_with_description, 1); - if (cmd[0].has("channel_maxclients") || (cmd[0].has("channel_flag_maxclients_unlimited") && !cmd["channel_flag_maxclients_unlimited"].as())) { - CACHED_PERM_CHECK(permission::b_channel_create_with_maxclients, 1); - if(!cmd[0]["channel_flag_permanent"].as() && !cmd[0]["channel_flag_semi_permanent"].as()) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = 1; - } - } - if (cmd[0].has("channel_maxfamilyclients")) CACHED_PERM_CHECK(permission::b_channel_create_with_maxfamilyclients, 1); - if (cmd[0].has("channel_needed_talk_power")) CACHED_PERM_CHECK(permission::b_channel_create_with_needed_talk_power, 1); - if (cmd[0].has("channel_topic")) CACHED_PERM_CHECK(permission::b_channel_create_with_topic, 1); - - auto target_tree = this->server ? this->server->channelTree : serverInstance->getChannelTree().get(); - auto& tree_lock = this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(); - unique_lock tree_channel_lock(tree_lock); - - if(cmd[0].has("channel_conversation_history_length")) { - auto value = cmd["channel_conversation_history_length"].as(); - if(value == 0) { - CACHED_PERM_CHECK(permission::b_channel_create_modify_conversation_history_unlimited, 1); - } else { - CACHED_PERM_CHECK(permission::i_channel_create_modify_conversation_history_length, 1); - } - } - - { - auto delete_delay = cmd[0].has("channel_delete_delay") ? cmd["channel_delete_delay"].as() : 0UL; - if(delete_delay == 0) { - if(this->server) - cmd["channel_delete_delay"] = this->server->properties()[property::VIRTUALSERVER_CHANNEL_TEMP_DELETE_DELAY_DEFAULT].as(); - else - cmd["channel_delete_delay"] = 0; - } else { - CACHED_PERM_CHECK(permission::i_channel_create_modify_with_temp_delete_delay, cmd["channel_delete_delay"].as()); - } - } - - { - size_t created_tmp = 0, created_semi = 0, created_perm = 0; - auto own_cldbid = this->getClientDatabaseId(); - for(const auto& channel : target_tree->channels()) { - if(channel->properties()[property::CHANNEL_CREATED_BY] == own_cldbid) { - if(channel->properties()[property::CHANNEL_FLAG_PERMANENT].as()) - created_perm++; - else if(channel->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as()) - created_semi++; - else - created_tmp++; - } - } - - - auto max_channels = this->permissionValue(permission::i_client_max_channels, nullptr, permission_cache); - - if(max_channels >= 0) { - if(max_channels <= created_perm + created_semi + created_tmp) - return command_result{permission::i_client_max_channels}; - } - - if (cmd[0]["channel_flag_permanent"].as()) { - max_channels = this->permissionValue(permission::i_client_max_permanent_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_perm) - return command_result{permission::i_client_max_permanent_channels}; - } - } - else if (cmd[0]["channel_flag_semi_permanent"].as()) { - max_channels = this->permissionValue(permission::i_client_max_semi_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_semi) - return command_result{permission::i_client_max_semi_channels}; - } - } - else { - max_channels = this->permissionValue(permission::i_client_max_temporary_channels, nullptr, permission_cache); - if(max_channels >= 0) { - if(max_channels <= created_tmp) - return command_result{permission::i_client_max_temporary_channels}; - } - } - } - - //TODO check voice (opus etc) - std::shared_ptr parent = nullptr; - std::shared_ptr created_channel = nullptr, old_default_channel; - - //bool enforce_permanent_parent = cmd[0]["channel_flag_default"].as(); //TODO check parents here - { //Checkout the parent(s) - if (cmd[0].has("cpid") && cmd["cpid"].as() != 0 && cmd["cpid"].as() != -1) { - parent = target_tree->findLinkedChannel(cmd["cpid"].as()); - if (!parent) return command_result{error::channel_invalid_id, "Cant resolve parent channel"}; - } - - { - - auto min_channel_deep = this->permissionValue(permission::i_channel_min_depth, nullptr, permission_cache); - auto max_channel_deep = this->permissionValue(permission::i_channel_max_depth, nullptr, permission_cache); - - if(min_channel_deep >= 0 || max_channel_deep >= 0) { - auto channel_deep = 0; - auto local_parent = parent; - while(local_parent) { - channel_deep++; - { - const auto typed_parent = dynamic_pointer_cast(local_parent->entry); - if(typed_parent->deleted) return command_result{error::channel_is_deleted, "Oneof parent is deleted"}; - } - local_parent = local_parent->parent.lock(); - } - - if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return command_result{permission::i_channel_min_depth}; - if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return command_result{permission::i_channel_max_depth}; - } - } - } - - if(!cmd[0].has("channel_order")) { - auto last = parent ? parent->child_head : target_tree->tree_head(); - while(last && last->next) - last = last->next; - if(last) - cmd["channel_order"] = last->entry->channelId(); - } else { - - } - - if (cmd["channel_name"].string().length() < 1) return command_result{error::channel_name_inuse, "Invalid channel name"}; - - { - if (target_tree->findChannel(cmd["channel_name"].as(), parent ? dynamic_pointer_cast(parent->entry) : nullptr)) return command_result{error::channel_name_inuse, "Name already in use"}; - - created_channel = target_tree->createChannel(parent ? parent->entry->channelId() : (ChannelId) 0, cmd[0].has("channel_order") ? cmd["channel_order"].as() : (ChannelId) 0, cmd["channel_name"].as()); - } - - if (!created_channel) return command_result{error::channel_invalid_flags, "Could not create channel"}; - - auto created_linked_channel = target_tree->findLinkedChannel(created_channel->channelId()); - sassert(created_linked_channel); - - if (cmd[0].has("channel_flag_default") && cmd["channel_flag_default"].as()) { - old_default_channel = target_tree->getDefaultChannel(); - target_tree->setDefaultChannel(created_channel); - } - - created_channel->properties()[property::CHANNEL_CREATED_BY] = this->getClientDatabaseId(); - - { - auto permission_manager = created_channel->permissions(); - permission_manager->set_permission( - permission::i_channel_needed_modify_power, - {this->permissionValue(permission::i_channel_modify_power, this->currentChannel, permission_cache), 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing - ); - permission_manager->set_permission( - permission::i_channel_needed_delete_power, - {this->permissionValue(permission::i_channel_delete_power, this->currentChannel, permission_cache), 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing - ); - } - - for (auto &prop : cmd[0].keys()) { - if (prop == "channel_flag_default") continue; - if (prop == "channel_order") continue; - if (prop == "channel_name") continue; - if (prop == "cpid") continue; - if (prop == "cid") continue; - - const auto &property = property::info(prop); - if(*property == property::CHANNEL_UNDEFINED) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a not existing channel property " + prop); - continue; - } - - if(!property->validate_input(cmd[prop].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[prop].as() + "', Property: '" + property->name + "')"); - continue; - } - created_channel->properties()[property] = cmd[prop].as(); - } - if(created_channel->parent()) { - if(created_channel->parent()->channelType() > created_channel->channelType()) - created_channel->setChannelType(created_channel->parent()->channelType()); - } - - if(this->server) { - const auto self_lock = _this.lock(); - this->server->forEachClient([&, created_channel](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - auto result = client->channels->add_channel(created_linked_channel); - if(!result) return; - - client->notifyChannelCreate(created_channel, result->previous_channel, self_lock); - if(client == self_lock && this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["cid"] = created_channel->channelId(); - this->sendCommand(notify); - } - client->notifyChannelDescriptionChanged(created_channel); - if (client->subscribeToAll) - client->subscribeChannel({created_channel}, false, true); - - if(old_default_channel) { - //TODO: Reminder: client channel tree must be at least read locked here! - client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_lock, false); - } - }); - - GroupId adminGroup = this->server->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP]; - auto channel_admin_group = this->server->groups->findGroup(adminGroup); - if (!channel_admin_group) { - logError(this->getServerId(), "Missing server's default channel admin group! Using default channel group!"); - channel_admin_group = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); - } - this->server->groups->setChannelGroup(this->getClientDatabaseId(), channel_admin_group, created_channel); - - if (created_channel->channelType() == ChannelType::temporary && (this->getType() == ClientType::CLIENT_TEAMSPEAK || this->getType() == ClientType::CLIENT_WEB)) - this->server->client_move( - this->ref(), - created_channel, - nullptr, - "channel created", - ViewReasonId::VREASON_USER_ACTION, - true, - tree_channel_lock - ); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelDelete(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - RESOLVE_CHANNEL_W(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) /* channel gets already removed */ - return command_result{error::ok}; - - CHANNEL_PERMISSION_TEST(permission::i_channel_delete_power, permission::i_channel_needed_delete_power, channel, true); - for(const auto& ch : channel_tree->channels(channel)) { - if(ch->defaultChannel()) - return command_result{error::channel_can_not_delete_default}; - } - - if(this->server) { - auto clients = this->server->getClientsByChannelRoot(channel, false); - if(!clients.empty()) - PERM_CHECK_CHANNELR(permission::b_channel_delete_flag_force, 1, channel, true); - - this->server->delete_channel(channel, this->ref(), "channel deleted", channel_tree_write_lock); - } else { - auto channel_ids = channel_tree->deleteChannelRoot(channel); - this->notifyChannelDeleted(channel_ids, this->ref()); - } - return command_result{error::ok}; -} - -inline ssize_t count_characters(const std::string& in) { - size_t index = 0; - size_t count = 0; - while(index < in.length()){ - count++; - - auto current = (uint8_t) in[index]; - if(current >= 128) { //UTF8 check - if(current >= 192 && (current <= 193 || current >= 245)) { - return -1; - } else if(current >= 194 && current <= 223) { - if(in.length() - index <= 1) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191) index += 1; //Valid - else return -1; - } else if(current >= 224 && current <= 239) { - if(in.length() - index <= 2) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191) index += 2; //Valid - else return -1; - } else if(current >= 240 && current <= 244) { - if(in.length() - index <= 3) return -1; - else if((uint8_t) in[index + 1] >= 128 && (uint8_t) in[index + 1] <= 191 && - (uint8_t) in[index + 2] >= 128 && (uint8_t) in[index + 2] <= 191 && - (uint8_t) in[index + 3] >= 128 && (uint8_t) in[index + 3] <= 191) index += 3; //Valid - else return -1; - } else { - return -1; - } - } - index++; - } - return count; -} - -/* - * 1. check for permission and basic requirements - * 2. Lock the channel tree in write mode if required - * 3. Apply changed, test for advanced requirements like channel name etc - * 4. notify everyone - */ -command_result ConnectedClient::handleCommandChannelEdit(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) { - /* channel gets already removed */ - return command_result{error::ok}; - } - - std::deque> keys; - bool require_write_lock = false; - bool update_max_clients = false; - bool update_max_family_clients = false; - bool update_clients_in_channel = false; - bool update_password = false; - bool update_name = false; - /* Step 1 */ - - bool target_channel_type_changed = false; - ChannelType::ChannelType target_channel_type = channel->channelType(); - CHANNEL_PERM_TEST_INIT; - CHANNEL_PERMISSION_TEST(permission::i_channel_modify_power, permission::i_channel_needed_modify_power, channel, true); - - for (const auto &key : cmd[0].keys()) { - if(key == "cid") - continue; - if(key == "return_code") - continue; - - const auto &property = property::info(key); - if(*property == property::CHANNEL_UNDEFINED) { - logError(this->getServerId(), R"({} Tried to edit a not existing channel property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), "{} Tried to change a channel property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if(!property->validate_input(cmd[key].as())) { - logError(this->getServerId(), "{} Tried to change a channel property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if(channel->properties()[*property].as() == cmd[key].as()) - continue; /* we dont need to update stuff which is the same */ - - if(key == "channel_icon_id") { - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - } else if (key == "channel_order") { - CHANNEL_PERM_TEST(permission::b_channel_modify_sortorder, 1, true); - require_write_lock = true; - } else if (key == "channel_flag_default") { - if(!cmd["channel_flag_default"].as()) - return command_result{error::channel_invalid_flags}; - - CHANNEL_PERM_TEST(permission::b_channel_modify_make_default, 1, true); - require_write_lock = true; - } else if (key == "channel_name") { - CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); - if (count_characters(cmd["channel_name"]) < 1) - return command_result{error::channel_name_invalid}; - if (count_characters(cmd["channel_name"]) > 40) - return command_result{error::channel_name_invalid}; - require_write_lock = true; - update_name = true; - } else if (key == "channel_name_phonetic") { - CHANNEL_PERM_TEST(permission::b_channel_modify_name, 1, true); - } else if (key == "channel_topic") { - CHANNEL_PERM_TEST(permission::b_channel_modify_topic, 1, true); - } else if (key == "channel_description") { - CHANNEL_PERM_TEST(permission::b_channel_modify_description, 1, true); - if(!this->permissionGranted(permission::b_client_use_bbcode_any, 1, this->currentChannel)) { - auto bbcode_image = bbcode::sloppy::has_image(cmd[key]); - auto bbcode_url = bbcode::sloppy::has_url(cmd[key]); - debugMessage(this->getServerId(), "Channel description contains bb codes: Image: {} URL: {}", bbcode_image, bbcode_url); - if(bbcode_image && !this->permissionGranted(permission::b_client_use_bbcode_image, 1, this->currentChannel)) - return command_result{permission::b_client_use_bbcode_image}; - if(bbcode_url && !this->permissionGranted(permission::b_client_use_bbcode_url, 1, this->currentChannel)) - return command_result{permission::b_client_use_bbcode_url}; - } - } else if (key == "channel_codec") { - CHANNEL_PERM_TEST(permission::b_channel_modify_codec, 1, true); - } else if (key == "channel_codec_quality") { - CHANNEL_PERM_TEST(permission::b_channel_modify_codec_quality, 1, true); - } else if (key == "channel_codec_is_unencrypted") { - if (cmd["channel_codec_is_unencrypted"].as()) - CHANNEL_PERM_TEST(permission::b_channel_modify_make_codec_encrypted, 1, true); - } else if (key == "channel_needed_talk_power") { - CHANNEL_PERM_TEST(permission::b_channel_modify_needed_talk_power, 1, true); - update_clients_in_channel = true; - } else if (key == "channel_maxclients" || key == "channel_flag_maxclients_unlimited") { - CHANNEL_PERM_TEST(permission::b_channel_modify_maxclients, 1, true); - require_write_lock = true; - update_max_clients = true; - } else if(key == "channel_maxfamilyclients" || key == "channel_flag_maxfamilyclients_unlimited" || key == "channel_flag_maxfamilyclients_inherited") { - CHANNEL_PERM_TEST(permission::b_channel_modify_maxfamilyclients, 1, true); - require_write_lock = true; - update_max_family_clients = true; - } else if (key == "channel_flag_permanent" || key == "channel_flag_semi_permanent") { - if (cmd[0].has("channel_flag_permanent") && cmd["channel_flag_permanent"].as()) { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_permanent, 1, true); - target_channel_type = ChannelType::permanent; - } else if (cmd[0].has("channel_flag_semi_permanent") && cmd["channel_flag_semi_permanent"].as()) { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_semi_permanent, 1, true); - target_channel_type = ChannelType::semipermanent; - } else { - CHANNEL_PERM_TEST(permission::b_channel_modify_make_temporary, 1, true); - target_channel_type = ChannelType::temporary; - } - target_channel_type_changed = true; - require_write_lock = true; - } else if (key == "channel_delete_delay") { - CHANNEL_PERM_TEST(permission::b_channel_modify_temp_delete_delay, cmd["channel_delete_delay"].as(), true); - } else if (key == "channel_password" || key == "channel_flag_password") { - CHANNEL_PERM_TEST(permission::b_channel_modify_password, 1, true); - update_password = true; - } else if (key == "channel_conversation_history_length") { - auto value = cmd["channel_conversation_history_length"].as(); - if(value == 0) { - CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_history_unlimited, 1, true); - } else { - CHANNEL_PERM_TEST(permission::i_channel_create_modify_conversation_history_length, 1, true); - } - } else if (key == "channel_flag_conversation_private") { - CHANNEL_PERM_TEST(permission::b_channel_create_modify_conversation_private, 1, true); - } else { - logCritical( - this->getServerId(), - "The client " + this->getDisplayName() + " tried to change a editable channel property but we haven't found a permission. Please report this error. (Channel property: {})", - key - ); - continue; - } - keys.push_back(property); - } - - unique_lock server_channel_w_lock(this->server ? this->server->channel_tree_lock : serverInstance->getChannelTreeLock(), defer_lock); - if(require_write_lock) { - channel_tree_read_lock.unlock(); - server_channel_w_lock.lock(); - - /* not that while we're waiting to edit the server the channel got deleted... fuck my english */ - if(channel->deleted) - return command_result{error::ok}; - } - - /* test the password parameters */ - if(update_password) { - if(!cmd[0].has("channel_password")) { - if(cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) - return command_result{error::parameter_missing}; - else - cmd["channel_password"] = ""; /* no password set */ - keys.push_back(property::info(property::CHANNEL_PASSWORD)); - } - if(!cmd[0].has("channel_flag_password")) { - cmd["channel_flag_password"] = !cmd["channel_password"].string().empty(); - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); - } - - if(cmd["channel_flag_password"].as()) { - if(cmd["channel_password"].string().empty()) - return command_result{error::channel_invalid_flags}; /* we cant enable a password without a given password */ - - /* we've to "encode" the password */ - if(this->getType() == ClientType ::CLIENT_QUERY) - cmd["channel_password"] = base64::encode(digest::sha1(cmd["channel_password"].string())); - } else { - cmd["channel_password"] = ""; /* flag password if false so we set the password to empty */ - } - } - - /* test the default channel update */ - if(cmd[0].has("channel_flag_default") || channel->defaultChannel()) { - if(target_channel_type != ChannelType::permanent) - return command_result{error::channel_default_require_permanent}; /* default channel is not allowed to be non permanent */ - - if((cmd[0].has("channel_flag_password") && cmd["channel_flag_password"].as()) || channel->properties()[property::CHANNEL_FLAG_PASSWORD]) { - cmd["channel_flag_password"] = false; - cmd["channel_password"] = ""; - keys.push_back(property::info(property::CHANNEL_FLAG_PASSWORD)); - } - - if(cmd[0].has("channel_flag_default")) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - update_max_clients = true; - - cmd["channel_maxfamilyclients"] = -1; - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - update_max_family_clients = true; - } - } - - /* "fix" max client for temporary channels */ - if(target_channel_type_changed) { - if(target_channel_type == ChannelType::temporary) { - if(channel->properties()[property::CHANNEL_MAXCLIENTS].as() != -1) { - cmd["channel_maxclients"] = -1; - cmd["channel_flag_maxclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - update_max_clients = true; - } - if(channel->properties()[property::CHANNEL_MAXFAMILYCLIENTS].as() != -1) { - cmd["channel_maxfamilyclients"] = -1; - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - update_max_family_clients = true; - } - } - - if(target_channel_type != ChannelType::permanent) { - /* test if any child is the default channel */ - for(const auto& child : channel_tree->channels(channel)) - if(child->defaultChannel()) - return command_result{error::channel_default_require_permanent}; /* default channel is not allowed to be non permanent */ - } - auto parent = channel->parent(); - if(parent && parent->channelType() > target_channel_type) - return command_result{error::channel_parent_not_permanent}; - } - - /* test the max clients parameters */ - if(update_max_clients) { - if(!cmd[0].has("channel_maxclients")) { - if(cmd[0].has("channel_flag_maxclients_unlimited") && cmd["channel_flag_maxclients_unlimited"].as()) - cmd["channel_maxclients"] = -1; - else - return command_result{error::parameter_missing, "channel_maxclients"}; /* max clients must be specified */ - keys.push_back(property::info(property::CHANNEL_MAXCLIENTS)); - } - - if(!cmd[0].has("channel_flag_maxclients_unlimited")) { - cmd["channel_flag_maxclients_unlimited"] = cmd["channel_maxclients"].as() < 0; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXCLIENTS_UNLIMITED)); - } - - if(cmd["channel_flag_maxclients_unlimited"].as() && cmd["channel_maxclients"].as() != -1) - return command_result{error::channel_invalid_flags}; /* channel cant have a max client settings AND be unlimited as well */ - - if(!cmd["channel_flag_maxclients_unlimited"].as() && target_channel_type == ChannelType::temporary) - return command_result{error::channel_invalid_flags}; /* temporary channels cant have a limited user count */ - } - - /* test the max family clients parameters */ - if(update_max_family_clients) { - if(!cmd[0].has("channel_maxfamilyclients")) { - if(cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { - if(cmd["channel_flag_maxfamilyclients_unlimited"].as()) - cmd["channel_flag_maxfamilyclients_inherited"] = false; - else - cmd["channel_flag_maxfamilyclients_inherited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_INHERITED)); - } else if(cmd[0].has("channel_flag_maxfamilyclients_inherited")) { - if(cmd["channel_flag_maxfamilyclients_inherited"].as()) - cmd["channel_flag_maxfamilyclients_unlimited"] = false; - else - cmd["channel_flag_maxfamilyclients_unlimited"] = true; - keys.push_back(property::info(property::CHANNEL_FLAG_MAXFAMILYCLIENTS_UNLIMITED)); - } else /* not really possible */ - return command_result{error::parameter_missing, "channel_maxfamilyclients"}; /* family max clients must be */ - cmd["channel_maxfamilyclients"] = -1; - keys.push_back(property::info(property::CHANNEL_MAXFAMILYCLIENTS)); - } - //keep this order because this command: "channeledit cid= channel_maxfamilyclients=-1" should set max family clients mode to inherited - if(!cmd[0].has("channel_flag_maxfamilyclients_inherited")) { - auto flag_unlimited = cmd[0].has("channel_flag_maxfamilyclients_unlimited") && cmd["channel_flag_maxfamilyclients_unlimited"].as(); - if(flag_unlimited) - cmd["channel_flag_maxfamilyclients_inherited"] = false; - else - cmd["channel_flag_maxfamilyclients_inherited"] = cmd["channel_maxfamilyclients"].as() < 0; - } - if(!cmd[0].has("channel_flag_maxfamilyclients_unlimited")) { - auto flag_inherited = cmd[0].has("channel_flag_maxfamilyclients_inherited") && cmd["channel_flag_maxfamilyclients_inherited"].as(); - if(flag_inherited) - cmd["channel_flag_maxfamilyclients_unlimited"] = false; - else - cmd["channel_flag_maxfamilyclients_unlimited"] = cmd["channel_maxfamilyclients"].as() < 0; - } - - if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_flag_maxfamilyclients_unlimited"].as()) - return command_result{error::channel_invalid_flags}; /* both at the same time are not possible */ - - if(cmd["channel_flag_maxfamilyclients_inherited"].as() && cmd["channel_maxfamilyclients"].as() != -1) - return command_result{error::channel_invalid_flags}; /* flag inherited required max users to be -1 */ - - if(cmd["channel_flag_maxfamilyclients_unlimited"].as() && cmd["channel_maxfamilyclients"].as() != -1) - return command_result{error::channel_invalid_flags}; /* flag unlimited required max users to be -1 */ - - if(cmd["channel_maxfamilyclients"].as() != -1 && target_channel_type == ChannelType::temporary) - return command_result{error::channel_invalid_flags}; /* temporary channels cant have a limited user count */ - } - - /* test the channel name */ - if(update_name) { - auto named_channel = channel_tree->findChannel(cmd["channel_name"].string(), channel->parent()); - if (named_channel) - return command_result{error::channel_name_inuse}; - } - - auto self_ref = this->ref(); - shared_ptr old_default_channel; - deque> child_channel_updated; - for(const std::shared_ptr& key : keys) { - if(*key == property::CHANNEL_ORDER) { - /* TODO: May move that up because if it fails may some other props have already be applied */ - if (!channel_tree->change_order(channel, cmd[key->name])) - return command_result{error::channel_invalid_order, "Can't change order id"}; - - if(this->server) { - auto parent = channel->hasParent() ? channel_tree->findLinkedChannel(channel->parent()->channelId()) : nullptr; - auto previous = channel_tree->findLinkedChannel(channel->previousChannelId()); - - this->server->forEachClient([&](const shared_ptr& cl) { - unique_lock client_channel_lock(cl->channel_lock); - - auto actions = cl->channels->change_order(l_channel, parent, previous); - std::deque deletions; - for(const auto& action : actions) { - switch (action.first) { - case ClientChannelView::NOTHING: - continue; - case ClientChannelView::ENTER_VIEW: - cl->notifyChannelShow(action.second->channel(), action.second->previous_channel); - break; - case ClientChannelView::DELETE_VIEW: - deletions.push_back(action.second->channelId()); - break; - case ClientChannelView::MOVE: - cl->notifyChannelMoved(action.second->channel(), action.second->previous_channel, this->ref()); - break; - case ClientChannelView::REORDER: - cl->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_ref, false); - break; - } - } - if(!deletions.empty()) { - cl->notifyChannelHide(deletions, false); - return; //Channel got deleted so we dont have to send the updates - } - }); - } - } else if(*key == property::CHANNEL_FLAG_DEFAULT) { - old_default_channel = channel_tree->getDefaultChannel(); - if(old_default_channel == channel) { - old_default_channel = nullptr; - continue; - } - - if(!cmd[key->name].as()) { - old_default_channel = nullptr; - continue; - } - - channel_tree->setDefaultChannel(channel); - - deque> updated_channels; - shared_ptr current_channel = channel; - do { - if(current_channel->channelType() == ChannelType::permanent) - break; - - current_channel->setChannelType(ChannelType::permanent); - if(current_channel != channel) - updated_channels.push_back(channel); - } while ((current_channel = current_channel->parent())); - - if(this->server && !updated_channels.empty()) { - std::reverse(updated_channels.begin(), updated_channels.end()); - auto this_ref = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - for(const auto& channel : updated_channels) { - client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, this_ref, false); - } - }); - } - } else if(*key == property::CHANNEL_FLAG_PERMANENT || *key == property::CHANNEL_FLAG_SEMI_PERMANENT) { - if(target_channel_type_changed) { /* must be true else the key would not appere here */ - target_channel_type_changed = false; /* we only need to check all subchannels once! */ - - - { /* check channel children */ - deque> channel_to_test = {channel}; - - while(!channel_to_test.empty()) { - auto current_channel = channel_to_test.front(); - channel_to_test.pop_front(); - - for(const auto& child : channel_tree->channels(current_channel, 1)) { - if(child == current_channel) - continue; - - if(child->channelType() < target_channel_type) { - child->setChannelType(target_channel_type); - channel_to_test.push_back(child); - child_channel_updated.push_back(child); - } - } - } - } - } - } else if(*key == property::CHANNEL_CONVERSATION_HISTORY_LENGTH) { - //channel_conversation_history_length - auto conversation_manager = this->server->conversation_manager(); - if(conversation_manager) { - auto conversation = conversation_manager->get(channel->channelId()); - if(conversation) - conversation->set_history_length(cmd[key->name]); - } - } - - channel->properties()[key] = cmd[key->name].string(); - } - if(this->server) { - vector key_vector; - key_vector.reserve(keys.size()); - for(const auto& key : keys) - key_vector.push_back((property::ChannelProperties) key->property_index); - - auto self_rev = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock client_channel_lock(client->channel_lock); - - for(const auto& channel : child_channel_updated) - client->notifyChannelEdited(channel, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); - - client->notifyChannelEdited(channel, key_vector, self_rev, false); - - if(old_default_channel) /* clients need to have one or more defualt channels... */ - client->notifyChannelEdited(old_default_channel, {property::CHANNEL_FLAG_DEFAULT}, self_rev, false); - }); - } - - if(server_channel_w_lock.owns_lock()) - server_channel_w_lock.unlock(); - - if(!channel_tree_read_lock.owns_lock()) - channel_tree_read_lock.lock(); - - if(update_clients_in_channel && this->server) { - for(const auto& client : this->server->getClientsByChannel(channel)) - client->updateChannelClientProperties(true, true); //TODO: May only update the talk power and not all? - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelMove(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - RESOLVE_CHANNEL_W(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - if(channel->deleted) - return command_result{error::channel_is_deleted}; - - if(!cmd[0].has("order")) - cmd["order"] = 0; - - auto l_parent = channel_tree->findLinkedChannel(cmd["cpid"]); - shared_ptr l_order; - if(cmd[0].has("order")) { - l_order = channel_tree->findLinkedChannel(cmd["order"]); - if (!l_order && cmd["order"].as() != 0) return command_result{error::channel_invalid_id}; - } else { - l_order = l_parent ? l_parent->child_head : (this->server ? this->server->getChannelTree() : serverInstance->getChannelTree().get())->tree_head(); - while(l_order && l_order->next) - l_order = l_order->next; - } - - auto parent = l_parent ? dynamic_pointer_cast(l_parent->entry) : nullptr; - auto order = l_order ? dynamic_pointer_cast(l_order->entry) : nullptr; - - if((parent && parent->deleted) || (order && order->deleted)) - return command_result{error::channel_is_deleted, "parent channel order previous channel has been deleted"}; - - if(channel->parent() == parent && channel->channelOrder() == (order ? order->channelId() : 0)) - return command_result{error::ok}; - - if (channel->parent() != parent) - PERM_CHECK_CHANNELR(permission::b_channel_modify_parent, 1, channel, true); - if ((order ? order->channelId() : 0) != channel->channelOrder()) - PERM_CHECK_CHANNELR(permission::b_channel_modify_sortorder, 1, channel, true); - - { - - auto min_channel_deep = this->permissionValue(permission::i_channel_min_depth, nullptr, nullptr); - auto max_channel_deep = this->permissionValue(permission::i_channel_max_depth, nullptr, nullptr); - - if(min_channel_deep >= 0 || max_channel_deep >= 0) { - auto channel_deep = 0; - auto local_parent = l_parent; - while(local_parent) { - channel_deep++; - local_parent = local_parent->parent.lock(); - } - - if(min_channel_deep >= 0 && channel_deep < min_channel_deep) return command_result{permission::i_channel_min_depth}; - if(max_channel_deep >= 0 && channel_deep > max_channel_deep) return command_result{permission::i_channel_max_depth}; - } - } - { - auto name = channel_tree->findChannel(channel->name(), parent); - if(name && name != channel) return command_result{error::channel_name_inuse}; - } - debugMessage(this->getServerId(), "Moving channel {} from old [{} | {}] to [{} | {}]", channel->name(), channel->channelOrder(), channel->parent() ? channel->parent()->channelId() : 0, order ? order->channelId() : 0, parent ? parent->channelId() : 0); - - if (!channel_tree->move_channel(channel, parent, order)) return command_result{error::channel_invalid_order, "Cant change order id"}; - - deque> channel_type_updates; - { - auto flag_default = channel->defaultChannel(); - auto current_channel = channel; - do { - if(flag_default) { - if(current_channel->channelType() != ChannelType::permanent) { - current_channel->setChannelType(ChannelType::permanent); - channel_type_updates.push_front(current_channel); - } - } else if(current_channel->hasParent()) { - if(current_channel->channelType() < current_channel->parent()->channelType()) { - current_channel->setChannelType(current_channel->parent()->channelType()); - channel_type_updates.push_front(current_channel); - } - } - } while ((current_channel = dynamic_pointer_cast(current_channel->parent()))); - } - - if(this->server) { - auto self_rev = this->ref(); - this->server->forEachClient([&](const shared_ptr& client) { - unique_lock channel_lock(client->channel_lock); - for(const auto& type_update : channel_type_updates) - client->notifyChannelEdited(type_update, {property::CHANNEL_FLAG_PERMANENT, property::CHANNEL_FLAG_SEMI_PERMANENT}, self_rev, false); - - auto actions = client->channels->change_order(l_channel, l_parent, l_order); - std::deque deletions; - for(const auto& action : actions) { - switch (action.first) { - case ClientChannelView::NOTHING: - continue; - case ClientChannelView::ENTER_VIEW: - client->notifyChannelShow(action.second->channel(), action.second->previous_channel); - break; - case ClientChannelView::DELETE_VIEW: - deletions.push_back(action.second->channelId()); - break; - case ClientChannelView::MOVE: - client->notifyChannelMoved(action.second->channel(), action.second->previous_channel, _this.lock()); - break; - case ClientChannelView::REORDER: - client->notifyChannelEdited(action.second->channel(), {property::CHANNEL_ORDER}, self_rev, false); - break; - } - } - if(!deletions.empty()) - client->notifyChannelHide(deletions, false); - }); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientPoke(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - if (client->getType() == CLIENT_MUSIC) return command_result{error::client_invalid_type}; - CACHED_PERM_CHECK(permission::i_client_poke_power, client->permissionValue(permission::i_client_needed_poke_power, client->currentChannel), false); - - client->notifyClientPoke(_this.lock(), cmd["msg"]); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelPermList(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - - CHANNEL_PERM_TEST_INIT; - CHANNEL_PERM_TEST(permission::b_virtualserver_channel_permission_list, 1, true); - - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); - } - - auto sids = cmd.hasParm("permsid"); - - Command result(this->notify_response_command("notifychannelpermlist")); - int index = 0; - result["cid"] = channel->channelId(); - - auto permission_mapper = serverInstance->getPermissionMapper(); - auto type = this->getType(); - auto permission_manager = channel->permissions(); - for (const auto &permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - if(sids) { - result[index]["permsid"] = permission_mapper->permission_name(type, std::get<0>(permission_data)); - } else { - result[index]["permid"] = std::get<0>(permission_data); - } - - result[index]["permvalue"] = permission.values.value; - result[index]["permnegated"] = permission.flags.negate; - result[index]["permskip"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - if(sids) { - result[index]["permsid"] = permission_mapper->permission_name_grant(type, std::get<0>(permission_data)); - } else { - result[index]["permid"] = (uint16_t) (std::get<0>(permission_data) | PERM_ID_GRANT); - } - result[index]["permvalue"] = permission.values.grant; - result[index]["permnegated"] = 0; - result[index]["permskip"] = 0; - index++; - } - } - if(index == 0) - return command_result{error::database_empty_result}; - - this->sendCommand(result); - return command_result{error::ok}; -} - -// -//channel_icon_id=18446744073297259750 -//channel_name -//channel_topic -//Desctiption has no extra parm -command_result ConnectedClient::handleCommandChannelAddPerm(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, channel); - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, channel); - auto updateClients = false, update_view = false, update_channel_properties = false; - - bool conOnError = cmd[0].has("continueonerror"); - - auto permission_manager = channel->permissions(); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) { - if(conOnError) continue; - - return command_result{permission::i_permission_modify_power}; - } - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, channel)) { - if(conOnError) continue; - - return command_result{permission::i_permission_modify_power}; - } - - if (grant) { - permission_manager->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - permission_manager->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, - - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - updateClients |= permission_is_client_property(permType); - update_view |= permType == permission::i_channel_needed_view_power; - update_channel_properties |= channel->permission_require_property_update(permType); - - if (permType == permission::i_icon_id) { - if(this->server) { - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, {property::CHANNEL_ICON_ID}, self_ref, false); - }); - } - continue; - } - } - } - - /* broadcast the updated channel properties */ - if(update_channel_properties) { - auto updates = channel->update_properties_from_permissions(); - if(!updates.empty() && this->server){ - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, updates, self_ref, false); - }); - } - } - - if(updateClients && this->server) - for(const auto& client : this->server->getClientsByChannel(channel)) { - /* let them lock the server channel tree as well (read lock so does not matter) */ - client->updateChannelClientProperties(true, true); - client->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - - if(update_view && this->server) { - auto l_source = this->server->channelTree->findLinkedChannel(channel->channelId()); - this->server->forEachClient([&](const shared_ptr& cl) { - /* server tree read lock still active */ - auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); - sassert(l_source); - if(cl->currentChannel) sassert(l_target); - - { - unique_lock client_channel_lock(cl->channel_lock); - - deque deleted; - for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { - if(update_entry.first) - cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - cl->notifyChannelHide(deleted, false); - } - }); - } - return command_result{error::ok};; -} - -command_result ConnectedClient::handleCommandChannelDelPerm(Command &cmd) { - CMD_RESET_IDLE; - - RESOLVE_CHANNEL_R(cmd["cid"], true) - auto channel = dynamic_pointer_cast(l_channel->entry); - assert(channel); - CHANNEL_PERMISSION_TEST(permission::i_channel_permission_modify_power, permission::i_channel_needed_permission_modify_power, channel, true); - - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, channel); - bool conOnError = cmd[0].has("continueonerror"); - auto updateClients = false, update_view = false, update_channel_properties = false; - - auto permission_manager = channel->permissions(); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, channel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - permission_manager->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); - updateClients |= permission_is_client_property(permType); - update_view |= permType == permission::i_channel_needed_view_power; - update_channel_properties |= channel->permission_require_property_update(permType); - } - } - - /* broadcast the updated channel properties */ - if(update_channel_properties) { - auto updates = channel->update_properties_from_permissions(); - if(!updates.empty() && this->server){ - auto self_ref = this->ref(); - this->server->forEachClient([&](std::shared_ptr cl) { - shared_lock client_channel_lock(cl->channel_lock); - cl->notifyChannelEdited(channel, updates, self_ref, false); - }); - } - } - - if(updateClients && this->server) - this->server->forEachClient([&](std::shared_ptr cl) { - if(cl->currentChannel == channel) { - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - if(update_view && this->server) { - this->server->forEachClient([&](std::shared_ptr cl) { - /* server tree read lock still active */ - auto l_source = cl->server->channelTree->findLinkedChannel(channel->channelId()); - auto l_target = !cl->currentChannel ? nullptr : cl->server->channelTree->findLinkedChannel(cl->currentChannel->channelId()); - sassert(l_source); - if(cl->currentChannel) sassert(l_target); - - { - unique_lock client_channel_lock(cl->channel_lock); - - deque deleted; - for(const auto& update_entry : cl->channels->update_channel(l_source, l_target)) { - if(update_entry.first) - cl->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - cl->notifyChannelHide(deleted, false); - } - }); - } - return command_result{error::ok}; -} - -//servergroupadd name=TestGroup type=1 -command_result ConnectedClient::handleCommandServerGroupAdd(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_create, 1, true); - if(cmd["name"].string().empty()) return command_result{error::parameter_invalid}; - - if(cmd["type"].as() == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return command_result{permission::b_serverinstance_modify_querygroup}; - } else if(cmd["type"].as() == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return command_result{permission::b_serverinstance_modify_templates}; - } else if(!this->server) return command_result{error::parameter_invalid, "you cant create normal groups on the template server!"}; - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - for(const auto& gr : group_manager->availableServerGroups(true)) - if(gr->name() == cmd["name"].string() && gr->target() == GroupTarget::GROUPTARGET_SERVER) return command_result{error::parameter_invalid, "Group already exists"}; - auto group = group_manager->createGroup(GroupTarget::GROUPTARGET_SERVER, cmd["type"].as(), cmd["name"].string()); - if (group) { - group->permissions()->set_permission(permission::b_group_is_permanent, {1,0}, permission::v2::set_value, permission::v2::do_nothing); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - } else return command_result{error::vs_critical}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupCopy(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto ref_server = this->server; - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_create, 1, true); - - auto group_manager = this->server ? this->server->groups : serverInstance->getGroupManager().get(); - - auto source_group_id = cmd["ssgid"].as(); - auto source_group = group_manager->findGroup(source_group_id); - - if(!source_group || source_group->target() != GROUPTARGET_SERVER) - return command_result{error::group_invalid_id}; - - const auto group_type_modificable = [&](GroupType type) { - switch(type) { - case GroupType::GROUP_TYPE_TEMPLATE: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_templates, 0), 1, true)) - return permission::b_serverinstance_modify_templates; - break; - case GroupType::GROUP_TYPE_QUERY: - if(!this->permission_granted(this->permissionValue(permission::b_serverinstance_modify_querygroup, 0), 1, true)) - return permission::b_serverinstance_modify_querygroup; - break; - - default: - break; - } - return permission::undefined; - }; - - { - auto result = group_type_modificable(source_group->type()); - if(result != permission::undefined) - return command_result{result}; - } - - auto global_update = false; - if(cmd[0].has("tsgid") && cmd["tsgid"].as() != 0) { - //Copy an existing group - auto target_group = group_manager->findGroup(cmd["tsgid"]); - if(!target_group || target_group->target() != GROUPTARGET_SERVER) - return command_result{error::group_invalid_id}; - - { - auto result = group_type_modificable(target_group->type()); - if(result != permission::undefined) - return command_result{result}; - } - - if(!target_group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true)) - return command_result{permission::i_server_group_modify_power}; - - if(!group_manager->copyGroupPermissions(source_group, target_group)) - return command_result{error::vs_critical, "failed to copy group permissions"}; - - global_update = !this->server || !group_manager->isLocalGroup(target_group); - } else { - //Copy a new group - auto target_type = cmd["type"].as(); - - { - auto result = group_type_modificable(target_type); - if(result != permission::undefined) - return command_result{result}; - } - - if(!ref_server && target_type == GroupType::GROUP_TYPE_NORMAL) - return command_result{error::parameter_invalid, "You cant create normal groups on the template server!"}; - - if(!group_manager->findGroup(GroupTarget::GROUPTARGET_SERVER, cmd["name"].string()).empty()) - return command_result{error::group_name_inuse, "You cant create normal groups on the template server!"}; - - auto target_group_id = group_manager->copyGroup(source_group, target_type, cmd["name"], target_type != GroupType::GROUP_TYPE_NORMAL ? 0 : this->getServerId()); - if(target_group_id == 0) - return command_result{error::vs_critical, "failed to copy group"}; - - if(this->getType() == ClientType::CLIENT_QUERY) { - Command notify(""); - notify["sgid"] = target_group_id; - this->sendCommand(notify); - } - - global_update = !this->server || !group_manager->isLocalGroup(group_manager->findGroup(target_group_id)); - } - - for(const auto& server : (global_update ? serverInstance->getVoiceServerManager()->serverInstances() : deque>{this->server})) - if(server) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupRename(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - - auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); - if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return command_result{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return command_result{permission::b_serverinstance_modify_templates}; - } - - group_manager->renameGroup(serverGroup, cmd["name"].string()); - if(this->server) - this->server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - - return command_result{error::ok}; -} - -//servergroupdel sgid=2 force=0 -command_result ConnectedClient::handleCommandServerGroupDel(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_delete, 1, true); - auto group_manager = this->server ? this->server->getGroupManager() : serverInstance->getGroupManager().get(); - auto serverGroup = group_manager->findGroup(cmd["sgid"].as()); - if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; - - if(this->server && this->server->properties()[property::VIRTUALSERVER_DEFAULT_SERVER_GROUP] == serverGroup->groupId()) - return command_result{error::parameter_invalid, "Could not delete default server group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERADMIN_GROUP] == serverGroup->groupId()) - return command_result{error::parameter_invalid, "Could not delete instance default server admin group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_TEMPLATE_SERVERDEFAULT_GROUP] == serverGroup->groupId()) - return command_result{error::parameter_invalid, "Could not delete instance default server group!"}; - if(serverInstance->properties()[property::SERVERINSTANCE_GUEST_SERVERQUERY_GROUP] == serverGroup->groupId()) - return command_result{error::parameter_invalid, "Could not delete instance default guest server query group!"}; - - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return command_result{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return command_result{permission::b_serverinstance_modify_templates}; - } - - if (!cmd["force"].as()) - if (!group_manager->listGroupMembers(serverGroup, false).empty()) - return command_result{error::database_empty_result, "group not empty!"}; - - if (group_manager->deleteGroup(serverGroup)) { - if(this->server) - this->server->forEachClient([&](shared_ptr cl) { - if(this->server->notifyClientPropertyUpdates(cl, this->server->groups->update_server_group_property(cl, true, cl->getChannel()))) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - cl->notifyServerGroupList(); - }); - } - - return command_result{error::ok}; -} - -//servergroupclientlist sgid=2 -//notifyservergroupclientlist sgid=6 cldbid=2 client_nickname=WolverinDEV client_unique_identifier=xxjnc14LmvTk+Lyrm8OOeo4tOqw= -command_result ConnectedClient::handleCommandServerGroupClientList(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_client_list, 1, true); - - auto server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto groupManager = server ? this->server->groups : serverInstance->getGroupManager().get(); - - auto serverGroup = groupManager->findGroup(cmd["sgid"].as()); - if (!serverGroup || serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::group_invalid_id}; - - Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyservergroupclientlist" : ""); - notify["sgid"] = cmd["sgid"].as(); - int index = 0; - for (const auto &clientEntry : groupManager->listGroupMembers(serverGroup)) { - notify[index]["cldbid"] = clientEntry->cldbId; - notify[index]["client_nickname"] = clientEntry->displayName; - notify[index]["client_unique_identifier"] = clientEntry->uid; - index++; - } - this->sendCommand(notify); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupAddClient(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); - - auto target_cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"}; - - auto needed_client_permission = this->server->calculatePermission(target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr); - if(needed_client_permission != permNotGranted) { - if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) - return command_result{permission::i_client_needed_permission_modify_power}; - } - - vector> target_groups; - vector> applied_groups; - target_groups.reserve(cmd.bulkCount()); - - auto continue_on_error = cmd.hasParm("continueonerror"); - { - auto permission_add_power = this->calculate_permission(permission::i_server_group_member_add_power, -1); - auto permission_self_add_power = this->calculate_permission(permission::i_server_group_member_add_power, -1); - - for(auto index = 0; index < cmd.bulkCount(); index++) { - auto group_id = cmd[index]["sgid"]; - if(!group_id.castable() && continue_on_error) - continue; - - auto gid = group_id.as(); - auto group = group_manager->findGroup(gid); - if(!group) { - if(continue_on_error) - continue; - - return command_result{error::group_invalid_id}; - } - - if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) - return command_result{error::parameter_invalid}; - - if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { - if(continue_on_error) - continue; - - return command_result{error::client_is_already_member_of_group}; - } - - /* permission tests */ - if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_add_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) { - if(continue_on_error) - continue; - - return command_result{permission::i_server_group_member_add_power}; - } - - if(!group->permission_granted(permission::i_server_group_needed_member_add_power, permission_self_add_power, true)) { - if(continue_on_error) - continue; - - return command_result{permission::i_server_group_self_add_power}; - } - } - - target_groups.push_back(std::move(group)); - } - } - - applied_groups.reserve(target_groups.size()); - if(target_groups.empty()) return command_result{error::ok}; - else if(target_groups.size() == 1) { - /* speed up thing, don't try to load any cache */ - auto group = target_groups[0]; - if(group_manager->hasServerGroupAssigned(target_cldbid, group)) - return command_result{error::client_is_already_member_of_group}; - - group_manager->addServerGroup(target_cldbid, group); - applied_groups.push_back(group); - } else { - group_manager->enableCache(target_cldbid); - scope_exit_callback cache_disable{[group_manager, target_cldbid]{ - group_manager->disableCache(target_cldbid); - }}; - - for(const auto& group : target_groups) { - if(group_manager->hasServerGroupAssigned(target_cldbid, group)) { - if(continue_on_error) - continue; - return command_result{error::client_is_already_member_of_group}; - } - - group_manager->addServerGroup(target_cldbid, group); - applied_groups.push_back(group); - } - } - - for(const auto& _server : target_server ? std::deque>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) { - for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) { - if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) { - for (const auto &client : _server->getClients()) { - if(client->isClientVisible(targetClient, true) || client == targetClient) - for(const auto& group : applied_groups) - client->notifyServerGroupClientAdd(_this.lock(), targetClient, group); - } - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - targetClient->updateChannelClientProperties(true, true); - } - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupDelClient(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto target_server = cmd[0].has("sid") && cmd["sid"] == 0 ? nullptr : this->server; - auto group_manager = target_server ? this->server->groups : serverInstance->getGroupManager().get(); - - auto target_cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(target_server, cmd["cldbid"])) return command_result{error::client_invalid_id, "invalid cldbid"}; - - auto needed_client_permission = this->server->calculatePermission(target_cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr); - if(needed_client_permission != permNotGranted) { - if(!this->permission_granted(this->permissionValue(permission::i_client_permission_modify_power), needed_client_permission)) - return command_result{permission::i_client_needed_permission_modify_power}; - } - - vector> target_groups; - vector> applied_groups; - target_groups.reserve(cmd.bulkCount()); - - auto continue_on_error = cmd.hasParm("continueonerror"); - { - auto permission_remove_power = this->calculate_permission(permission::i_server_group_member_remove_power, -1); - auto permission_self_remove_power = this->calculate_permission(permission::i_server_group_member_remove_power, -1); - - for(auto index = 0; index < cmd.bulkCount(); index++) { - auto group_id = cmd[index]["sgid"]; - if(!group_id.castable() && continue_on_error) - continue; - - auto gid = group_id.as(); - auto group = group_manager->findGroup(gid); - if(!group) { - if(continue_on_error) - continue; - - return command_result{error::group_invalid_id}; - } - - if(!target_server && group->target() != GroupTarget::GROUPTARGET_SERVER && group->type() != GroupType::GROUP_TYPE_QUERY) - return command_result{error::group_invalid_id}; - - if(find(target_groups.begin(), target_groups.end(), group) != target_groups.end()) { - if(continue_on_error) - continue; - - return command_result{error::parameter_invalid, "duplicate server group for id " + to_string(gid)}; - } - - /* permission tests */ - if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_remove_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) { - if(continue_on_error) - continue; - - return command_result{permission::i_server_group_member_remove_power}; - } - - if(!group->permission_granted(permission::i_server_group_needed_member_remove_power, permission_self_remove_power, true)) { - if(continue_on_error) - continue; - - return command_result{permission::i_server_group_self_remove_power}; - } - } - - target_groups.push_back(std::move(group)); - } - } - - applied_groups.reserve(target_groups.size()); - if(target_groups.empty()) return command_result{error::ok}; - else if(target_groups.size() == 1) { - /* speed up thing, don't try to load any cache */ - auto group = target_groups[0]; - auto assignment = group_manager->get_group_assignment(target_cldbid, group); - if(!assignment) { - return command_result{error::client_is_not_member_of_group}; - } - if(assignment->server != (target_server ? target_server->getServerId() : 0)) { - return command_result{error::group_not_assigned_over_this_server}; - } - - group_manager->removeServerGroup(target_cldbid, group); - applied_groups.push_back(group); - } else { - group_manager->enableCache(target_cldbid); - scope_exit_callback cache_disable{[group_manager, target_cldbid]{ - group_manager->disableCache(target_cldbid); - }}; - - for(const auto& group : target_groups) { - auto assignment = group_manager->get_group_assignment(target_cldbid, group); - if(!assignment) { - if(continue_on_error) - continue; - return command_result{error::client_is_not_member_of_group}; - } - if(assignment->server != (target_server ? target_server->getServerId() : 0)) { - if(continue_on_error) - continue; - return command_result{error::group_not_assigned_over_this_server}; - } - - applied_groups.push_back(group); - group_manager->removeServerGroup(target_cldbid, group); - } - } - - for(const auto& _server : target_server ? std::deque>{target_server} : serverInstance->getVoiceServerManager()->serverInstances()) { - for (const auto &targetClient : _server->findClientsByCldbId(target_cldbid)) { - if (_server->notifyClientPropertyUpdates(targetClient, _server->groups->update_server_group_property(targetClient, true, targetClient->getChannel()))) { - for (const auto &client : _server->getClients()) { - if(client->isClientVisible(targetClient, true) || client == targetClient) - for(const auto& group : applied_groups) - client->notifyServerGroupClientRemove(_this.lock(), targetClient, group); - } - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - targetClient->updateChannelClientProperties(true, true); - } - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupPermList(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - CACHED_PERM_CHECK(permission::b_virtualserver_servergroup_permission_list, 1, true); - - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); - if (!serverGroup) return command_result{error::group_invalid_id}; - - if(this->getType() == ClientType::CLIENT_TEAMSPEAK && this->command_times.last_notify + this->command_times.notify_timeout < system_clock::now()) { - this->sendTSPermEditorWarning(); - } - if (!this->notifyGroupPermList(serverGroup, cmd.hasParm("permsid"))) return command_result{error::database_empty_result}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupAddPerm(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); - if (!serverGroup) return command_result{error::group_invalid_id}; - if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return command_result{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return command_result{permission::b_serverinstance_modify_templates}; - } - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool conOnError = cmd[0].has("continueonerror"); - bool checkTp = false; - bool sgroupUpdate = false; - - auto permissions = serverGroup->permissions(); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - //permvalue='1' permnegated='0' permskip='0' - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - - if (grant) { - permissions->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - permissions->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, - - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - sgroupUpdate |= permission_is_group_property(permType); - checkTp |= permission_is_client_property(permType); - } - } - - if(sgroupUpdate) - serverGroup->apply_properties_from_permissions(); - - //TODO may update for every server? - if(this->server) { - auto lock = this->_this.lock(); - auto server = this->server; - threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { - if(sgroupUpdate) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - server->forEachClient([serverGroup, checkTp](shared_ptr cl) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - }).detach(); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupDelPerm(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto serverGroup = (this->server ? this->server->groups : serverInstance->getGroupManager().get())->findGroup(cmd["sgid"].as()); - if (!serverGroup) return command_result{error::group_invalid_id}; - if (serverGroup->target() != GROUPTARGET_SERVER) return command_result{error::parameter_invalid}; - GROUP_PERMISSION_TEST(permission::i_server_group_modify_power, permission::i_server_group_needed_modify_power, serverGroup, true); - - auto type = serverGroup->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_querygroup), 1, true)) - return command_result{permission::b_serverinstance_modify_querygroup}; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permission_granted(this->cached_permission_value(permission::b_serverinstance_modify_templates), 1, true)) - return command_result{permission::b_serverinstance_modify_templates}; - } - - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1); - bool conOnError = cmd[0].has("continueonerror"); - bool checkTp = false; - auto sgroupUpdate = false; - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - - if (grant) { - serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - serverGroup->permissions()->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - sgroupUpdate |= permission_is_group_property(permType); - checkTp |= permission_is_client_property(permType); - } - } - - if(sgroupUpdate) - serverGroup->apply_properties_from_permissions(); - - if(this->server) { - auto lock = this->_this.lock(); - auto server = this->server; - threads::Thread([checkTp, sgroupUpdate, serverGroup, lock, server]() { - if(sgroupUpdate) - server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - server->forEachClient([serverGroup, checkTp](shared_ptr cl) { - if (cl->serverGroupAssigned(serverGroup)) { - - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - }); - }).detach(); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupAutoAddPerm(ts::Command& cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto ref_server = this->server; - auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); - - deque> groups; - for(const auto& group : group_manager->availableGroups(false)) { - if(group->updateType() == cmd["sgtype"].as() && group->target() == GROUPTARGET_SERVER) { - if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true)) { - auto type = group->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permissionGranted(permission::b_serverinstance_modify_querygroup, 1)) - continue; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permissionGranted(permission::b_serverinstance_modify_templates, 1)) - continue; - } - groups.push_back(group);//sgtype - } - } - } - - if(groups.empty()) - return command_result{error::ok}; - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1); - bool conOnError = cmd[0].has("continueonerror"); - bool checkTp = false; - bool sgroupUpdate = false; - - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - //permvalue='1' permnegated='0' permskip='0'end - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - for(const auto& serverGroup : groups) { - if (grant) { - serverGroup->permissions()->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::set_value); - } else { - serverGroup->permissions()->set_permission( - permType, - {cmd[index]["permvalue"], 0}, - permission::v2::PermissionUpdateType::set_value, - permission::v2::PermissionUpdateType::do_nothing, - - cmd[index]["permskip"].as() ? 1 : 0, - cmd[index]["permnegated"].as() ? 1 : 0 - ); - } - } - sgroupUpdate |= permission_is_group_property(permType); - checkTp |= permission_is_client_property(permType); - } - - if(sgroupUpdate) - for(auto& group : groups) - group->apply_properties_from_permissions(); - - auto lock = this->_this.lock(); - if(ref_server) { - threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { - if(sgroupUpdate) - ref_server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - ref_server->forEachClient([groups, checkTp](shared_ptr cl) { - for(const auto& serverGroup : groups) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) {/* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - } - if (checkTp) { - cl->updateChannelClientProperties(true, true); - } - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate if needed */ - break; - } - } - }); - }).detach(); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupAutoDelPerm(ts::Command& cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto ref_server = this->server; - auto group_manager = ref_server ? this->server->groups : &*serverInstance->getGroupManager(); - - deque> groups; - for(const auto& group : group_manager->availableGroups(false)) { - if(group->updateType() == cmd["sgtype"].as() && group->target() == GROUPTARGET_SERVER) { - if(group->permission_granted(permission::i_server_group_needed_modify_power, this->calculate_permission(permission::i_server_group_modify_power, 0), true)) { - auto type = group->type(); - if(type == GroupType::GROUP_TYPE_QUERY) { - if(!this->permissionGranted(permission::b_serverinstance_modify_querygroup, 1)) - continue; - } else if(type == GroupType::GROUP_TYPE_TEMPLATE) { - if(!this->permissionGranted(permission::b_serverinstance_modify_templates, 1)) - continue; - } - groups.push_back(group);//sgtype - } - } - } - - if(groups.empty()) return command_result{error::ok}; - - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1); - bool conOnError = cmd[0].has("continueonerror"); - bool checkTp = false; - auto sgroupUpdate = false; - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) { - if(conOnError) continue; - return command_result{permission::i_permission_modify_power}; - } - - for(const auto& serverGroup : groups) { - if (grant) { - serverGroup->permissions()->set_permission(permType, permission::v2::empty_permission_values, permission::v2::PermissionUpdateType::do_nothing, permission::v2::PermissionUpdateType::delete_value); - } else { - serverGroup->permissions()->set_permission( - permType, - permission::v2::empty_permission_values, - permission::v2::PermissionUpdateType::delete_value, - permission::v2::PermissionUpdateType::do_nothing - ); - sgroupUpdate |= permission_is_group_property(permType); - } - } - checkTp |= permission_is_client_property(permType); - } - - - if(sgroupUpdate) { - for(auto& group : groups) - group->apply_properties_from_permissions(); - } - - if(ref_server) { - auto lock = this->_this.lock(); - threads::Thread([checkTp, sgroupUpdate, groups, lock, ref_server]() { - if(sgroupUpdate) - ref_server->forEachClient([](shared_ptr cl) { - cl->notifyServerGroupList(); - }); - ref_server->forEachClient([groups, checkTp](shared_ptr cl) { - for(const auto& serverGroup : groups) { - if (cl->serverGroupAssigned(serverGroup)) { - if(cl->update_cached_permissions()) /* update cached calculated permissions */ - cl->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if (checkTp) - cl->updateChannelClientProperties(true, true); - cl->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - break; - } - } - }); - }).detach(); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandSetClientChannelGroup(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto serverGroup = this->server->groups->findGroup(cmd["cgid"].as()); - if (!serverGroup && cmd["cgid"].as() == 0) - serverGroup = this->server->groups->defaultGroup(GroupTarget::GROUPTARGET_CHANNEL); - - if (!serverGroup || serverGroup->target() != GROUPTARGET_CHANNEL) - return command_result{error::group_invalid_id}; - - shared_lock server_channel_lock(this->server->channel_tree_lock); /* ensure we dont get moved or somebody could move us */ - auto channel_id = cmd["cid"].as(); - auto channel = this->server->channelTree->findChannel(channel_id); - if (!channel) return command_result{error::channel_invalid_id}; - - auto target_cldbid = cmd["cldbid"].as(); - { - auto channel_group_member_add_power = this->calculate_permission(permission::i_channel_group_member_add_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_member_add_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) - return command_result{permission::i_channel_group_member_add_power}; - - auto channel_group_self_add_power = this->calculate_permission(permission::i_channel_group_self_add_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_add_power, channel_group_self_add_power, true)) - return command_result{permission::i_channel_group_self_add_power}; - } - - - auto client_permission_modify_power = this->calculate_permission(permission::i_client_permission_modify_power, channel_id); - auto client_needed_permission_modify_power = this->server->calculatePermission2( - permission::i_client_needed_permission_modify_power, target_cldbid, ClientType::CLIENT_TEAMSPEAK, channel_id); - - - if(client_needed_permission_modify_power.has_value) { - if(!this->permission_granted(client_permission_modify_power, client_needed_permission_modify_power.value, true)) - return command_result{permission::i_client_permission_modify_power}; - } - } - - { - auto old_group = this->server->groups->getChannelGroupExact(target_cldbid, channel, false); - if(old_group) { - auto channel_group_member_remove_power = this->calculate_permission(permission::i_channel_group_member_remove_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_member_remove_power, true)) { - if(target_cldbid != this->getClientDatabaseId()) - return command_result{permission::i_channel_group_member_remove_power}; - - auto channel_group_self_remove_power = this->calculate_permission(permission::i_channel_group_self_remove_power, channel_id); - if(!serverGroup->permission_granted(permission::i_channel_group_needed_member_remove_power, channel_group_self_remove_power, true)) - return command_result{permission::i_channel_group_self_remove_power}; - } - } - } - - this->server->groups->setChannelGroup(target_cldbid, serverGroup, channel); - for (const auto &targetClient : this->server->findClientsByCldbId(target_cldbid)) { - unique_lock client_channel_lock_w(targetClient->channel_lock); - auto updates = this->server->groups->update_server_group_property(targetClient, false, targetClient->getChannel()); /* needs a write lock */ - client_channel_lock_w.unlock(); - shared_lock client_channel_lock_r(targetClient->channel_lock); - auto result = this->server->notifyClientPropertyUpdates(targetClient, updates); - if (result) { - if(targetClient->update_cached_permissions()) /* update cached calculated permissions */ - targetClient->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - - if(targetClient->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID] == channel->channelId()) { //Only if group assigned over the channel - for (const auto &viewer : this->server->getClients()) { - /* if in view will be tested within that method */ - shared_lock viewer_channel_lock(viewer->channel_lock, defer_lock); - if(viewer != targetClient) - viewer_channel_lock.lock(); - viewer->notifyClientChannelGroupChanged(_this.lock(), targetClient, targetClient->getChannel(), channel, serverGroup, false); - } - } - } - targetClient->updateChannelClientProperties(false, true); - } - - return command_result{error::ok}; -} - -//sendtextmessage targetmode=1 <1 = direct | 2 = channel | 3 = server> msg=asd target=1 -command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto timestamp = system_clock::now(); - if (cmd["targetmode"].as() == ChatMessageMode::TEXTMODE_PRIVATE) { - auto target = this->server->findClient(cmd["target"].as()); - if (!target) return command_result{error::client_invalid_id}; - - bool chat_open = false; - { - - shared_lock channel_lock(this->channel_lock); - this->openChats.erase(remove_if(this->openChats.begin(), this->openChats.end(), [](const weak_ptr& weak) { return !weak.lock(); }), this->openChats.end()); - for(const auto& entry : this->openChats) { - if(entry.lock() == target) { - chat_open = true; - break; - } - } - } - - if(!chat_open) { - if (target == this) { - PERM_CHECK_CHANNELR(permission::b_client_even_textmessage_send, 1, this->currentChannel, true); - } - - PERM_CHECK_CHANNELR(permission::i_client_private_textmessage_power, target->permissionValue(permission::i_client_needed_private_textmessage_power, target->currentChannel), this->currentChannel, false); - - - { - unique_lock channel_lock(target->channel_lock); - target->openChats.push_back(_this); - } - - { - unique_lock channel_lock(this->channel_lock); - this->openChats.push_back(target); - } - } - - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, cmd["msg"], target)) return command_result{error::ok}; - target->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, timestamp, cmd["msg"].string()); - this->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), target->getClientId(), 0, timestamp, cmd["msg"].string()); - } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_CHANNEL) { - if(!cmd[0].has("cid")) - cmd["cid"] = 0; - RESOLVE_CHANNEL_R(cmd["cid"], false); - auto channel = l_channel ? dynamic_pointer_cast(l_channel->entry) : nullptr; - if(!channel) { - CMD_REQ_CHANNEL; - channel = this->currentChannel; - channel_id = this->currentChannel->channelId(); - } - if(channel == this->currentChannel) { - channel_tree_read_lock.unlock(); //Method may creates a music bot which modifies the channel tree - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, cmd["msg"], nullptr)) - return command_result{error::ok}; - channel_tree_read_lock.lock(); - } - - if(!this->permissionGranted(permission::b_client_channel_textmessage_send, 1, channel, false)) - return command_result{permission::b_client_channel_textmessage_send}; - - bool conversation_private = channel->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as(); - if(channel != this->currentChannel) { - if(conversation_private) - return command_result{error::conversation_is_private}; - - if(!this->calculate_and_get_join_state(channel)) - return command_result{permission::unknown}; /* You're not allowed to send messages :) */ - } - - auto message = cmd["msg"].string(); - auto _this = this->_this.lock(); - auto client_id = this->getClientId(); - - auto flag_password = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as(); - for(const auto& client : this->server->getClients()) { - if(client->connectionState() != ConnectionState::CONNECTED) - continue; - - auto type = client->getType(); - if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; - - auto own_channel = client->currentChannel == this->currentChannel; - if(conversation_private && !own_channel) - continue; - - if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) { - if(!own_channel && &*client != this) { - if(flag_password) - continue; /* TODO: Send notification about new message. The client then could request messages via message history */ - - if(!client->calculate_and_get_join_state(channel)) - continue; - } - client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this, client_id, channel_id, timestamp, message); - } - } - - if(!conversation_private) { - auto conversations = this->server->conversation_manager(); - auto conversation = conversations->get_or_create(channel->channelId()); - conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); - } - } else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) { - CACHED_PERM_CHECK(permission::b_client_server_textmessage_send, 1); - - if(this->handleTextMessage(ChatMessageMode::TEXTMODE_SERVER, cmd["msg"], nullptr)) return command_result{error::ok}; - for(const auto& client : this->server->getClients()) { - if (client->connectionState() != ConnectionState::CONNECTED) - continue; - - auto type = client->getType(); - if (type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; - - client->notifyTextMessage(ChatMessageMode::TEXTMODE_SERVER, _this.lock(), this->getClientId(), 0, timestamp, cmd["msg"].string()); - } - - { - auto conversations = this->server->conversation_manager(); - auto conversation = conversations->get_or_create(0); - conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string()); - } - } else return command_result{error::parameter_invalid, "invalid target mode"}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientChatComposing(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(0); - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - - client->notifyClientChatComposing(_this.lock()); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientChatClosed(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - { - unique_lock channel_lock(this->channel_lock); - this->openChats.erase(remove_if(this->openChats.begin(), this->openChats.end(), [client](const weak_ptr& weak) { - return weak.lock() == client; - }), this->openChats.end()); - } - { - unique_lock channel_lock(client->channel_lock); - client->openChats.erase(remove_if(client->openChats.begin(), client->openChats.end(), [&](const weak_ptr& weak) { - return weak.lock().get() == this; - }), client->openChats.end()); - } - client->notifyClientChatClosed(_this.lock()); - return command_result{error::ok}; -} - -//ftgetfilelist cid=1 cpw path=\/ return_code=1:x -//Answer: -//1 .. n -// notifyfilelist cid=1 path=\/ return_code=1:x name=testFile size=35256 datetime=1509459767 type=1|name=testDir size=0 datetime=1509459741 type=0|name=testDir_2 size=0 datetime=1509459763 type=0 -//notifyfilelistfinished cid=1 path=\/ -inline void cmd_filelist_append_files(ServerId sid, Command &command, vector> files) { - int index = 0; - - logTrace(sid, "Sending file list for path {}", command["path"].string()); - for (const auto& fileEntry : files) { - logTrace(sid, " - {} ({})", fileEntry->name, fileEntry->type == file::FileType::FILE ? "file" : "directory"); - - command[index]["name"] = fileEntry->name; - command[index]["datetime"] = std::chrono::duration_cast(fileEntry->lastChanged.time_since_epoch()).count(); - command[index]["type"] = fileEntry->type; - if (fileEntry->type == file::FileType::FILE) - command[index]["size"] = static_pointer_cast(fileEntry)->fileSize; - else - command[index]["size"] = 0; - index++; - } -} - -#define CMD_REQ_FSERVER if(!serverInstance->getFileServer()) return command_result{error::vs_critical, "file server not started yet!"} - -command_result ConnectedClient::handleCommandFTGetFileList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_REQ_FSERVER; - std::string code = cmd["return_code"].size() > 0 ? cmd["return_code"].string() : ""; - - Command fileList(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyfilelist" : ""); - Command fileListFinished("notifyfilelistfinished"); - - fileList["path"] = cmd["path"].as(); - if(!code.empty()) fileList["return_code"] = code; - fileListFinished["path"] = cmd["path"].as(); - fileList["cid"] = cmd["cid"].as(); - fileListFinished["cid"] = cmd["cid"].as(); - - if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); - cmd_filelist_append_files( - this->getServerId(), - fileList, - serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as())) - ); - } else { - if (cmd["path"].as() == "/icons" || cmd["path"].as() == "/icons/") { - cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->iconDirectory(this->server))); - PERM_CHECKR(permission::b_icon_manage, 1, true); - } else if (cmd["path"].as() == "/") { - cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->avatarDirectory(this->server))); - PERM_CHECKR(permission::b_icon_manage, 1, true); - } else { - logMessage(this->getServerId(), "{} Requested file list for unknown path/name: path: {} name: {}", cmd["path"].string(), cmd["name"].string()); - return command_result{error::not_implemented}; - } - } - - if (fileList[0].has("name")) { - if(dynamic_cast(this)) { - dynamic_cast(this)->sendCommand0(fileList, false, true); /* We need to process this directly else the order could get shuffled up! */ - this->sendCommand(fileListFinished); - } else { - this->sendCommand(fileList); - if(this->getType() != CLIENT_QUERY) - this->sendCommand(fileListFinished); - } - return command_result{error::ok}; - } else { - return command_result{error::database_empty_result}; - } -} - -//ftcreatedir cid=4 cpw dirname=\/TestDir return_code=1:17 -command_result ConnectedClient::handleCommandFTCreateDir(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CMD_REQ_FSERVER; - - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - CHANNEL_PERMISSION_TEST(permission::i_ft_directory_create_power, permission::i_ft_needed_directory_create_power, channel, true); - - auto dir = serverInstance->getFileServer()->createDirectory(cmd["dirname"], serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as())); - if (!dir) return command_result{error::file_invalid_path, "could not create dir"}; - - return command_result{error::ok}; -} - - -command_result ConnectedClient::handleCommandFTDeleteFile(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CMD_REQ_FSERVER; - - std::vector> files; - - if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_delete_power, permission::i_ft_needed_file_delete_power, channel, true); - for (int index = 0; index < cmd.bulkCount(); index++) - files.push_back(serverInstance->getFileServer()->findFile(cmd[index]["name"].as(), serverInstance->getFileServer()->resolveDirectory(this->server, channel))); - } else { - if (cmd["name"].string().find("/icon_") == 0 && cmd["path"].string().empty()) { - PERM_CHECKR(permission::b_icon_manage, 1, true); - files.push_back(serverInstance->getFileServer()->findFile(cmd["name"].string(), serverInstance->getFileServer()->iconDirectory(this->server))); - } else if (cmd["name"].string().find("/avatar_") == 0 && cmd["path"].string().empty()) { - if (cmd["name"].string() != "/avatar_") { - PERM_CHECKR(permission::b_client_avatar_delete_other, 1, true); - - auto uid = cmd["name"].string().substr(strlen("/avatar_")); - auto avId = hex::hex(base64::decode(uid), 'a', 'q'); - - auto cls = this->server->findClientsByUid(uid); - for (const auto &cl : cls) { - cl->properties()[property::CLIENT_FLAG_AVATAR] = ""; - this->server->notifyClientPropertyUpdates(cl, deque{property::CLIENT_FLAG_AVATAR}); - } - - cmd["name"] = "/avatar_" + avId; - } else { - cmd["name"] = "/avatar_" + this->getAvatarId(); - this->properties()[property::CLIENT_FLAG_AVATAR] = ""; - this->server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_FLAG_AVATAR}); - } - files.push_back(serverInstance->getFileServer()->findFile(cmd["name"].string(), serverInstance->getFileServer()->avatarDirectory(this->server))); - } else { - logError(this->getServerId(), "Unknown requested file to delete: {}", cmd["path"].as()); - return command_result{error::not_implemented}; - } - } - - for (const auto &file : files) { - if (!file) continue; - if (!serverInstance->getFileServer()->deleteFile(file)) { - logCritical(this->getServerId(), "Could not delete file {}/{}", file->path, file->name); - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandFTInitUpload(Command &cmd) { - CMD_REQ_SERVER; - CMD_REQ_FSERVER; - - std::shared_ptr directory = nullptr; - - if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - CHANNEL_PERMISSION_TEST(permission::i_ft_file_upload_power, permission::i_ft_needed_file_upload_power, channel, true); - directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel); - } else { - if (cmd["path"].as().empty() && cmd["name"].as().find("/icon_") == 0) { - auto max_size = this->permissionValue(permission::i_max_icon_filesize, this->currentChannel); - if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return command_result{permission::i_max_icon_filesize}; - directory = serverInstance->getFileServer()->iconDirectory(this->server); - } else if (cmd["path"].as().empty() && cmd["name"].string() == "/avatar") { - auto max_size = this->permissionValue(permission::i_client_max_avatar_filesize, this->currentChannel); - if(max_size != -1 && max_size < (ssize_t) cmd["size"].as()) return command_result{permission::i_client_max_avatar_filesize}; - - directory = serverInstance->getFileServer()->avatarDirectory(this->server); - cmd["name"] = "/avatar_" + this->getAvatarId(); - } else { - cerr << "Unknown requested directory: " << cmd["path"].as() << endl; - return command_result{error::not_implemented}; - } - } - - if (!directory || directory->type != file::FileType::DIRECTORY) { //Should not happen - cerr << "Invalid upload file path!" << endl; - return command_result{error::file_invalid_path, "could not resolve directory"}; - } - - { - auto server_quota = this->server->properties()[property::VIRTUALSERVER_UPLOAD_QUOTA].as(); - auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as(); - server_used_quota += cmd["size"].as(); - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) - server_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) - server_used_quota += trans->remaining_bytes(); - if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return command_result{error::file_transfer_server_quota_exceeded}; - - auto client_quota = this->permissionValue(permission::i_ft_quota_mb_upload_per_client, this->currentChannel); - auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED].as(); - client_used_quota += cmd["size"].as(); - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) - client_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) - client_used_quota += trans->remaining_bytes(); - - if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return command_result{error::file_transfer_client_quota_exceeded}; - } - - if (cmd["overwrite"].as() && cmd["resume"].as()) return command_result{error::file_overwrite_excludes_resume}; - if (serverInstance->getFileServer()->findFile(cmd["name"].as(), directory) && !(cmd["overwrite"].as() || cmd["resume"].as())) - return command_result{error::file_already_exists, "file already exists"}; - - //Request: clientftfid=1 serverftfid=6 ftkey=itRNdsIOvcBiBg\/Xj4Ge51ZSrsShHuid port=30033 seekpos=0 - //Reqpose: notifystartupload clientftfid=4079 serverftfid=1 ftkey=aX9CFQbfaddHpOYxhQiSLu\/BumfVtPyP port=30033 seekpos=0 proto=1 - string error = "success"; - auto key = serverInstance->getFileServer()->generateUploadTransferKey(error, directory->path + "/" + directory->name + cmd["name"].string(), cmd["size"].as(), 0, _this.lock()); //TODO implement resume! - if (!key) return command_result{error::vs_critical}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifystartupload" : ""); - result["clientftfid"] = cmd["clientftfid"].as(); - result["ftkey"] = key->key; - - auto bindings = serverInstance->getFileServer()->list_bindings(); - if(!bindings.empty()) { - result["port"] = net::port(bindings[0]->address); - string ip = ""; - for(auto& entry : bindings) { - if(net::is_anybind(entry->address)) { - ip = ""; - break; - } - ip += net::to_string(entry->address, false) + ","; - } - if(!ip.empty()) - result["ip"] = ip; - } else { - return command_result{error::server_is_not_running, "file server is not bound to any address"}; - } - result["seekpos"] = 0; - result["proto"] = 1; - result["serverftfid"] = key->key_id; //TODO generate! - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandFTInitDownload(Command &cmd) { - CMD_REQ_SERVER; - CMD_REQ_FSERVER; - - std::shared_ptr directory = nullptr; - - if(!cmd[0].has("path")) cmd["path"] = ""; - - if (cmd[0].has("cid") && cmd["cid"] != (ChannelId) 0) { //Channel - auto channel = this->server->channelTree->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - - CHANNEL_PERMISSION_TEST(permission::i_ft_file_download_power, permission::i_ft_needed_file_download_power, channel, true); - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - - directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel); - } else { - if (cmd["path"].as().empty() && cmd["name"].as().find("/icon_") == 0) { - directory = serverInstance->getFileServer()->iconDirectory(this->server); - } else if (cmd["path"].as().empty() && cmd["name"].as().find("/avatar_") == 0) { //TODO fix avatar download not working - directory = serverInstance->getFileServer()->avatarDirectory(this->server); - } else { - cerr << "Unknown requested directory: " << cmd["path"].as() << endl; - return command_result{error::not_implemented}; - } - } - - if (!directory || directory->type != file::FileType::DIRECTORY) { //Should not happen - cerr << "Invalid download file path!" << endl; - return command_result{error::file_invalid_path, "could not resolve directory"}; - } - - if (!serverInstance->getFileServer()->findFile(cmd["name"].as(), directory)) - return command_result{error::file_not_found, "file not exists"}; - - string error = "success"; - auto key = serverInstance->getFileServer()->generateDownloadTransferKey(error, directory->path + "/" + directory->name + cmd["name"].as(), 0, _this.lock()); //TODO implement resume! - if (!key) return command_result{error::vs_critical, "cant generate key (" + error + ")"}; - - { - auto server_quota = this->server->properties()[property::VIRTUALSERVER_DOWNLOAD_QUOTA].as(); - auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as(); - server_used_quota += key->size; - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers()) - server_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers()) - server_used_quota += trans->remaining_bytes(); - if(server_quota >= 0 && server_quota * 1024 * 1024 < (int64_t) server_used_quota) return command_result{error::file_transfer_server_quota_exceeded}; - - - auto client_quota = this->permissionValue(permission::i_ft_quota_mb_download_per_client, this->currentChannel); - auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - client_used_quota += key->size; - for(const auto& trans : serverInstance->getFileServer()->pending_file_transfers(_this.lock())) - client_used_quota += trans->size; - for(const auto& trans : serverInstance->getFileServer()->running_file_transfers(_this.lock())) - client_used_quota += trans->remaining_bytes(); - - if(client_quota >= 0 && client_quota * 1024 * 1024 < (int64_t) client_used_quota) return command_result{error::file_transfer_client_quota_exceeded}; - } - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifystartdownload" : ""); - result["clientftfid"] = cmd["clientftfid"].as(); - result["proto"] = 1; - result["serverftfid"] = key->key_id; - result["ftkey"] = key->key; - - auto bindings = serverInstance->getFileServer()->list_bindings(); - if(!bindings.empty()) { - result["port"] = net::port(bindings[0]->address); - string ip = ""; - for(auto& entry : bindings) { - if(net::is_anybind(entry->address)) { - ip = ""; - break; - } - ip += net::to_string(entry->address, false) + ","; - } - if(!ip.empty()) - result["ip"] = ip; - } else { - return command_result{error::server_is_not_running, "file server is not bound to any address"}; - } - - result["size"] = key->size; - this->sendCommand(result); - - return command_result{error::ok}; -} - -/* - * Usage: ftgetfileinfo cid={channelID} cpw={channelPassword} name={filePath}... - -Permissions: - i_ft_file_browse_power - i_ft_needed_file_browse_power - -Description: - Displays detailed information about one or more specified files stored in a - channels file repository. - -Example: - ftgetfileinfo cid=2 cpw= path=\/Pic1.PNG|cid=2 cpw= path=\/Pic2.PNG - cid=2 path=\/ name=Stuff size=0 datetime=1259415210 type=0|name=Pic1.PNG size=563783 datetime=1259425462 type=1|name=Pic2.PNG ... - error id=0 msg=ok - - */ - -command_result ConnectedClient::handleCommandFTGetFileInfo(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_REQ_FSERVER; - CMD_RESET_IDLE; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyfileinfo" : ""); - - int result_index = 0; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto& request = cmd[index]; - std::shared_ptr file; - if (request.has("cid") && request["cid"].as() != 0) { //Channel - auto channel = this->server->channelTree->findChannel(request["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - if (!channel->passwordMatch(cmd["cpw"]) && !this->permissionGranted(permission::b_ft_ignore_password, 1, channel, true)) - return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password}; - - CHANNEL_PERMISSION_TEST(permission::i_ft_file_browse_power, permission::i_ft_needed_file_browse_power, channel, true); - file = serverInstance->getFileServer()->findFile(request["name"],serverInstance->getFileServer()->resolveDirectory(this->server, channel, request["path"])); - } else { - std::shared_ptr directory; - if (!request.has("path") || request["path"].as() == "/") { - directory = serverInstance->getFileServer()->avatarDirectory(this->server); - } else if (request["path"].as() == "/icons" || request["path"].as() == "/icons/") { - directory = serverInstance->getFileServer()->iconDirectory(this->server); - } else { - cerr << "Unknown requested directory: '" << request["path"].as() << "'" << endl; - return command_result{error::not_implemented}; - } - - if(!directory) continue; - file = serverInstance->getFileServer()->findFile(cmd["name"].as(), directory); - } - if(!file) continue; - - result[result_index]["cid"] = request["cid"].as(); - result[result_index]["name"] = request["name"]; - result[result_index]["path"] = request["path"]; - result[result_index]["type"] = file->type; - result[result_index]["datetime"] = duration_cast(file->lastChanged.time_since_epoch()).count(); - if (file->type == file::FileType::FILE) - result[result_index]["size"] = static_pointer_cast(file)->fileSize; - else - result[result_index]["size"] = 0; - result_index++; - } - - if(result_index == 0) - return command_result{error::database_empty_result}; - this->sendCommand(result); - - return command_result{error::ok}; -} -//notifybanlist banid=3 ip name uid=zbex8X3bFRTIKLI7mzeyJGZsh64= lastnickname=Wolf\sC++\sXXXX created=1510357269 duration=3600 invokername=WolverinDEV invokercldbid=5 invokeruid=xxjnc14LmvTk+Lyrm8OOeo4tOqw= reason=Prefix\sFake\s\p\sName enforcements=3 -command_result ConnectedClient::handleCommandBanList(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; - - if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_list_global), 1)) - return command_result{permission::b_client_ban_list_global}; - } else { - auto server = serverInstance->getVoiceServerManager()->findServerById(sid); - if (!server) return command_result{error::parameter_invalid}; - - if (server->calculatePermission(this->getClientDatabaseId(), permission::b_client_ban_list, this->getType(), nullptr) != 1) - return command_result{permission::b_client_ban_list}; - } - - //When empty: return command_result{error::database_empty_result}; - auto banList = serverInstance->banManager()->listBans(sid); - if (banList.empty()) return command_result{error::database_empty_result}; - - auto allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifybanlist" : ""); - int index = 0; - for (const auto &elm : banList) { - notify[index]["sid"] = elm->serverId; - notify[index]["banid"] = elm->banId; - if(allow_ip) - notify[index]["ip"] = elm->ip; - else - notify[index]["ip"] = "hidden"; - notify[index]["name"] = elm->name; - notify[index]["uid"] = elm->uid; - notify[index]["hwid"] = elm->hwid; - notify[index]["lastnickname"] = elm->name; //Maybe update? - - notify[index]["created"] = chrono::duration_cast(elm->created.time_since_epoch()).count(); - if (elm->until.time_since_epoch().count() != 0) - notify[index]["duration"] = chrono::duration_cast(elm->until - elm->created).count(); - else - notify[index]["duration"] = 0; - - notify[index]["reason"] = elm->reason; - notify[index]["enforcements"] = elm->triggered; - - notify[index]["invokername"] = elm->invokerName; - notify[index]["invokercldbid"] = elm->invokerDbId; - notify[index]["invokeruid"] = elm->invokerUid; - index++; - } - this->sendCommand(notify); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanAdd(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - string ip = cmd[0].has("ip") ? cmd["ip"].string() : ""; - string name = cmd[0].has("name") ? cmd["name"].string() : ""; - string uid = cmd[0].has("uid") ? cmd["uid"].string() : ""; - string hwid = cmd[0].has("hwid") ? cmd["hwid"].string() : ""; - string banreason = cmd[0].has("banreason") ? cmd["banreason"].string() : "No reason set"; - auto time = cmd[0].has("time") ? cmd["time"].as() : 0L; - - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; - - if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_create_global), 1)) - return command_result{permission::b_client_ban_create_global}; - } else { - auto server = serverInstance->getVoiceServerManager()->findServerById(sid); - if (!server) return command_result{error::parameter_invalid}; - if (server->calculatePermission(this->getClientDatabaseId(), permission::b_client_ban_create, this->getType(), nullptr) != 1) - return command_result{permission::b_client_ban_create_global}; - } - - auto max_ban_time = this->cached_permission_value(permission::i_client_ban_max_bantime); - if (max_ban_time != permNotGranted) { - if (max_ban_time != -1 && max_ban_time < time) - return command_result{permission::i_client_ban_max_bantime}; - } - - chrono::time_point until = time > 0 ? chrono::system_clock::now() + chrono::seconds(time) : chrono::time_point(); - - auto existing = serverInstance->banManager()->findBanExact(sid, banreason, uid, ip, name, hwid); - bool banned = false; - if(existing) { - if(existing->invokerDbId == this->getClientDatabaseId()) { - if(existing->until == until) return command_result{error::database_duplicate_entry}; - else { - existing->until = until; - serverInstance->banManager()->updateBan(existing); - banned = true; - } - } else if(!banned) { - serverInstance->banManager()->unban(existing); - } - } - if(!banned) serverInstance->banManager()->registerBan(sid, this->getClientDatabaseId(), banreason, uid, ip, name, hwid, until); - - for(auto server : (this->server ? std::deque>{this->server} : serverInstance->getVoiceServerManager()->serverInstances())) - server->testBanStateChange(_this.lock()); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanEdit(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; - - auto ban = serverInstance->banManager()->findBanById(sid, cmd["banid"].as()); - if (!ban) return command_result{error::database_empty_result}; - - if (sid == 0) { - if(!this->permission_granted(this->cached_permission_value(permission::b_client_ban_edit_global), 1)) - return command_result{permission::b_client_ban_edit_global}; - } else { - auto server = serverInstance->getVoiceServerManager()->findServerById(sid); - if (!server) return command_result{error::parameter_invalid}; - - if (server->calculatePermission(this->getClientDatabaseId(), permission::b_client_ban_edit, this->getType(), nullptr) != 1) return command_result{permission::b_client_ban_edit}; - } - - /* ip name uid reason time hwid */ - bool changed = false; - if (cmd[0].has("ip")) { - ban->ip = cmd["ip"].as(); - changed |= true; - } - - if (cmd[0].has("name")) { - ban->name = cmd["name"].as(); - changed |= true; - } - - if (cmd[0].has("uid")) { - ban->uid = cmd["uid"].as(); - changed |= true; - } - - if (cmd[0].has("reason")) { - ban->reason = cmd["reason"].as(); - changed |= true; - } - if (cmd[0].has("banreason")) { - ban->reason = cmd["banreason"].as(); - changed |= true; - } - - if (cmd[0].has("time")) { - ban->until = ban->created + seconds(cmd["time"].as()); - changed |= true; - } - - if (cmd[0].has("hwid")) { - ban->hwid = cmd["hwid"].as(); - changed |= true; - } - - if (changed) - serverInstance->banManager()->updateBan(ban); - else return command_result{error::parameter_invalid}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanClient(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - PERM_CHECKR(permission::i_client_ban_power, 1, true); //Require a little ban power - - string uid; - string reason = cmd[0].has("banreason") ? cmd["banreason"].string() : ""; - auto time = cmd[0].has("time") ? cmd["time"].as() : 0UL; - chrono::time_point until = time > 0 ? chrono::system_clock::now() + chrono::seconds(time) : chrono::time_point(); - - const auto no_nickname = cmd.hasParm("no-nickname"); - const auto no_hwid = cmd.hasParm("no-hardware-id"); - const auto no_ip = cmd.hasParm("no-ip"); - - deque> target_clients; - if (cmd[0].has("uid")) { - target_clients = this->server->findClientsByUid(uid = cmd["uid"].string()); - for(const auto& client : target_clients) - if(client->getType() == ClientType::CLIENT_MUSIC) - return command_result{error::client_invalid_id, "You cant ban a music bot!"}; - } else { - target_clients = {this->server->findClient(cmd["clid"].as())}; - if(!target_clients[0]) { - return command_result{error::client_invalid_id, "Could not find target client"}; - } - if(target_clients[0]->getType() == ClientType::CLIENT_MUSIC) { - return command_result{error::client_invalid_id, "You cant ban a music bot!"}; - } - uid = target_clients[0]->getUid(); - } - - ClientDbId target_dbid = 0; - if (!target_clients.empty()) target_dbid = target_clients[0]->getClientDatabaseId(); - else { - auto info = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->getServer(), {uid}); - if (!info.empty()) - target_dbid = info[0]->cldbid; - else - return command_result{error::client_unknown}; - } - if (target_dbid != 0) { - if (this->server->calculatePermission(target_dbid, permission::i_client_needed_ban_power, ClientType::CLIENT_TEAMSPEAK, nullptr) > this->permissionValue(permission::i_client_ban_power)) - return command_result{permission::i_client_ban_power}; - if (this->server->calculatePermission(target_dbid, permission::b_client_ignore_bans, ClientType::CLIENT_TEAMSPEAK, nullptr) >= 1) return command_result{permission::b_client_ignore_bans}; - } - deque ban_ids; - auto _id = serverInstance->banManager()->registerBan(this->getServer()->getServerId(), this->getClientDatabaseId(), reason, uid, "", "", "", until); - ban_ids.push_back(_id); - - auto b_ban_name = this->cached_permission_value(permission::b_client_ban_name) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_name), 1, false); - auto b_ban_ip = this->cached_permission_value(permission::b_client_ban_ip) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_ip), 1, false); - auto b_ban_hwid = this->cached_permission_value(permission::b_client_ban_hwid) == permNotGranted || this->permission_granted(this->cached_permission_value(permission::b_client_ban_hwid), 1, false); - - auto max_value = this->cached_permission_value(permission::i_client_ban_max_bantime); - if(max_value != permNotGranted && max_value != -1) { - if(time > max_value) return command_result{permission::i_client_ban_max_bantime}; - if(time == 0) return command_result{permission::i_client_ban_max_bantime}; - } - - for (const auto &client : target_clients) { - if (client->getType() != CLIENT_TEAMSPEAK && client->getType() != CLIENT_QUERY) continue; //Remember if you add new type you have to change stuff here - - this->server->notify_client_ban(client, this->ref(), reason, time); - client->closeConnection(system_clock::now() + seconds(2)); - - string entry_name, entry_ip, entry_hardware_id; - if(b_ban_name && !no_nickname) { - entry_name = client->getDisplayName(); - } - if(b_ban_ip && !no_ip && !config::server::disable_ip_saving) { - entry_ip = client->getPeerIp(); - } - if(b_ban_hwid && !no_hwid) { - entry_hardware_id = client->getHardwareId(); - } - auto exact = serverInstance->banManager()->findBanExact(this->getServer()->getServerId(), reason, "", entry_ip, entry_name, entry_hardware_id); - if(exact) { - exact->until = until; - exact->invokerDbId = this->getClientDatabaseId(); - serverInstance->banManager()->updateBan(exact); - ban_ids.push_back(exact->banId); - } else { - auto id = serverInstance->banManager()->registerBan(this->getServer()->getServerId(), this->getClientDatabaseId(), reason, "", entry_ip, entry_name, entry_hardware_id, until); - ban_ids.push_back(id); - } - } - this->server->testBanStateChange(_this.lock()); - - if (this->getType() == CLIENT_QUERY) { - Command notify(""); - int index = 0; - for(const auto& ban_id : ban_ids) - notify[index++]["banid"] = ban_id; - this->sendCommand(notify); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanDel(Command &cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - ServerId sid = this->getServerId(); - if (cmd[0].has("sid")) - sid = cmd["sid"]; - - auto ban = serverInstance->banManager()->findBanById(sid, cmd["banid"].as()); - if (!ban) return command_result{error::database_empty_result}; - - if (sid == 0) { - const auto permission = ban->invokerDbId == this->getClientDatabaseId() ? permission::b_client_ban_delete_own_global : permission::b_client_ban_delete_global; - if(!this->permission_granted(this->cached_permission_value(permission), 1)) - return command_result{permission}; - } else { - auto server = serverInstance->getVoiceServerManager()->findServerById(sid); - if (!server) return command_result{error::parameter_invalid}; - - auto perm = ban->invokerDbId == this->getClientDatabaseId() ? permission::b_client_ban_delete_own : permission::b_client_ban_delete; - if (server->calculatePermission(this->getClientDatabaseId(), perm, this->getType(), nullptr) != 1) return command_result{perm}; - } - serverInstance->banManager()->unban(ban); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanDelAll(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_client_ban_delete, 1, true); - - serverInstance->banManager()->deleteAllBans(server->getServerId()); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandBanTriggerList(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_client_ban_trigger_list, 1, true); - - CMD_REQ_PARM("banid"); - - auto record = serverInstance->banManager()->findBanById(this->getServerId(), cmd["banid"]); - if(!record) return command_result{error::parameter_invalid, "Invalid ban id"}; - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifybantriggerlist" : ""); - notify["banid"] = record->banId; - notify["serverid"] = record->serverId; - - auto allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - int index = 0; - for (auto &entry : serverInstance->banManager()->trigger_list(record, this->getServerId(), cmd[0].has("offset") ? cmd["offset"].as() : 0, cmd[0].has("limit") ? cmd["limit"].as() : -1)) { - notify[index]["client_unique_identifier"] = entry->unique_id; - notify[index]["client_hardware_identifier"] = entry->hardware_id; - notify[index]["client_nickname"] = entry->name; - if(allow_ip) - notify[index]["connection_client_ip"] = entry->ip; - else - notify[index]["connection_client_ip"] = "hidden"; - notify[index]["timestamp"] = duration_cast(entry->timestamp.time_since_epoch()).count(); - index++; - } - if (index == 0) return command_result{error::database_empty_result}; - - this->sendCommand(notify); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandTokenList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_list, 1, true); - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifytokenlist" : ""); - int index = 0; - for (auto &token : this->server->tokenManager->avariableTokes()) { - notify[index]["token"] = token->token; - notify[index]["token_type"] = token->type; - notify[index]["token_id1"] = token->groupId; - notify[index]["token_id2"] = token->channelId; - notify[index]["token_created"] = chrono::duration_cast(token->created.time_since_epoch()).count(); - notify[index]["token_description"] = token->description; - index++; - } - if (index == 0) return command_result{error::database_empty_result}; - - this->sendCommand(notify); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandTokenAdd(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_add, 1, true); - - TokenType ttype = static_cast(cmd["tokentype"].as()); - auto gId = cmd["tokenid1"].as(); - auto cId = cmd["tokenid2"].as(); - string description = cmd["tokendescription"].string(); - - { - auto group = this->server->groups->findGroup(gId); - if(!group) return command_result{error::group_invalid_id}; - if(group->type() == GroupType::GROUP_TYPE_TEMPLATE) return command_result{error::parameter_invalid, "invalid server group type"}; - - if(group->target() == GroupTarget::GROUPTARGET_SERVER) - GROUP_PERMISSION_TEST(permission::i_server_group_member_add_power, permission::i_server_group_needed_member_add_power, group, true); - else - GROUP_PERMISSION_TEST(permission::i_channel_group_member_add_power, permission::i_channel_group_needed_member_add_power, group, true); - } - if (ttype == TokenType::TOKEN_CHANNEL) { - if (!this->server->channelTree->findChannel(cId)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - } else if (ttype == TokenType::TOKEN_SERVER); - else return command_result{error::parameter_invalid, "invalid token target type"}; - - - auto result = this->server->tokenManager->createToken(ttype, gId, description, cId, cmd["token"]); - if (!result) return command_result{error::vs_critical}; - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifytokenadd" : ""); - notify["token"] = result->token; - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandTokenUse(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_use, 1, true); - - auto strToken = cmd["token"].string(); - auto token = this->server->tokenManager->findToken(strToken); - if (!token) return command_result{error::token_invalid_id, "Invalid token. (Token not registered)"}; - this->server->tokenManager->deleteToke(token->token); - - auto serverGroup = this->server->groups->findGroup(token->groupId); - if (!serverGroup) return command_result{error::token_invalid_id, "Token invalid groupId"}; - this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; //TODO test if its the default token - - std::shared_ptr channel = nullptr; - if (token->channelId != 0) { - channel = this->server->channelTree->findChannel(token->channelId); - if (!channel) return command_result{error::token_invalid_id, "Token invalid channelId"}; - - this->server->groups->setChannelGroup(this->getClientDatabaseId(), serverGroup, channel); - } else { - if(!this->server->groups->hasServerGroupAssigned(this->getClientDatabaseId(), serverGroup)) - this->server->groups->addServerGroup(this->getClientDatabaseId(), serverGroup); - else { - return command_result{error::ok}; - } - } - - if (this->server->notifyClientPropertyUpdates(_this.lock(), this->server->groups->update_server_group_property(_this.lock(), true, this->getChannel()))) { - if(this->update_cached_permissions()) /* update cached calculated permissions */ - this->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - - { - for (auto &viewer : this->server->getClients()) { - if(viewer->isClientVisible(_this.lock(), true)) - viewer->notifyServerGroupClientAdd(this->server->serverRoot, _this.lock(), serverGroup); - } - } - this->notifyServerGroupClientAdd(this->server->serverRoot, _this.lock(), serverGroup); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandTokenDelete(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - CACHED_PERM_CHECK(permission::b_virtualserver_token_delete, 1, true); - - auto strToken = cmd["token"].string(); - auto token = this->server->tokenManager->findToken(strToken); - if (!token) return command_result{error::token_invalid_id, "Invalid token. (Token not registered)"}; - this->server->tokenManager->deleteToke(token->token); - if(token->token == this->server->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY].as()) { - this->server->properties()[property::VIRTUALSERVER_AUTOGENERATED_PRIVILEGEKEY] = ""; - this->server->properties()[property::VIRTUALSERVER_ASK_FOR_PRIVILEGEKEY] = false; - logMessage(this->getServerId(), "{} Deleting the default server token. Don't ask anymore for this a token!", CLIENT_STR_LOG_PREFIX); - } - return command_result{error::ok}; -} - -//start=0 duration=10 -//pattern=%asd% - -struct ClientDbArgs { - shared_ptr server; - int index = 0; - int offset = 0; - int resultIndex = 0; - bool showIp = false; - bool largeInfo = false; - Command *result = nullptr; -}; - -command_result ConnectedClient::handleCommandClientDbList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_virtualserver_client_dblist, 1, true); - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdblist" : ""); - - if (!cmd[0].has("start")) - cmd["start"] = 0; - if (!cmd[0].has("duration")) - cmd["duration"] = 20; - if (cmd[0]["duration"].as() > 2000) cmd["duration"] = 2000; - if (cmd[0]["duration"].as() < 1) cmd["duration"] = 1; - - auto maxIndex = cmd["start"].as() + cmd["duration"].as(); - ClientDbArgs args; - args.server = this->server; - args.offset = cmd["start"].as(); - args.result = ¬ify; - args.resultIndex = 0; - args.index = 0; - args.showIp = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - args.largeInfo = cmd.hasParm("details"); - - (LOG_SQL_CMD)(sql::command(this->server->getSql(), "SELECT * FROM `clients` WHERE `serverId` = :sid ORDER BY `cldbid` ASC" + (maxIndex > 0 ? " LIMIT " + to_string(maxIndex) : ""), variable{":sid", this->server->getServerId()}).query( - [](ClientDbArgs *pArgs, int length, char **values, char **column) { - pArgs->index++; - if (pArgs->offset < pArgs->index) { - ClientDbId id = 0; - string uid, name, ip; - string created = "0", lastConnected = "0", connections = "0"; - for (int index = 0; index < length; index++) { - string key = column[index]; - if (key == "cldbid") - id = stoll(values[index]); - else if (key == "clientUid") - uid = values[index]; - else if (key == "firstConnect") - created = values[index]; - else if (key == "lastConnect") - lastConnected = values[index]; - else if (key == "connections") - connections = values[index]; - else if (key == "lastName") - name = values[index]; - } - - pArgs->result->operator[](pArgs->resultIndex)["cldbid"] = id; - pArgs->result->operator[](pArgs->resultIndex)["client_unique_identifier"] = uid; - pArgs->result->operator[](pArgs->resultIndex)["client_nickname"] = name; - pArgs->result->operator[](pArgs->resultIndex)["client_created"] = created; - pArgs->result->operator[](pArgs->resultIndex)["client_lastconnected"] = lastConnected; - pArgs->result->operator[](pArgs->resultIndex)["client_totalconnections"] = connections; - pArgs->result->operator[](pArgs->resultIndex)["client_description"] = ""; - - auto props = serverInstance->databaseHelper()->loadClientProperties(pArgs->server, id, ClientType::CLIENT_TEAMSPEAK); - if (props) { - pArgs->result->operator[](pArgs->resultIndex)["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as(); - pArgs->result->operator[](pArgs->resultIndex)["client_description"] = (*props)[property::CLIENT_DESCRIPTION].as(); - - if (pArgs->largeInfo) { - pArgs->result->operator[](pArgs->resultIndex)["client_badges"] = (*props)[property::CLIENT_BADGES].as(); - pArgs->result->operator[](pArgs->resultIndex)["client_version"] = (*props)[property::CLIENT_VERSION].as(); - pArgs->result->operator[](pArgs->resultIndex)["client_platform"] = (*props)[property::CLIENT_PLATFORM].as(); - pArgs->result->operator[](pArgs->resultIndex)["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as(); - } - } - if (!pArgs->showIp) - pArgs->result->operator[](pArgs->resultIndex)["client_lastip"] = "hidden"; - pArgs->resultIndex++; - } - return 0; - }, &args)); - - if (args.resultIndex == 0) return command_result{error::database_empty_result}; - if (cmd.hasParm("count")) { - size_t result = 0; - sql::command(this->server->getSql(), "SELECT COUNT(*) AS `count` FROM `clients` WHERE `serverId` = :sid", variable{":sid", this->server->getServerId()}).query([](size_t *ptr, int, char **v, char **) { - *ptr = static_cast(stoll(v[0])); - return 0; - }, &result); - notify[0]["count"] = result; - } - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientDBEdit(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_client_modify_dbproperties, 1, true); - - if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return command_result{error::database_empty_result, "invalid cldbid"}; - auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, cmd["cldbid"], ClientType::CLIENT_TEAMSPEAK); - - for (auto &elm : cmd[0].keys()) { - if (elm == "cldbid") continue; - - auto info = property::info(elm); - if(*info == property::CLIENT_UNDEFINED) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change someone's db entry, but the entry in unknown: " + elm); - continue; - } - if(!info->validate_input(cmd[elm].as())) { - logError(this->getServerId(), "Client " + this->getDisplayName() + " tried to change a property to an invalid value. (Value: '" + cmd[elm].as() + "', Property: '" + info->name + "')"); - continue; - } - (*props)[info] = cmd[elm].string(); - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPluginCmd(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto mode = cmd["targetmode"].as(); - - if (mode == PluginTargetMode::PLUGINCMD_CURRENT_CHANNEL) { - CMD_REQ_CHANNEL; - for (auto &cl : this->server->getClientsByChannel(this->currentChannel)) - cl->notifyPluginCmd(cmd["name"], cmd["data"], _this.lock()); - } else if (mode == PluginTargetMode::PLUGINCMD_SUBSCRIBED_CLIENTS) { - for (auto &cl : this->server->getClients()) - if (cl->isClientVisible(_this.lock(), true)) - cl->notifyPluginCmd(cmd["name"], cmd["data"], _this.lock()); - } else if (mode == PluginTargetMode::PLUGINCMD_SERVER) { - for (auto &cl : this->server->getClients()) - cl->notifyPluginCmd(cmd["name"], cmd["data"], _this.lock()); - } else if (mode == PluginTargetMode::PLUGINCMD_CLIENT) { - for (int index = 0; index < cmd.bulkCount(); index++) { - auto target = cmd[index]["target"].as(); - auto cl = this->server->findClient(target); - if (!cl) return command_result{error::client_invalid_id, "invalid target id"}; - cl->notifyPluginCmd(cmd["name"], cmd["data"], _this.lock()); - } - } - - /* - else if(mode == PluginTargetMode::PLUGINCMD_SEND_COMMAND) { - auto target = _this.lock(); - if(cmd[0].has("target")) - target = this->server->findClient(cmd["target"].as()); - if(!target) return command_result{error::client_invalid_id, "invalid target id"}; - - target->sendCommand(Command(cmd["command"].string()), cmd[0].has("low") ? cmd["low"] : false); - } - */ - - else return command_result{error::not_implemented}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientEdit(ts::Command &cmd) { - CMD_REQ_SERVER; - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client) return command_result{error::client_invalid_id}; - return this->handleCommandClientEdit(cmd, client); -} - -command_result ConnectedClient::handleCommandClientEdit(Command &cmd, const std::shared_ptr& client) { - assert(client); - auto self = client == this; - CMD_CHK_AND_INC_FLOOD_POINTS(self ? 15 : 25); - CMD_RESET_IDLE; - - - bool update_talk_rights = false; - unique_ptr> nickname_lock; - deque> keys; - for(const auto& key : cmd[0].keys()) { - if(key == "return_code") continue; - if(key == "clid") continue; - - const auto &info = property::info(key); - if(*info == property::CLIENT_UNDEFINED) { - logError(this->getServerId(), R"([{}] Tried to change a not existing client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } - - if((info->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), R"([{}] Tried to change a not user editable client property for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } - - if(!info->validate_input(cmd[key].as())) { - logError(this->getServerId(), R"([{}] Tried to change a client property to an invalid value for {}. (Key: "{}", Value: "{}"))", CLIENT_STR_LOG_PREFIX, CLIENT_STR_LOG_PREFIX_(client), key, cmd[key].string()); - continue; - } - if(client->properties()[info].as() == cmd[key].as()) continue; - - if (*info == property::CLIENT_DESCRIPTION) { - if (self) - PERM_CHECKR(permission::b_client_modify_own_description, 1, true); - else if(client->getType() == ClientType::CLIENT_MUSIC) { - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else { - PERM_CHECKR(permission::b_client_modify_description, 1, true); - } - - string value = cmd["client_description"].string(); - if (count_characters(value) > 200) return command_result{error::parameter_invalid, "Invalid description length. A maximum of 200 characters is allowed!"}; - } else if (*info == property::CLIENT_IS_TALKER) { - PERM_CHECK_CHANNELR(permission::b_client_set_flag_talker, 1, client->getChannel(), true); - cmd["client_is_talker"] = cmd["client_is_talker"].as(); - cmd["client_talk_request"] = 0; - update_talk_rights = true; - - keys.emplace_back(property::CLIENT_IS_TALKER, "client_is_talker"); - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); - continue; - } else if(*info == property::CLIENT_NICKNAME) { - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_rename_power, client->permissionValue(permission::i_client_music_needed_rename_power, client->currentChannel), true); - } - } - - string name = cmd["client_nickname"].string(); - if (count_characters(name) < 3) return command_result{error::parameter_invalid, "Invalid name length. A minimum of 3 characters is required!"}; - if (count_characters(name) > 30) return command_result{error::parameter_invalid, "Invalid name length. A maximum of 30 characters is allowed!"}; - - auto banIgnore = this->permissionGranted(permission::b_client_ignore_bans, 1, this->currentChannel); - if (!banIgnore) { - auto banRecord = serverInstance->banManager()->findBanByName(this->getServerId(), name); - if (banRecord) - return command_result{error::client_nickname_inuse, string() + "This nickname is " + (banRecord->serverId == 0 ? "globally " : "") + "banned for the reason: " + banRecord->reason}; - } - if (this->server) { - nickname_lock = std::make_unique>(this->server->client_nickname_lock); - bool self = false; - for (const auto &cl : this->server->getClients()) { - if (cl->getDisplayName() == cmd["client_nickname"].string()) { - if(cl == this) - self = true; - else - return command_result{error::client_nickname_inuse, "This nickname is already in use"}; - } - } - if(self) { - nickname_lock.reset(); - continue; - } - } - } else if(*info == property::CLIENT_PLAYER_VOLUME) { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - auto bot = dynamic_pointer_cast(client); - assert(bot); - - auto volume = cmd["player_volume"].as(); - auto max_volume = this->cached_permission_value(permission::i_client_music_create_modify_max_volume); - if(max_volume != permNotGranted && !this->permission_granted(max_volume, volume * 100)) return command_result{permission::i_client_music_create_modify_max_volume}; - - bot->volume_modifier(cmd["player_volume"]); - } else if(*info == property::CLIENT_IS_CHANNEL_COMMANDER) { - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } - - if(cmd["client_is_channel_commander"].as()) { - CACHED_PERM_CHECK(permission::b_client_use_channel_commander, 1); - } - } else if(*info == property::CLIENT_IS_PRIORITY_SPEAKER) { - //FIXME allow other to remove this thing - if(!self) { - if(client->getType() != ClientType::CLIENT_MUSIC) - return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } - - if(cmd["client_is_priority_speaker"].as()) { - PERM_CHECK_CHANNELR(permission::b_client_use_priority_speaker, 1, this->currentChannel, true); - } - } else if (self && key == "client_talk_request") { - CMD_CHK_AND_INC_FLOOD_POINTS(20); - CACHED_PERM_CHECK(permission::b_client_request_talker, 1, true); - - if (cmd["client_talk_request"].as()) - cmd["client_talk_request"] = duration_cast(system_clock::now().time_since_epoch()).count(); - else - cmd["client_talk_request"] = 0; - keys.emplace_back(property::CLIENT_TALK_REQUEST, "client_talk_request"); - continue; - } else if (self && key == "client_badges") { - std::string str = cmd[key]; - size_t index = 0; - int badgesTags = 0; - do { - index = str.find("badges", index); - if (index < str.length()) badgesTags++; - index++; - } while (index < str.length() && index != 0); - if (badgesTags >= 2) { - if (!this->permissionGranted(permission::b_client_allow_invalid_badges, 1, this->currentChannel)) - ((VoiceClient *) this)->disconnect(VREASON_SERVER_KICK, config::messages::kick_invalid_badges, this->server ? this->server->serverAdmin : dynamic_pointer_cast(serverInstance->getInitialServerAdmin()), true); - return command_result{error::parameter_invalid, "Invalid badges"}; - } - //FIXME stuff here - } else if(!self && key == "client_version") { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_platform") { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_country") { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && (*info == property::CLIENT_FLAG_NOTIFY_SONG_CHANGE/* || *info == property::CLIENT_NOTIFY_SONG_MESSAGE*/)) { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - } else if(!self && key == "client_uptime_mode") { - if(client->getType() != ClientType::CLIENT_MUSIC) return command_result{error::client_invalid_type}; - if(client->properties()[property::CLIENT_OWNER] != this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::i_client_music_modify_power, client->permissionValue(permission::i_client_music_needed_modify_power, client->currentChannel), true); - } - - if(cmd[key].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { - cmd["client_lastconnected"] = duration_cast(this->server->startTimestamp.time_since_epoch()).count(); - } else { - string value = client->properties()[property::CLIENT_CREATED]; - if(value.empty()) - value = "0"; - cmd["client_lastconnected"] = value; - } - - keys.emplace_back(property::CLIENT_LASTCONNECTED, "client_lastconnected"); - } else if(!self && *info == property::CLIENT_BOT_TYPE) { - auto type = cmd["client_bot_type"].as(); - if(type == MusicClient::Type::TEMPORARY) { - CACHED_PERM_CHECK(permission::b_client_music_modify_temporary, 1, true); - } else if(type == MusicClient::Type::SEMI_PERMANENT) { - CACHED_PERM_CHECK(permission::b_client_music_modify_semi_permanent, 1, true); - } else if(type == MusicClient::Type::PERMANENT) { - CACHED_PERM_CHECK(permission::b_client_music_modify_permanent, 1, true); - } else - return command_result{error::parameter_invalid}; - } else if(*info == property::CLIENT_AWAY_MESSAGE) { - if(!self) continue; - - if(cmd["client_away_message"].string().length() > 256) - return command_result{error::parameter_invalid}; - } else if(!self) { /* dont edit random properties of other clients. For us self its allowed to edit the rest without permissions */ - continue; - } - - keys.emplace_back((property::ClientProperties) info->property_index, key); - } - - deque updates; - for(const auto& key : keys) { - if(key.first == property::CLIENT_IS_PRIORITY_SPEAKER) { - client->clientPermissions->set_permission(permission::b_client_is_priority_speaker, {1, 0}, cmd["client_is_priority_speaker"].as() ? permission::v2::PermissionUpdateType::set_value : permission::v2::PermissionUpdateType::delete_value, permission::v2::PermissionUpdateType::do_nothing); - } - client->properties()[key.first] = cmd[0][key.second].value(); - updates.push_back(key.first); - } - if(update_talk_rights) - client->updateTalkRights(client->properties()[property::CLIENT_TALK_POWER]); - - if(this->server) - this->server->notifyClientPropertyUpdates(client, updates); - nickname_lock.reset(); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientUpdate(Command &cmd) { - return this->handleCommandClientEdit(cmd, _this.lock()); -} - -command_result ConnectedClient::handleCommandClientMute(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client || client->getClientId() == this->getClientId()) return command_result{error::client_invalid_id}; - - { - unique_lock channel_lock(this->channel_lock); - for(const auto& weak : this->mutedClients) - if(weak.lock() == client) return command_result{error::ok}; - this->mutedClients.push_back(client); - } - - if (config::voice::notifyMuted) - client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, system_clock::now(), config::messages::mute_notify_message); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientUnmute(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - auto client = this->server->findClient(cmd["clid"].as()); - if (!client || client->getClientId() == this->getClientId()) return command_result{error::client_invalid_id}; - - { - unique_lock channel_lock(this->channel_lock); - this->mutedClients.erase(std::remove_if(this->mutedClients.begin(), this->mutedClients.end(), [client](const weak_ptr& weak) { - auto c = weak.lock(); - return !c || c == client; - }), this->mutedClients.end()); - } - - if (config::voice::notifyMuted) - client->notifyTextMessage(ChatMessageMode::TEXTMODE_PRIVATE, _this.lock(), client->getClientId(), 0, system_clock::now(), config::messages::unmute_notify_message); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - bool allow_ip = false; - if (cmd.hasParm("ip")) - allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - Command result(""); - - int index = 0; - this->server->forEachClient([&](shared_ptr client) { - if (client->getType() == ClientType::CLIENT_INTERNAL) return; - - result[index]["clid"] = client->getClientId(); - if (client->getChannel()) - result[index]["cid"] = client->getChannel()->channelId(); - else result[index]["cid"] = 0; - result[index]["client_database_id"] = client->getClientDatabaseId(); - result[index]["client_nickname"] = client->getDisplayName(); - result[index]["client_type"] = client->getType(); - - if (cmd.hasParm("uid")) - result[index]["client_unique_identifier"] = client->getUid(); - if (cmd.hasParm("away")) { - result[index]["client_away"] = client->properties()[property::CLIENT_AWAY].as(); - result[index]["client_away_message"] = client->properties()[property::CLIENT_AWAY_MESSAGE].as(); - } - if (cmd.hasParm("groups")) { - result[index]["client_channel_group_id"] = client->properties()[property::CLIENT_CHANNEL_GROUP_ID].as(); - result[index]["client_servergroups"] = client->properties()[property::CLIENT_SERVERGROUPS].as(); - result[index]["client_channel_group_inherited_channel_id"] = client->properties()[property::CLIENT_CHANNEL_GROUP_INHERITED_CHANNEL_ID].as(); - } - if (cmd.hasParm("times")) { - result[index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); - result[index]["client_total_online_time"] = client->properties()[property::CLIENT_TOTAL_ONLINE_TIME].as() + duration_cast(system_clock::now() - client->lastOnlineTimestamp).count(); - result[index]["client_month_online_time"] = client->properties()[property::CLIENT_MONTH_ONLINE_TIME].as() + duration_cast(system_clock::now() - client->lastOnlineTimestamp).count(); - result[index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); - result[index]["client_created"] = client->properties()[property::CLIENT_CREATED].as(); - result[index]["client_lastconnected"] = client->properties()[property::CLIENT_LASTCONNECTED].as(); - } - if (cmd.hasParm("info")) { - result[index]["client_version"] = client->properties()[property::CLIENT_VERSION].as(); - result[index]["client_platform"] = client->properties()[property::CLIENT_PLATFORM].as(); - } - - if (cmd.hasParm("badges")) - result[index]["client_badges"] = client->properties()[property::CLIENT_BADGES].as(); - if (cmd.hasParm("country")) - result[index]["client_country"] = client->properties()[property::CLIENT_COUNTRY].as(); - if (cmd.hasParm("ip")) - result[index]["connection_client_ip"] = allow_ip ? client->properties()[property::CONNECTION_CLIENT_IP].as() : "hidden"; - if (cmd.hasParm("icon")) - result[index]["client_icon_id"] = client->properties()[property::CLIENT_ICON_ID].as(); - - if (cmd.hasParm("voice")) { - result[index]["client_talk_power"] = client->properties()[property::CLIENT_TALK_POWER].as(); - result[index]["client_flag_talking"] = client->properties()[property::CLIENT_FLAG_TALKING].as(); - result[index]["client_input_muted"] = client->properties()[property::CLIENT_INPUT_MUTED].as(); - result[index]["client_output_muted"] = client->properties()[property::CLIENT_OUTPUT_MUTED].as(); - result[index]["client_input_hardware"] = client->properties()[property::CLIENT_INPUT_HARDWARE].as(); - result[index]["client_output_hardware"] = client->properties()[property::CLIENT_OUTPUT_HARDWARE].as(); - result[index]["client_is_talker"] = client->properties()[property::CLIENT_IS_TALKER].as(); - result[index]["client_is_priority_speaker"] = client->properties()[property::CLIENT_IS_PRIORITY_SPEAKER].as(); - result[index]["client_is_recording"] = client->properties()[property::CLIENT_IS_RECORDING].as(); - result[index]["client_is_channel_commander"] = client->properties()[property::CLIENT_IS_CHANNEL_COMMANDER].as(); - - } - index++; - }); - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandWhoAmI(Command &cmd) { - CMD_RESET_IDLE; - - Command result(""); - - if (this->server) { - result["virtualserver_status"] = ServerState::string(this->getServer()->state); - result["virtualserver_id"] = this->server->getServerId(); - result["virtualserver_unique_identifier"] = this->server->properties()[property::VIRTUALSERVER_UNIQUE_IDENTIFIER].as(); - result["virtualserver_port"] = 0; - if (this->server->udpVoiceServer) { - result["virtualserver_port"] = this->server->properties()[property::VIRTUALSERVER_PORT].as_save(); - } - } else { - result["virtualserver_status"] = "template"; - result["virtualserver_id"] = "0"; - result["virtualserver_unique_identifier"] = ""; //TODO generate uid - result["virtualserver_port"] = "0"; - } - - result["client_id"] = this->getClientId(); - result["client_channel_id"] = this->currentChannel ? this->currentChannel->channelId() : 0; - result["client_nickname"] = this->getDisplayName(); - result["client_database_id"] = this->getClientDatabaseId(); - result["client_login_name"] = this->properties()[property::CLIENT_LOGIN_NAME].as(); - result["client_unique_identifier"] = this->getUid(); - - { - auto query = dynamic_cast(this); - if(query) { - auto account = query->getQueryAccount(); - result["client_origin_server_id"] = account ? account->bound_server : 0; - } else - result["client_origin_server_id"] = 0; - } - - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandServerGroupsByClientId(Command &cmd) { - CMD_RESET_IDLE; - - ClientDbId cldbid = cmd["cldbid"]; - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) return command_result{error::client_invalid_id}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyservergroupsbyclientid" : ""); - - int index = 0; - if (this->server) { - for (const auto &group : this->server->groups->getAssignedServerGroups(cldbid)) { - result[index]["name"] = group->group->name(); - result[index]["sgid"] = group->group->groupId(); - result[index]["cldbid"] = cldbid; - index++; - } - } else { - for (const auto &group : serverInstance->getGroupManager()->getAssignedServerGroups(cldbid)) { - result[index]["name"] = group->group->name(); - result[index]["sgid"] = group->group->groupId(); - result[index]["cldbid"] = cldbid; - index++; - } - } - - if (index == 0) return command_result{error::database_empty_result}; - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetDBIDfromUID(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - deque unique_ids; - for(int index = 0; index < cmd.bulkCount(); index++) - unique_ids.push_back(cmd[index]["cluid"].as()); - - auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids); - if (res.empty()) return command_result{error::database_empty_result}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientdbidfromuid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result_index++; - } - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetNameFromDBID(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - deque dbids; - for(int index = 0; index < cmd.bulkCount(); index++) - dbids.push_back(cmd[index]["cldbid"].as()); - - auto res = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, dbids); - if (res.empty()) return command_result{error::database_empty_result}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetnamefromdbid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result[result_index]["name"] = info->lastName; - result[result_index]["clname"] = info->lastName; - result_index++; - } - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetNameFromUid(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - deque unique_ids; - for(int index = 0; index < cmd.bulkCount(); index++) - unique_ids.push_back(cmd[index]["cluid"].as()); - - auto res = serverInstance->databaseHelper()->queryDatabaseInfoByUid(this->server, unique_ids); - if (res.empty()) return command_result{error::database_empty_result}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientnamefromuid" : ""); - int result_index = 0; - for(auto& info : res) { - result[result_index]["cluid"] = info->uniqueId; - result[result_index]["cldbid"] = info->cldbid; - result[result_index]["name"] = info->lastName; - result[result_index]["clname"] = info->lastName; - result_index++; - } - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientGetUidFromClid(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - bool error = false; - bool found = false; - auto client_list = this->server->getClients(); - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientgetuidfromclid" : ""); - int result_index = 0; - - for(int index = 0; index < cmd.bulkCount(); index++) { - auto client_id = cmd[index]["clid"].as(); - for(const auto& entry : client_list) { - if(entry->getClientId() == client_id) { - notify[result_index]["clname"] = entry->getDisplayName(); - notify[result_index]["clid"] = entry->getClientId(); - notify[result_index]["cluid"] = entry->getUid(); - notify[result_index]["cldbid"] = entry->getClientDatabaseId(); - result_index++; - found = true; - } - } - if(found) found = false; - else error = false; - } - - if(result_index > 0) - this->sendCommand(notify); - if(error) - return command_result{error::database_empty_result}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientAddPerm(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto cldbid = cmd["cldbid"].as(); - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return command_result{error::client_invalid_id}; - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - - PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); - auto update_channels = false; - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return command_result{permission::i_permission_modify_power}; - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - mgr->set_permission(permType, {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); - } else { - mgr->set_permission(permType, {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); - update_channels |= permission_is_client_property(permType); - } - } - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - auto onlineClients = this->server->findClientsByCldbId(cldbid); - if (!onlineClients.empty()) - for (const auto &elm : onlineClients) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(update_channels) - elm->updateChannelClientProperties(true, true); - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientDelPerm(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto cldbid = cmd["cldbid"].as(); - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return command_result{error::client_invalid_id}; - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(cldbid, permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); - - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); - auto onlineClients = this->server->findClientsByCldbId(cmd["cldbid"]); - auto update_channel = false; - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd) - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - - if (grant) { - mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); - } else { - mgr->set_permission(permType, permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); - update_channel |= permission_is_client_property(permType); - } - } - - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - if (!onlineClients.empty()) - for (const auto &elm : onlineClients) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - if(update_channel) - elm->updateChannelClientProperties(true, true); - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientPermList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - PERM_CHECKR(permission::b_virtualserver_client_permission_list, 1, true); - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return command_result{error::client_invalid_id}; - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"]); - if (!this->notifyClientPermList(cmd["cldbid"], mgr, cmd.hasParm("permsid"))) return command_result{error::database_empty_result}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelClientPermList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_virtualserver_channelclient_permission_list, 1, true); - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return command_result{error::vs_critical}; - - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cmd["cldbid"])) return command_result{error::client_invalid_id}; - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cmd["cldbid"].as()); - - Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifychannelclientpermlist" : ""); - - auto permissions = mgr->channel_permissions(channel->channelId()); - if(permissions.empty()) - return command_result{error::database_empty_result}; - - int index = 0; - res[index]["cid"] = channel->channelId(); - res[index]["cldbid"] = cmd["cldbid"].as(); - - auto sids = cmd.hasParm("permsid"); - auto permission_mapper = serverInstance->getPermissionMapper(); - auto type = this->getType(); - - for (const auto &permission_data : permissions) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - if (sids) - res[index]["permsid"] = permission_mapper->permission_name(type, get<0>(permission_data)); - else - res[index]["permid"] = get<0>(permission_data); - res[index]["permvalue"] = permission.values.value; - - res[index]["permnegated"] = permission.flags.negate; - res[index]["permskip"] = permission.flags.skip; - index++; - } - - - if(permission.flags.grant_set) { - if (sids) - res[index]["permsid"] = permission_mapper->permission_name_grant(type, get<0>(permission_data)); - else - res[index]["permid"] = (get<0>(permission_data) | PERM_ID_GRANT); - res[index]["permvalue"] = permission.values.grant; - res[index]["permnegated"] = 0; - res[index]["permskip"] = 0; - index++; - } - } - - this->sendCommand(res); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelClientDelPerm(Command &cmd) { - CMD_REF_SERVER(server_ref); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return command_result{error::parameter_invalid, "Invalid manager db id"}; - - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - PERM_CHECKR(permission::i_client_permission_modify_power, this->server->calculatePermission(cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, nullptr), true); - - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return command_result{error::vs_critical}; - - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"), update_view = false; - auto cll = this->server->findClientsByCldbId(cldbid); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::do_nothing, permission::v2::delete_value); - } else { - mgr->set_channel_permission(permType, channel->channelId(), permission::v2::empty_permission_values, permission::v2::delete_value, permission::v2::do_nothing); - update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; - } - } - - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - if (!cll.empty()) { - for (const auto &elm : cll) { - if(elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - - if(elm->currentChannel == channel) { - elm->updateChannelClientProperties(true, true); - } else if(update_view) { - unique_lock client_channel_lock(this->channel_lock); - - auto elm_channel = elm->currentChannel; - if(elm_channel) { - deque deleted; - for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { - if(update_entry.first) - elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ - } - } - - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelClientAddPerm(Command &cmd) { - CMD_REF_SERVER(server_ref); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto cldbid = cmd["cldbid"].as(); - if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, cldbid)) - return command_result{error::parameter_invalid, "Invalid client db id"}; - - RESOLVE_CHANNEL_R(cmd["cid"], true); - auto channel = dynamic_pointer_cast(l_channel->entry); - if(!channel) return command_result{error::vs_critical}; - - auto mgr = serverInstance->databaseHelper()->loadClientPermissionManager(this->server, cldbid); - PERM_CHECK_CHANNELR(permission::i_client_permission_modify_power, this->server->calculatePermission(cmd["cldbid"], permission::i_client_needed_permission_modify_power, ClientType::CLIENT_TEAMSPEAK, channel), channel, true); - - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permissionGranted(permission::b_permission_modify_power_ignore, 1, this->currentChannel); - bool conOnError = cmd[0].has("continueonerror"); - auto onlineClientInstances = this->server->findClientsByCldbId(cldbid); - bool update_view = false; - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return command_result{permission::i_permission_modify_power}; - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - - if (grant) { - mgr->set_channel_permission(permType, channel->channelId(), {0, cmd[index]["permvalue"]}, permission::v2::do_nothing, permission::v2::set_value); - } else { - mgr->set_channel_permission(permType, channel->channelId(), {cmd[index]["permvalue"], 0}, permission::v2::set_value, permission::v2::do_nothing, cmd[index]["permskip"] ? 1 : 0, cmd[index]["permnegated"] ? 1 : 0); - update_view = permType == permission::b_channel_ignore_view_power || permType == permission::i_channel_view_power; - } - } - - serverInstance->databaseHelper()->saveClientPermissions(this->server, cldbid, mgr); - if (!onlineClientInstances.empty()) - for (const auto &elm : onlineClientInstances) { - if (elm->update_cached_permissions()) /* update cached calculated permissions */ - elm->sendNeededPermissions(false); /* cached permissions had changed, notify the client */ - - if(elm->currentChannel == channel) { - elm->updateChannelClientProperties(true, true); - } else if(update_view) { - unique_lock client_channel_lock(this->channel_lock); - - auto elm_channel = elm->currentChannel; - if(elm_channel) { - deque deleted; - for(const auto& update_entry : elm->channels->update_channel_path(l_channel, this->server->channelTree->findLinkedChannel(elm->currentChannel->channelId()))) { - if(update_entry.first) - elm->notifyChannelShow(update_entry.second->channel(), update_entry.second->previous_channel); - else deleted.push_back(update_entry.second->channelId()); - } - if(!deleted.empty()) - elm->notifyChannelHide(deleted, false); /* we've locked the tree before */ - } - } - elm->join_state_id++; /* join permission may changed, all channels need to be recalculate dif needed */ - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientDbInfo(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_virtualserver_client_dbinfo, 1, true); - - deque cldbids; - for(int index = 0; index < cmd.bulkCount(); index++) - cldbids.push_back(cmd[index]["cldbid"]); - - auto basic = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, cldbids); - if (basic.empty()) return command_result{error::database_empty_result}; - - auto allow_ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - Command res(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyclientdbinfo" : ""); - - size_t index = 0; - for(const auto& info : basic) { - res[index]["client_base64HashClientUID"] = hex::hex(base64::validate(info->uniqueId) ? base64::decode(info->uniqueId) : info->uniqueId, 'a', 'q'); - res[index]["client_unique_identifier"] = info->uniqueId; - res[index]["client_nickname"] = info->lastName; - res[index]["client_database_id"] = info->cldbid; - res[index]["client_created"] = chrono::duration_cast(info->created.time_since_epoch()).count(); - res[index]["client_lastconnected"] = chrono::duration_cast(info->lastjoin.time_since_epoch()).count(); - res[index]["client_totalconnections"] = info->connections; - res[index]["client_database_id"] = info->cldbid; - - auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, info->cldbid, ClientType::CLIENT_TEAMSPEAK); - if (allow_ip) - res[index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as(); - else - res[index]["client_lastip"] = "hidden"; - - res[index]["client_icon_id"] = (*props)[property::CLIENT_ICON_ID].as(); - res[index]["client_badges"] = (*props)[property::CLIENT_BADGES].as(); - res[index]["client_version"] = (*props)[property::CLIENT_VERSION].as(); - res[index]["client_platform"] = (*props)[property::CLIENT_PLATFORM].as(); - res[index]["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as(); - res[index]["client_total_bytes_downloaded"] = (*props)[property::CLIENT_TOTAL_BYTES_DOWNLOADED].as(); - res[index]["client_total_bytes_uploaded"] = (*props)[property::CLIENT_TOTAL_BYTES_UPLOADED].as(); - res[index]["client_month_bytes_downloaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - res[index]["client_month_bytes_uploaded"] = (*props)[property::CLIENT_MONTH_BYTES_DOWNLOADED].as(); - res[index]["client_description"] = (*props)[property::CLIENT_DESCRIPTION].as(); - res[index]["client_flag_avatar"] = (*props)[property::CLIENT_FLAG_AVATAR].as(); - - res[index]["client_month_online_time"] = (*props)[property::CLIENT_MONTH_ONLINE_TIME].as(); - res[index]["client_total_online_time"] = (*props)[property::CLIENT_TOTAL_ONLINE_TIME].as(); - index++; - } - - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientDBDelete(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_client_delete_dbproperties, 1, true); - - ClientDbId id = cmd["cldbid"]; - if (!serverInstance->databaseHelper()->validClientDatabaseId(this->server, id)) return command_result{error::database_empty_result}; - serverInstance->databaseHelper()->deleteClient(this->server, id); - - return command_result{error::ok}; -} - -struct DBFindArgs { - int index = 0; - bool full = false; - bool ip = false; - Command cmd{""}; -}; - -command_result ConnectedClient::handleCommandClientDBFind(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_virtualserver_client_dbsearch, 1, true); - - bool uid = cmd.hasParm("uid"); - string pattern = cmd["pattern"]; - - DBFindArgs args{}; - args.cmd = Command(this->getType() == CLIENT_QUERY ? "" : "notifyclientdbfind"); - args.full = cmd.hasParm("details"); - args.ip = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - auto res = sql::command(this->sql, string() + "SELECT * FROM `clients` WHERE `serverId` = :sid AND `" + (uid ? "clientUid" : "lastName") + "` LIKE '" + pattern + "' LIMIT 50", variable{":sid", this->server->getServerId()}).query( - [&](DBFindArgs *ptr, int len, char **values, char **names) { - for (int index = 0; index < len; index++) - if (strcmp(names[index], "cldbid") == 0) - ptr->cmd[ptr->index]["cldbid"] = values[index]; - else if (strcmp(names[index], "clientUid") == 0 && ptr->full) - ptr->cmd[ptr->index]["client_unique_identifier"] = values[index]; - else if (strcmp(names[index], "lastConnect") == 0 && ptr->full) - ptr->cmd[ptr->index]["client_lastconnected"] = values[index]; - else if (strcmp(names[index], "connections") == 0 && ptr->full) - ptr->cmd[ptr->index]["client_totalconnections"] = values[index]; - else if (strcmp(names[index], "lastName") == 0 && ptr->full) - ptr->cmd[ptr->index]["client_nickname"] = values[index]; - if (ptr->full) { - auto props = serverInstance->databaseHelper()->loadClientProperties(this->server, ptr->cmd[ptr->index]["cldbid"], ClientType::CLIENT_TEAMSPEAK); - if (props) { - if (ptr->ip) { - ptr->cmd[ptr->index]["client_lastip"] = (*props)[property::CONNECTION_CLIENT_IP].as(); - } else { - ptr->cmd[ptr->index]["client_lastip"] = "hidden"; - } - ptr->cmd[ptr->index]["client_badges"] = (*props)[property::CLIENT_BADGES].as(); - ptr->cmd[ptr->index]["client_version"] = (*props)[property::CLIENT_VERSION].as(); - ptr->cmd[ptr->index]["client_platform"] = (*props)[property::CLIENT_PLATFORM].as(); - ptr->cmd[ptr->index]["client_hwid"] = (*props)[property::CLIENT_HARDWARE_ID].as(); - } - } - ptr->index++; - return 0; - }, &args); - auto pf = LOG_SQL_CMD; - pf(res); - if (args.index == 0) return command_result{error::database_empty_result}; - - this->sendCommand(args.cmd); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientInfo(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - - Command res(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyclientinfo" : ""); - bool trigger_error = false; - bool view_remote = this->permission_granted(this->cached_permission_value(permission::b_client_remoteaddress_view), 1); - - int result_index = 0; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto client_id = cmd[index]["clid"].as(); - if(client_id == 0) continue; - - auto client = this->server->findClient(client_id); - if(!client) { - trigger_error = true; - continue; - } - - for (const auto &key : client->properties()->list_properties(property::FLAG_CLIENT_VIEW | property::FLAG_CLIENT_VARIABLE | property::FLAG_CLIENT_INFO, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - res[result_index][key.type().name] = key.value(); - if(view_remote) - res[result_index]["connection_client_ip"] = client->properties()[property::CONNECTION_CLIENT_IP].as(); - else - res[result_index]["connection_client_ip"] = "hidden"; - res[result_index]["client_idle_time"] = duration_cast(system_clock::now() - client->idleTimestamp).count(); - res[result_index]["connection_connected_time"] = duration_cast(system_clock::now() - client->connectTimestamp).count(); - { - auto channel = client->currentChannel; - if(channel) - res[result_index]["cid"] = channel->channelId(); - else - res[result_index]["cid"] = 0; - } - - result_index++; - } - - - if(result_index > 0) { - this->sendCommand(res); - } - - if(trigger_error || result_index == 0) - return command_result{error::client_invalid_id}; - else - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientFind(Command &cmd) { - CMD_REQ_SERVER; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - string pattern = cmd["pattern"]; - std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower); - - Command res(""); - int index = 0; - for (const auto &cl : this->server->getClients()) { - string name = cl->getDisplayName(); - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - if (name.find(pattern) != std::string::npos) { - res[index]["clid"] = cl->getClientId(); - res[index]["client_nickname"] = cl->getDisplayName(); - index++; - } - } - if (index == 0) return command_result{error::database_empty_result}; - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandVersion(Command &) { - CMD_RESET_IDLE; - - Command res(""); - res["version"] = build::version()->string(false); - res["build_count"] = build::buildCount(); - res["build"] = duration_cast(build::version()->timestamp.time_since_epoch()).count(); -#ifdef WIN32 - res["platform"] = "Windows"; -#else - res["platform"] = "Linux"; -#endif - this->sendCommand(res); - return command_result{error::ok}; -} - -//cid=%d password=%s -command_result ConnectedClient::handleCommandVerifyChannelPassword(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - std::shared_ptr channel = (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - - std::string password = cmd["password"]; - if (!channel->passwordMatch(password, false)) return command_result{error::server_invalid_password}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandVerifyServerPassword(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - std::string password = cmd["password"]; - if (!this->server->verifyServerPassword(password, false)) return command_result{error::server_invalid_password}; - return command_result{error::ok}; -} - -//msgid=2 cluid=IkBXingb46\/z1Q3hhMvJEweb3lw= subject=The\sSubject timestamp=1512224138 flag_read=0 -//notifymessagelist msgid=2 cluid=IkBXingb46\/z1Q3hhMvJEweb3lw= subject=The\sSubject timestamp=1512224138 flag_read=0 -command_result ConnectedClient::handleCommandMessageList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto msgList = this->server->letters->avariableLetters(this->getUid()); - if (msgList.empty()) return command_result{error::database_empty_result, "no letters available"}; - - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymessagelist" : ""); - - int index = 0; - for (const auto &elm : msgList) { - notify[index]["msgid"] = elm->id; - notify[index]["cluid"] = elm->sender; - notify[index]["subject"] = elm->subject; - notify[index]["timestamp"] = duration_cast(elm->created.time_since_epoch()).count(); - notify[index]["flag_read"] = elm->read; - index++; - } - - this->sendCommand(notify); - return command_result{error::ok}; -} - -//messageadd cluid=ePHuXhcai9nk\/4Fd\/xkxrokvnNk= subject=Test message=Message -command_result ConnectedClient::handleCommandMessageAdd(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_client_offline_textmessage_send, 1, true); - - this->server->letters->createLetter(this->getUid(), cmd["cluid"], cmd["subject"], cmd["message"]); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMessageGet(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(10); - - auto letter = this->server->letters->getFullLetter(cmd["msgid"]); - - //msgid=2 cluid=IkBXingb46\/z1Q3hhMvJEweb3lw= subject=The\sSubject message=The\sbody timestamp=1512224138 - Command notify(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymessage" : ""); - notify["msgid"] = cmd["msgid"]; - notify["cluid"] = letter->sender; - notify["subject"] = letter->subject; - notify["message"] = letter->message; - notify["timestamp"] = duration_cast(letter->created.time_since_epoch()).count(); - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMessageUpdateFlag(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - this->server->letters->updateReadFlag(cmd["msgid"], cmd["flag"]); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMessageDel(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - this->server->letters->deleteLetter(cmd["msgid"]); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPermGet(Command &cmd) { - CMD_RESET_IDLE; - CACHED_PERM_CHECK(permission::b_client_permissionoverview_own, 1, true); - - Command res(""); - - deque requrested; - auto permission_mapper = serverInstance->getPermissionMapper(); - auto type = this->getType(); - for (int index = 0; index < cmd.bulkCount(); index++) { - permission::PermissionType permType = permission::unknown; - if (cmd[index].has("permid")) - permType = cmd[index]["permid"].as(); - else if (cmd[index].has("permsid")) - permType = permission::resolvePermissionData(cmd[index]["permsid"].as())->type; //TODO: Map the other way around! - if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) return command_result{error::parameter_invalid, "could not resolve permission"}; - - requrested.push_back(permType); - } - - int index = 0; - for(const auto& entry : this->permissionValues(requrested, this->currentChannel)) { - res[index]["permsid"] = permission_mapper->permission_name(type, entry.first);; - res[index]["permid"] = entry.first; - res[index++]["permvalue"] = entry.second; - } - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPermIdGetByName(Command &cmd) { - auto found = permission::resolvePermissionData(cmd["permsid"].as()); //TODO: Map the other way around - Command res(""); - res["permid"] = found->type; - this->sendCommand(res); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPermFind(Command &cmd) { - struct PermissionEntry { - permission::PermissionType permission_type; - permission::PermissionValue permission_value; - permission::PermissionSqlType type; - - GroupId group_id; - ChannelId channel_id; - ClientDbId client_id; - - bool negate; - bool skip; - }; - - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_virtualserver_permission_find, 1, true); - - deque, bool>> permissions; - std::shared_ptr permission; - for(size_t index = 0; index < cmd.bulkCount(); index++) { - bool granted = false; - if (cmd[index].has("permid")) { - permission = permission::resolvePermissionData((permission::PermissionType) (cmd[index]["permid"].as() & (~PERM_ID_GRANT))); - granted = (cmd[index]["permid"].as() & PERM_ID_GRANT) > 0; - - if(permission->type == permission::PermissionType::unknown) - return command_result{error::parameter_invalid, "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; - } else if (cmd[index].has("permsid")) { - permission = permission::resolvePermissionData(cmd[index]["permsid"].as()); //TODO: Map the other way around - granted = permission->grant_name == cmd[index]["permsid"].as(); - - if(permission->type == permission::PermissionType::unknown) - return command_result{error::parameter_invalid, "could not resolve permission (id=" + cmd[index]["permid"].string() + ")"}; - } else { - continue; - } - - permissions.emplace_back(pair, bool>{{permission->name, permission->type}, granted}); - } - - if(permissions.empty()) - return command_result{error::database_empty_result}; - - map flags; - map quick_mapping; - string query_string; - for(const auto& entry : permissions) { - if(flags[entry.first.first] == 0) { - quick_mapping[entry.first.first] = entry.first.second; - query_string += string(query_string.empty() ? "" : " OR ") + "`permId` = '" + entry.first.first + "'"; - } - - flags[entry.first.first] |= entry.second ? 2 : 1; - } - - deque> entries; - //`serverId` INT NOT NULL, `type` INT, `id` INT, `channelId` INT, `permId` VARCHAR(" UNKNOWN_KEY_LENGTH "), `value` INT, `grant` INT - sql::command(this->sql, "SELECT `permId`, `type`, `id`, `channelId`, `value`, `grant`, `flag_skip`, `flag_negate` FROM `permissions` WHERE `serverId` = :sid AND (" + query_string + ") AND `type` != :playlist", - variable{":sid", this->server->getServerId()}, - variable{":playlist", permission::SQL_PERM_PLAYLIST} - ).query([&](int length, string* values, string* columns) { - permission::PermissionSqlType type = permission::SQL_PERM_GROUP; - uint64_t id = 0; - ChannelId channel_id = 0; - permission::PermissionValue value = 0, - granted_value = 0; - string permission_name; - bool negate = false, skip = false; - for (int index = 0; index < length; index++) { - try { - if(columns[index] == "type") - type = static_cast(stoll(values[index])); - else if(columns[index] == "permId") - permission_name = values[index]; - else if(columns[index] == "id") - id = static_cast(stoll(values[index])); - else if(columns[index] == "channelId") - channel_id = static_cast(stoll(values[index])); - else if(columns[index] == "value") - value = static_cast(stoll(values[index])); - else if(columns[index] == "grant") - granted_value = static_cast(stoll(values[index])); - else if(columns[index] == "flag_negate") - negate = !values[index].empty() && stol(values[index]) == 1; - else if(columns[index] == "flag_skip") - skip = !values[index].empty() && stol(values[index]) == 1; - } catch(std::exception& ex) { - debugMessage(this->getServerId(), "[{}] 'permfind' iterates over invalid permission entry. Key: {}, Value: {}, Error: {}", CLIENT_STR_LOG_PREFIX, columns[index], values[index], ex.what()); - return 0; - } - } - - /* value */ - if((flags[permission_name] & 0x1) > 0 && value > 0) { - auto result = make_unique(); - result->permission_type = quick_mapping[permission_name]; - result->permission_value = value; - result->type = type; - result->channel_id = channel_id; - result->negate = negate; - result->skip = skip; - if (type == permission::SQL_PERM_GROUP) { - auto gr = this->server->groups->findGroup(id); - if (!gr) return 0; - - result->group_id = id; - if(gr->target() == GROUPTARGET_CHANNEL) - result->channel_id = 1; - } else if(type == permission::SQL_PERM_USER) { - result->client_id = id; - } - - if(result) - entries.push_back(std::move(result)); - } - - /* granted */ - if((flags[permission_name] & 0x2) > 0 && granted_value > 0) { - auto result = make_unique(); - result->permission_type = (permission::PermissionType) (quick_mapping[permission_name] | PERM_ID_GRANT); - result->permission_value = granted_value; - result->type = type; - result->channel_id = channel_id; - result->negate = negate; - result->skip = skip; - - if (type == permission::SQL_PERM_GROUP) { - auto gr = this->server->groups->findGroup(id); - if (!gr) return 0; - - result->group_id = id; - if(gr->target() == GROUPTARGET_CHANNEL) - result->channel_id = 1; - } else if(type == permission::SQL_PERM_USER) { - result->client_id = id; - } - - if(result) - entries.push_back(std::move(result)); - } - return 0; - }); - - struct CommandPerm { - permission::PermissionType p; - permission::PermissionValue v; - int64_t id1; - int64_t id2; - uint8_t t; - }; - - std::deque perms; - perms.resize(entries.size()); - size_t index = 0; - for(const auto& entry : entries) { - auto& perm = perms[index++]; - - perm.p = entry->permission_type; - perm.v = entry->permission_value; - - if(entry->type == permission::SQL_PERM_USER) { - if(entry->channel_id > 0) { - perm.id1 = entry->client_id; - perm.id2 = entry->channel_id; - perm.t = 4; /* client channel */ - } else { - perm.id1 = 0; - perm.id2 = entry->client_id; - perm.t = 1; /* client server */ - } - } else if(entry->type == permission::SQL_PERM_CHANNEL) { - perm.id1 = 0; - perm.id2 = entry->channel_id; - perm.t = 2; /* channel permission */ - } else if(entry->type == permission::SQL_PERM_GROUP) { - if(entry->channel_id > 0) { - perm.id1 = entry->group_id; - perm.id2 = 0; - perm.t = 3; /* channel group */ - } else { - perm.id1 = entry->group_id; - perm.id2 = 0; - perm.t = 0; /* server group */ - } - } - } - - - sort(perms.begin(), perms.end(), [](const CommandPerm& a, const CommandPerm& b) { - if(a.t < b.t) return true; - else if(b.t < a.t) return false; - - if(a.id1 < b.id1) return true; - else if(b.id1 < a.id1) return false; - - if(a.id2 < b.id2) return true; - else if(b.id2 < a.id2) return false; - - if(a.p < b.p) return true; - else if(b.p < a.p) return false; - - return &a > &b; - }); - - Command result(""); - index = 0; - - // http://yat.qa/ressourcen/server-query-kommentare/#permfind - for(const auto& e : perms) { - result[index]["p"] = e.p; - result[index]["v"] = e.v; - result[index]["id1"] = e.id1; - result[index]["id2"] = e.id2; - result[index]["t"] = e.t; - index++; - } - - if(index == 0) return command_result{error::database_empty_result}; - this->sendCommand(result); - return command_result{error::ok}; -} - -/* - * - Alle rechte der aktuellen server gruppen vom client - * - Alle client rechte | channel cleint rechte - * - Alle rechte des channels - */ -command_result ConnectedClient::handleCommandPermOverview(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto client_dbid = cmd["cldbid"].as(); - if(!serverInstance->databaseHelper()->validClientDatabaseId(this->getServer(), client_dbid)) return command_result{error::client_invalid_id}; - - if(client_dbid == this->getClientDatabaseId()) { - CACHED_PERM_CHECK(permission::b_client_permissionoverview_own, 1, true); - } else { - CACHED_PERM_CHECK(permission::b_client_permissionoverview_view, 1, true); - } - - string channel_query, perm_query; - - auto channel = this->server ? this->server->channelTree->findChannel(cmd["cid"]) : serverInstance->getChannelTree()->findChannel(cmd["cid"]); - if(!channel) return command_result{error::channel_invalid_id}; - - auto server_groups = this->server->getGroupManager()->getServerGroups(client_dbid, ClientType::CLIENT_TEAMSPEAK); - auto channel_group = this->server->getGroupManager()->getChannelGroup(client_dbid, channel, true); - auto permission_manager = serverInstance->databaseHelper()->loadClientPermissionManager(this->getServer(), client_dbid); - - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifypermoverview" : ""); - size_t index = 0; - result["cldbid"] = client_dbid; - result["cid"] = channel->channelId(); - if(cmd["return_code"].size() > 0) - result["return_code"] = cmd["return_code"].string(); - - for(const auto& server_group : server_groups) { - auto permission_manager = server_group->group->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 0; /* server group */ - result[index]["id1"] = server_group->group->groupId(); - result[index]["id2"] = 0; - - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 0; /* server group */ - result[index]["id1"] = server_group->group->groupId(); - result[index]["id2"] = 0; - - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } - - { - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = client_dbid; - result[index]["id2"] = 0; - - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = client_dbid; - result[index]["id2"] = 0; - - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } - - { - auto permission_manager = channel->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 2; /* server channel */ - result[index]["id1"] = channel->channelId(); - result[index]["id2"] = 0; - - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 2; /* server channel */ - result[index]["id1"] = channel->channelId(); - result[index]["id2"] = 0; - - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } - - { - auto permission_manager = channel_group->group->permissions(); - for(const auto& permission_data : permission_manager->permissions()) { - auto& permission = std::get<1>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 3; /* channel group */ - result[index]["id1"] = channel_group->channelId; - result[index]["id2"] = channel_group->group->groupId(); - - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 3; /* channel group */ - result[index]["id1"] = channel_group->channelId; - result[index]["id2"] = channel_group->group->groupId(); - - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } - - { - for(const auto& permission_data : permission_manager->channel_permissions()) { - auto& permission = std::get<2>(permission_data); - if(permission.flags.value_set) { - result[index]["t"] = 4; /* client channel */ - result[index]["id1"] = std::get<1>(permission_data); - result[index]["id2"] = client_dbid; - - result[index]["p"] = std::get<0>(permission_data); - result[index]["v"] = permission.values.value; - result[index]["n"] = permission.flags.negate; - result[index]["s"] = permission.flags.skip; - index++; - } - if(permission.flags.grant_set) { - result[index]["t"] = 1; /* client */ - result[index]["id1"] = std::get<1>(permission_data); - result[index]["id2"] = client_dbid; - - result[index]["p"] = (std::get<0>(permission_data) | PERM_ID_GRANT); - result[index]["v"] = permission.values.grant; - result[index]["n"] = false; - result[index]["s"] = false; - index++; - } - } - } - - if (index == 0) return command_result{error::database_empty_result}; - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelFind(Command &cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - string pattern = cmd["pattern"]; - std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower); - - Command res(""); - int index = 0; - for (const auto &cl : (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->channels()) { - string name = cl->name(); - std::transform(name.begin(), name.end(), name.begin(), ::tolower); - if (name.find(pattern) != std::string::npos) { - res[index]["cid"] = cl->channelId(); - res[index]["channel_name"] = cl->name(); - index++; - } - } - if (index == 0) return command_result{error::database_empty_result}; - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandChannelInfo(Command &cmd) { - std::shared_ptr channel = (this->server ? this->server->channelTree : serverInstance->getChannelTree().get())->findChannel(cmd["cid"].as()); - if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"}; - - Command res(""); - - for (const auto &prop : channel->properties().list_properties(property::FLAG_CHANNEL_VIEW | property::FLAG_CHANNEL_VARIABLE, this->getType() == CLIENT_TEAMSPEAK ? property::FLAG_NEW : (uint16_t) 0)) - res[prop.type().name] = prop.value(); - - res["seconds_empty"] = channel->emptySince(); - res["pid"] = res["cpid"].string(); - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandClientSetServerQueryLogin(Command &cmd) { - PERM_CHECKR(permission::b_client_create_modify_serverquery_login, 1, true); - - if(!cmd[0].has("client_login_password")) cmd["client_login_password"] = ""; - - std::string password = cmd["client_login_password"]; - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); - - auto old = serverInstance->getQueryServer()->find_query_account_by_name(cmd["client_login_name"]); - if (old) { - if(old->unique_id == this->getUid()) { - serverInstance->getQueryServer()->change_query_password(old, password); - } else { - return command_result{error::client_not_logged_in}; - } - } else { - serverInstance->getQueryServer()->create_query_account(cmd["client_login_name"], this->getServerId(), this->getUid(), password); - } - - Command res(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifyclientserverqueryloginpassword" : ""); - res["client_login_password"] = password; - this->sendCommand(res); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandComplainAdd(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - ClientDbId target = cmd["tcldbid"]; - std::string msg = cmd["message"]; - - auto cl = this->server->findClientsByCldbId(target); - if (cl.empty()) return command_result{error::client_invalid_id}; - PERM_CHECKR(permission::i_client_complain_power, cl[0]->permissionValue(permission::i_client_needed_complain_power), true); - - /* - if(!serverInstance->databaseHelper()->validClientDatabaseId(target)) - return command_result{error::client_invalid_id, "invalid database id"}; - */ - - for (const auto &elm : this->server->complains->findComplainsFromTarget(target)) - if (elm->invoker == this->getClientDatabaseId()) - return command_result{error::database_duplicate_entry, "you already send a complain"}; - - if (!this->server->complains->createComplain(target, this->getClientDatabaseId(), msg)) return command_result{error::vs_critical, "could not create complains"}; - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandComplainList(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - CACHED_PERM_CHECK(permission::b_client_complain_list, 1, true); - - ClientDbId id = cmd[0].has("tcldbid") ? cmd["tcldbid"].as() : 0; - auto list = id == 0 ? this->server->complains->complains() : this->server->complains->findComplainsFromTarget(id); - if (list.empty()) return command_result{error::database_empty_result}; - - deque nameQuery; - for (const auto &elm : list) { - if (std::find(nameQuery.begin(), nameQuery.end(), elm->invoker) == nameQuery.end()) - nameQuery.push_back(elm->invoker); - if (std::find(nameQuery.begin(), nameQuery.end(), elm->target) == nameQuery.end()) - nameQuery.push_back(elm->target); - } - - auto dbInfo = serverInstance->databaseHelper()->queryDatabaseInfo(this->server, nameQuery); - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifycomplainlist" : ""); - int index = 0; - for (const auto &elm : list) { - result[index]["tcldbid"] = elm->target; - result[index]["tname"] = "unknown"; - - result[index]["fcldbid"] = elm->invoker; - result[index]["fname"] = "unknown"; - - result[index]["message"] = elm->reason; - result[index]["timestamp"] = chrono::duration_cast(elm->created.time_since_epoch()).count(); - - for (const auto &e : dbInfo) { - if (e->cldbid == elm->target) - result[index]["tname"] = e->lastName; - if (e->cldbid == elm->invoker) - result[index]["fname"] = e->lastName; - } - index++; - } - - this->sendCommand(result); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandComplainDel(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - ClientDbId tid = cmd["tcldbid"]; - ClientDbId fid = cmd["fcldbid"]; - - shared_ptr entry; - for (const auto &elm : this->server->complains->findComplainsFromTarget(tid)) - if (elm->invoker == fid) { - entry = elm; - break; - } - if (!entry) return command_result{error::database_empty_result}; - - if (entry->invoker == this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::b_client_complain_delete_own, 1, true); - else - CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); - - this->server->complains->deleteComplain(entry); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandComplainDelAll(Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - CACHED_PERM_CHECK(permission::b_client_complain_delete, 1, true); - ClientDbId tid = cmd["tcldbid"]; - if (!this->server->complains->deleteComplainsFromTarget(tid)) return command_result{error::database_empty_result}; - return command_result{error::ok}; -} - - -command_result ConnectedClient::handleCommandMusicBotCreate(Command& cmd) { - if(!config::music::enabled) return command_result{error::music_disabled}; - - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - if(this->server->musicManager->max_bots() != -1 && this->server->musicManager->max_bots() <= this->server->musicManager->current_bot_count()){ - if(config::license->isPremium()) - return command_result{error::music_limit_reached}; - else - return command_result{error::music_limit_reached, strobf("You reached the server music bot limit. You could increase this limit by extend your server with a premium license.").string()}; - } - - - auto permissions_list = this->permissionValues({ - permission::i_client_music_limit, - permission::b_client_music_create_permanent, - permission::b_client_music_create_semi_permanent, - permission::b_client_music_create_temporary, - permission::i_channel_join_power, - permission::i_client_music_delete_power, - permission::i_client_music_create_modify_max_volume - }, this->currentChannel); - - auto permissions = map(permissions_list.begin(), permissions_list.end()); - - auto max_bots = permissions[permission::i_client_music_limit]; - if(max_bots >= 0) { - auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId()); - if(ownBots.size() > max_bots) - return command_result{error::music_client_limit_reached}; - } - - MusicClient::Type::value create_type; - if(cmd[0].has("type")) { - create_type = cmd["type"].as(); - switch(create_type) { - case MusicClient::Type::PERMANENT: - if(permissions[permission::b_client_music_create_permanent] != 1) - return command_result{permission::b_client_music_create_permanent}; - break; - case MusicClient::Type::SEMI_PERMANENT: - if(permissions[permission::b_client_music_create_semi_permanent] != 1) - return command_result{permission::b_client_music_create_semi_permanent}; - break; - case MusicClient::Type::TEMPORARY: - if(permissions[permission::b_client_music_create_temporary] != 1) - return command_result{permission::b_client_music_create_temporary}; - break; - default: - return command_result{error::vs_critical}; - } - } else { - if(permissions[permission::b_client_music_create_permanent] == 1) - create_type = MusicClient::Type::PERMANENT; - else if(permissions[permission::b_client_music_create_semi_permanent] == 1) - create_type = MusicClient::Type::SEMI_PERMANENT; - else if(permissions[permission::b_client_music_create_temporary] == 1) - create_type = MusicClient::Type::TEMPORARY; - else - return command_result{permission::b_client_music_create_temporary}; - } - - shared_lock server_channel_lock(this->server->channel_tree_lock); - auto channel = cmd[0].has("cid") ? this->server->channelTree->findChannel(cmd["cid"]) : this->currentChannel; - if(!channel) { - if(cmd[0].has("cid")) return command_result{error::channel_invalid_id}; - } else { - CHANNEL_PERMISSION_TEST(permission::i_channel_description_view_power, permission::i_channel_needed_description_view_power, channel, false); - auto permission_granted = this->calculate_permission(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) - channel = nullptr; - } - if(!channel) { - channel = this->server->channelTree->getDefaultChannel(); - } - - auto bot = this->server->musicManager->createBot(this->getClientDatabaseId()); - if(!bot) return command_result{error::vs_critical}; - bot->set_bot_type(create_type); - { - if(permissions[permission::i_client_music_create_modify_max_volume] > 0) { - auto max_volume = min(100, max(0, permissions[permission::i_client_music_create_modify_max_volume])); - if(max_volume >= 0) - bot->volume_modifier(max_volume / 100.f); - } - } - this->selectedBot = bot; - - { - server_channel_lock.unlock(); - unique_lock server_channel_w_lock(this->server->channel_tree_lock); - this->server->client_move( - bot, - channel, - nullptr, - "music bot created", - ViewReasonId::VREASON_USER_ACTION, - false, - server_channel_w_lock - ); - } - bot->properties()[property::CLIENT_LAST_CHANNEL] = channel ? channel->channelId() : 0; - bot->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; - - if(permissions[permission::i_client_music_delete_power] > 0) { - bot->clientPermissions->set_permission(permission::i_client_music_needed_delete_power, {permissions[permission::i_client_music_delete_power],0}, permission::v2::set_value, permission::v2::do_nothing); - } - - Command notify(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifymusiccreated" : ""); - notify["bot_id"] = bot->getClientDatabaseId(); - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMusicBotDelete(Command& cmd) { - if(!config::music::enabled) return command_result{error::music_disabled}; - - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - - bool permPower = this->permissionGranted(permission::i_client_music_delete_power, bot->permissionValue(permission::i_client_music_needed_delete_power)); - if(bot->getOwner() != this->getClientDatabaseId()) { - if(!permPower) return command_result{permission::i_client_music_delete_power}; - } - - this->server->musicManager->deleteBot(bot); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMusicBotSetSubscription(ts::Command &cmd) { - if(!config::music::enabled) return command_result{error::music_disabled}; - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot && cmd["bot_id"].as() != 0) return command_result{error::music_invalid_id}; - - { - auto old_bot = this->subscribed_bot.lock(); - if(old_bot) - old_bot->remove_subscriber(_this.lock()); - } - - if(bot) { - bot->add_subscriber(_this.lock()); - this->subscribed_bot = bot; - } - - return command_result{error::ok}; -} - -void apply_song(Command& command, const std::shared_ptr& element, int index = 0) { - if(!element) return; - - command[index]["song_id"] = element ? element->getSongId() : 0; - command[index]["song_url"] = element ? element->getUrl() : ""; - command[index]["song_invoker"] = element ? element->getInvoker() : 0; - command[index]["song_loaded"] = false; - - auto entry = dynamic_pointer_cast(element); - if(entry) { - auto data = entry->song_loaded_data(); - command[index]["song_loaded"] = entry->song_loaded() && data; - - if(entry->song_loaded() && data) { - command[index]["song_title"] = data->title; - command[index]["song_description"] = data->description; - command[index]["song_thumbnail"] = data->thumbnail; - command[index]["song_length"] = data->length.count(); - } - } -} - -command_result ConnectedClient::handleCommandMusicBotPlayerInfo(Command& cmd) { - if(!config::music::enabled) return command_result{error::music_disabled}; - - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - - Command result(this->getExternalType() == CLIENT_TEAMSPEAK ? "notifymusicplayerinfo" : ""); - result["bot_id"] = bot->getClientDatabaseId(); - - result["player_state"] =(int) bot->player_state(); - if(bot->current_player()) { - result["player_buffered_index"] = bot->current_player()->bufferedUntil().count(); - result["player_replay_index"] = bot->current_player()->currentIndex().count(); - result["player_max_index"] = bot->current_player()->length().count(); - result["player_seekable"] = bot->current_player()->seek_supported(); - - result["player_title"] = bot->current_player()->songTitle(); - result["player_description"] = bot->current_player()->songDescription(); - } else { - result["player_buffered_index"] = 0; - result["player_replay_index"] = 0; - result["player_max_index"] = 0; - result["player_seekable"] = 0; - result["player_title"] = ""; - result["player_description"] = ""; - } - - apply_song(result, bot->current_song()); - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMusicBotPlayerAction(Command& cmd) { - if(!config::music::enabled) return command_result{error::music_disabled}; - - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - - if(cmd["action"] == 0) { - bot->stopMusic(); - } else if(cmd["action"] == 1) { - bot->playMusic(); - } else if(cmd["action"] == 2) { - bot->player_pause(); - } else if(cmd["action"] == 3) { - bot->forwardSong(); - } else if(cmd["action"] == 4) { - bot->rewindSong(); - } else if(cmd["action"] == 5) { - if(!bot->current_player()) return command_result{error::music_no_player}; - bot->current_player()->forward(::music::PlayerUnits(cmd["units"].as())); - } else if(cmd["action"] == 6) { - if(!bot->current_player()) return command_result{error::music_no_player}; - bot->current_player()->rewind(::music::PlayerUnits(cmd["units"].as())); - } else return command_result{error::music_invalid_action}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistList(ts::Command &cmd) { - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto self_dbid = this->getClientDatabaseId(); - auto playlist_view_power = this->cached_permission_value(permission::i_playlist_view_power); - auto playlists = this->server->musicManager->playlists(); - - playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr& playlist) { - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] == self_dbid) - return false; - - auto needed_view_power = playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power); - return !this->permission_granted(playlist_view_power, needed_view_power, false); - }), playlists.end()); - - if(playlists.empty()) - return command_result{error::database_empty_result}; - - Command notify(this->notify_response_command("notifyplaylistlist")); - - size_t index = 0; - for(const auto& entry : playlists) { - notify[index]["playlist_id"] = entry->playlist_id(); - auto bot = entry->current_bot(); - notify[index]["playlist_bot_id"] = bot ? bot->getClientDatabaseId() : 0; - notify[index]["playlist_title"] = entry->properties()[property::PLAYLIST_TITLE].value(); - notify[index]["playlist_type"] = entry->properties()[property::PLAYLIST_TYPE].value(); - notify[index]["playlist_owner_dbid"] = entry->properties()[property::PLAYLIST_OWNER_DBID].value(); - notify[index]["playlist_owner_name"] = entry->properties()[property::PLAYLIST_OWNER_NAME].value(); - notify[index]["needed_power_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power); - notify[index]["needed_power_permission_modify"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power); - notify[index]["needed_power_delete"] = entry->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power); - notify[index]["needed_power_song_add"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power); - notify[index]["needed_power_song_move"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power); - notify[index]["needed_power_song_remove"] = entry->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power); - index++; - } - - this->sendCommand(notify); - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistCreate(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - CACHED_PERM_CHECK(permission::b_playlist_create, 1, true); - - { - auto max_playlists = this->cached_permission_value(permission::i_max_playlists); - if(max_playlists != permNotGranted) { - auto playlists = ref_server->musicManager->find_playlists_by_client(this->getClientDatabaseId()); - if(!this->permission_granted(this->cached_permission_value(permission::i_max_playlists), playlists.size(), false)) - return command_result{permission::i_max_playlists}; - } - } - - auto playlist = ref_server->musicManager->create_playlist(this->getClientDatabaseId(), this->getDisplayName()); - if(!playlist) return command_result{error::vs_critical}; - - playlist->properties()[property::PLAYLIST_TYPE] = music::Playlist::Type::GENERAL; - - { - auto max_songs = this->cached_permission_value(permission::i_max_playlist_size); - if(max_songs != permNotGranted) - playlist->properties()[property::PLAYLIST_MAX_SONGS] = max_songs; - } - - playlist->permissions()->setPermission(permission::i_playlist_song_needed_remove_power, this->cached_permission_value(permission::i_playlist_song_remove_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_delete_power, this->cached_permission_value(permission::i_playlist_delete_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_modify_power, this->cached_permission_value(permission::i_playlist_modify_power), nullptr); - playlist->permissions()->setPermission(permission::i_playlist_needed_permission_modify_power, this->cached_permission_value(permission::i_playlist_permission_modify_power), nullptr); - - Command notify(this->notify_response_command("notifyplaylistcreated")); - notify["playlist_id"] = playlist->playlist_id(); - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistDelete(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_delete_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_delete_power)); - - string error; - if(!ref_server->musicManager->delete_playlist(playlist->playlist_id(), error)) { - logError(this->getServerId(), "Failed to delete playlist with id {}. Error: {}", playlist->playlist_id(), error); - return command_result{error::vs_critical}; - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistInfo(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - - - Command notify(this->notify_response_command("notifyplaylistinfo")); - for(const auto& property : playlist->properties().list_properties(property::FLAG_PLAYLIST_VARIABLE)) { - notify[property.type().name] = property.value(); - } - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistEdit(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power)); - - deque, string>> properties; - - for(const auto& key : cmd[0].keys()) { - if(key == "playlist_id") continue; - if(key == "return_code") continue; - - auto property = property::info(key); - if(*property == property::PLAYLIST_UNDEFINED) { - logError(this->getServerId(), R"([{}] Tried to edit a not existing playlist property "{}" to "{}")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if((property->flags & property::FLAG_USER_EDITABLE) == 0) { - logError(this->getServerId(), "[{}] Tried to change a playlist property which is not changeable. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if(!property->validate_input(cmd[key].as())) { - logError(this->getServerId(), "[{}] Tried to change a playlist property to an invalid value. (Key: {}, Value: \"{}\")", CLIENT_STR_LOG_PREFIX, key, cmd[key].string()); - continue; - } - - if(*property == property::PLAYLIST_CURRENT_SONG_ID) { - auto song_id = cmd[key].as(); - auto song = song_id > 0 ? playlist->find_song(song_id) : nullptr; - if(song_id != 0 && !song) - return command_result{error::playlist_invalid_song_id}; - } else if(*property == property::PLAYLIST_MAX_SONGS) { - auto value = cmd[key].as(); - auto max_value = this->cached_permission_value(permission::i_max_playlist_size); - if(!this->permission_granted(max_value, value, false)) - return command_result{permission::i_max_playlist_size}; - } - - properties.emplace_back(property, key); - } - for(const auto& property : properties) { - if(*property.first == property::PLAYLIST_CURRENT_SONG_ID) { - playlist->set_current_song(cmd[property.second]); - continue; - } - - playlist->properties()[property.first] = cmd[property.second].string(); - } - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistPermList(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::b_virtualserver_playlist_permission_list, 1, true); - - auto permissions = playlist->permissions()->listPermissions(PERM_FLAG_PUBLIC); - if(permissions.empty()) - return command_result{error::vs_critical}; - - Command result(this->notify_response_command("notifyplaylistpermlist")); - int index = 0; - result["playlist_id"] = playlist->playlist_id(); - for (const auto &elm : permissions) { - if(elm->hasValue()) { - result[index]["permid"] = elm->type->type; - - result[index]["permvalue"] = elm->value; - result[index]["permnegated"] = elm->flag_negate; - result[index]["permskip"] = elm->flag_skip; - index++; - } - if(elm->hasGrant()) { - result[index]["permid"] = (uint16_t) (elm->type->type | PERM_ID_GRANT); - result[index]["permvalue"] = elm->granted; - result[index]["permnegated"] = 0; - result[index]["permskip"] = 0; - index++; - } - } - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistAddPerm(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); - - auto maxValue = this->getPermissionGrantValue(permission::i_permission_modify_power, this->currentChannel); - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); - - bool conOnError = cmd[0].has("continueonerror"); - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - auto val = cmd[index]["permvalue"].as(); - if(permission_require_granted_value(permType) && val > maxValue) - return command_result{permission::i_permission_modify_power}; - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - playlist->permissions()->setPermissionGranted(permType, cmd[index]["permvalue"], nullptr); - } else { - - playlist->permissions()->setPermission(permType, cmd[index]["permvalue"], nullptr, cmd[index]["permskip"], cmd[index]["permnegated"]); - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistDelPerm(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power), true); - - bool ignoreGrant = this->permission_granted(this->cached_permission_value(permission::b_permission_modify_power_ignore), 1, true); - bool conOnError = cmd[0].has("continueonerror"); - - for (int index = 0; index < cmd.bulkCount(); index++) { - PARSE_PERMISSION(cmd); - - if(!ignoreGrant && !this->permissionGrantGranted(permType, 1, this->currentChannel)) - return command_result{permission::i_permission_modify_power}; - - if (grant) { - playlist->permissions()->setPermissionGranted(permType, permNotGranted, nullptr); - } else { - playlist->permissions()->deletePermission(permType, nullptr); - } - } - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - - auto songs = playlist->list_songs(); - if(songs.empty()) - return command_result{error::database_empty_result}; - - Command notify(this->notify_response_command("notifyplaylistsonglist")); - notify["playlist_id"] = playlist->playlist_id(); - - size_t index = 0; - for(const auto& song : songs) { - notify[index]["song_id"] = song->id; - notify[index]["song_invoker"] = song->invoker; - notify[index]["song_previous_song_id"] = song->previous_song_id; - notify[index]["song_url"] = song->url; - notify[index]["song_url_loader"] = song->url_loader; - notify[index]["song_loaded"] = song->loaded; - notify[index]["song_metadata"] = song->metadata; - index++; - } - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_add_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_add_power)); - - if(!cmd[0].has("invoker")) - cmd["invoker"] = ""; - if(!cmd[0].has("previous")) { - auto songs = playlist->list_songs(); - if(songs.empty()) - cmd["previous"] = "0"; - else - cmd["previous"] = songs.back()->id; - } - - - auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]); - if(!song) return command_result{error::vs_critical}; - - Command notify(this->notify_response_command("notifyplaylistsongadd")); - notify["song_id"] = song->id; - this->sendCommand(notify); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistSongReorder(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_move_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_move_power)); - - SongId song_id = cmd["song_id"]; - SongId previous_id = cmd["song_previous_song_id"]; - - auto song = playlist->find_song(song_id); - if(!song) return command_result{error::playlist_invalid_song_id}; - - if(!playlist->reorder_song(song_id, previous_id)) - return command_result{error::vs_critical}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist) return command_result{error::playlist_invalid_id}; - - if(playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_song_remove_power, playlist->permissions()->getPermissionValue(permission::i_playlist_song_needed_remove_power)); - - SongId song_id = cmd["song_id"]; - - auto song = playlist->find_song(song_id); - if(!song) return command_result{error::playlist_invalid_song_id}; - - if(!playlist->delete_song(song_id)) - return command_result{error::vs_critical}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) { - return command_result{error::not_implemented}; //FIXME - - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - PERM_CHECK_CHANNELR(permission::i_client_music_info, bot->permissionValue(permission::i_client_music_needed_info, bot->currentChannel), this->currentChannel, true); - - - bool bulked = cmd.hasParm("bulk") || cmd.hasParm("balk") || cmd.hasParm("pipe") || cmd.hasParm("bar") || cmd.hasParm("paypal"); - int command_index = 0; - - Command notify(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - { - auto history = bot->queue()->history(); - for(int index = history.size(); index > 0; index--) { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - - apply_song(notify, history[index - 1], command_index); - notify[command_index]["queue_index"] = -index; - - if(!bulked) - this->sendCommand(notify); - else - command_index++; - } - } - - { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - - auto song = bot->queue()->currentSong(); - apply_song(notify, song, command_index); - notify[command_index]["queue_index"] = 0; - - if(!bulked) - this->sendCommand(notify); - else if(song) - command_index++; - } - - - { - auto queue = bot->queue()->queueEntries(); - for(int index = 0; index < queue.size(); index++) { - if(!bulked) - notify = Command(this->getExternalType() == CLIENT_VOICE ? "notifymusicqueueentry" : ""); - - apply_song(notify, queue[index], command_index); - notify[command_index]["queue_index"] = index + 1; - - if(!bulked) - this->sendCommand(notify); - else - command_index++; - } - } - - debugMessage(this->getServerId(),"Send: {}",notify.build()); - if(bulked) { - if(command_index > 0) { - this->sendCommand(notify); - } else return { ErrorType::DBEmpty }; - } - - if(this->getExternalType() == CLIENT_VOICE) { - Command notify("notifymusicqueuefinish"); - notify["bot_id"] = bot->getClientDatabaseId(); - this->sendCommand(notify); - } - - return command_result{error::ok}; - */ -} - -command_result ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) { - return command_result{error::not_implemented}; //FIXME - - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - - MusicClient::loader_t loader; - auto& type = cmd[0]["type"]; - if((type.castable() && type.as() == 0) || type.as() == "yt") { - loader = bot->ytLoader(this->getServer()); - } else if((type.castable() && type.as() == 1) || type.as() == "ffmpeg") { - loader = bot->ffmpegLoader(this->getServer()); - } else if((type.castable() && type.as() == 2) || type.as() == "channel") { - loader = bot->channelLoader(this->getServer()); - } else if((type.castable() && type.as() == -1) || type.as() == "any") { - loader = bot->providerLoader(this->getServer(), ""); - } - if(!loader) return command_result{error::music_invalid_action}; - - auto entry = bot->queue()->insertEntry(cmd["url"], _this.lock(), loader); - if(!entry) return command_result{error::vs_critical}; - - this->server->forEachClient([&](shared_ptr client) { - client->notifyMusicQueueAdd(bot, entry, bot->queue()->queueEntries().size() - 1, _this.lock()); - }); - - return command_result{error::ok}; - */ -} - -command_result ConnectedClient::handleCommandMusicBotQueueRemove(Command& cmd) { - return command_result{error::not_implemented}; //FIXME - - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - - std::deque> songs; - for(int index = 0; index < cmd.bulkCount(); index++) { - auto entry = bot->queue()->find_queue(cmd["song_id"]); - if(!entry) { - if(cmd.hasParm("skip_error")) continue; - return command_result{error::database_empty_result}; - } - - songs.push_back(move(entry)); - } - - for(const auto& entry : songs) - bot->queue()->deleteEntry(dynamic_pointer_cast(entry)); - this->server->forEachClient([&](shared_ptr client) { - client->notifyMusicQueueRemove(bot, songs, _this.lock()); - }); - return command_result{error::ok}; - */ -} - -command_result ConnectedClient::handleCommandMusicBotQueueReorder(Command& cmd) { - return command_result{error::not_implemented}; //FIXME - - /* - CMD_REQ_SERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = this->server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - - auto entry = bot->queue()->find_queue(cmd["song_id"]); - if(!entry) return command_result{error::database_empty_result}; - - auto order = bot->queue()->changeOrder(entry, cmd["index"]); - if(order < 0) return command_result{error::vs_critical}; - this->server->forEachClient([&](shared_ptr client) { - client->notifyMusicQueueOrderChange(bot, entry, order, _this.lock()); - }); - return command_result{error::ok}; - */ -} - -command_result ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto bot = ref_server->musicManager->findBotById(cmd["bot_id"]); - if(!bot) return command_result{error::music_invalid_id}; - if(bot->getOwner() != this->getClientDatabaseId()) - PERM_CHECK_CHANNELR(permission::i_client_music_play_power, bot->permissionValue(permission::i_client_music_needed_play_power, bot->currentChannel), this->currentChannel, true); - - auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]); - if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id}; - - if(ref_server->musicManager->find_bot_by_playlist(playlist)) - return command_result{error::playlist_already_in_use}; - - if(playlist && playlist->properties()[property::PLAYLIST_OWNER_DBID] != this->getClientDatabaseId()) - CACHED_PERM_CHECK(permission::i_playlist_view_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_view_power)); - - if(!ref_server->musicManager->assign_playlist(bot, playlist)) - return command_result{error::vs_critical}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandHelp(Command& cmd) { - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(5); - PERM_CHECKR(permission::b_serverinstance_help_view, 1, false); - - string command = cmd[0].has("command") ? cmd["command"].as() : ""; - if(command.empty()) - for(const auto& key : cmd[0].keys()) { - command = key; - break; - } - if(command.empty()) - command = "help"; - std::transform(command.begin(), command.end(), command.begin(), ::tolower); - - auto file = fs::u8path("commanddocs/" + command + ".txt"); - if(!fs::exists(file)) return command_result{error::file_not_found}; - - string line; - ifstream stream(file); - if(!stream) return command_result{error::file_io_error}; - while(getline(stream, line)) - this->sendCommand(Command{line}); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandPermReset(ts::Command& cmd) { - CMD_REQ_FSERVER; - CMD_RESET_IDLE; - CMD_CHK_AND_INC_FLOOD_POINTS(50); - PERM_CHECKR(permission::b_virtualserver_permission_reset, 1, true); - - string token; - if(!this->server->resetPermissions(token)) - return command_result{error::vs_critical}; - - Command result(""); - result["token"] = token; - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandLogView(ts::Command& cmd) { - CMD_CHK_AND_INC_FLOOD_POINTS(50); - - auto lagacy = this->getType() == CLIENT_TEAMSPEAK || cmd.hasParm("lagacy") || cmd.hasParm("legacy"); - string log_path; - ServerId target_server = cmd[0].has("instance") && cmd["instance"].as() ? (ServerId) 0 : this->getServerId(); - string server_identifier; - if(target_server > 0) - server_identifier = to_string(target_server); - else server_identifier = "[A-Z]{0,7}"; - - if(target_server == 0) - PERM_CHECKR(permission::b_serverinstance_log_view, 1, true); - else - PERM_CHECKR(permission::b_virtualserver_log_view, 1, true); - - for(const auto& sink : logger::logger(target_server)->sinks()) { - if(dynamic_pointer_cast(sink)) { - log_path = dynamic_pointer_cast(sink)->filename(); - break; - } else if(dynamic_pointer_cast(sink)) { - log_path = dynamic_pointer_cast(sink)->filename(); - break; - } - } - if(log_path.empty()) - return command_result{error::file_not_found, "Cant find log file (May log disabled?)"}; - - { //Replace " within the log path - size_t index = 0; - while((index = log_path.find('"', index)) != string::npos) { - log_path.replace(index, 1, "\\\""); - index += 2; - } - } - string command = "cat \"" + log_path + "\""; - command += " | grep -E "; - command += "\"\\] \\[.*\\]( ){0,6}?" + server_identifier + " \\|\""; - - size_t beginpos = cmd[0].has("begin_pos") ? cmd["begin_pos"].as() : 0ULL; //TODO test it? - size_t file_index = 0; - size_t max_lines = cmd[0].has("lines") ? cmd["lines"].as() : 100ULL; //TODO bounds? - deque> lines; - { - debugMessage(target_server, "Logview command: \"{}\"", command); - - array buffer{}; - string line_buffer; - - std::shared_ptr pipe(popen(command.c_str(), "r"), pclose); - if (!pipe) return command_result{error::file_io_error, "Could not execute command"}; - while (!feof(pipe.get())) { - auto read = fread(buffer.data(), 1, buffer.size(), pipe.get()); - if(read > 0) { - if(beginpos == 0 || file_index < beginpos) { - if(beginpos != 0 && file_index + read > beginpos) { //We're done we just want to get the size later - line_buffer += string(buffer.data(), beginpos - file_index); - - lines.push_back({file_index, line_buffer}); - if(lines.size() > max_lines) lines.pop_front(); - //debugMessage(LOG_GENERAL, "Final line {}", line_buffer); - line_buffer = ""; - } else { - line_buffer += string(buffer.data(), read); - - size_t index; - size_t length; - size_t cut_offset = 0; - while((index = line_buffer.find("\n")) != string::npos || (index = line_buffer.find("\r")) != string::npos) { - length = 0; - if(index > 0) { - if(line_buffer[index - 1] == '\r' || line_buffer[index - 1] == '\n') { - length = 2; - index--; - } - } - if(length == 0) { - if(index + 1 < line_buffer.length()) { - if(line_buffer[index + 1] == '\r' || line_buffer[index + 1] == '\n') { - length = 2; - } - } - } - if(length == 0) length = 1; - - //debugMessage(LOG_GENERAL, "Got line {}", line_buffer.substr(0, index)); - lines.push_back({file_index + cut_offset, line_buffer.substr(0, index)}); - if(lines.size() > max_lines) lines.pop_front(); - - cut_offset += index + length; - line_buffer = line_buffer.substr(index + length); - } - } - } - file_index += read; - } else if(read < 0) return command_result{error::file_io_error, "fread(...) returned " + to_string(read) + " (" + to_string(errno) + ")"}; - } - - if(!line_buffer.empty()) { - lines.push_back({file_index - line_buffer.length(), line_buffer}); - if(lines.size() > max_lines) lines.pop_front(); - } - } - //last_pos=1558 file_size=1764 l - if(lines.empty()) return command_result{error::database_empty_result}; - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyserverlog" : ""); - result["last_pos"] = lines.front().first; - result["file_size"] = file_index; - - if(!(cmd.hasParm("reverse") && cmd["revers"].as())) - std::reverse(lines.begin(), lines.end()); - - int index = 0; - for(const auto& index_line : lines) { - auto line = index_line.second; - //2018-07-15 21:01:46.488639 - //TeamSpeak format: - //YYYY-MM-DD hh:mm:ss.millis|{:<8}|{:<14}|{:<3}|.... - //2018-07-15 21:01:47.066367|INFO |VirtualServer |1 |listening on 0.0.0.0:9989, [::]:9989 - - //TeaSpeak: - //[2018-07-15 23:21:47] [ERROR] Timer sql_test tick needs more than 9437 microseconds. Max allowed was 5000 microseconds. - if(lagacy) { - string ts = line.substr(1, 19) + ".000000|"; - - { - string type = "unknown"; - - auto idx = line.find_first_of('[', 2); - if(idx != string::npos) { - type = line.substr(idx + 1, line.find(']', idx + 1) - idx - 1); - } - - ts += type + " | | |" + line.substr(line.find('|') + 1); - } - result[index++]["l"] = ts; - } else { - result[index++]["l"] = line; - } - } - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandUpdateMyTsId(ts::Command &) { - if(config::voice::suppress_myts_warnings) return command_result{error::ok}; - return command_result{error::not_implemented}; -} - -command_result ConnectedClient::handleCommandUpdateMyTsData(ts::Command &) { - if(config::voice::suppress_myts_warnings) return command_result{error::ok}; - return command_result{error::not_implemented}; -} - - -command_result ConnectedClient::handleCommandQueryList(ts::Command &cmd) { - OptionalServerId server_id = EmptyServerId; - if(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK) - server_id = this->getServerId(); - - if(cmd[0].has("server_id")) - server_id = cmd["server_id"]; - if(cmd[0].has("sid")) - server_id = cmd["sid"]; - - auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); - if(!server && server_id != EmptyServerId && server_id != 0) - return command_result{error::server_invalid_id}; - - auto global_list = this->permissionGranted(permission::b_client_query_list, 1, nullptr, true, nullptr, server, true); - auto own_list = global_list || this->permissionGranted(permission::b_client_query_list_own, 1, nullptr, true, nullptr, server, true); - - if(!own_list && !global_list) - return command_result{permission::b_client_query_list}; - - auto accounts = serverInstance->getQueryServer()->list_query_accounts(server_id); - if(!global_list) { - accounts.erase(remove_if(accounts.begin(), accounts.end(), [&](const std::shared_ptr& account) { - return account->unique_id != this->getUid(); - }), accounts.end()); - } - - if(accounts.empty()) - return command_result{error::database_empty_result}; - - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerylist" : ""); - result["server_id"] = server_id; - result["flag_own"] = own_list; - result["flag_all"] = global_list; - - size_t index = 0; - for(const auto& account : accounts) { - result[index]["client_unique_identifier"] = account->unique_id; - result[index]["client_login_name"] = account->username; - result[index]["client_bound_server"] = account->bound_server; - index++; - } - - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandQueryCreate(ts::Command &cmd) { - OptionalServerId server_id = this->getServerId(); - if(cmd[0].has("server_id")) - server_id = cmd["server_id"]; - if(cmd[0].has("sid")) - server_id = cmd["sid"]; - - auto server = server_id == EmptyServerId ? nullptr : serverInstance->getVoiceServerManager()->findServerById(server_id); - if(!server && server_id != EmptyServerId && server_id != 0) - return command_result{error::server_invalid_id}; - - if(!this->permissionGranted(permission::b_client_query_create, 1, nullptr, true, nullptr, server, true)) - return command_result{permission::b_client_query_create}; - - auto username = cmd["client_login_name"].as(); - auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; - - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); - - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(account) return command_result{error::query_already_exists}; - - account = serverInstance->getQueryServer()->create_query_account(username, server_id, this->getUid(), password); - if(!account) - return command_result{error::vs_critical}; - - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerycreated" : ""); - result["client_unique_identifier"] = account->unique_id; - result["client_login_name"] = account->username; - result["client_login_password"] = password; - result["client_bound_server"] = account->bound_server; - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandQueryDelete(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return command_result{error::query_not_exists}; - - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - /* If the server is not existing anymore, we're asking for global permissions - if(!server && account->bounded_server != 0) - return command_result{error::server_invalid_id}; - */ - - auto delete_all = this->permissionGranted(permission::b_client_query_delete, 1, nullptr, true, nullptr, server, true); - auto delete_own = delete_all || this->permissionGranted(permission::b_client_query_delete_own, 1, nullptr, true, nullptr, server, true); - - if(account->unique_id == this->getUid()) { - if(!delete_own) - return command_result{permission::b_client_query_delete_own}; - } else { - if(!delete_all) - return command_result{permission::b_client_query_delete}; - } - if(account->unique_id == "serveradmin" && account->username == "serveradmin") - return command_result{error::vs_critical}; - - serverInstance->getQueryServer()->delete_query_account(account); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandQueryRename(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto new_username = cmd["client_new_login_name"].as(); - - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return command_result{error::query_not_exists}; - - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - if(!server && account->bound_server != 0) - return command_result{error::server_invalid_id}; - - auto rename_all = this->permissionGranted(permission::b_client_query_rename, 1, nullptr, true, nullptr, server, true); - auto rename_own = rename_all || this->permissionGranted(permission::b_client_query_rename_own, 1, nullptr, true, nullptr, server, true); - - if(account->unique_id == this->getUid()) { - if(!rename_own) - return command_result{permission::b_client_query_rename_own}; - } else { - if(!rename_all) - return command_result{permission::b_client_query_rename}; - } - - auto target_account = serverInstance->getQueryServer()->find_query_account_by_name(new_username); - if(target_account) return command_result{error::query_already_exists}; - - if(account->unique_id == "serveradmin" && account->username == "serveradmin") - return command_result{error::parameter_invalid}; - - if(!serverInstance->getQueryServer()->rename_query_account(account, new_username)) - return command_result{error::vs_critical}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandQueryChangePassword(ts::Command &cmd) { - auto username = cmd["client_login_name"].as(); - auto account = serverInstance->getQueryServer()->find_query_account_by_name(username); - if(!account) - return command_result{error::query_not_exists}; - - auto server = serverInstance->getVoiceServerManager()->findServerById(account->bound_server); - if(!server && account->bound_server != 0) - return command_result{error::server_invalid_id}; - - auto change_all = this->permissionGranted(server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global, 1, nullptr, true, nullptr, server, true); - auto change_own = change_all || this->permissionGranted(permission::b_client_query_change_own_password, 1, nullptr, true, nullptr, server, true); - - auto password = cmd[0].has("client_login_password") ? cmd["client_login_password"].as() : ""; - - if(password.empty()) - password = rnd_string(QUERY_PASSWORD_LENGTH); - - if(account->unique_id == this->getUid()) { - if(!change_own) - return command_result{permission::b_client_query_change_own_password}; - } else { - if(!change_all) - return command_result{server ? permission::b_client_query_change_password : permission::b_client_query_change_password_global}; - } - - if(!serverInstance->getQueryServer()->change_query_password(account, password)) - return command_result{error::vs_critical}; - - Command result(this->getExternalType() == ClientType::CLIENT_TEAMSPEAK ? "notifyquerypasswordchanges" : ""); - result["client_login_name"] = account->username; - result["client_login_password"] = password; - this->sendCommand(result); - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandDummy_IpChange(ts::Command &cmd) { - CMD_REF_SERVER(server); - logMessage(this->getServerId(), "[{}] Address changed from {} to {}", CLIENT_STR_LOG_PREFIX, cmd["old_ip"].string(), cmd["new_ip"].string()); - - if(geoloc::provider_vpn && !this->permissionGranted(permission::b_client_ignore_vpn, 1)) { - auto provider = this->isAddressV4() ? geoloc::provider_vpn->resolveInfoV4(this->getPeerIp(), true) : geoloc::provider_vpn->resolveInfoV6(this->getPeerIp(), true); - if(provider) { - this->disconnect(strvar::transform(ts::config::messages::kick_vpn, strvar::StringValue{"provider.name", provider->name}, strvar::StringValue{"provider.website", provider->side})); - return command_result{error::ok}; - } - } - - string new_country = config::geo::countryFlag; - if(geoloc::provider) { - auto loc = this->isAddressV4() ? geoloc::provider->resolveInfoV4(this->getPeerIp(), false) : geoloc::provider->resolveInfoV6(this->getPeerIp(), false); - if(loc) { - logError(this->getServerId(), "[{}] Received new ip location. IP {} traced to {} ({}).", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp(), loc->name, loc->identifier); - this->properties()[property::CLIENT_COUNTRY] = loc->identifier; - server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_COUNTRY}); - new_country = loc->identifier; - } else { - logError(this->getServerId(), "[{}] Failed to resolve ip location for IP {}.", CLIENT_STR_LOG_PREFIX, this->getLoggingPeerIp()); - } - } - - this->properties()[property::CONNECTION_CLIENT_IP] = this->getLoggingPeerIp(); - return command_result{error::ok}; -} - -//conversationhistory cid=1 [cpw=xxx] [timestamp_begin] [timestamp_end (0 := no end)] [message_count (default 25| max 100)] [-merge] -command_result ConnectedClient::handleCommandConversationHistory(ts::Command &command) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - if(!command[0].has("cid") || !command[0]["cid"].castable()) - return command_result{error::conversation_invalid_id}; - - auto conversation_id = command[0]["cid"].as(); - /* test if we have access to the conversation */ - { - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(conversation_id); - if(!channel) - return command_result{error::conversation_invalid_id}; - if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) - return command_result{error::conversation_is_private}; - } - - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(conversation_id); - if(!channel) /* should never happen! */ - return command_result{error::conversation_invalid_id}; - - if(!command[0].has("cpw")) - command[0]["cpw"] = ""; - - if (!channel->passwordMatch(command["cpw"], true)) - if (!this->permissionGranted(permission::b_channel_join_ignore_password, 1, channel, true)) - return command_result{error::channel_invalid_password, "invalid password"}; - - if(!this->permissionGranted(permission::b_channel_ignore_join_power, 1, channel, true)) { - CHANNEL_PERMISSION_TEST(permission::i_channel_join_power, permission::i_channel_needed_join_power, channel, false); - } - } - } - - auto conversation_manager = ref_server->conversation_manager(); - auto conversation = conversation_manager->get(conversation_id); - if(!conversation) - return command_result{error::database_empty_result}; - - system_clock::time_point timestamp_begin = system_clock::now(); - system_clock::time_point timestamp_end; - size_t message_count = 25; - - if(command[0].has("timestamp_begin")) - timestamp_begin = system_clock::time_point{} + milliseconds(command[0]["timestamp_begin"].as()); - - if(command[0].has("timestamp_end")) - timestamp_end = system_clock::time_point{} + milliseconds(command[0]["timestamp_end"].as()); - - if(command[0].has("message_count")) - message_count = command[0]["message_count"].as(); - - if(timestamp_begin < timestamp_end) - return command_result{error::parameter_invalid}; - if(message_count > 100) - message_count = 100; - - auto messages = conversation->message_history(timestamp_begin, message_count + 1, timestamp_end); /* query one more to test for more data */ - if(messages.empty()) - return command_result{error::database_empty_result}; - bool more_data = messages.size() > message_count; - if(more_data) - messages.pop_back(); - - Command notify(this->notify_response_command("notifyconversationhistory")); - size_t index = 0; - size_t length = 0; - bool merge = command.hasParm("merge"); - - for(auto it = messages.rbegin(); it != messages.rend(); it++) { - if(index == 0) { - notify[index]["cid"] = conversation_id; - notify[index]["flag_volatile"] = conversation->volatile_only(); - } - - auto& message = *it; - notify[index]["timestamp"] = duration_cast(message->message_timestamp.time_since_epoch()).count(); - notify[index]["sender_database_id"] = message->sender_database_id; - notify[index]["sender_unique_id"] = message->sender_unique_id; - notify[index]["sender_name"] = message->sender_name; - - notify[index]["msg"] = message->message; - length += message->message.size(); - length += message->sender_name.size(); - length += message->sender_unique_id.size(); - if(length > 1024 * 8 || !merge) { - index = 0; - this->sendCommand(notify); - notify = Command{this->notify_response_command("notifyconversationhistory")}; - } else - index++; - } - if(index > 0) - this->sendCommand(notify); - - if(more_data) - return command_result{error::conversation_more_data}; - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandConversationFetch(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - - Command result(this->notify_response_command("notifyconversationindex")); - size_t result_index = 0; - - auto conversation_manager = ref_server->conversation_manager(); - for(size_t index = 0; index < cmd.bulkCount(); index++) { - auto& bulk = cmd[index]; - - if(!bulk.has("cid") || !bulk["cid"].castable()) - continue; - auto conversation_id = bulk["cid"].as(); - - auto& result_bulk = result[result_index++]; - result_bulk["cid"] = conversation_id; - - /* test if we have access to the conversation */ - { - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(conversation_id); - if(!channel) { - auto error = findError("conversation_invalid_id"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - if(channel->channel()->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as()) { - auto error = findError("conversation_is_private"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - } - - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(conversation_id); - if(!channel) { /* should never happen! */ - auto error = findError("conversation_invalid_id"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - - if(!bulk.has("cpw")) - bulk["cpw"] = ""; - - if (!channel->passwordMatch(bulk["cpw"], true)) - if (!this->permissionGranted(permission::b_channel_join_ignore_password, 1, channel, true)) { - auto error = findError("channel_invalid_password"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - continue; - } - - if(!this->permissionGranted(permission::b_channel_ignore_join_power, 1, channel, true)) { - auto permission_granted = this->calculate_permission(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) { - auto error = findError("server_insufficeient_permissions"); - result_bulk["error_id"] = error.errorId; - result_bulk["error_msg"] = error.message; - result_bulk["failed_permid"] = permission::i_channel_join_power; - continue; - } - } - } - } - - auto conversation = conversation_manager->get(conversation_id); - if(conversation) - result_bulk["timestamp"] = duration_cast(conversation->last_message().time_since_epoch()).count(); - else - result_bulk["timestamp"] = 0; - result_bulk["flag_volatile"] = conversation->volatile_only(); - } - if(result_index == 0) - return command_result{error::database_empty_result}; - this->sendCommand(result); - - - return command_result{error::ok}; -} - -command_result ConnectedClient::handleCommandConversationMessageDelete(ts::Command &cmd) { - CMD_REF_SERVER(ref_server); - CMD_CHK_AND_INC_FLOOD_POINTS(25); - - auto conversation_manager = ref_server->conversation_manager(); - std::shared_ptr current_conversation; - ChannelId current_conversation_id = 0; - - for(size_t index = 0; index < cmd.bulkCount(); index++) { - auto &bulk = cmd[index]; - - if(!bulk.has("cid") || !bulk["cid"].castable()) - continue; - - /* test if we have access to the conversation */ - if(current_conversation_id != bulk["cid"].as()) { - current_conversation_id = bulk["cid"].as(); - - /* test if we're able to see the channel */ - { - shared_lock channel_view_lock(this->channel_lock); - auto channel = this->channel_view()->find_channel(current_conversation_id); - if(!channel) - return command_result{error::conversation_invalid_id}; - } - - /* test if there is a channel password or join power which denies that we see the conversation */ - { - shared_lock channel_view_lock(ref_server->channel_tree_lock); - auto channel = ref_server->getChannelTree()->findChannel(current_conversation_id); - if(!channel) - return command_result{error::conversation_invalid_id}; - - if(!bulk.has("cpw")) - bulk["cpw"] = ""; - - if (!channel->passwordMatch(bulk["cpw"], true)) - if (!this->permissionGranted(permission::b_channel_join_ignore_password, 1, channel, true)) - return command_result{error::channel_invalid_password}; - - if (!this->permissionGranted(permission::b_channel_conversation_message_delete, 1, channel)) - return command_result{permission::b_channel_conversation_message_delete}; - - if(!this->permissionGranted(permission::b_channel_ignore_join_power, 1, channel, true)) { - auto permission_granted = this->calculate_permission(permission::i_channel_join_power, channel->channelId()); - if(!channel->permission_granted(permission::i_channel_needed_join_power, permission_granted, false)) - return command_result{permission::i_channel_needed_join_power}; - } - } - } - - current_conversation = conversation_manager->get(current_conversation_id); - if(!current_conversation) continue; - - auto timestamp_begin = system_clock::time_point{} + milliseconds{bulk["timestamp_begin"]}; - auto timestamp_end = system_clock::time_point{} + milliseconds{bulk.has("timestamp_end") ? bulk["timestamp_end"].as() : 0}; - auto limit = bulk.has("limit") ? bulk["limit"].as() : 1; - if(limit > 100) - limit = 100; - auto delete_count = current_conversation->delete_messages(timestamp_end, limit, timestamp_begin, bulk["cldbid"]); - if(delete_count > 0) { - for(const auto& client : ref_server->getClients()) { - if(client->connectionState() != ConnectionState::CONNECTED) - continue; - - auto type = client->getType(); - if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC) - continue; - - client->notifyConversationMessageDelete(current_conversation_id, timestamp_begin, timestamp_end, bulk["cldbid"], delete_count); - } - } - } - - return command_result{error::ok}; -} - - - - - - - - - - - - - - - - - - -