Bläddra i källkod

JsonCommander & FileManager on daemon

I moved the json logic to JsonCommander and the file management to
FileManager.

I also implemented the commands for the new protocol version 0.2

(Sorry for monster committing this)
Jonas Pflanzer 4 år sedan
förälder
incheckning
850404315e

+ 1 - 1
daemon/CMakeLists.txt

@@ -5,7 +5,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
 project(ccats)
 
-add_executable(ccats src/main.cpp src/Sniffer.cpp src/Server.cpp src/base64.cpp)
+add_executable(ccats src/main.cpp src/Sniffer.cpp src/Server.cpp src/base64.cpp src/JsonCommander.cpp src/FileManager.cpp)
 
 # use pkg-config to fix building on debian unstable
 find_package(PkgConfig REQUIRED)

+ 128 - 0
daemon/include/FileManager.h

@@ -0,0 +1,128 @@
+#ifndef FILEMANAGER_H
+#define FILEMANAGER_H
+
+#include <fstream>
+#include <vector>
+
+/**
+ * @class FileManager
+ *
+ * Manages file writes for uploads and file reads for downloads
+ */
+class FileManager {
+private:
+  const std::string fileDirectory = "./files/";
+
+  /**
+   * file stream for get command
+   */
+  std::ifstream getFile;
+
+  /**
+   * file stream for put command
+   */
+  std::ofstream putFile;
+
+  /**
+   * file name for put command
+   * (used to delete the file if the upload is canceled)
+   */
+  std::string putFileName;
+
+  /**
+   * file name for put command
+   */
+  std::string putBaseFileName;
+
+  /**
+   * file name for get command
+   */
+  std::string getBaseFileName;
+
+public:
+  enum { max_data_length = 512 };
+
+  /**
+   * Creates the file manager
+   */
+  FileManager();
+
+  /**
+   * Destroys the file manager
+   */
+  ~FileManager();
+
+  /**
+   * Checks if an upload is running
+   * @return true - upload running | false - no upload
+   */
+  bool isUploading();
+
+  /**
+   * Check if a download is running
+   * @return true - download running | false - no download
+   */
+  bool isDownloading();
+
+  /**
+   * Opens put file if it doesn't exist
+   * @return true - file is open | false - file is not open
+   */
+  bool openPutFile(const std::string &filename);
+
+  /**
+   * Opens get file if it exists and reports the amount of chunks
+   * @return true - file is open | false - file is not open
+   */
+  bool openGetFile(const std::string &filename, int &chunks);
+
+  /**
+   * Closes file
+   */
+  void closePutFile();
+
+  /**
+   * Closes file
+   */
+  void closeGetFile();
+
+  /**
+   * Closes put file and deletes it
+   */
+  void cancelPut();
+
+  /**
+   * Checks if a file name is valid
+   * @return true - name is valid | false - name is invalid
+   */
+  bool checkFilename(const std::string &name);
+
+  /**
+   * Return the name of the download file
+   * @return name of the download file
+   */
+  std::string getGetBaseFileName();
+
+  /**
+   * Return the name of the upload file
+   * @return name of the upload file
+   */
+  std::string getPutBaseFileName();
+
+  /**
+   * Writes data to put file
+   */
+  void writePut(const std::vector<char> &data);
+
+  /**
+   * Reads data from get file
+   */
+  std::vector<char> readGet();
+
+  /**
+   * Returns number of chunks for download
+   */
+  int getGetChunks();
+};
+
+#endif

+ 117 - 0
daemon/include/JsonCommander.h

