Using new permissions system consequently
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,486 @@
|
||||
//
|
||||
// Created by wolverindev on 26.01.20.
|
||||
//
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
#include <openssl/sha.h>
|
||||
#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 <experimental/filesystem>
|
||||
#include <cstdint>
|
||||
#include <StringVariable.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#include <Properties.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/sassert.h>
|
||||
#include <misc/base64.h>
|
||||
#include <misc/hex.h>
|
||||
#include <misc/digest.h>
|
||||
#include <misc/rnd.h>
|
||||
#include <misc/timer.h>
|
||||
#include <misc/strobf.h>
|
||||
#include <misc/scope_guard.h>
|
||||
#include <bbcode/bbcodes.h>
|
||||
|
||||
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
|
||||
|
||||
//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<std::shared_ptr<file::FileEntry>> 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<std::chrono::seconds>(fileEntry->lastChanged.time_since_epoch()).count();
|
||||
command[index]["type"] = fileEntry->type;
|
||||
if (fileEntry->type == file::FileType::FILE)
|
||||
command[index]["size"] = static_pointer_cast<file::File>(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<std::string>();
|
||||
if(!code.empty()) fileList["return_code"] = code;
|
||||
fileListFinished["path"] = cmd["path"].as<std::string>();
|
||||
fileList["cid"] = cmd["cid"].as<size_t>();
|
||||
fileListFinished["cid"] = cmd["cid"].as<ChannelId>();
|
||||
|
||||
if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel
|
||||
auto channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_browse_power, permission::i_ft_file_browse_power, true);
|
||||
|
||||
cmd_filelist_append_files(
|
||||
this->getServerId(),
|
||||
fileList,
|
||||
serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as<std::string>()))
|
||||
);
|
||||
} else {
|
||||
if (cmd["path"].as<string>() == "/icons" || cmd["path"].as<string>() == "/icons/") {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_icon_manage, 1);
|
||||
cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->iconDirectory(this->server)));
|
||||
} else if (cmd["path"].as<string>() == "/") {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_icon_manage, 1);
|
||||
cmd_filelist_append_files(this->getServerId(), fileList, serverInstance->getFileServer()->listFiles(serverInstance->getFileServer()->avatarDirectory(this->server)));
|
||||
} 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<VoiceClient*>(this)) {
|
||||
dynamic_cast<VoiceClient*>(this)->sendCommand0(fileList.build(), 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<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_directory_create_power, permission::i_ft_directory_create_power, true);
|
||||
|
||||
auto dir = serverInstance->getFileServer()->createDirectory(cmd["dirname"], serverInstance->getFileServer()->resolveDirectory(this->server, channel, cmd["path"].as<std::string>()));
|
||||
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<std::shared_ptr<file::FileEntry>> files;
|
||||
|
||||
if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel
|
||||
auto channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_delete_power, permission::i_ft_file_delete_power, true);
|
||||
|
||||
for (int index = 0; index < cmd.bulkCount(); index++)
|
||||
files.push_back(serverInstance->getFileServer()->findFile(cmd[index]["name"].as<std::string>(), serverInstance->getFileServer()->resolveDirectory(this->server, channel)));
|
||||
} else {
|
||||
if (cmd["name"].string().find("/icon_") == 0 && cmd["path"].string().empty()) {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_icon_manage, 1);
|
||||
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_") {
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_client_avatar_delete_other, 1);
|
||||
|
||||
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::ClientProperties>{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::ClientProperties>{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<std::string>());
|
||||
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<file::Directory> directory = nullptr;
|
||||
|
||||
if (cmd[0].has("cid") && cmd["cid"] != 0) { //Channel
|
||||
auto channel = this->server->channelTree->findChannel(cmd["cid"].as<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_upload_power, permission::i_ft_file_upload_power, true);
|
||||
directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel);
|
||||
} else {
|
||||
if (cmd["path"].as<std::string>().empty() && cmd["name"].as<std::string>().find("/icon_") == 0) {
|
||||
auto max_size = this->calculate_permission(permission::i_max_icon_filesize, 0);
|
||||
if(max_size.has_value && !max_size.has_infinite_power() && (max_size.value < 0 || max_size.value < cmd["size"].as<size_t>()))
|
||||
return command_result{permission::i_max_icon_filesize};
|
||||
directory = serverInstance->getFileServer()->iconDirectory(this->server);
|
||||
} else if (cmd["path"].as<std::string>().empty() && cmd["name"].string() == "/avatar") {
|
||||
auto max_size = this->calculate_permission(permission::i_client_max_avatar_filesize, 0);
|
||||
if(max_size.has_value && !max_size.has_infinite_power() && (max_size.value < 0 || max_size.value < cmd["size"].as<size_t>()))
|
||||
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<std::string>() << 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<ssize_t>();
|
||||
auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_UPLOADED].as<size_t>();
|
||||
server_used_quota += cmd["size"].as<uint64_t>();
|
||||
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->calculate_permission(permission::i_ft_quota_mb_upload_per_client, 0);
|
||||
auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_UPLOADED].as<size_t>();
|
||||
client_used_quota += cmd["size"].as<uint64_t>();
|
||||
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.has_value && !client_quota.has_infinite_power() && (client_quota.value < 0 || client_quota.value * 1024 * 1024 < (int64_t) client_used_quota))
|
||||
return command_result{error::file_transfer_client_quota_exceeded};
|
||||
}
|
||||
|
||||
if (cmd["overwrite"].as<bool>() && cmd["resume"].as<bool>()) return command_result{error::file_overwrite_excludes_resume};
|
||||
if (serverInstance->getFileServer()->findFile(cmd["name"].as<std::string>(), directory) && !(cmd["overwrite"].as<bool>() || cmd["resume"].as<bool>()))
|
||||
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<uint64_t>(), 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<uint64_t>();
|
||||
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<file::Directory> 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<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_download_power, permission::i_ft_file_download_power, true);
|
||||
|
||||
directory = serverInstance->getFileServer()->resolveDirectory(this->server, channel);
|
||||
} else {
|
||||
if (cmd["path"].as<std::string>().empty() && cmd["name"].as<std::string>().find("/icon_") == 0) {
|
||||
directory = serverInstance->getFileServer()->iconDirectory(this->server);
|
||||
} else if (cmd["path"].as<std::string>().empty() && cmd["name"].as<std::string>().find("/avatar_") == 0) { //TODO fix avatar download not working
|
||||
directory = serverInstance->getFileServer()->avatarDirectory(this->server);
|
||||
} else {
|
||||
cerr << "Unknown requested directory: " << cmd["path"].as<std::string>() << 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<std::string>(), 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<std::string>(), 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<ssize_t>();
|
||||
auto server_used_quota = this->server->properties()[property::VIRTUALSERVER_MONTH_BYTES_DOWNLOADED].as<size_t>();
|
||||
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->calculate_permission(permission::i_ft_quota_mb_download_per_client, 0);
|
||||
auto client_used_quota = this->properties()[property::CLIENT_MONTH_BYTES_DOWNLOADED].as<size_t>();
|
||||
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.has_value && !client_quota.has_infinite_power() && (client_quota.value < 0 || client_quota.value * 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<uint64_t>();
|
||||
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::FileEntry> file;
|
||||
if (request.has("cid") && request["cid"].as<ChannelId>() != 0) { //Channel
|
||||
auto channel = this->server->channelTree->findChannel(request["cid"].as<ChannelId>());
|
||||
if (!channel) return command_result{error::channel_invalid_id, "Cant resolve channel"};
|
||||
if (!channel->passwordMatch(cmd["cpw"]) && !permission::v2::permission_granted(1, this->calculate_permission(permission::b_ft_ignore_password, channel->channelId())))
|
||||
return cmd["cpw"].string().empty() ? command_result{permission::b_ft_ignore_password} : command_result{error::channel_invalid_password};
|
||||
|
||||
ACTION_REQUIRES_CHANNEL_PERMISSION(channel, permission::i_ft_needed_file_browse_power, permission::i_ft_file_browse_power, true);
|
||||
file = serverInstance->getFileServer()->findFile(request["name"],serverInstance->getFileServer()->resolveDirectory(this->server, channel, request["path"]));
|
||||
} else {
|
||||
std::shared_ptr<file::Directory> directory;
|
||||
if (!request.has("path") || request["path"].as<string>() == "/") {
|
||||
directory = serverInstance->getFileServer()->avatarDirectory(this->server);
|
||||
} else if (request["path"].as<string>() == "/icons" || request["path"].as<string>() == "/icons/") {
|
||||
directory = serverInstance->getFileServer()->iconDirectory(this->server);
|
||||
} else {
|
||||
cerr << "Unknown requested directory: '" << request["path"].as<std::string>() << "'" << endl;
|
||||
return command_result{error::not_implemented};
|
||||
}
|
||||
|
||||
if(!directory) continue;
|
||||
file = serverInstance->getFileServer()->findFile(cmd["name"].as<std::string>(), directory);
|
||||
}
|
||||
if(!file) continue;
|
||||
|
||||
result[result_index]["cid"] = request["cid"].as<ChannelId>();
|
||||
result[result_index]["name"] = request["name"];
|
||||
result[result_index]["path"] = request["path"];
|
||||
result[result_index]["type"] = file->type;
|
||||
result[result_index]["datetime"] = duration_cast<seconds>(file->lastChanged.time_since_epoch()).count();
|
||||
if (file->type == file::FileType::FILE)
|
||||
result[result_index]["size"] = static_pointer_cast<file::File>(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};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
/** Permission tests against required values **/
|
||||
/* use this for any action which will do something with the server */
|
||||
#define ACTION_REQUIRES_GLOBAL_PERMISSION_CACHED(required_permission_type, required_value, cache) \
|
||||
do { \
|
||||
if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, 0, false, cache))) \
|
||||
return command_result{required_permission_type}; \
|
||||
} while(0)
|
||||
|
||||
#define ACTION_REQUIRES_GLOBAL_PERMISSION(required_permission_type, required_value) \
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION_CACHED(required_permission_type, required_value, nullptr)
|
||||
|
||||
//TODO: Fixme: Really check for instance permissions!
|
||||
#define ACTION_REQUIRES_INSTANCE_PERMISSION(required_permission_type, required_value) \
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION_CACHED(required_permission_type, required_value, nullptr)
|
||||
|
||||
/* use this for anything which will do something local in relation to the target channel */
|
||||
#define ACTION_REQUIRES_PERMISSION(required_permission_type, required_value, channel_id) \
|
||||
do { \
|
||||
if(!permission::v2::permission_granted(required_value, this->calculate_permission(required_permission_type, channel_id))) \
|
||||
return command_result{required_permission_type}; \
|
||||
} while(0)
|
||||
|
||||
/** Permission tests against groups **/
|
||||
/* use this when testing a permission against a group */
|
||||
#define ACTION_REQUIRES_GROUP_PERMISSION(group, required_permission_type, own_permission_type, is_required) \
|
||||
do { \
|
||||
auto _permission_granted = this->calculate_permission(own_permission_type, 0); \
|
||||
if(!(group)->permission_granted(required_permission_type, _permission_granted, is_required)) \
|
||||
return command_result{own_permission_type}; \
|
||||
} while(0)
|
||||
|
||||
/** Permission tests against channels **/
|
||||
/* use this when testing a permission against a group */
|
||||
#define ACTION_REQUIRES_CHANNEL_PERMISSION(channel, required_permission_type, own_permission_type, is_required) \
|
||||
do { \
|
||||
auto _permission_granted = this->calculate_permission(own_permission_type, channel ? channel->channelId() : 0); \
|
||||
if(!(channel)->permission_granted(required_permission_type, _permission_granted, is_required)) \
|
||||
return command_result{own_permission_type}; \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Helper methods for channel resolve */
|
||||
#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<ChannelId>(); \
|
||||
auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as<ChannelId>()) : 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<ChannelId>(); \
|
||||
auto l_channel = channel_id ? channel_tree->findLinkedChannel(command.as<ChannelId>()) : nullptr; \
|
||||
if (!l_channel && (channel_id != 0 || force)) return command_result{error::channel_invalid_id, "Cant resolve channel"}; \
|
||||
|
||||
|
||||
|
||||
//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<permission::PermissionType>(); \
|
||||
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<string>()); \
|
||||
permType = resv->type; \
|
||||
if (resv->grantName() == cmd[index]["permsid"].as<string>()) grant = true; \
|
||||
} \
|
||||
if (permission::resolvePermissionData(permType)->type == permission::PermissionType::unknown) { \
|
||||
if (conOnError) continue; \
|
||||
return ts::command_result{error::parameter_invalid}; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline bool permission_require_granted_value(ts::permission::PermissionType type) {
|
||||
using namespace ts;
|
||||
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(ts::permission::PermissionType type) {
|
||||
using namespace ts;
|
||||
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(ts::permission::PermissionType type) {
|
||||
using namespace ts;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,924 @@
|
||||
//
|
||||
// Created by wolverindev on 26.01.20.
|
||||
//
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#include <algorithm>
|
||||
#include <openssl/sha.h>
|
||||
#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 <experimental/filesystem>
|
||||
#include <cstdint>
|
||||
#include <StringVariable.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
#include <Properties.h>
|
||||
#include <log/LogUtils.h>
|
||||
#include <misc/sassert.h>
|
||||
#include <misc/base64.h>
|
||||
#include <misc/hex.h>
|
||||
#include <misc/digest.h>
|
||||
#include <misc/rnd.h>
|
||||
#include <misc/timer.h>
|
||||
#include <misc/strobf.h>
|
||||
#include <misc/scope_guard.h>
|
||||
#include <bbcode/bbcodes.h>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
using namespace std::chrono;
|
||||
using namespace std;
|
||||
using namespace ts;
|
||||
using namespace ts::server;
|
||||
using namespace ts::token;
|
||||
|
||||
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->calculate_permissions({
|
||||
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->getChannelId());
|
||||
|
||||
auto permissions = map<permission::PermissionType, permission::v2::PermissionFlaggedValue>(permissions_list.begin(), permissions_list.end());
|
||||
|
||||
auto max_bots = permissions[permission::i_client_music_limit];
|
||||
if(max_bots.has_value) {
|
||||
auto ownBots = this->server->musicManager->listBots(this->getClientDatabaseId());
|
||||
if(!permission::v2::permission_granted(ownBots.size() + 1, 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<MusicClient::Type::value>();
|
||||
switch(create_type) {
|
||||
case MusicClient::Type::PERMANENT:
|
||||
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_permanent]))
|
||||
return command_result{permission::b_client_music_create_permanent};
|
||||
break;
|
||||
case MusicClient::Type::SEMI_PERMANENT:
|
||||
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_semi_permanent]))
|
||||
return command_result{permission::b_client_music_create_semi_permanent};
|
||||
break;
|
||||
case MusicClient::Type::TEMPORARY:
|
||||
if(!permission::v2::permission_granted(1, permissions[permission::b_client_music_create_temporary]))
|
||||
return command_result{permission::b_client_music_create_temporary};
|
||||
break;
|
||||
default:
|
||||
return command_result{error::vs_critical};
|
||||
}
|
||||
} else {
|
||||
if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_permanent]))
|
||||
create_type = MusicClient::Type::PERMANENT;
|
||||
else if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_semi_permanent]))
|
||||
create_type = MusicClient::Type::SEMI_PERMANENT;
|
||||
else if(permission::v2::permission_granted(1, permissions[permission::b_client_music_create_temporary]))
|
||||
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 {
|
||||
if(!this->calculate_and_get_join_state(channel))
|
||||
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].has_value) {
|
||||
auto max_volume = min(100, max(0, permissions[permission::i_client_music_create_modify_max_volume].value));
|
||||
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].has_value && permissions[permission::i_client_music_delete_power].value >= 0) {
|
||||
bot->clientPermissions->set_permission(permission::i_client_music_needed_delete_power, {permissions[permission::i_client_music_delete_power].value,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};
|
||||
|
||||
if(bot->getOwner() != this->getClientDatabaseId()) {
|
||||
ACTION_REQUIRES_PERMISSION(permission::i_client_music_delete_power, bot->calculate_permission(permission::i_client_music_needed_delete_power, bot->getChannelId()), this->getChannelId());
|
||||
}
|
||||
|
||||
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<ClientDbId>() != 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<ts::music::SongInfo>& 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<ts::music::PlayableSong>(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};
|
||||
ACTION_REQUIRES_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, bot->getChannelId()), this->getChannelId());
|
||||
|
||||
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<int64_t>()));
|
||||
} 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<int64_t>()));
|
||||
} 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->calculate_permission(permission::i_playlist_view_power, 0);
|
||||
auto playlists = this->server->musicManager->playlists();
|
||||
|
||||
playlists.erase(find_if(playlists.begin(), playlists.end(), [&](const shared_ptr<music::PlayablePlaylist>& 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 !permission::v2::permission_granted(needed_view_power, playlist_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);
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_playlist_create, 1);
|
||||
|
||||
{
|
||||
auto max_playlists = this->calculate_permission(permission::i_max_playlists, 0);
|
||||
if(max_playlists.has_value) {
|
||||
auto playlists = ref_server->musicManager->find_playlists_by_client(this->getClientDatabaseId());
|
||||
if(!permission::v2::permission_granted(playlists.size(), max_playlists))
|
||||
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->calculate_permission(permission::i_max_playlist_size, 0);
|
||||
if(max_songs.has_value && max_songs.value >= 0)
|
||||
playlist->properties()[property::PLAYLIST_MAX_SONGS] = max_songs.value;
|
||||
}
|
||||
|
||||
auto power = this->calculate_permission(permission::i_playlist_song_remove_power, 0);
|
||||
if(power.has_value && power.value >= 0)
|
||||
playlist->permissions()->setPermission(permission::i_playlist_song_needed_remove_power, power.value, nullptr);
|
||||
|
||||
power = this->calculate_permission(permission::i_playlist_delete_power, 0);
|
||||
if(power.has_value && power.value >= 0)
|
||||
playlist->permissions()->setPermission(permission::i_playlist_needed_delete_power, power.value, nullptr);
|
||||
|
||||
power = this->calculate_permission(permission::i_playlist_modify_power, 0);
|
||||
if(power.has_value && power.value >= 0)
|
||||
playlist->permissions()->setPermission(permission::i_playlist_needed_modify_power, power.value, nullptr);
|
||||
|
||||
power = this->calculate_permission(permission::i_playlist_permission_modify_power, 0);
|
||||
if(power.has_value && power.value >= 0)
|
||||
playlist->permissions()->setPermission(permission::i_playlist_needed_permission_modify_power, power.value, 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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_modify_power));
|
||||
|
||||
deque<pair<shared_ptr<property::PropertyDescription>, string>> properties;
|
||||
|
||||
for(const auto& key : cmd[0].keys()) {
|
||||
if(key == "playlist_id") continue;
|
||||
if(key == "return_code") continue;
|
||||
|
||||
auto property = property::info<property::PlaylistProperties>(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<string>())) {
|
||||
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<SongId>();
|
||||
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<int32_t>();
|
||||
auto max_value = this->calculate_permission(permission::i_max_playlist_size, this->getChannelId());
|
||||
if(max_value.has_value && !permission::v2::permission_granted(value, max_value))
|
||||
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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_virtualserver_playlist_permission_list, 1);
|
||||
|
||||
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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power));
|
||||
|
||||
auto max_value = this->calculate_permission(permission::i_permission_modify_power, 0, true);
|
||||
if(!max_value.has_value) return command_result{permission::i_permission_modify_power};
|
||||
|
||||
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
|
||||
|
||||
|
||||
bool conOnError = cmd[0].has("continueonerror");
|
||||
for (int index = 0; index < cmd.bulkCount(); index++) {
|
||||
PARSE_PERMISSION(cmd);
|
||||
|
||||
auto val = cmd[index]["permvalue"].as<permission::PermissionValue>();
|
||||
if(permission_require_granted_value(permType) && !permission::v2::permission_granted(val, max_value)) {
|
||||
if(conOnError) continue;
|
||||
return command_result{permission::i_permission_modify_power};
|
||||
}
|
||||
|
||||
if(!ignore_granted_values && !permission::v2::permission_granted(val, this->calculate_permission(permType, 0, true))) {
|
||||
if(conOnError) continue;
|
||||
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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_playlist_permission_modify_power, playlist->permissions()->getPermissionValue(permission::i_playlist_needed_permission_modify_power));
|
||||
|
||||
auto ignore_granted_values = permission::v2::permission_granted(1, this->calculate_permission(permission::b_permission_modify_power_ignore, 0));
|
||||
bool conOnError = cmd[0].has("continueonerror");
|
||||
|
||||
for (int index = 0; index < cmd.bulkCount(); index++) {
|
||||
PARSE_PERMISSION(cmd);
|
||||
|
||||
if(!ignore_granted_values && !permission::v2::permission_granted(0, this->calculate_permission(permType, 0, true))) {
|
||||
if(conOnError) continue;
|
||||
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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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<int>() && type.as<int>() == 0) || type.as<string>() == "yt") {
|
||||
loader = bot->ytLoader(this->getServer());
|
||||
} else if((type.castable<int>() && type.as<int>() == 1) || type.as<string>() == "ffmpeg") {
|
||||
loader = bot->ffmpegLoader(this->getServer());
|
||||
} else if((type.castable<int>() && type.as<int>() == 2) || type.as<string>() == "channel") {
|
||||
loader = bot->channelLoader(this->getServer());
|
||||
} else if((type.castable<int>() && type.as<int>() == -1) || type.as<string>() == "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<ConnectedClient> 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<std::shared_ptr<music::SongInfo>> 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<music::PlayableSong>(entry));
|
||||
this->server->forEachClient([&](shared_ptr<ConnectedClient> 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<ConnectedClient> 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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::i_client_music_play_power, bot->calculate_permission(permission::i_client_music_needed_play_power, 0));
|
||||
|
||||
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())
|
||||
ACTION_REQUIRES_GLOBAL_PERMISSION(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};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user