#include "../include/JsonCommander.h" #include "../include/Config.h" #include "../include/Notifications.h" #include "../include/Queue.h" #include "../include/UserManager.h" #include "../include/base64.h" JsonCommander::JsonCommander(FileManager &fileManager) : fileManager(fileManager) { commandsMap["status"] = &JsonCommander::executeStatus; commandsMap["close"] = &JsonCommander::executeClose; commandsMap["list"] = &JsonCommander::executeList; commandsMap["listdata"] = &JsonCommander::executeListData; commandsMap["put"] = &JsonCommander::executePut; commandsMap["putdata"] = &JsonCommander::executePutdata; commandsMap["get"] = &JsonCommander::executeGet; commandsMap["getdata"] = &JsonCommander::executeGetdata; commandsMap["head"] = &JsonCommander::executeHead; commandsMap["deleteme"] = &JsonCommander::executeDeleteMe; commandsMap["deletefile"] = &JsonCommander::executeDeleteFile; commandsMap["extendedstatus"] = &JsonCommander::executeExtendedStatus; commandsMap["notifications"] = &JsonCommander::executeNotifications; commandsMap["extendedlist"] = &JsonCommander::executeExtendedList; commandsMap["extendedlistdata"] = &JsonCommander::executeExtendedListData; commandsMap["queue"] = &JsonCommander::executeQueue; commandsMap["dequeue"] = &JsonCommander::executeDequeue; } JsonCommander::~JsonCommander() {} JsonCommander::Response JsonCommander::execute(const Json::Value &message) { JsonCommander::Response response; Response (JsonCommander::*commandExecutor)(const Json::Value &) = commandsMap[message["command"].asString()]; if (commandExecutor != nullptr) { response = (this->*commandExecutor)(message); } else { // command does not exist response.action = close; } return response; } JsonCommander::Response JsonCommander::executeStatus(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "status"; // answer a real status message std::string status; if (this->fileManager.isUploading() && this->fileManager.isDownloading()) { status = "download and upload running"; } else if (this->fileManager.isUploading()) { status = "upload running"; } else if (this->fileManager.isDownloading()) { status = "download running"; } else { status = "ok"; } response.json["response"] = status; return response; } JsonCommander::Response JsonCommander::executeClose(const Json::Value &message) { JsonCommander::Response response; response.action = closeAndSend; response.json["command"] = "close"; response.json["response"] = "bye"; return response; } JsonCommander::Response JsonCommander::executeList(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "list"; int chunks; if (fileManager.getRemainingListChunks() > 0) { response.json["accept"] = false; response.json["chunks"] = -1; response.json["items"] = -1; response.json["error"] = "there is already an open list command"; } else if ((chunks = fileManager.openList()) == -1) { // this should not happen on a current system response.json["accept"] = false; response.json["chunks"] = -1; response.json["items"] = -1; response.json["error"] = "there is a filename which is too long"; } else { response.json["accept"] = true; response.json["chunks"] = chunks; response.json["items"] = fileManager.getListSize(); response.json["error"] = ""; } return response; } JsonCommander::Response JsonCommander::executeListData(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "listdata"; Json::Value array; const int remainingListchunks = fileManager.getRemainingListChunks(); if (!message["chunk"].isInt() || !message["cancel"].isBool()) { response.action = closeAndSend; response.json["cancel"] = true; response.json["remaining"] = -1; response.json["names"] = Json::arrayValue; response.json["error"] = "incorrect listdata command request"; } else if (remainingListchunks == 0) { response.json["cancel"] = true; response.json["remaining"] = -1; response.json["names"] = Json::arrayValue; response.json["error"] = "there are no chunks to send"; } else if (message["cancel"].asBool()) { response.json["cancel"] = true; response.json["remaining"] = message["chunk"].asInt(); // so it can be associated to the request response.json["names"] = Json::arrayValue; response.json["error"] = ""; fileManager.cancelList(); } else if (remainingListchunks - 1 != message["chunk"].asInt()) { response.action = closeAndSend; response.json["cancel"] = true; response.json["remaining"] = -1; response.json["names"] = Json::arrayValue; response.json["error"] = "wrong chunk number"; } else { std::vector v = fileManager.getNextChunkFromList(); for (int i = 0; i < v.size(); i++) array.append(v.at(i)); response.json["remaining"] = message["chunk"].asInt(); response.json["cancel"] = false; response.json["names"] = array; response.json["error"] = ""; } return response; } JsonCommander::Response JsonCommander::executePut(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "put"; response.json["file"] = message["file"].asString(); if (!message["file"].isString() || !message["size"].isInt() || !message["chunks"].isInt()) { // if request is incomplete close connection response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "incorrect put command request"; } else if (fileManager.isUploading()) { // if an upload is alread running deny request response.action = send; response.json["accept"] = false; response.json["error"] = "upload already running"; } else if (message["chunks"].asInt() <= 0) { response.action = send; response.json["accept"] = false; response.json["error"] = "there must be at least one chunk"; } else if (fileManager.checkFilename(message["file"].asString())) { // accept request response.action = send; bool opened = fileManager.openPutFile(message["file"].asString()); if (opened) { response.json["accept"] = true; response.json["error"] = ""; this->putFileReceived = message["chunks"].asInt(); this->putSize = message["chunks"].asInt(); } else { response.json["accept"] = false; response.json["error"] = "file already exists"; } } else { // deny request if file name is not valid response.action = send; response.json["accept"] = false; response.json["error"] = "invalid file name"; } return response; } JsonCommander::Response JsonCommander::executePutdata(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "putdata"; response.json["file"] = message["file"].asString(); response.json["received"] = message["remaining"].asInt(); if (!message["file"].isString() || !message["data"].isString() || !message["remaining"].isInt() || !message["cancel"].isBool()) { // if request is incomplete close connection response.action = closeAndSend; response.json["cancel"] = true; response.json["error"] = "incorrect putdata command request"; this->fileManager.cancelPut(); } else if (!fileManager.isUploading()) { // no upload running -> command response.action = send; response.json["cancel"] = true; response.json["error"] = "no upload running"; } else if (message["cancel"].asBool()) { response.action = send; response.json["cancel"] = true; response.json["error"] = ""; this->fileManager.cancelPut(); } else if (message["file"].asString().compare(fileManager.getPutBaseFileName()) == 0) { if (--this->putFileReceived == message["remaining"].asInt()) { // accept request response.action = send; response.json["cancel"] = false; response.json["error"] = ""; const std::vector data = base64::decode>(message["data"].asString()); fileManager.writePut(data); this->putFileReceived = message["remaining"].asInt(); if (this->putFileReceived == 0) { // close file after last chunk was received this->fileManager.closePutFile(); } } else { // wrong remaining number response.action = send; response.json["cancel"] = true; response.json["error"] = "wrong remaining number"; this->fileManager.cancelPut(); } } else { // wrong file name response.action = send; response.json["cancel"] = true; response.json["error"] = "another file was already being uploaded"; this->fileManager.cancelPut(); } return response; } JsonCommander::Response JsonCommander::executeGet(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "get"; response.json["file"] = message["file"].asString(); if (!message["file"].isString()) { // if request is incomplete close connection response.action = closeAndSend; response.json["accept"] = false; response.json["chunks"] = -1; response.json["error"] = "incorrect get command request"; } else if (fileManager.isDownloading()) { // if an upload is alread running deny request response.action = send; response.json["accept"] = false; response.json["chunks"] = -1; response.json["error"] = "download already running"; } else if (fileManager.checkFilename(message["file"].asString())) { // accept request response.action = send; std::pair opened = fileManager.openGetFile(message["file"].asString()); if (opened.first) { this->getFileRemaining = opened.second; this->getSize = opened.second; response.json["accept"] = true; response.json["chunks"] = this->getFileRemaining; response.json["error"] = ""; } else { response.json["accept"] = false; response.json["chunks"] = -1; response.json["error"] = "file does not exist"; } } else { // deny request if file name is not valid response.action = send; response.json["accept"] = false; response.json["chunks"] = -1; response.json["error"] = "invalid file name"; } return response; } JsonCommander::Response JsonCommander::executeGetdata(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "getdata"; response.json["file"] = message["file"].asString(); response.json["remaining"] = message["chunk"].asInt(); if (!message["file"].isString() || !message["chunk"].isInt() || !message["cancel"].isBool()) { // if request is incomplete close connection response.action = closeAndSend; response.json["cancel"] = true; response.json["data"] = ""; response.json["error"] = "incorrect putdata command request"; this->fileManager.closeGetFile(); } else if (!fileManager.isDownloading()) { // no upload running -> command response.action = send; response.json["cancel"] = true; response.json["data"] = ""; response.json["error"] = "no download running"; } else if (message["cancel"].asBool()) { response.action = send; response.json["cancel"] = true; response.json["data"] = ""; response.json["error"] = ""; this->fileManager.closeGetFile(); } else if (message["file"].asString().compare(fileManager.getGetBaseFileName()) == 0) { if (--this->getFileRemaining == message["chunk"].asInt()) { // accept request response.action = send; response.json["cancel"] = false; response.json["error"] = ""; const std::vector data = fileManager.readGet(); response.json["data"] = base64::encode>(data); fileManager.writePut(data); if (this->getFileRemaining == 0) { // close file after last chunk was sent this->fileManager.closeGetFile(); } } else { // wrong chunk number response.action = send; response.json["cancel"] = true; response.json["data"] = ""; response.json["error"] = "wrong chunk number"; this->fileManager.closeGetFile(); } } else { // wrong file name response.action = send; response.json["cancel"] = true; response.json["data"] = ""; response.json["error"] = "another file was already being downloaded"; this->fileManager.closeGetFile(); } return response; } JsonCommander::Response JsonCommander::executeHead(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "head"; if (!message["file"].isString()) { response.json["accept"] = false; response.json["file"] = ""; response.json["data"] = ""; response.json["error"] = "incorrect head command request"; } else { std::pair, FileManager::Error> res = fileManager.getBytesFromFile(message["file"].asString(), 32); if (res.second == FileManager::Error::file_too_small) res = fileManager.getBytesFromFile(message["file"].asString(), 4); switch (res.second) { case FileManager::Error::no_error: response.json["accept"] = true; response.json["file"] = message["file"].asString(); response.json["data"] = base64::encode>(res.first); response.json["error"] = ""; break; case FileManager::Error::no_such_file: response.json["accept"] = false; response.json["file"] = message["file"].asString(); response.json["data"] = ""; response.json["error"] = "no such file"; break; case FileManager::Error::file_too_small: response.json["accept"] = false; response.json["file"] = message["file"].asString(); response.json["data"] = ""; response.json["error"] = "file is smaller than specified size"; break; default: response.action = closeAndSend; response.json["accept"] = false; response.json["file"] = message["file"]; response.json["data"] = ""; response.json["error"] = "internal error. Please fix this!!!"; } } return response; } JsonCommander::Response JsonCommander::executeDeleteMe(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "deleteme"; if (!message["pass"].isString()) { response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "incorrect deleteme command request"; } else if (UserManager::deleteUser(currentUser, message["pass"].asString())) { // success response.action = closeAndSend; response.json["accept"] = true; response.json["error"] = ""; } else { response.action = send; response.json["accept"] = false; response.json["error"] = "wrong password"; } return response; } JsonCommander::Response JsonCommander::testVersion(const Json::Value &message) { JsonCommander::Response response; response.json["major"] = this->protocolMajorVersion; response.json["minor"] = this->protocolMinorVersion; if (!message["major"].isInt() || !message["minor"].isInt()) { response.action = closeAndSend; response.json["accept"] = false; } else if (message["major"].asInt() == this->protocolMajorVersion && message["minor"].asInt() <= this->protocolMinorVersion) { response.action = send; response.json["accept"] = true; } else { response.action = closeAndSend; response.json["accept"] = false; } return response; } JsonCommander::Response JsonCommander::checkLogin(const Json::Value &message) { JsonCommander::Response response; if (!message["login"].isBool() || !message["user"].isString() || !message["pass"].isString() || !message["cancel"].isBool()) { // invalid login request response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "invalid login request"; } else if (message["cancel"].asBool()) { response.action = close; } else if (message["login"].asBool() && UserManager::isAllowed(message["user"].asString(), message["pass"].asString())) { // credential check response.action = send; response.json["accept"] = true; response.json["error"] = ""; currentUser = message["user"].asString(); } else if (!message["login"].asBool()) { // add user. Check if already exists before if (!UserManager::addUser(message["user"].asString(), message["pass"].asString())) { response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "user does already exist"; } else { response.action = send; response.json["accept"] = true; response.json["error"] = ""; currentUser = message["user"].asString(); } } else { // reject user response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "wrong username or password"; } return response; } JsonCommander::Response JsonCommander::executeDeleteFile(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "deletefile"; if (!message["file"].isString()) { response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "incorrect deletefile command request"; response.json["file"] = ""; } else { response.action = send; FileManager::Error err = fileManager.deleteFile(message["file"].asString()); switch (err) { case FileManager::Error::no_error: response.json["accept"] = true; response.json["error"] = ""; response.json["file"] = message["file"]; break; case FileManager::Error::not_allowed: response.json["accept"] = false; response.json["error"] = "deleting files is disabled"; response.json["file"] = message["file"]; break; case FileManager::Error::no_such_file: response.json["accept"] = false; response.json["error"] = "no such file"; response.json["file"] = message["file"]; break; default: response.action = closeAndSend; response.json["accept"] = false; response.json["error"] = "internal error. Please fix this!!!"; response.json["file"] = message["file"]; } } return response; } JsonCommander::Response JsonCommander::executeExtendedStatus(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "extendedstatus"; // get status from client server transfers int index = 0; if (fileManager.isUploading()) { response.json["transfersclientserver"][index]["upload"] = true; response.json["transfersclientserver"][index]["file"] = fileManager.getPutBaseFileName(); int progress = 0; if (this->putSize != 0) { double d = (double)(this->putSize - this->putFileReceived) / (double)this->putSize; progress = (int)(d * 100); } response.json["transfersclientserver"][index]["progress"] = progress; index++; } if (fileManager.isDownloading()) { response.json["transfersclientserver"][index]["upload"] = false; response.json["transfersclientserver"][index]["file"] = fileManager.getGetBaseFileName(); int progress = 0; if (this->getSize != 0) { double d = 1 - ((double)this->getFileRemaining / (double)this->getSize); progress = (int)(d * 100); } response.json["transfersclientserver"][index]["progress"] = progress; } // get status from covert channel index = 0; if (Queue::channel != nullptr && Queue::channel->isTransferRunning()) { response.json["transfersserverserver"][index]["type"] = Config::getValue("passiveMode").compare("true") == 0 ? "download" : "upload"; response.json["transfersserverserver"][index]["file"] = Queue::curTransfer(); auto rawprogress = Queue::channel->getProgress(); double d; if (rawprogress.second == 0) d = 0.0; else d = ((double)rawprogress.first / (double)rawprogress.second); int progress = (int)(d * 100); response.json["transfersserverserver"][index]["progress"] = progress; std::time_t startTime = Queue::channel->getTransferStart(); std::time_t curTime = std::time(nullptr); std::time_t diffTime = curTime - startTime; double speed = (double)rawprogress.first / (double)diffTime; response.json["transfersserverserver"][index]["speed"] = speed; response.json["transfersserverserver"][index]["method"] = Config::getValue("covertChannelMode"); index++; } for (int i = 0; i < Queue::queue.size(); i++) { response.json["transfersserverserver"][index]["type"] = "queued"; response.json["transfersserverserver"][index]["file"] = Queue::queue[i]; response.json["transfersserverserver"][index]["progress"] = 0; response.json["transfersserverserver"][index]["speed"] = 0; response.json["transfersserverserver"][index]["method"] = Config::getValue("covertChannelMode"); index++; } response.json["accept"] = true; response.json["error"] = ""; response.action = send; return response; } JsonCommander::Response JsonCommander::executeNotifications(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "notifications"; Json::Value array; std::vector v = Notifications::getMessages(currentUser); for (int i = 0; i < v.size(); i++) array.append(v.at(i)); response.action = send; response.json["messages"] = array; response.json["accept"] = true; response.json["error"] = ""; return response; } JsonCommander::Response JsonCommander::executeExtendedList(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "extendedlist"; int chunks; if (fileManager.getRemainingExtendedListChunks() > 0) { response.json["accept"] = false; response.json["chunks"] = -1; response.json["items"] = -1; response.json["error"] = "there is already an open extendedlist command"; } else if ((chunks = fileManager.openExtendedList()) == -1) { response.json["accept"] = false; response.json["chunks"] = -1; response.json["items"] = -1; response.json["error"] = "there is a filename which is too long"; } else { response.json["accept"] = true; response.json["chunks"] = chunks; response.json["items"] = fileManager.getExtendedListSize(); response.json["error"] = ""; } return response; } JsonCommander::Response JsonCommander::executeQueue(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "queue"; if (!message["file"].isString()) { response.action = closeAndSend; response.json["file"] = ""; response.json["accept"] = false; response.json["error"] = "invalid request"; } else { bool res = Queue::push(message["file"].asString()); if (res) { response.action = send; response.json["file"] = message["file"].asString(); response.json["accept"] = true; response.json["error"] = ""; } else { response.action = send; response.json["file"] = message["file"].asString(); response.json["accept"] = false; response.json["error"] = "file could not be queued"; } } return response; } JsonCommander::Response JsonCommander::executeExtendedListData(const Json::Value &message) { JsonCommander::Response response; response.action = send; response.json["command"] = "extendedlistdata"; Json::Value array; const int remainingListchunks = fileManager.getRemainingExtendedListChunks(); if (!message["chunk"].isInt() || !message["cancel"].isBool()) { response.action = closeAndSend; response.json["cancel"] = true; response.json["remaining"] = -1; response.json["files"] = Json::arrayValue; response.json["error"] = "incorrect listdata command request"; } else if (remainingListchunks == 0) { response.json["cancel"] = true; response.json["remaining"] = -1; response.json["files"] = Json::arrayValue; response.json["error"] = "there are no chunks to send"; } else if (message["cancel"].asBool()) { response.json["cancel"] = true; response.json["remaining"] = message["chunk"].asInt(); response.json["files"] = Json::arrayValue; response.json["error"] = ""; fileManager.cancelExtendedList(); } else if (remainingListchunks - 1 != message["chunk"].asInt()) { response.action = closeAndSend; response.json["cancel"] = true; response.json["remaining"] = -1; response.json["files"] = Json::arrayValue; response.json["error"] = "wrong chunk number"; } else { auto v = fileManager.getNextChunkFromExtendedList(); for (int i = 0; i < v.size(); i++) { Json::Value obj; obj["name"] = std::get<0>(v.at(i)); obj["head"] = std::get<1>(v.at(i)).compare("") == 0 ? "" : base64::encode(std::get<1>(v.at(i))); obj["size"] = std::get<2>(v.at(i)); array.append(obj); } response.json["remaining"] = message["chunk"].asInt(); response.json["cancel"] = false; response.json["files"] = array; response.json["error"] = ""; } return response; } JsonCommander::Response JsonCommander::executeDequeue(const Json::Value &message) { JsonCommander::Response response; response.json["command"] = "dequeue"; if (!message["file"].isString()) { response.action = closeAndSend; response.json["file"] = ""; response.json["accept"] = false; response.json["error"] = "invalid request"; } else { bool res = Queue::remove(message["file"].asString()); if (res) { response.action = send; response.json["file"] = message["file"].asString(); response.json["accept"] = true; response.json["error"] = ""; } else { response.action = send; response.json["file"] = message["file"].asString(); response.json["accept"] = false; response.json["error"] = "cannot remove this file from queue"; } } return response; }