@@ -0,0 +1,117 @@
+#ifndef JSONCOMMANDER_H
+#define JSONCOMMANDER_H
+
+#include <json/json.h>
+
+#include "FileManager.h"
+
+/**
+ * @class JsonCommander
+ *
+ * A class to execute the json commands which were sent by a client.
+ * It also uses the file manager for writing and reading files.
+ */
+class JsonCommander {
+public:
+  /**
+   * Action for the json response.
+   * send         - send the json answer
+   * closeAndSend - send the json answer and close the socket
+   * close        - close the socket
+   */
+  enum Action { send, closeAndSend, close };
+
+  /**
+   * Response for commands
+   *
+   * If action is set to close json can be uninitialized.
+   */
+  struct Response {
+    Action action;
+    Json::Value json;
+  };
+
+  /**
+   * Creates a JsonCommaner
+   */
+  JsonCommander(FileManager &fileManager);
+
+  /**
+   * Deletes the JsonCommander
+   */
+  ~JsonCommander();
+
+  /**
+   * Executes commands
+   */
+  Response execute(const Json::Value &message);
+
+  /**
+   * Does version check before login
+   */
+  Response testVersion(const Json::Value &message);
+
+  /**
+   * Checks if login is valid
+   */
+  Response checkLogin(const Json::Value &message);
+
+private:
+  /**
+   * protocol version of the client server protocol
+   */
+  const std::string protocolVersion = "0.2";
+
+  /**
+   * file manager for reading and writing files
+   */
+  FileManager &fileManager;
+
+  /**
+   *
+   * Last chunk number which was sent.
+   */
+  int getFileRemaining;
+
+  /**
+   * Last chunk number which was received.
+   */
+  int putFileReceived;
+
+  /**
+   * Executes the status command
+   */
+  Response executeStatus(const Json::Value &message);
+
+  /**
+   * Executes the close command
+   */
+  Response executeClose(const Json::Value &message);
+
+  /**
+   * Executes the list command
+   */
+  Response executeList(const Json::Value &message);
+
+  /**
+   * Executes the put command
+   */
+  Response executePut(const Json::Value &message);
+
+  /**
+   * Executes the putdata command
+   */
+  Response executePutdata(const Json::Value &message);
+
+  /**
+   * Executes the get command
+   */
+  Response executeGet(const Json::Value &message);
+
+  /**
+   * Executes the getdata command
+   */
+  Response executeGetdata(const Json::Value &message);
+};
+
+#endif

+ 11 - 33
daemon/include/Server.h

@@ -1,14 +1,14 @@
 #ifndef SERVER_H
 #define SERVER_H
 
-#include <fstream>
-
 #include <boost/asio.hpp>
 #include <boost/bind.hpp>
 #include <boost/enable_shared_from_this.hpp>
 
 #include <json/json.h>
 
+#include "JsonCommander.h"
+
 using namespace boost::asio;
 using ip::tcp;
 
