Procházet zdrojové kódy

Resolve "US26.1: Delete command (Server)"

Sander, Paul před 4 roky
rodič
revize
b7bcb8f431

+ 20 - 1
Client-Server Protocol.md

@@ -316,8 +316,27 @@ Server:
 ```
 If `accept` is true the user has been deleted and the connection will be closed by the server.
 
-### TODO
+### 2.7 Deletefile
 
+The `deletefile` command deletes a file from the server if its allowed to in config.
+
+Client:
+```
+{
+	"command": "deletefile",
+	"file": string
+}
+```
+
+Server:
+```
+{
+	"command": "deletefile",
+	"file": string,
+	"accept": bool,
+	"error": string
+}
+```
 
 ## 3. Close connection
 

+ 2 - 0
daemon/Daemon-Config-Reference.md

@@ -7,6 +7,7 @@ The config file must be in the same directory from where you run the binary.
 `port` : The port where the server listens. Must be a valid port.
 `interface` : The sniffer interface you want to use.
 `userdatabase` : The file where userdata is stored in format: user;password
+`deleteAllowed` : Says if a client is allowed to delete files from its file directory
 `filedirectory` : The directory where files from the clients will be stored and read from
 `SSLenabled` : When set to true, the server will only use and accept SSL connections from clients. Set to false to disable this
 `SSLcertificate` : The certificate file to use for SSL connections
@@ -42,6 +43,7 @@ There no further config needed. Forward should work out of the pox
 port=1234
 userdatabase=userStorage.txt
 filedirectory=./files/
+deleteAllowed=true
 SSLenabled=true
 SSLcertificate=user.crt
 SSLprivatekey=user.key

+ 24 - 12
daemon/include/FileManager.h

@@ -11,8 +11,16 @@
  */
 class FileManager {
 private:
+	/**
+	 * Directory of the files uploaded
+	 */
 	const std::string fileDirectory;
 
+	/**
+	 * allows file deletion
+	 */
+	const bool deleteAllowed;
+
 	/**
 	 * file stream for get command
 	 */
@@ -54,6 +62,13 @@ private:
 public:
 	enum { max_data_length = 512 };
 
+	/**
+	 * FileManager Errors
+	 *
+	 * errors which the head or deleteFile command returns
+	 */
+	enum Error { no_error, no_such_file, file_too_small, not_allowed };
+
 	/**
 	 * Creates the file manager
 	 */
@@ -163,17 +178,6 @@ public:
 	 */
 	void cancelList();
 
-	/**
-	 * head error
-	 *
-	 * errors which the head command returns
-	 *
-	 * - no error
-	 * - no such file
-	 * - file is smaller than specified size
-	 */
-	enum headError { no_error, no_such_file, file_too_small };
-
 	/**
 	 * Get the first n bytes of a file as string.
 	 * @param filename name of the files
@@ -182,7 +186,15 @@ public:
 	 * @return first: the array of chars containing the data if no error occured
 	 *         second: an error code
 	 */
-	virtual std::pair<std::vector<char>, headError> getBytesFromFile(const std::string &filename, int numOfBytes);
+	virtual std::pair<std::vector<char>, Error> getBytesFromFile(const std::string &filename, int numOfBytes);
+
+	/**
+	 * Delete a file in the file directory.
+	 * @param filename name of the file
+	 *
+	 * @return return code 0: ok 1: disabled in config 2: file not found
+	 */
+	virtual Error deleteFile(const std::string &filename);
 };
 
 #endif

+ 5 - 0
daemon/include/JsonCommander.h

@@ -140,6 +140,11 @@ private:
 	 * Executes the deleteme command
 	 */
 	Response executeDeleteMe(const Json::Value &message);
+
+	/**
+	 * Executes the deletefile command
+	 */
+	Response executeDeleteFile(const Json::Value &message);
 };
 
 #endif

+ 22 - 5
daemon/src/FileManager.cpp

@@ -5,7 +5,7 @@
 
 #include "../include/Config.h"
 
-FileManager::FileManager() : fileDirectory(Config::getValue("filedirectory")) {}
+FileManager::FileManager() : fileDirectory(Config::getValue("filedirectory")), deleteAllowed(Config::getValue("deleteAllowed") == "true") {}
 
 FileManager::~FileManager() {
 	cancelPut();
@@ -146,7 +146,7 @@ std::vector<std::string> FileManager::getNextChunkFromList() {
 
 void FileManager::cancelList() { this->list.clear(); }
 
-std::pair<std::vector<char>, FileManager::headError> FileManager::getBytesFromFile(const std::string &filename, int numOfBytes) {
+std::pair<std::vector<char>, FileManager::Error> FileManager::getBytesFromFile(const std::string &filename, int numOfBytes) {
 	std::ifstream file;
 	std::string fname = this->fileDirectory;
 	fname.append(filename);
@@ -155,12 +155,12 @@ std::pair<std::vector<char>, FileManager::headError> FileManager::getBytesFromFi
 	std::vector<char> bytes;
 
 	if (!file.is_open()) {
-		return std::make_pair(bytes, FileManager::headError::no_such_file);
+		return std::make_pair(bytes, FileManager::Error::no_such_file);
 	}
 
 	auto size = file.tellg();
 	if (size < numOfBytes) {
-		return std::make_pair(bytes, FileManager::headError::file_too_small);
+		return std::make_pair(bytes, FileManager::Error::file_too_small);
 	}
 
 	bytes.resize(numOfBytes);
@@ -170,5 +170,22 @@ std::pair<std::vector<char>, FileManager::headError> FileManager::getBytesFromFi
 
 	file.close();
 
-	return std::make_pair(bytes, FileManager::headError::no_error);
+	return std::make_pair(bytes, FileManager::Error::no_error);
+}
+
+FileManager::Error FileManager::deleteFile(const std::string &filename) {
+	if (!this->deleteAllowed) {
+		return not_allowed;
+	}
+
+	std::string fname = this->fileDirectory;
+	fname.append(filename);
+
+	if (!boost::filesystem::exists(fname)) {
+		return no_such_file;
+	}
+
+	boost::filesystem::remove(fname);
+
+	return no_error;
 }

+ 54 - 4
daemon/src/JsonCommander.cpp

@@ -13,6 +13,7 @@ JsonCommander::JsonCommander(FileManager &fileManager) : fileManager(fileManager
 	commandsMap["getdata"] = &JsonCommander::executeGetdata;
 	commandsMap["head"] = &JsonCommander::executeHead;
 	commandsMap["deleteme"] = &JsonCommander::executeDeleteMe;
+	commandsMap["deletefile"] = &JsonCommander::executeDeleteFile;
 }
 
 JsonCommander::~JsonCommander() {}
@@ -365,22 +366,32 @@ JsonCommander::Response JsonCommander::executeHead(const Json::Value &message) {
 		response.json["data"] = "";
 		response.json["error"] = "incorrect head command request";
 	} else {
-		std::pair<std::vector<char>, FileManager::headError> res = fileManager.getBytesFromFile(message["file"].asString(), 4);
-		if (res.second == FileManager::headError::no_error) {
+		std::pair<std::vector<char>, FileManager::Error> 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<std::vector<char>>(res.first);
 			response.json["error"] = "";
-		} else if (res.second == FileManager::headError::no_such_file) {
+			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";
-		} else {
+			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!!!";
 		}
 	}
 
@@ -463,3 +474,42 @@ JsonCommander::Response JsonCommander::checkLogin(const Json::Value &message) {
 
 	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;
+}

+ 2 - 1
daemon/test/FileManagerMock.h

@@ -30,7 +30,8 @@ public:
 	MOCK_METHOD(int, getListSize, (), (override));
 	MOCK_METHOD(std::vector<std::string>, getNextChunkFromList, (), (override));
 
-	MOCK_METHOD((std::pair<std::vector<char>, headError>), getBytesFromFile, (const std::string &filename, int numOfBytes), (override));
+	MOCK_METHOD(FileManager::Error, deleteFile, (const std::string &filename), (override));
+	MOCK_METHOD((std::pair<std::vector<char>, FileManager::Error>), getBytesFromFile, (const std::string &filename, int numOfBytes), (override));
 };
 
 #endif

+ 89 - 5
daemon/test/JsonCommanderTest.cpp

@@ -885,7 +885,7 @@ TEST(Head, Positive) {
 
 	std::vector<char> bytes = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
 	const std::string bytesAsString = "YWJjZGVmZ2g=";
-	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::headError::no_error)));
+	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::Error::no_error)));
 
 	JsonCommander::Response response = jsonCommander.execute(message);
 
@@ -932,7 +932,7 @@ TEST(Head, NoSuchFile) {
 	message["file"] = file;
 
 	std::vector<char> bytes;
-	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::headError::no_such_file)));
+	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::Error::no_such_file)));
 
 	JsonCommander::Response response = jsonCommander.execute(message);
 
@@ -957,7 +957,7 @@ TEST(Head, FileTooSmall) {
 	message["file"] = file;
 
 	std::vector<char> bytes;
-	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::headError::file_too_small)));
+	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::Error::file_too_small)));
 
 	JsonCommander::Response response = jsonCommander.execute(message);
 
@@ -1024,8 +1024,6 @@ TEST(Deleteme, Negative) {
 
 	JsonCommander::Response response = jsonCommander.execute(message);
 
-	std::cout << response.json << std::endl;
-
 	EXPECT_EQ(response.action, JsonCommander::Action::send);
 	EXPECT_EQ(response.json["command"].asString(), command);
 	EXPECT_FALSE(response.json["accept"].asBool());
@@ -1062,4 +1060,90 @@ TEST(Deleteme, InvalidRequest) {
 	EXPECT_NE(response.json["error"].asString(), "");
 }
 
+TEST(DeleteFile, Positive) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "deletefile";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	EXPECT_CALL(fileManager, deleteFile(file)).WillOnce(testing::Return(FileManager::Error::no_error));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(DeleteFile, InvalidRequest) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "deletefile";
+	const int file = 3641;
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_EQ(response.json["file"].asString(), "");
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(DeleteFile, FileDoesNotExist) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "deletefile";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	EXPECT_CALL(fileManager, deleteFile(file)).WillOnce(testing::Return(FileManager::Error::no_such_file));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(DeleteFile, DisabledInConfig) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "deletefile";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	EXPECT_CALL(fileManager, deleteFile(file)).WillOnce(testing::Return(FileManager::Error::not_allowed));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
 } // namespace