2020-01-26 14:21:34 +01:00
|
|
|
//
|
|
|
|
// 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"
|
2020-05-03 14:06:34 +02:00
|
|
|
#include "src/server/file/LocalFileServer.h"
|
2020-01-26 14:21:34 +01:00
|
|
|
#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")) {
|
2020-04-24 22:04:07 +02:00
|
|
|
this->sendCommand(fileList);
|
|
|
|
if(this->getType() != CLIENT_QUERY)
|
2020-01-26 14:21:34 +01:00
|
|
|
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 {
|
|
|
|
return command_result{error::not_implemented};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!directory || directory->type != file::FileType::DIRECTORY) { //Should not happen
|
|
|
|
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};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|