@@ -22,16 +22,11 @@ using ip::tcp;
 class con_handler : public boost::enable_shared_from_this<con_handler> {
 private:
   tcp::socket sock;
-  const std::string message = "Hello From Server!";
-
-  const std::string protocolVersion = "0.1";
-
-  const std::string fileDirectory = "./files/";
 
   /**
    * max buffer length
    */
-  enum { max_length = 1024, max_data_length = 512 };
+  enum { max_length = 1024 };
 
   /**
    * data buffer
@@ -39,41 +34,24 @@ private:
   streambuf buf;
 
   /**
-   * file stream for get command
-   */
-  std::ifstream getFile;
-
-  /**
-   * file stream for put command
-   */
-  std::ofstream putFile;
-
-  /**
-   * file stream for put command
-   * (used to delete the file if the upload is canceled)
-   */
-  std::string putFileName;
-
-  /**
-
-   * Last chunk number which was sent.
+   * string builder for json
    */
-  int getFileRemaining;
+  Json::StreamWriterBuilder jsonStringBuilder;
 
   /**
-   * Last chunk number which was received.
+   * json reader to parse json strings
    */
-  int putFileReceived;
+  std::unique_ptr<Json::CharReader> jsonReader;
 
   /**
-   * string builder for json
+   * Executes json commands
    */
-  Json::StreamWriterBuilder jsonStringBuilder;
+  JsonCommander jsonCommander;
 
   /**
-   * json reader to parse json strings
+   * File manager used by jsonCommander
    */
-  std::unique_ptr<Json::CharReader> jsonReader;
+  FileManager fileManager;
 
   /**
    * Reads data and binds it to a handler.

+ 81 - 0
daemon/src/FileManager.cpp

@@ -0,0 +1,81 @@
+#include "../include/FileManager.h"
+
+FileManager::FileManager() {}
+
+FileManager::~FileManager() {
+  cancelPut();
+  closeGetFile();
+}
+
+bool FileManager::isUploading() { return this->putFile.is_open(); }
+
+bool FileManager::isDownloading() { return this->getFile.is_open(); }
+
+bool FileManager::openPutFile(const std::string &filename) {
+  this->putBaseFileName = filename;
+  this->putFileName = this->fileDirectory;
+  this->putFileName.append(filename);
+
+  // open file and test if it already exists
+  this->putFile.open(this->putFileName, std::ios::app | std::ios::binary);
+  if (this->putFile.tellp() != std::ios::beg) {
+    // file already exists
+    closePutFile();
+    return false;
+  } else {
+    return true;
+  }
+}
+
+bool FileManager::openGetFile(const std::string &filename, int &chunks) {
+  this->getBaseFileName = filename;
+  std::string file = this->fileDirectory;
+  file.append(filename);
+
+  this->getFile.open(file, std::ios::ate | std::ios::binary);
+
+  if (this->getFile.is_open() == 0) {
+    // file does not exist or cannot be opened
+    return false;
+  } else {
+    size_t size = this->getFile.tellg();
+    chunks = size / max_data_length + (size % max_data_length == 0 ? 0 : 1);
+
+    this->getFile.seekg(std::ios::beg);
+    return true;
+  }
+}
+
+void FileManager::closePutFile() { this->putFile.close(); }
+
+void FileManager::closeGetFile() { this->getFile.close(); }
+
+void FileManager::cancelPut() {
+  if (isUploading()) {
+    closePutFile();
+    std::remove(this->putFileName.c_str());
+  }
+}
+
+bool FileManager::checkFilename(const std::string &name) {
+  return name.find('/') == std::string::npos;
+}
+
+std::string FileManager::getGetBaseFileName() { return this->getBaseFileName; }
+
+std::string FileManager::getPutBaseFileName() { return this->putBaseFileName; }
+
+void FileManager::writePut(const std::vector<char> &data) {
+  std::ostreambuf_iterator<char> output_iterator(this->putFile);
+  std::copy(data.begin(), data.end(), output_iterator);
+}
+
+std::vector<char> FileManager::readGet() {
+  char fileBuffer[max_data_length];
+  int read = this->getFile.readsome(fileBuffer, max_data_length);
+
+  std::vector<char> data;
+  data.assign(fileBuffer, fileBuffer + read);
+
+  return data;
+}

+ 351 - 0
daemon/src/JsonCommander.cpp

@@ -0,0 +1,351 @@
+#include "../include/JsonCommander.h"
+#include "../include/base64.h"
+
+JsonCommander::JsonCommander(FileManager &fileManager)
+    : fileManager(fileManager) {}
+
+JsonCommander::~JsonCommander() {}
+
+JsonCommander::Response JsonCommander::execute(const Json::Value &message) {
+  JsonCommander::Response response;
+  if (message["command"].asString().compare("status") == 0) {
+    response = executeStatus(message);
+  } else if (message["command"].asString().compare("close") == 0) {
+    response = executeClose(message);
+  } else if (message["command"].asString().compare("list") == 0) {
+    response = executeList(message);
+  } else if (message["command"].asString().compare("put") == 0) {
+    response = executePut(message);
+  } else if (message["command"].asString().compare("putdata") == 0) {
+    response = executePutdata(message);
+  } else if (message["command"].asString().compare("get") == 0) {
+    response = executeGet(message);
+  } else if (message["command"].asString().compare("getdata") == 0) {
+    response = executeGetdata(message);
+  } else {
+    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";
+
+  // TODO return real file list
+
+  Json::Value array;
+  array.append("some");
+  array.append("important");
+  array.append("data");
+  response.json["names"] = array;
+  response.json["remaining"] = 0;
+
+  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()) {
+    // 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 (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 = -1;
+    } 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 == -1 ||
+        --this->putFileReceived == message["remaining"].asInt()) {
+      // accept request
+      response.action = send;
+      response.json["cancel"] = false;
+      response.json["error"] = "";
+
+      const std::vector<char> data =
+          base64::decodeVector(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;
+
+    bool opened = fileManager.openGetFile(message["file"].asString(),
+                                          this->getFileRemaining);
+    if (opened) {
+      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<char> data = fileManager.readGet();
+      response.json["data"] = base64::encodeVector(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::testVersion(const Json::Value &message) {
+  JsonCommander::Response response;
+  response.json["version"] = this->protocolVersion;
+
+  // check version string is the same
+  if (message["version"].asString().compare(this->protocolVersion) == 0) {
+    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["login"].asBool() &&
+             message["user"].asString().compare("user") == 0 &&
+             message["pass"].asString().compare("pass") == 0) {
+    // TODO replace with real credentials check
+    response.action = send;
+    response.json["accept"] = true;
+    response.json["error"] = "";
+  } else if (!message["login"].asBool()) {
+    // TODO implement registration
+    response.action = closeAndSend;
+    response.json["accept"] = false;
+    response.json["error"] = "registration is not yet implemented";
+  } else {
+    response.action = closeAndSend;
+    response.json["accept"] = false;
+    response.json["error"] = "wrong username or password";
+  }
+
+  return response;
+}

+ 25 - 249
daemon/src/Server.cpp

@@ -1,5 +1,4 @@
 #include "../include/Server.h"
-#include "../include/base64.h"
 
 #include <iostream>
 
@@ -12,7 +11,7 @@ using ip::tcp;
 
 con_handler::con_handler(
     basic_socket_acceptor<ip::tcp>::executor_type &io_service)
-    : sock(io_service), buf(max_length) {
+    : sock(io_service), buf(max_length), jsonCommander(fileManager) {
   // disable indentation for json
   this->jsonStringBuilder.settings_["indentation"] = "";
 
@@ -38,26 +37,16 @@ void con_handler::handle_read_version(const boost::system::error_code &err,
     // set up json stuff
     Json::Value root = parseMessage();
 
-    // create answer
-    Json::Value answer;
-
-    answer["version"] = this->protocolVersion;
-
-    // check version string
-    if (root["version"].asString().compare(this->protocolVersion) == 0) {
-      answer["accept"] = true;
+    JsonCommander::Response response = this->jsonCommander.testVersion(root);
 
+    switch (response.action) {
+    case JsonCommander::Action::send:
       read(&con_handler::handle_read_login);
-
-      // send answer
-      sendJson(answer);
-    } else {
-      answer["accept"] = false;
-
-      // send answer
-      sendJson(answer);
-      ;
-      // close connection
+      sendJson(response.json);
+      break;
+    case JsonCommander::Action::closeAndSend:
+      sendJson(response.json);
+    default:
       sock.close();
     }
 
@@ -73,25 +62,16 @@ void con_handler::handle_read_login(const boost::system::error_code &err,
     // set up json stuff
     Json::Value root = parseMessage();
 
-    Json::Value answer;
-
-    // user credentials
-    // TODO check user credentials!!!
-    if (root["user"].asString().compare("user") == 0 &&
-        root["pass"].asString().compare("pass") == 0) {
-      answer["accept"] = true;
+    JsonCommander::Response response = this->jsonCommander.checkLogin(root);
 
-      // read next data
+    switch (response.action) {
+    case JsonCommander::Action::send:
       read(&con_handler::handle_read_command);
-
-      // send answer
-      sendJson(answer);
-    } else {
-      answer["accept"] = false;
-
-      // send answer
-      sendJson(answer);
-      // close connection
+      sendJson(response.json);
+      break;
+    case JsonCommander::Action::closeAndSend:
+      sendJson(response.json);
+    default:
       sock.close();
     }
 
@@ -107,220 +87,16 @@ void con_handler::handle_read_command(const boost::system::error_code &err,
     // set up json stuff
     Json::Value root = parseMessage();
 
-    Json::Value answer;
+    JsonCommander::Response response = this->jsonCommander.execute(root);
 
-    // check command
-    if (root["command"].asString().compare("status") == 0) {
-      // read next data
+    switch (response.action) {
+    case JsonCommander::Action::send:
       read(&con_handler::handle_read_command);
-
-      answer["command"] = "status";
-
-      // TODO answer a real status message
-      std::string response;
-      if (this->getFile.is_open() && this->putFile.is_open()) {
-        response = "download and upload running";
-      } else if (this->putFile.is_open()) {
-        response = "upload running";
-      } else if (this->getFile.is_open()) {
-        response = "download running";
-      } else {
-        response = "ok";
-      }
-
-      answer["response"] = response;
-
-      // send answer
-      sendJson(answer);
-    } else if (root["command"].compare("list") == 0) {
-
-      // read next data
-      read(&con_handler::handle_read_command);
-
-      answer["command"] = "list";
-
-      // TODO look for real data
-      Json::Value array;
-      array.append("some");
-      array.append("important");
-      array.append("data");
-      answer["names"] = array;
-      answer["remaining"] = 0;
-
-      // send answer
-      sendJson(answer);
-
-    } else if (root["command"].asString().compare("put") == 0) {
-      // read next data
-      read(&con_handler::handle_read_command);
-
-      answer["command"] = "put";
-
-      if (this->putFile.is_open() && root["cancel"].asBool() == true) {
-        // cancel upload
-        this->putFile.close();
-        std::remove(this->putFileName.c_str());
-
-      } else if (this->putFile.is_open() &&
-                 (this->putFile.tellp() == std::ios::beg ||
-                  root["remaining"].asInt() == this->putFileReceived - 1)) {
-        // upload chunks
-        // decode base64 string
-        const std::vector<char> data =
-            base64::decodeVector(root["data"].asString());
-
-        // write the vector data to the output file
-        std::ostream_iterator<char> output_iterator(putFile);
-        std::copy(data.begin(), data.end(), output_iterator);
-
-        this->putFileReceived = root["remaining"].asInt();
-        // close file if put sends remaining = 0
-        if (this->putFileReceived == 0) {
-          this->putFile.close();
-        }
-
-        answer["cancel"] = false;
-        answer["received"] = this->putFileReceived;
-
-        // send answer
-        sendJson(answer);
-
-      } else {
-        // start upload
-        const std::string filename = root["file"].asString();
-        if (filename.find("/") != std::string::npos) {
-          // slashes in file names are illegal
-          answer["accept"] = false;
-
-          // send answer
-          sendJson(answer);
-        } else {
-          // build file path
-          this->putFileName = this->fileDirectory;
-          this->putFileName.append(filename);
-
-          // open file and test if it already exists
-          this->putFile.open(this->putFileName,
-                             std::ios::app | std::ios::binary);
-          if (this->putFile.tellp() != std::ios::beg &&
-              root["file"].isString()) {
-            // file already exists
-            this->putFile.close();
-            answer["accept"] = false;
-
-            // send answer
-            sendJson(answer);
-          } else {
-            // accept file
-            answer["accept"] = true;
-
-            // send answer
-            sendJson(answer);
-          }
-        }
-      }
-    } else if (root["command"].asString().compare("get") == 0) {
-      // read next data
-      read(&con_handler::handle_read_command);
-
-      answer["command"] = "get";
-
-      if (this->getFile.is_open()) {
-        // a get request is already being progressed
-        if (root["received"].asInt() == this->getFileRemaining) {
-          if (root["cancel"].asBool()) {
-            // cancel get
-            this->getFile.close();
-          } else if (this->getFileRemaining > 0) {
-            char fileBuffer[max_data_length];
-            size_t read = this->getFile.readsome(fileBuffer, max_data_length);
-            this->getFileRemaining--;
-
-            // store binary data in vector because a string whould end with
-            // '\0'
-            std::vector<char> data;
-            data.assign(fileBuffer, fileBuffer + read);
-
-            answer["remaining"] = this->getFileRemaining;
-            answer["cancel"] = false;
-            answer["data"] = base64::encodeVector(data);
-
-            sendJson(answer);
-          } else {
-            // remaining 0 and received by client so you can close the file
-            this->getFile.close();
-          }
-        } else {
-          answer["accept"] = false;
-
-          // send answer
-          sendJson(answer);
-        }
-      } else {
-        // open file
-        const std::string filename = root["file"].asString();
-        if (filename.find("/") != std::string::npos) {
-          // slashes in file names are illegal
-          answer["accept"] = false;
-
-          // send answer
-          sendJson(answer);
-        } else {
-          std::string file = this->fileDirectory;
-          file.append(filename);
-
-          this->getFile = std::ifstream(file, std::ios::ate | std::ios::binary);
-
-          if (this->getFile.is_open() == 0) {
-            // file does not exist or cannot be opened
-            answer["accept"] = false;
-
-            // send answer
-            sendJson(answer);
-          } else {
-            answer["accept"] = true;
-
-            sendJson(answer);
-
-            answer = Json::Value();
-            answer["command"] = "get";
-
-            // get size of file
-            size_t size = this->getFile.tellg();
-            this->getFile.seekg(std::ios::beg);
-
-            char fileBuffer[max_data_length];
-            size_t read = this->getFile.readsome(fileBuffer, max_data_length);
-            size -= read;
-            this->getFileRemaining =
-                size / max_data_length + (size % max_data_length == 0 ? 0 : 1);
-
-            // store binary data in vector because a string whould end with
-            // '\0'
-            std::vector<char> data;
-            data.assign(fileBuffer, fileBuffer + read);
-
-            answer["remaining"] = this->getFileRemaining;
-            answer["cancel"] = false;
-            answer["data"] = base64::encodeVector(data);
-
-            sendJson(answer);
-          }
-        }
-      }
-    } else if (root["command"].asString().compare("close") == 0) {
-      answer["command"] = "close";
-      answer["response"] = "bye";
-
-      // send answer
-      sendJson(answer);
-
-      // close connection
-      sock.close();
-    } else {
-      // TODO handle error
-
-      // close connection
+      sendJson(response.json);
+      break;
+    case JsonCommander::Action::closeAndSend:
+      sendJson(response.json);
+    default:
       sock.close();
     }