Browse Source

Merge branch 'develop' into 'master'

Merge develop in master

See merge request tobias.wach/ccats!66
Sander, Paul 4 years ago
parent
commit
342d7e589d
80 changed files with 4572 additions and 994 deletions
  1. 29 0
      .gitlab-ci.yml
  2. 26 5
      Client-Server Protocol.md
  3. 53 0
      Dockerfile
  4. 22 4
      GUI-CLI Protocol.md
  5. 41 1
      autoformat.sh
  6. 4 4
      cli/CMakeLists.txt
  7. 100 0
      cli/include/batchioman.h
  8. 7 0
      cli/include/cmdman.h
  9. 49 5
      cli/include/fileman.h
  10. 3 2
      cli/include/global.h
  11. 40 34
      cli/include/ioman.h
  12. 4 3
      cli/include/machineioman.h
  13. 6 3
      cli/include/userioman.h
  14. 401 0
      cli/src/batchioman.cpp
  15. 123 17
      cli/src/cmdman.cpp
  16. 300 34
      cli/src/fileman.cpp
  17. 125 72
      cli/src/ioman.cpp
  18. 4 14
      cli/src/machineioman.cpp
  19. 19 9
      cli/src/main.cpp
  20. 43 19
      cli/src/userioman.cpp
  21. 220 0
      cli/test/cryptotest-gcm.c
  22. 2 0
      cli/test/samplekey1.bin
  23. 1 0
      cli/test/samplekey2.bin
  24. 38 3
      daemon/Daemon-Config-Reference.md
  25. 26 0
      daemon/createsslfiles.sh
  26. 174 0
      daemon/include/CovertChannel/CovertChannel.h
  27. 64 0
      daemon/include/CovertChannel/ForwardChannel.h
  28. 82 0
      daemon/include/CovertChannel/ProxyChannel.h
  29. 25 13
      daemon/include/FileManager.h
  30. 8 2
      daemon/include/JsonCommander.h
  31. 32 3
      daemon/include/Server.h
  32. 0 70
      daemon/include/Sniffer.h
  33. 4 21
      daemon/include/base64.h
  34. 3 2
      daemon/src/CMakeLists.txt
  35. 86 0
      daemon/src/CovertChannel/CovertChannel.cpp
  36. 11 0
      daemon/src/CovertChannel/ForwardChannel.cpp
  37. 82 0
      daemon/src/CovertChannel/ProxyChannel.cpp
  38. 22 5
      daemon/src/FileManager.cpp
  39. 64 11
      daemon/src/JsonCommander.cpp
  40. 58 25
      daemon/src/Server.cpp
  41. 0 33
      daemon/src/Sniffer.cpp
  42. 4 4
      daemon/src/base64.cpp
  43. 35 8
      daemon/src/main.cpp
  44. 1 1
      daemon/test/CMakeLists.txt
  45. 17 14
      daemon/test/FileManagerMock.h
  46. 336 11
      daemon/test/JsonCommanderTest.cpp
  47. 13 0
      daemon/test/UserManagerMock.cpp
  48. 8 0
      daemon/test/test-dh2048.pem
  49. 4 4
      gui/CMakeLists.txt
  50. 33 0
      gui/include/cmdmanager.h
  51. 18 0
      gui/include/config.h
  52. 11 0
      gui/include/jsonhandler.h
  53. 138 0
      gui/include/qmlhandler.h
  54. 28 5
      gui/src/Forms/Connect/IpPopup.ui.qml
  55. 30 3
      gui/src/Forms/Connect/LoginForm.ui.qml
  56. 3 4
      gui/src/Forms/Connect/LoginSignupPopup.ui.qml
  57. 146 0
      gui/src/Forms/Connect/SignupForm.ui.qml
  58. 0 0
      gui/src/Forms/Help/HelpForm.ui.qml
  59. 0 0
      gui/src/Forms/Main/FooterForm.ui.qml
  60. 128 0
      gui/src/Forms/Main/InvalidCliPathPopup.ui.qml
  61. 78 0
      gui/src/Forms/Main/InvalidConfigPopup.ui.qml
  62. 108 0
      gui/src/Forms/Main/NoConfigFoundPopup.ui.qml
  63. 20 5
      gui/src/Forms/Main/main.qml
  64. 0 0
      gui/src/Forms/Messages/MessagesForm.ui.qml
  65. 87 0
      gui/src/Forms/Receiving/ReceivingFileTemplate.ui.qml
  66. 56 0
      gui/src/Forms/Receiving/ReceivingFileTemplateDeletePopup.ui.qml
  67. 69 0
      gui/src/Forms/Receiving/ReceivingForm.ui.qml
  68. 0 0
      gui/src/Forms/Sending/SendingForm.ui.qml
  69. 107 0
      gui/src/Forms/Settings/DeleteMePopup.ui.qml
  70. 244 0
      gui/src/Forms/Settings/SettingsForm.ui.qml
  71. 0 93
      gui/src/ReceivingForm.ui.qml
  72. 0 80
      gui/src/SettingsForm.ui.qml
  73. 0 111
      gui/src/SignupForm.ui.qml
  74. 146 0
      gui/src/cmdmanager.cpp
  75. 100 0
      gui/src/config.cpp
  76. 26 0
      gui/src/jsonhandler.cpp
  77. 6 2
      gui/src/main.cpp
  78. 24 11
      gui/src/qml.qrc
  79. 147 131
      gui/src/qmlhandler.cpp
  80. 0 93
      gui/src/qmlhandler.h

+ 29 - 0
.gitlab-ci.yml

@@ -0,0 +1,29 @@
+image: docker:19.03.1
+
+services:
+  - docker:dind
+
+stages:
+  - build
+  - test
+
+build-image:
+  stage: build
+  before_script:
+  - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+  script:
+    - docker build --pull -t "$CI_REGISTRY_IMAGE" .
+    - docker push "$CI_REGISTRY_IMAGE"
+     
+jsonCommaderTest:
+  stage: test
+  before_script:
+  - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+  after_script:
+  - docker stop cont
+  - docker rm cont
+  script:
+  - docker pull "$CI_REGISTRY_IMAGE"
+  - docker run -d --name cont "$CI_REGISTRY_IMAGE" 
+  - docker exec cont test/jsonCommanderTest
+    

+ 26 - 5
Client-Server Protocol.md

@@ -1,6 +1,6 @@
 # Client-Server Protocol
 
-Protocol version: <b>"0.2"</b>
+Protocol version: <b>"0.3"</b>
 
 Every json message must be minimized and be followed by a newline. This rule makes receiving and parsing the messages easier because you can read until a newline comes and you will parse every json message seperately because you won't read multiple json messages from the buffer at a time.
 
@@ -14,15 +14,17 @@ You can close the connection by sending a cancel message.
 Client:
 ```
 {
-	"version": string
+	"major": int,
+	"minor": int
 }
 ```
-
+(For example, if the version number is 42.1, then major is 42, minor is 1.)
 
 Server:
 ```
 {
-	"version": string,
+	"major": int,
+	"minor": int,
 	"accept": bool
 }
 ```
@@ -316,8 +318,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
 

+ 53 - 0
Dockerfile

@@ -0,0 +1,53 @@
+FROM debian
+
+RUN apt-get update -y && apt-get install -y \
+    build-essential git cmake libpcap-dev libjsoncpp-dev wget libssl-dev
+
+WORKDIR /root/build
+
+RUN wget https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.gz \
+    && tar xfz boost_1_72_0.tar.gz \
+    && rm boost_1_72_0.tar.gz \
+    && cd boost_1_72_0 \
+    && ./bootstrap.sh --prefix=/usr/local \
+    && ./b2 install \
+    && cd .. \
+    && rm -rf boost_1_72_0
+
+# Build and install libtins
+RUN git clone https://github.com/mfontanini/libtins.git \
+    && cd libtins && git checkout v4.2 \
+    && mkdir build && cd build \
+    && cmake ../ -DLIBTINS_ENABLE_CXX11=1 && make && make install \
+    && cd ../.. && rm -rf libtins
+
+# Build and install googletest
+RUN git clone https://github.com/google/googletest.git \
+    && cd googletest \
+    && mkdir build && cd build \
+    && cmake .. && make && make install \
+    && cd ../.. && rm -rf googletest
+
+RUN mkdir /root/build/files \
+    && touch /root/build/config.txt \
+    && echo "port=1234" >> /root/build/config.txt \
+    && echo "interface=lo" >> /root/build/config.txt \
+    && echo "userdatabase=userStorage.txt" >> /root/build/config.txt \
+    && echo "filedirectory=./files" >> /root/build/config.txt
+
+# Copy all required data into image
+COPY .cmake_modules/ /root/.cmake_modules
+COPY /daemon/include /root/build/include
+COPY /daemon/src /root/build/src
+COPY /daemon/test /root/build/test
+COPY /daemon/CMakeLists.txt /root/build
+
+# Compile ccats with tests
+RUN cmake -DENABLE_TESTS=true . && make \
+    && rm -rf src include CMakeFiles \
+    && rm Makefile CMakeCache.txt cmake_install.cmake CMakeLists.txt \
+    CTestTestfile.cmake
+
+# Set entrypoint and expose port
+ENTRYPOINT /root/build/bin/ccats
+EXPOSE 1234

+ 22 - 4
GUI-CLI Protocol.md

@@ -99,9 +99,7 @@ CLI:
 #### 2.2.1 list - request file list download
 GUI:
 ```
-{
-	write: "list"
-}
+write: "list"
 ```
 CLI:
 ```
@@ -159,7 +157,7 @@ Get is split in two commands. `get` to request a file download and `getdata` to
 
 GUI:
 ```
-write: "get" file_name
+write: "get" file_path
 ```
 
 CLI:
@@ -189,6 +187,26 @@ CLI:
 If `cancel` is `true` then the download of the file is canceled and an error message should be in `error`.
 `speed` shall contain a float describing the download speed in kB/s.
 
+### 2.5 Deletefile command
+
+Requests the deletion of a file from the server.
+
+#### 2.5.1  - request file list download
+GUI:
+```
+write: "deletefile" file_name
+```
+CLI:
+```
+{
+	"command": "deletefile",
+	"file": string,
+	"accept": bool,
+	"error": string
+}
+```
+If `accept` is `true` the command is valid and the server has deleted the file. Else the request was rejected no changes have been made. `error` should contain an error string in that case.
+
 
 ### TODO
 

+ 41 - 1
autoformat.sh

@@ -1,2 +1,42 @@
 #!/bin/bash
-clang-format -i {daemon,cli,gui}/src/*.cpp {daemon,cli,gui}/include/*.h {daemon,cli,gui}/test/*.cpp
+folderArray=( "daemon" "cli" "gui" )
+
+# Variable that will hold the name of the clang-format command
+FMT=""
+
+# Some distros just call it clang-format. Others (e.g. Ubuntu) are insistent
+# that the version number be part of the command. We prefer clang-format if
+# that's present, otherwise we work backwards from highest version to lowest
+# version.
+for clangfmt in clang-format{,-{4,3}.{9,8,7,6,5,4,3,2,1,0}}; do
+    if which "$clangfmt" &>/dev/null; then
+        FMT="$clangfmt"
+        break
+    fi
+done
+
+# Check if we found a working clang-format
+if [ -z "$FMT" ]; then
+    echo "failed to find clang-format"
+    exit 1
+fi
+
+function format() {
+    for f in $(find $@ -type d -path "$@/build" -prune -o -type f -name '*.h' -or -name '*.m' -or -name '*.mm' -or -name '*.c' -or -name '*.cpp'); do
+        if [ ! -d "${f}" ]; then
+            echo "format ${f}";
+            ${FMT} -i ${f};
+        fi
+    done
+
+    echo "~~~ $@ Done ~~~";
+}
+
+# Check all of the arguments first to make sure they're all directories
+for dir in ${folderArray[@]}; do
+    if [ ! -d "${dir}" ]; then
+        echo "${dir} is not a directory";
+    else
+        format ${dir};
+    fi
+done

+ 4 - 4
cli/CMakeLists.txt

@@ -4,8 +4,7 @@ cmake_minimum_required(VERSION 2.8)
 
 project(ccats-cli)
 
-# add_executable(ccats-cli src/main.cpp src/ioman.cpp src/machineioman.cpp src/userioman.cpp src/base64.cpp src/netman.cpp src/cmdman.cpp src/fileman.cpp)
-add_executable(ccats-cli src/main.cpp src/ioman.cpp src/machineioman.cpp src/userioman.cpp src/base64.cpp src/cmdman.cpp src/fileman.cpp)
+add_executable(ccats-cli src/main.cpp src/ioman.cpp src/machineioman.cpp src/userioman.cpp src/batchioman.cpp src/base64.cpp src/cmdman.cpp src/fileman.cpp)
 
 # use pkg-config to find readline as it doesnt provide cmake files
 find_package(PkgConfig REQUIRED)
@@ -13,9 +12,10 @@ pkg_check_modules(READLINE REQUIRED readline)
 pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 find_package(Threads)
-find_package(Boost 1.67 REQUIRED COMPONENTS system program_options)
+find_package(OpenSSL REQUIRED)
+find_package(Boost 1.70 REQUIRED COMPONENTS system program_options)
 
 
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} include)
 
-target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${READLINE_LIBRARIES} ${JSONCPP_LIBRARIES})
+target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${READLINE_LIBRARIES} ${JSONCPP_LIBRARIES})

+ 100 - 0
cli/include/batchioman.h

@@ -0,0 +1,100 @@
+#ifndef BATCHIOMAN_H
+#define BATCHIOMAN_H
+
+#include "ioman.h"
+#include <fstream>
+#include <string>
+#include <vector>
+
+/**
+ * @class BatchIoMan
+ *
+ * Provides specific implementations of IoMan outputs and prompts
+ * for unattended sessions
+ */
+class BatchIoMan : public IoMan {
+private:
+	/**
+	 * Map containing pointers to the appropriate member functions for printing
+	 * formatted json output
+	 */
+	map<string, std::string (BatchIoMan::*)(Json::Value)> printmap;
+
+	/**
+	 * Class-wide json functionality
+	 */
+	Json::CharReader *reader;
+	Json::StreamWriterBuilder wbuilder;
+	string jsonerror;
+
+	std::ofstream normalout;
+	std::ofstream errorout;
+	std::ofstream debugout;
+	std::ifstream batchin;
+
+	/**
+	 * Mutex for synchronized message output
+	 */
+	std::recursive_mutex msgmutex;
+
+	bool getnextline;
+	bool verbose;
+	string filepath;
+	std::mutex linemutex;
+	std::condition_variable linecv;
+
+	/**
+	 * Format and pretty print json for logfile output
+	 */
+	std::string printJson(Json::Value root);
+
+	/**
+	 * Method prototypes for printing json output
+	 */
+	/* printing commands go here */
+	std::string printError(Json::Value root);
+	std::string printConnect(Json::Value root);
+	std::string printHelp(Json::Value root);
+	std::string printStatus(Json::Value root);
+	std::string printDisconnect(Json::Value root);
+	std::string printPut(Json::Value root);
+	std::string printGet(Json::Value root);
+	std::string printList(Json::Value root);
+	std::string printVersion(Json::Value root);
+	std::string printLogin(Json::Value root);
+	std::string printSignup(Json::Value root);
+	std::string printPutdata(Json::Value root);
+	std::string printGetdata(Json::Value root);
+	std::string printListdata(Json::Value root);
+	std::string printHead(Json::Value root);
+	std::string printDeletefile(Json::Value root);
+	std::string printDeleteme(Json::Value root);
+	std::string printKeyfile(Json::Value root);
+	std::string printClosekey(Json::Value root);
+
+public:
+	/**
+	 * Constructor and destructor
+	 */
+	BatchIoMan(char *ipcstring, bool usessl, bool beverbose, string batchpath);
+	~BatchIoMan();
+
+	bool init();
+	void run();
+
+protected:
+	/**
+	 * Specific implementations for printing messages
+	 */
+	void printMessage(std::string msg, OutMsgType type);
+	void printWelcomeMessage();
+	void handleInCmdResponse(CmdMan::CmdRet cmdret);
+	void handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::string> &toput);
+
+	/**
+	 * Return the specific prompt strings for IoMan prompts
+	 */
+	std::string getCmdPrompt();
+};
+
+#endif

+ 7 - 0
cli/include/cmdman.h

@@ -111,6 +111,12 @@ private:
 	CmdRet cmdList(vector<string> args);
 	const string descHead = "request the first four bytes of a file from the server";
 	CmdRet cmdHead(vector<string> args);
+	const string descDeletefile = "delete a file from the server";
+	CmdRet cmdDeletefile(vector<string> args);
+	const string descKeyfile = "set keyfile to use";
+	CmdRet cmdKeyfile(vector<string> args);
+	const string descClosekey = "stop using the previously selected keyfile";
+	CmdRet cmdClosekey(vector<string> args);
 
 	const string descLogin = "login to the server";
 	CmdRet cmdLogin(vector<string> args);
@@ -145,6 +151,7 @@ private:
 	CmdRet handleLogin(Json::Value);
 	CmdRet handleSignup(Json::Value);
 	CmdRet handleHead(Json::Value);
+	CmdRet handleDeletefile(Json::Value);
 	CmdRet handleDeleteme(Json::Value);
 };
 

+ 49 - 5
cli/include/fileman.h

@@ -2,11 +2,12 @@
 #define FILEMAN_H
 
 #include <fstream>
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
 #include <string>
 #include <vector>
 
-#define BLOCKSIZE 8
-
 /**
  * @class FileMan
  *
@@ -25,11 +26,11 @@ private:
 	 * Boolean replacement for filestreams being open for list
 	 *
 	 */
-	std::ifstream putfile;
-	std::ofstream getfile;
+	std::vector<std::vector<char>> putdata;
+	std::fstream getfile;
 	std::vector<std::string> listdata;
 	std::string getpath, getname, putpath, putname;
-	const unsigned int max_read_len = 8;
+	const unsigned int max_read_len = 4096;
 	int putsize;
 	int putchunks;
 	int putchunksRemaining;
@@ -38,7 +39,31 @@ private:
 	int listchunks;
 	int listchunksRemaining;
 
+	bool isputting;
 	bool islisting;
+	bool keyenabled;
+	bool cryptoreadye;
+	bool cryptoreadyd;
+	bool pendingerr;
+
+	unsigned char iv[12];  // 96bits
+	unsigned char tag[16]; // 128bits
+	unsigned char key[32]; // 256bits
+	const unsigned int cipherblocklen = 128;
+
+	EVP_CIPHER_CTX *cryptctxe;
+	EVP_CIPHER_CTX *cryptctxd;
+	std::string opensslerr;
+
+	void setOpensslError();
+	bool initCryptoE();
+	bool initCryptoD();
+	void deinitCryptoE();
+	void deinitCryptoD();
+
+	std::vector<std::vector<char>> chunkify(char *data, unsigned int size);
+	void writeEnc(const std::vector<char> data);
+	const char signature[4] = {'C', 'C', 'A', 'T'};
 
 public:
 	/**
@@ -55,6 +80,7 @@ public:
 	bool isGetting();
 	bool isPutting();
 	bool isListing();
+	bool isEncrypted();
 
 	/**
 	 * Check for and prepare state and streams for reading/writing
@@ -65,6 +91,14 @@ public:
 	bool openGet(const std::string &path);
 	bool openList();
 
+	/**
+	 * Open file and read a hex string from it as key and initialize the IV
+	 * This will fail is a put or a get is in progress
+	 *
+	 * Return true if successful, false otherwise. After this put and get will en/decrypt data
+	 */
+	bool openKey(const std::string &path);
+
 	/**
 	 * Close the respective filestream
 	 */
@@ -72,6 +106,14 @@ public:
 	void closeGet();
 	void closeList();
 
+	/**
+	 * Reset internal key state and disable en/decryption of data
+	 * This will fail if a put or get is in progress
+	 *
+	 * Return true if key was reset, false otherwise. After this put and get will use unencrypted data
+	 */
+	bool closeKey();
+
 	/**
 	 * Query the names of the file currently being put or get
 	 */
@@ -130,6 +172,8 @@ public:
 	 * Returns the filename of the passed (relative) path of a file
 	 */
 	std::string pathToFilename(std::string path);
+
+	std::string getOpensslError();
 };
 
 #endif

+ 3 - 2
cli/include/global.h

@@ -3,6 +3,7 @@
 
 #include <string>
 
-const std::string protocolVersion = "0.2";
+const int protocolMajorVersion = 0;
+const int protocolMinorVersion = 3;
 
-#endif
+#endif

+ 40 - 34
cli/include/ioman.h

@@ -5,6 +5,7 @@
 #include "fileman.h"
 
 #include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
 #include <condition_variable>
 #include <json/json.h>
 #include <mutex>
@@ -49,27 +50,60 @@ public:
 	std::vector<std::string> localinput;
 	std::condition_variable localcv;
 
+	boost::system::error_code errcode;
+
+protected:
+	/**
+	 * Prompt messages for readline prompt
+	 */
+	virtual void printWelcomeMessage() = 0;
+	virtual std::string getCmdPrompt() = 0;
+
+	/**
+	 * Enum for internal login and version state
+	 * Variables for keeping said state
+	 * Matching mutex and condition variable
+	 */
+	enum Status { off, on, err };
+	Status loginstatus;
+	Status versionstatus;
+	std::mutex initmutex;
+	std::condition_variable initcv;
+	bool connected;
+
+	virtual void handleInCmdResponse(CmdMan::CmdRet cmdret);
+	virtual void handleOutCmdResponse(CmdMan::CmdRet cmdret, vector<string> &toput);
+
+	tcp::socket *tcpsock;
+	boost::asio::ssl::stream<tcp::socket &> *sslsock;
+	bool usessl;
+
 private:
 	/**
 	 * Internal state to provide class-wide asio networking functionality
 	 */
 	boost::asio::io_service ios;
 	boost::asio::streambuf recvbuf;
-	boost::system::error_code errcode;
-	tcp::socket *tcpsock;
+	boost::asio::ssl::context *sslctx;
 	/**
 	 * The IP and port to connect to
 	 * Flag telling wether one is connected
 	 */
 	std::string ipstring;
 	unsigned short port;
-	bool connected;
 	/**
 	 * Instances of CmdMan and FileMan to process user input and handle File I/O
 	 */
 	CmdMan cmdman;
 	FileMan fileman;
 
+	/**
+	 * Class-wide json functionality
+	 */
+	Json::CharReader *reader;
+	Json::StreamWriterBuilder wbuilder;
+	string jsonerror;
+
 	/**
 	 * Thread handles for processing local and network input as well as generating
 	 * responses to both Matching mutexes for the flags wether the threads should
@@ -91,24 +125,6 @@ private:
 	std::mutex netmutex;
 	std::condition_variable netcv;
 
-	/**
-	 * Class-wide json functionality
-	 */
-	Json::CharReader *reader;
-	Json::StreamWriterBuilder wbuilder;
-	string jsonerror;
-
-	/**
-	 * Enum for internal login and version state
-	 * Variables for keeping said state
-	 * Matching mutex and condition variable
-	 */
-	enum Status { off, on, err };
-	Status versionstatus;
-	Status loginstatus;
-	std::mutex initmutex;
-	std::condition_variable initcv;
-
 	/**
 	 * Tokenizes input based on space as seperator
 	 * Respects double-quoted tokens
@@ -116,34 +132,24 @@ private:
 	 */
 	std::vector<std::string> tokenizeInput(std::string in);
 
-protected:
-	/**
-	 * Prompt messages for readline prompt
-	 */
-	virtual void printWelcomeMessage() = 0;
-	virtual std::string getCmdPrompt() = 0;
-	// following prompts currently not in use because login happens by entering
-	// command
-	//~  virtual std::string getUserPrompt() = 0;
-	//~  virtual std::string getPassPrompt() = 0;
-
 public:
 	/**
 	 * Constructor and destructor
 	 */
 	IoMan(char *ipcstring);
+	IoMan(char *ipcstring, bool enablessl);
 	virtual ~IoMan();
 
 	/**
 	 * Establish connection to server and perform vesion check
 	 * Return true if successful, false otherwise
 	 */
-	bool init();
+	virtual bool init();
 
 	/**
 	 * Main loop, call init first
 	 */
-	void run();
+	virtual void run();
 
 	/**
 	 * Establish connection to server

+ 4 - 3
cli/include/machineioman.h

@@ -10,8 +10,11 @@
  * for interactive non-user sessions
  */
 class MachineIoMan : public IoMan {
+private:
+	bool verbose;
+
 public:
-	using IoMan::IoMan;
+	MachineIoMan(char *ipcstring, bool usessl, bool beverbose);
 
 protected:
 	/**
@@ -24,8 +27,6 @@ protected:
 	 * Return the specific prompt strings for IoMan prompts
 	 */
 	std::string getCmdPrompt();
-	std::string getUserPrompt();
-	std::string getPassPrompt();
 };
 
 #endif

+ 6 - 3
cli/include/userioman.h

@@ -24,6 +24,8 @@ private:
 	Json::StreamWriterBuilder wbuilder;
 	string jsonerror;
 
+	bool verbose;
+
 	/**
 	 * Format and pretty print json for terminal output
 	 */
@@ -48,7 +50,10 @@ private:
 	void printGetdata(Json::Value root);
 	void printListdata(Json::Value root);
 	void printHead(Json::Value root);
+	void printDeletefile(Json::Value root);
 	void printDeleteme(Json::Value root);
+	void printKeyfile(Json::Value root);
+	void printClosekey(Json::Value root);
 
 	/**
 	 * Mutex for synchronized message output
@@ -59,7 +64,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	UserIoMan(char *ipcstring);
+	UserIoMan(char *ipcstring, bool usessl, bool verbose);
 	~UserIoMan();
 
 protected:
@@ -73,8 +78,6 @@ protected:
 	 * Return the specific prompt strings for IoMan prompts
 	 */
 	std::string getCmdPrompt();
-	std::string getUserPrompt();
-	std::string getPassPrompt();
 };
 
 #endif

+ 401 - 0
cli/src/batchioman.cpp

@@ -0,0 +1,401 @@
+#include "../include/batchioman.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+BatchIoMan::BatchIoMan(char *ipcstring, bool usessl, bool beverbose, std::string batchpath) : IoMan(ipcstring, usessl) {
+	/* setup json stuff */
+	Json::CharReaderBuilder rbuilder;
+	wbuilder.settings_["indentation"] = "";
+	reader = rbuilder.newCharReader();
+
+	/* initialize print command map */
+	printmap["error"] = &BatchIoMan::printError;
+	printmap["connect"] = &BatchIoMan::printConnect;
+	printmap["help"] = &BatchIoMan::printHelp;
+	printmap["status"] = &BatchIoMan::printStatus;
+	printmap["disconnect"] = &BatchIoMan::printDisconnect;
+	printmap["put"] = &BatchIoMan::printPut;
+	printmap["get"] = &BatchIoMan::printGet;
+	printmap["list"] = &BatchIoMan::printList;
+	printmap["version"] = &BatchIoMan::printVersion;
+	printmap["login"] = &BatchIoMan::printLogin;
+	printmap["signup"] = &BatchIoMan::printSignup;
+	printmap["putdata"] = &BatchIoMan::printPutdata;
+	printmap["getdata"] = &BatchIoMan::printGetdata;
+	printmap["head"] = &BatchIoMan::printHead;
+	printmap["deletefile"] = &BatchIoMan::printDeletefile;
+	printmap["deleteme"] = &BatchIoMan::printDeleteme;
+	printmap["keyfile"] = &BatchIoMan::printKeyfile;
+	printmap["closekey"] = &BatchIoMan::printClosekey;
+
+	getnextline = false;
+	verbose = beverbose;
+	filepath = batchpath;
+}
+
+bool BatchIoMan::init() {
+	batchin.open(filepath);
+	normalout.open(filepath + ".out");
+	if (verbose)
+		debugout.open(filepath + ".debug");
+	errorout.open(filepath + ".err");
+	if (!batchin.is_open() || !normalout.is_open() || (verbose && !debugout.is_open()) || !errorout.is_open())
+		return false;
+	return IoMan::init();
+}
+
+BatchIoMan::~BatchIoMan() {
+	batchin.close();
+	normalout.close();
+	if (verbose)
+		debugout.close();
+	errorout.close();
+	delete reader;
+}
+
+void BatchIoMan::printMessage(std::string msg, OutMsgType type) {
+	Json::Value root;
+	msgmutex.lock();
+	switch (type) {
+	case normal: {
+		// this should never happen outside of development
+		if (!reader->parse(msg.c_str(), msg.c_str() + msg.size(), &root, &jsonerror)) {
+			printMessage(string(__PRETTY_FUNCTION__) + " couldnt parse json data: " + jsonerror, debug);
+		} else {
+			normalout << printJson(root) << std::endl;
+		}
+		break;
+	}
+	case error: {
+		// this should never happen outside of development
+		if (!reader->parse(msg.c_str(), msg.c_str() + msg.size(), &root, &jsonerror)) {
+			printMessage(string(__PRETTY_FUNCTION__) + " couldnt parse json data: " + jsonerror, debug);
+		} else {
+			errorout << printJson(root) << std::endl;
+		}
+		break;
+	}
+	case debug: {
+		if (verbose)
+			debugout << msg << std::endl;
+		break;
+	}
+	}
+	msgmutex.unlock();
+}
+
+void BatchIoMan::printWelcomeMessage() {}
+
+std::string BatchIoMan::getCmdPrompt() { return ""; }
+
+/* modified handleInCmdResponse to abort on error */
+void BatchIoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
+	// determine wether to send something and do so if required
+	if (cmdret.type & CmdMan::rettype::print) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
+		if (!(cmdret.type ^ CmdMan::rettype::print)) {
+			// xor here works because flag is set at this point
+			// if we only printed something get the next line
+			linemutex.lock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+			getnextline = true;
+			linemutex.unlock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+			linecv.notify_all();
+		}
+	}
+	if (cmdret.type & CmdMan::rettype::send) {
+		printMessage("IoMan::inputMain() sending json \"" + Json::writeString(wbuilder, cmdret.msg) + "\"", debug);
+		if (usessl)
+			boost::asio::write(*sslsock, boost::asio::buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
+		else
+			boost::asio::write(*tcpsock, boost::asio::buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
+		if (errcode) {
+			printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", error);
+			mainmutex.lock();
+			runmain = false;
+			mainmutex.unlock();
+			linemutex.lock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+			getnextline = true;
+			linemutex.unlock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+			linecv.notify_all();
+			return;
+		}
+	}
+	if (cmdret.type & CmdMan::rettype::error) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+		linemutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+		getnextline = true;
+		linemutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+		linecv.notify_all();
+	}
+	if (cmdret.type & CmdMan::rettype::close) {
+		/* TODO i dunno */
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+		linemutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+		getnextline = true;
+		linemutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+		linecv.notify_all();
+	}
+}
+
+/* modified handleOutCmdResponse to fetch next command and abort on error */
+void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::string> &toput) {
+	if (cmdret.type & CmdMan::rettype::close) {
+		/* TODO i dunno */
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+		linemutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+		getnextline = true;
+		linemutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+		linecv.notify_all();
+		return;
+	}
+	if (cmdret.type & CmdMan::rettype::error) {
+		initmutex.lock();
+		if (versionstatus == off)
+			versionstatus = err;
+		else if (loginstatus == off)
+			loginstatus = err;
+		initmutex.unlock();
+		initcv.notify_all();
+		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+		linemutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+		getnextline = true;
+		linemutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+		linecv.notify_all();
+		return;
+	}
+	if (cmdret.type & CmdMan::rettype::seton) {
+		initmutex.lock();
+		if (versionstatus == off)
+			versionstatus = on;
+		else
+			loginstatus = on;
+		initmutex.unlock();
+		initcv.notify_all();
+	}
+	if (cmdret.type & CmdMan::rettype::print) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
+	}
+	if (cmdret.type & CmdMan::rettype::send) {
+		printMessage(string(__PRETTY_FUNCTION__) + string(" send new cmd"), debug);
+		if (cmdret.nextcommand.size()) {
+			toput.push_back(cmdret.nextcommand);
+		}
+	}
+	if (versionstatus == on && !(cmdret.type & CmdMan::rettype::send)) {
+		// only fetch next line if we did not send a new command on our own
+		// if we managed to get here, get next command from file
+		linemutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
+		getnextline = true;
+		linemutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
+		linecv.notify_all();
+	}
+}
+
+/* main user input loop */
+void BatchIoMan::run() {
+	std::string line;
+	printMessage(string(__PRETTY_FUNCTION__) + " begin", debug);
+	std::unique_lock<std::mutex> ulock;
+
+	runmain = true;
+
+	mainmutex.lock();
+	while (runmain) {
+		mainmutex.unlock();
+
+		line.erase();
+		ulock = std::unique_lock<std::mutex>(linemutex);
+		while (!getnextline && runmain) {
+			linecv.wait(ulock);
+		}
+
+		if (!runmain)
+			break;
+		printMessage(string(__PRETTY_FUNCTION__) + " fetch next line", debug);
+		while (!line.size()) {
+			// skip empty lines until either eof or non-empty line
+			if (batchin.eof()) {
+				line = "disconnect";
+			} else
+				std::getline(batchin, line);
+		}
+
+		getnextline = false;
+		linemutex.unlock();
+
+		localmutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
+		localinput.push_back(line);
+		localmutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
+		localcv.notify_all();
+
+		if (!connected)
+			break;
+		if (loginstatus == err)
+			break;
+
+		mainmutex.lock();
+	}
+	mainmutex.unlock();
+}
+
+std::string BatchIoMan::printJson(Json::Value root) {
+	map<string, std::string (BatchIoMan::*)(Json::Value)>::iterator it = printmap.find(root["command"].asString());
+	if (it == printmap.end()) {
+		// this should never happen outside of development
+		printMessage(string(__PRETTY_FUNCTION__) + " unknown command \"" + root["command"].asString() + "\".\nensure code is implemented.", debug);
+		return "";
+	}
+	return (this->*(printmap[root["command"].asString()]))(root);
+}
+
+std::string BatchIoMan::printError(Json::Value root) { return std::string("Error: ") + root["error"].asString(); }
+
+std::string BatchIoMan::printConnect(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return std::string("Couldnt connect to ") + root["address"].asString() + ":" + std::to_string(root["port"].asUInt()) + "\n" +
+		       "Reason: " + root["error"].asString();
+	}
+	return "";
+}
+
+std::string BatchIoMan::printHelp(Json::Value root) {
+	std::string ret = std::string("Available commands are: ") + "\n";
+	for (Json::Value i : root["names"])
+		ret += i.asString() + "\n";
+	return ret;
+}
+
+std::string BatchIoMan::printStatus(Json::Value root) { return std::string("Server reports status: ") + root["response"].asString(); }
+
+std::string BatchIoMan::printDisconnect(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return "Disconnect failed.";
+	} else {
+		return "Disconnect successful.";
+	}
+}
+
+std::string BatchIoMan::printPut(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		if (root.isMember("file")) {
+			return std::string("Upload request for file ") + root["file"].asString() + " failed: " + root["error"].asString();
+		} else {
+			return std::string("Upload request failed: ") + root["error"].asString();
+		}
+	} else
+		return std::string("Begin uploading file ") + root["file"].asString();
+}
+
+std::string BatchIoMan::printGet(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		if (root.isMember("file")) {
+			return std::string("Download request for file ") + root["file"].asString() + " failed: " + root["error"].asString();
+		} else {
+			return std::string("Download request failed: ") + root["error"].asString();
+		}
+	} else
+		return std::string("Begin downloading file ") + root["file"].asString();
+}
+
+std::string BatchIoMan::printList(Json::Value root) {
+	std::string ret;
+	if (!root["accept"].asBool()) {
+		ret = std::string("Listing files failed: ") + root["error"].asString();
+	} else {
+		ret = std::string("Listing files stored on server: ") + "\n";
+		for (Json::Value i : root["names"])
+			ret += i.asString() + "\n";
+		ret += "End of list.";
+	}
+	return ret;
+}
+
+std::string BatchIoMan::printVersion(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return std::string("Version check failed. Server reports ") + root["serverversion"].asString() + " but client is " + root["clientversion"].asString();
+	} else
+		return "Version check ok.";
+}
+
+std::string BatchIoMan::printLogin(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return std::string("Login failed: ") + root["error"].asString();
+	} else
+		return "Login ok.";
+}
+
+std::string BatchIoMan::printSignup(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return std::string("Signup failed: ") + root["error"].asString();
+	} else
+		return "Signup ok. You are now logged in.";
+}
+
+std::string BatchIoMan::printDeleteme(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return std::string("User deletion failed: ") + root["error"].asString();
+	} else
+		return "User deletion ok. You are now disconnected from the server.";
+}
+
+std::string BatchIoMan::printPutdata(Json::Value root) { return ""; }
+
+std::string BatchIoMan::printGetdata(Json::Value root) { return ""; }
+
+std::string BatchIoMan::printListdata(Json::Value root) { return ""; }
+
+std::string BatchIoMan::printHead(Json::Value root) {
+	if (!root["accept"].asBool())
+		return std::string("Request of the first four bytes failed. ") + root["error"].asString();
+	else
+		return std::string("First four bytes of file ") + root["file"].asString() + " are: " + root["data"].asString();
+}
+
+std::string BatchIoMan::printDeletefile(Json::Value root) {
+	if (!root["accept"].asBool())
+		return std::string("Deletion of file ") + root["file"].asString() + " failed. " + root["error"].asString();
+	else
+		return std::string("File ") + root["file"].asString() + " deleted succesfully";
+}
+
+std::string BatchIoMan::printKeyfile(Json::Value root) {
+	if (!root["accept"].asBool())
+		return std::string("Couldnt select keyfile ") + root["file"].asString() + ": " + root["error"].asString();
+	else
+		return std::string("Using keyfile ") + root["file"].asString();
+}
+
+std::string BatchIoMan::printClosekey(Json::Value root) {
+	if (!root["accept"].asBool())
+		return std::string("Failed to close key: ") + root["error"].asString();
+	else
+		return "Key closed.";
+}

+ 123 - 17
cli/src/cmdman.cpp

@@ -33,7 +33,10 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	execmap["getdata"] = &CmdMan::cmdGetdata;
 	execmap["listdata"] = &CmdMan::cmdListdata;
 	execmap["head"] = &CmdMan::cmdHead;
+	execmap["deletefile"] = &CmdMan::cmdDeletefile;
 	execmap["deleteme"] = &CmdMan::cmdDeleteme;
+	execmap["keyfile"] = &CmdMan::cmdKeyfile;
+	execmap["closekey"] = &CmdMan::cmdClosekey;
 
 	/* initialize description map */
 	helpmap["help"] = descHelp;
@@ -45,7 +48,10 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	helpmap["head"] = descHead;
 	helpmap["login"] = descLogin;
 	helpmap["signup"] = descSignup;
+	helpmap["deletefile"] = descDeletefile;
 	helpmap["deleteme"] = descDeleteme;
+	helpmap["keyfile"] = descKeyfile;
+	helpmap["closekey"] = descClosekey;
 
 	/* initialize handle command map */
 	handlemap["status"] = &CmdMan::handleStatus;
@@ -60,6 +66,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	handlemap["signup"] = &CmdMan::handleSignup;
 	handlemap["listdata"] = &CmdMan::handleListdata;
 	handlemap["head"] = &CmdMan::handleHead;
+	handlemap["deletefile"] = &CmdMan::handleDeletefile;
 	handlemap["deleteme"] = &CmdMan::handleDeleteme;
 
 	debugprintfunc = dpf;
@@ -124,16 +131,23 @@ CmdMan::CmdRet CmdMan::cmdPut(vector<string> args) {
 		root["accept"] = false;
 		root["error"] = "not enough arguments, at least 1 argument required";
 	} else {
-		bool opened = fileman.openPut(args[0]);
-		root["file"] = fileman.getPutName();
-		if (opened) {
-			root["size"] = fileman.getPutSize();
-			root["chunks"] = fileman.getPutChunks();
-			retval.type = send;
-		} else {
+		if (fileman.isPutting()) {
 			retval.type = error;
+			root["file"] = args[0];
 			root["accept"] = false;
-			root["error"] = "couldnt open local file \"" + args[0] + "\"";
+			root["error"] = "already putting file \"" + fileman.getPutName() + "\"";
+		} else {
+			bool opened = fileman.openPut(args[0]);
+			root["file"] = fileman.getPutName();
+			if (opened) {
+				root["size"] = fileman.getPutSize();
+				root["chunks"] = fileman.getPutChunks();
+				retval.type = send;
+			} else {
+				retval.type = error;
+				root["accept"] = false;
+				root["error"] = "couldnt open local file \"" + args[0] + "\"";
+			}
 		}
 	}
 
@@ -168,14 +182,21 @@ CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
 		root["accept"] = false;
 		root["error"] = "not enough arguments, at least 1 argument required";
 	} else {
-		bool opened = fileman.openGet(args[0]);
-		root["file"] = fileman.getGetName();
-		if (opened) {
-			retval.type = send;
-		} else {
+		if (fileman.isGetting()) {
 			retval.type = error;
+			root["file"] = args[0];
 			root["accept"] = false;
-			root["error"] = "local file \"" + args[0] + "\" already exists";
+			root["error"] = "already getting file \"" + fileman.getGetName() + "\"";
+		} else {
+			bool opened = fileman.openGet(args[0]);
+			root["file"] = fileman.getGetName();
+			if (opened) {
+				retval.type = send;
+			} else {
+				retval.type = error;
+				root["accept"] = false;
+				root["error"] = "local file \"" + args[0] + "\" already exists";
+			}
 		}
 	}
 
@@ -250,6 +271,25 @@ CmdMan::CmdRet CmdMan::cmdHead(vector<string> args) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::cmdDeletefile(vector<string> args) {
+	CmdRet retval;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	Json::Value root;
+	root["command"] = "deletefile";
+
+	if (args.size() < 1) {
+		retval.type = error;
+		root["accept"] = false;
+		root["error"] = "not enough arguments, at least 1 argument required";
+	} else {
+		root["file"] = args[0];
+		retval.type = send;
+	}
+	retval.msg = root;
+
+	return retval;
+}
+
 CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " using command \"" + cmd + "\" with arguments [ ");
@@ -302,6 +342,51 @@ CmdMan::CmdRet CmdMan::cmdDeleteme(vector<string> args) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::cmdKeyfile(vector<string> args) {
+	CmdRet retval;
+	Json::Value root;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	root["command"] = "keyfile";
+	if (args.size() < 1) {
+		retval.type = error;
+		root["accept"] = false;
+		root["error"] = "not enough arguments, at least 1 argument required";
+	} else {
+		DEBUGPRINT(string(__PRETTY_FUNCTION__) + " haveargs");
+		if (!fileman.openKey(args[0])) {
+			DEBUGPRINT(string(__PRETTY_FUNCTION__) + " openkey fail");
+			root["accept"] = false;
+			root["file"] = args[0];
+			root["error"] = string("couldnt open keyfile, openssl reports: ") + fileman.getOpensslError();
+			retval.type = error;
+		} else {
+			DEBUGPRINT(string(__PRETTY_FUNCTION__) + " openkey good");
+			root["accept"] = true;
+			root["file"] = args[0];
+			retval.type = print;
+		}
+	}
+	retval.msg = root;
+	return retval;
+}
+
+CmdMan::CmdRet CmdMan::cmdClosekey(vector<string> args) {
+	CmdRet retval;
+	Json::Value root;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	root["command"] = "closekey";
+	if (!fileman.closeKey()) {
+		root["accept"] = false;
+		root["error"] = "couldnt close keyfile. ensure no put or get is running";
+		retval.type = error;
+	} else {
+		root["accept"] = true;
+		retval.type = print;
+	}
+	retval.msg = root;
+	return retval;
+}
+
 /* login and signup commands */
 CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
 	CmdRet retval;
@@ -372,7 +457,8 @@ CmdMan::CmdRet CmdMan::cmdVersion(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["version"] = protocolVersion;
+	root["major"] = protocolMajorVersion;
+	root["minor"] = protocolMinorVersion;
 	retval.type = send;
 	retval.msg = root;
 
@@ -637,8 +723,8 @@ CmdMan::CmdRet CmdMan::handleVersion(Json::Value root) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 
 	output["command"] = "version";
-	output["serverversion"] = root["version"].asString();
-	output["clientversion"] = protocolVersion;
+	output["serverversion"] = root["major"].asString() + "." + root["minor"].asString();
+	output["clientversion"] = std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion);
 
 	if (!root["accept"].asBool()) {
 		retval.type = error;
@@ -711,6 +797,26 @@ CmdMan::CmdRet CmdMan::handleHead(Json::Value root) {
 		output["command"] = "head";
 		output["file"] = root["file"];
 		output["error"] = "Server reports: " + root["error"].asString();
+		output["accept"] = false;
+		retval.type = error;
+		retval.msg = output;
+	} else {
+		retval.type = print;
+		retval.msg = root;
+	}
+	return retval;
+}
+
+CmdMan::CmdRet CmdMan::handleDeletefile(Json::Value root) {
+	CmdRet retval;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+
+	if (!root["accept"].asBool()) {
+		Json::Value output;
+		output["command"] = "deletefile";
+		output["file"] = root["file"];
+		output["error"] = "Server reports: " + root["error"].asString();
+		output["accept"] = false;
 		retval.type = error;
 		retval.msg = output;
 	} else {

+ 300 - 34
cli/src/fileman.cpp

@@ -1,41 +1,143 @@
 #include "../include/fileman.h"
 #include "../include/base64.h"
 
-FileMan::FileMan() { islisting = false; }
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+
+using std::string;
+using std::vector;
+
+FileMan::FileMan() {
+	isputting = false;
+	islisting = false;
+	keyenabled = false;
+	cryptoreadye = false;
+	cryptoreadyd = false;
+	ERR_load_crypto_strings();
+}
 
 FileMan::~FileMan() {
 	cancelGet();
 	cancelPut();
 	cancelList();
+	if (keyenabled)
+		closeKey();
+	ERR_free_strings();
 }
 
 bool FileMan::isGetting() { return getfile.is_open(); }
 
-bool FileMan::isPutting() { return putfile.is_open(); }
+bool FileMan::isPutting() { return isputting; }
 
 bool FileMan::isListing() { return islisting; }
 
-bool FileMan::openPut(const std::string &path) {
+bool FileMan::isEncrypted() { return keyenabled; }
+
+vector<vector<char>> FileMan::chunkify(char *data, unsigned int size) {
+	vector<vector<char>> ret;
+	vector<char> chunk;
+	unsigned int i;
+	for (i = 0; (i + max_read_len) < size; i += max_read_len) {
+		chunk = vector<char>(data + i, data + i + max_read_len);
+		ret.push_back(chunk);
+	}
+	chunk = vector<char>(data + i, data + size);
+	ret.push_back(chunk);
+	return ret;
+}
+
+bool FileMan::openPut(const string &path) {
+	std::ifstream putfile;
+	std::fstream keyfile;
 	putpath = path;
 	putname = pathToFilename(path);
 	putfile.open(path, std::ios::ate | std::ios::binary);
 	if (putfile.is_open()) {
 		size_t size = putfile.tellg();
+		//~ putsize = size;
+		// increment by one for CCATs signature in its own chunk
+		//~ putchunks = 1 + (size / max_read_len + ((size % max_read_len) ? 1 : 0));
+		if (keyenabled) {
+			// fill IV for file
+			keyfile.open("/dev/urandom", std::ios::binary | std::ios::in);
+			keyfile.read((char *)iv, sizeof(iv));
+			keyfile.close();
+		}
+		putfile.seekg(0);
+
+		// read into memory and chunkify
+
+		char *temp = new char[size + sizeof(signature)];
+		memcpy(temp, signature, sizeof(signature));
+		putfile.read(temp + sizeof(signature), size);
+
+		size += sizeof(signature);
+
+		// have all file in memory prepended with signature
+		// if crypto enabled, encrypt now
+		if (keyenabled) {
+			if (!initCryptoE()) {
+				// failed to init crypto, do not continue
+				delete[] temp;
+				return false;
+			}
+			// resize buffer to also fit IV and tag
+			size_t additionalsize = sizeof(iv) + sizeof(tag);
+			unsigned char *cipher = new unsigned char[size];
+			int cipherlen;
+
+			if (!EVP_EncryptUpdate(cryptctxe, cipher, &cipherlen, (unsigned char *)temp, size)) {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
+			}
+			if (!EVP_EncryptFinal_ex(cryptctxe, cipher + cipherlen, &cipherlen)) {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
+			}
+			// obtain tag
+			if (!EVP_CIPHER_CTX_ctrl(cryptctxe, EVP_CTRL_GCM_GET_TAG, 16, tag)) {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to get tag " << getOpensslError() << std::endl;
+			}
+
+			delete[] temp;
+			temp = new char[size + additionalsize];
+
+			// prepend IV and tag
+			memcpy(temp, iv, sizeof(iv));
+			memcpy(temp + sizeof(iv), tag, sizeof(tag));
+			memcpy(temp + additionalsize, cipher, size);
+
+			delete[] cipher;
+
+			// increase size to also include IV and tag
+			size += sizeof(iv) + sizeof(tag);
+			deinitCryptoE();
+		}
+
+		// chunkify
+		putdata = chunkify(temp, size);
 		putsize = size;
-		putchunks = size / max_read_len + ((size % max_read_len) ? 1 : 0);
-		putchunksRemaining = putchunks;
-		putfile.seekg(std::ios::beg);
+		putchunksRemaining = putchunks = putdata.size();
+
+		delete[] temp;
+
+		// end read into memory and chunkify
+
+		isputting = true;
+
 		return true;
 	}
 	return false;
 }
 
-bool FileMan::openGet(const std::string &path) {
+bool FileMan::openGet(const string &path) {
 	getpath = path;
 	getname = pathToFilename(path);
 	getchunks = 0;
 	getchunksRemaining = 0;
-	getfile.open(path, std::ios::app | std::ios::binary);
+	getfile.open(path, std::ios::app | std::ios::binary | std::ios::out);
 	if (getfile.tellp() != std::ios::beg) {
 		closeGet();
 		return false;
@@ -47,17 +149,59 @@ bool FileMan::openList() {
 	if (isListing()) {
 		return false;
 	}
-	listdata = std::vector<std::string>();
+	listdata = vector<string>();
 	islisting = true;
 	return true;
 }
 
-void FileMan::closePut() { putfile.close(); }
+bool FileMan::openKey(const string &path) {
+	std::ifstream keyfile;
+	std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+	if (isPutting() || isGetting())
+		return false; // do not enable key mid-operation
+	if (keyenabled)
+		closeKey();
+	keyfile.open(path, std::ios::ate | std::ios::binary);
+	std::cerr << __PRETTY_FUNCTION__ << " open keyfile" << std::endl;
+	if (keyfile.is_open()) {
+		std::cerr << __PRETTY_FUNCTION__ << " keyfile open " << keyfile.tellg() << " " << sizeof(key) << std::endl;
+		if (keyfile.tellg() == sizeof(key)) {
+			keyfile.seekg(0);
+			std::cerr << "keyfile is at " << keyfile.tellg() << std::endl;
+			keyfile.read((char *)key, sizeof(key));
+			keyfile.close();
+			keyenabled = true;
+			return true;
+		}
+	}
+	return false;
+}
+
+void FileMan::closePut() {
+	putdata = vector<vector<char>>();
+	isputting = false;
+	memset(iv, 0, sizeof(iv));
+	memset(tag, 0, sizeof(tag));
+}
 
-void FileMan::closeGet() { getfile.close(); }
+void FileMan::closeGet() {
+	getfile.close();
+	memset(iv, 0, sizeof(iv));
+	memset(tag, 0, sizeof(tag));
+}
 
 void FileMan::closeList() { islisting = false; }
 
+bool FileMan::closeKey() {
+	if (isPutting() || isGetting())
+		return false;
+	if (keyenabled) {
+		memset(key, 0, sizeof(key));
+		keyenabled = false;
+	}
+	return true;
+}
+
 void FileMan::cancelPut() {
 	if (isPutting()) {
 		closePut();
@@ -77,19 +221,33 @@ void FileMan::cancelList() {
 	}
 }
 
-void FileMan::writeGet(const std::vector<char> data) {
-	getfile.write(data.data(), data.size());
+void FileMan::writeGet(const vector<char> data) {
+	if (getchunksRemaining == getchunks - 1 && !keyenabled) {
+		// check if signature matches
+		if (memcmp(data.data(), signature, 4)) {
+			// mismatch, encrypted file without enabled key, write as is
+			getfile.write(data.data(), data.size());
+		} else {
+			// skip signature for unencrypted files
+			getfile.write(data.data() + 4, data.size() - 4);
+		}
+	} else {
+		getfile.write(data.data(), data.size());
+	}
 	getchunksRemaining--;
 }
 
-void FileMan::writeBase64(std::string data) { writeGet(base64::decodeVector(data)); }
+void FileMan::writeBase64(string data) {
+	vector<char> decode = base64::decodeVector(data);
+	keyenabled ? writeEnc(decode) : writeGet(decode);
+}
 
-void FileMan::putListData(std::vector<std::string> names) {
+void FileMan::putListData(vector<string> names) {
 	listdata.insert(listdata.end(), names.begin(), names.end());
 	listchunksRemaining--;
 }
 
-std::vector<std::string> FileMan::getListData() { return listdata; }
+vector<string> FileMan::getListData() { return listdata; }
 
 int FileMan::getGetChunks() { return getchunks; }
 
@@ -105,28 +263,17 @@ void FileMan::setListChunks(int chunks) {
 	listchunksRemaining = chunks - 1;
 }
 
-std::vector<char> FileMan::readPut() {
-	char buf[max_read_len];
-	std::vector<char> data;
-
-	std::streamoff read = this->putfile.tellg();
-	if (read + max_read_len > this->putsize) {
-		read = this->putsize % max_read_len;
-	} else {
-		read = max_read_len;
-	}
-
-	putfile.read(buf, read);
-	data.assign(buf, buf + read);
+vector<char> FileMan::readPut() {
+	vector<char> data = putdata[putchunks - putchunksRemaining];
 	putchunksRemaining--;
 	return data;
 }
 
-std::string FileMan::readBase64() { return base64::encodeVector(readPut()); }
+string FileMan::readBase64() { return base64::encodeVector(readPut()); }
 
-std::string FileMan::getPutName() { return putname; }
+string FileMan::getPutName() { return putname; }
 
-std::string FileMan::getGetName() { return getname; }
+string FileMan::getGetName() { return getname; }
 
 int FileMan::getPutChunks() { return putchunks; }
 
@@ -138,11 +285,11 @@ int FileMan::getListRemainingChunks() { return listchunksRemaining; }
 
 int FileMan::getListChunks() { return listchunks; }
 
-std::string FileMan::pathToFilename(std::string path) {
+string FileMan::pathToFilename(string path) {
 
 	int lastFoundIndex = -1;
 
-	for (int currentIndex = path.find("/"); currentIndex != std::string::npos; currentIndex = path.find("/", currentIndex + 1)) {
+	for (int currentIndex = path.find("/"); currentIndex != string::npos; currentIndex = path.find("/", currentIndex + 1)) {
 
 		// check if the "/" was escaped
 		if (currentIndex > 0 && path[currentIndex - 1] == '\\')
@@ -162,3 +309,122 @@ std::string FileMan::pathToFilename(std::string path) {
 
 	return path.substr(lastFoundIndex + 1);
 }
+
+string FileMan::getOpensslError() {
+	pendingerr = false;
+	return opensslerr;
+}
+
+void FileMan::setOpensslError() {
+	opensslerr = ERR_error_string(ERR_get_error(), NULL);
+	pendingerr = true;
+}
+
+bool FileMan::initCryptoE() {
+	// try to initialize crypto context
+	if (!cryptoreadye) {
+		std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
+		if (!(cryptctxe = EVP_CIPHER_CTX_new())) {
+			setOpensslError();
+			return false;
+		}
+		if (!EVP_EncryptInit_ex(cryptctxe, EVP_aes_256_gcm(), NULL, key, iv)) {
+			setOpensslError();
+			return false;
+		}
+		cryptoreadye = true;
+	}
+	return true;
+}
+
+bool FileMan::initCryptoD() {
+	// try to initialize crypto context
+	if (!cryptoreadyd) {
+		std::cerr << __PRETTY_FUNCTION__ << " init crypto" << std::endl;
+		if (!(cryptctxd = EVP_CIPHER_CTX_new())) {
+			setOpensslError();
+			return false;
+		}
+		if (!EVP_DecryptInit_ex(cryptctxd, EVP_aes_256_gcm(), NULL, key, iv)) {
+			setOpensslError();
+			return false;
+		}
+		cryptoreadyd = true;
+	}
+	return true;
+}
+
+void FileMan::deinitCryptoE() {
+	if (cryptoreadye) {
+		EVP_CIPHER_CTX_free(cryptctxe);
+		cryptctxe = NULL;
+		cryptoreadye = false;
+	}
+}
+
+void FileMan::deinitCryptoD() {
+	if (cryptoreadyd) {
+		EVP_CIPHER_CTX_free(cryptctxd);
+		cryptctxd = NULL;
+		cryptoreadyd = false;
+	}
+}
+
+void FileMan::writeEnc(const vector<char> data) {
+	std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+	writeGet(data);
+	if (getchunksRemaining < 0) {
+		// loaded everything, try to decrypt
+		unsigned char *cipher, *plain;
+		int plainlen = 0, finallen = 0;
+		getfile.close();
+		getfile.open(getpath, std::ios::binary | std::ios::in | std::ios::ate);
+		size_t size = getfile.tellg();
+		if (size < (sizeof(iv) + sizeof(tag))) {
+			// avoid underflow with files that are too small
+			return;
+		}
+		size -= (sizeof(iv) + sizeof(tag));
+		std::cerr << __PRETTY_FUNCTION__ << " size is " << size << std::endl;
+		cipher = new unsigned char[size];
+		plain = new unsigned char[size];
+		getfile.seekg(0);
+		getfile.read((char *)iv, sizeof(iv));
+		getfile.read((char *)tag, sizeof(tag));
+		getfile.read((char *)cipher, size);
+		getfile.close();
+
+		if (initCryptoD()) {
+			if (!EVP_DecryptUpdate(cryptctxd, plain, &plainlen, cipher, size)) {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to update " << getOpensslError() << std::endl;
+			}
+			std::cerr << __PRETTY_FUNCTION__ << " decrypted" << std::endl;
+			if (!EVP_CIPHER_CTX_ctrl(cryptctxd, EVP_CTRL_GCM_SET_TAG, 16, tag)) {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
+			}
+			std::cerr << __PRETTY_FUNCTION__ << " set tag" << std::endl;
+			if (0 < EVP_DecryptFinal_ex(cryptctxd, plain + plainlen, &finallen)) {
+				plainlen += finallen;
+				std::cerr << __PRETTY_FUNCTION__ << " finalized with len " << plainlen << std::endl;
+				getfile.close();
+				// check signature
+				if (memcmp(plain, signature, 4)) {
+					std::cerr << __PRETTY_FUNCTION__ << " signature mismatch" << std::endl;
+				} else {
+					// signatur matches, skip it and dump the rest to disk
+					getfile.open(getpath, std::ios::binary | std::ios::out | std::ios::trunc);
+					getfile.write((char *)(plain + sizeof(signature)), plainlen - sizeof(signature));
+					getfile.close();
+				}
+			} else {
+				setOpensslError();
+				std::cerr << __PRETTY_FUNCTION__ << " failed to finalize " << getOpensslError() << std::endl;
+			}
+			deinitCryptoD();
+		}
+		delete[] cipher;
+		delete[] plain;
+	}
+}

+ 125 - 72
cli/src/ioman.cpp

@@ -25,11 +25,12 @@ extern IoMan *gIOMAN;
 
 void ioman_externalDebugPrint(string msg) { gIOMAN->printMessage(msg, gIOMAN->OutMsgType::debug); }
 
-IoMan::IoMan(char *ipcstring) : cmdman(fileman, &ioman_externalDebugPrint) {
+IoMan::IoMan(char *ipcstring, bool enablessl) : cmdman(fileman, &ioman_externalDebugPrint), recvbuf(16384) {
 	ipstring = std::string(ipcstring);
 	port = 1234;
 	tcpsock = new tcp::socket(ios);
 	connected = false;
+	usessl = false;
 
 	/* to be put elsewhere */
 
@@ -42,10 +43,23 @@ IoMan::IoMan(char *ipcstring) : cmdman(fileman, &ioman_externalDebugPrint) {
 	runresponse = false;
 	versionstatus = off;
 	loginstatus = off;
+	usessl = enablessl;
+	if (usessl) {
+		sslctx = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
+		sslctx->set_verify_mode(boost::asio::ssl::verify_peer);
+		sslctx->set_options(boost::asio::ssl::context::no_sslv2);
+		sslctx->load_verify_file("rootca.crt");
+		sslsock = new boost::asio::ssl::stream<tcp::socket &>(*tcpsock, *sslctx);
+	}
 }
 
 IoMan::~IoMan() {
 	initcv.notify_all();
+
+	if (connected) {
+		disconnect();
+	}
+
 	if (runnetwork) {
 		networkmutex.lock();
 		runnetwork = false;
@@ -67,11 +81,11 @@ IoMan::~IoMan() {
 		tresponse.join();
 	}
 
-	if (connected) {
-		disconnect();
-	}
-
+	if (usessl)
+		delete sslsock;
 	delete tcpsock;
+	if (usessl)
+		delete sslctx;
 	delete reader;
 }
 
@@ -132,6 +146,9 @@ vector<string> IoMan::tokenizeInput(string in) {
 	return args;
 }
 
+// callback for async connect, used to get timeout
+void connect_async_handler(const boost::system::error_code &error) { gIOMAN->errcode = error; }
+
 bool IoMan::connect() {
 	tcp::endpoint *ep = NULL;
 	address addr;
@@ -145,9 +162,16 @@ bool IoMan::connect() {
 		connected = false;
 	} else {
 		// establish connection
-		printMessage("IoMan::connect() connecting to " + ipstring, debug);
+		printMessage(string(__PRETTY_FUNCTION__) + string(" connecting to ") + ipstring, debug);
 		ep = new tcp::endpoint(addr, port);
-		tcpsock->connect(*ep, errcode);
+		// connect never returns would_block, initialize errcode so we can determine whats up
+		errcode = boost::asio::error::would_block;
+
+		tcpsock->async_connect(*ep, &connect_async_handler);
+
+		ios.run_for(std::chrono::seconds(5));
+
+		//~ tcpsock->connect(*ep, errcode);
 		if (errcode) {
 			root["error"] = errcode.message();
 			connected = false;
@@ -157,6 +181,18 @@ bool IoMan::connect() {
 		}
 		delete ep;
 	}
+	if (usessl) {
+		// try to do ssl handshake
+		printMessage(string(__PRETTY_FUNCTION__) + string(" doing ssl handshake with ") + ipstring, debug);
+		sslsock->handshake(boost::asio::ssl::stream_base::client, errcode);
+		if (errcode) {
+			root["error"] = errcode.message();
+			connected = false;
+		} else {
+			connected = true;
+			root["error"] = "";
+		}
+	}
 	root["accept"] = connected;
 	printMessage(Json::writeString(wbuilder, root), normal);
 	return connected;
@@ -164,7 +200,9 @@ bool IoMan::connect() {
 
 void IoMan::disconnect() {
 	printMessage("IoMan::disconnect()", debug);
-	tcpsock->close();
+	tcpsock->shutdown(tcp::socket::shutdown_both, errcode);
+	if (errcode)
+		printMessage(string(__PRETTY_FUNCTION__) + string("tcp shutdown says ") + errcode.message(), error);
 	connected = false;
 }
 
@@ -183,7 +221,7 @@ bool IoMan::init() {
 
 	localmutex.lock();
 	printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
-	localinput.push_back("version " + protocolVersion);
+	localinput.push_back("version");
 	localmutex.unlock();
 	printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
 	localcv.notify_all();
@@ -239,7 +277,10 @@ void IoMan::networkMain() {
 		*/
 
 		// read from network
-		readsize = boost::asio::read_until(*tcpsock, recvbuf, '\n', errcode);
+		if (usessl)
+			readsize = boost::asio::read_until(*sslsock, recvbuf, '\n', errcode);
+		else
+			readsize = boost::asio::read_until(*tcpsock, recvbuf, '\n', errcode);
 		printMessage(string(__PRETTY_FUNCTION__) + string(" asio::read() ok ") + std::to_string(readsize), debug);
 		// printMessage(string("have ") + std::to_string(toprocess.size()) +
 		// string(" commands"), debug);
@@ -247,8 +288,9 @@ void IoMan::networkMain() {
 			printMessage("IoMan::networkMain() couldnt read json data\n" + errcode.message(), error);
 			continue;
 		}
-		if (!readsize)
+		if (readsize < 1) {
 			break;
+		}
 		recvjson = (char *)(boost::asio::buffer_cast<const char *>(recvbuf.data()));
 		recvjson[readsize] = 0;
 		while (strchr(recvjson, '\n')) {
@@ -260,6 +302,7 @@ void IoMan::networkMain() {
 			if (!reader->parse(recvjson, recvjson + jsonsize, &root, &jsonerror)) {
 				printMessage("IoMan::networkMain() couldnt parse json data: " + jsonerror, error);
 				recvbuf.consume(jsonsize);
+				recvjson += jsonsize;
 				continue;
 			}
 			recvbuf.consume(jsonsize);
@@ -267,8 +310,7 @@ void IoMan::networkMain() {
 
 			printMessage(string(__PRETTY_FUNCTION__) + string(" remaining recvbuf ") + string(boost::asio::buffer_cast<const char *>(recvbuf.data())), debug);
 
-			for (int i = 0; i < jsonsize; i++)
-				recvjson++;
+			recvjson += jsonsize;
 			// store locally
 			toput.push_back(root);
 		}
@@ -337,27 +379,7 @@ void IoMan::inputMain() {
 			args.erase(args.begin());
 			cmdret = cmdman.execute(command, args);
 
-			// determine wether to send something and do so if required
-			if (cmdret.type & CmdMan::rettype::print) {
-				printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
-			}
-			if (cmdret.type & CmdMan::rettype::send) {
-				printMessage("IoMan::inputMain() sending json \"" + Json::writeString(wbuilder, cmdret.msg) + "\"", debug);
-				boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
-				if (errcode) {
-					printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", error);
-					continue;
-				}
-			}
-			if (cmdret.type & CmdMan::rettype::error) {
-				printMessage(Json::writeString(wbuilder, cmdret.msg), error);
-			}
-			if (cmdret.type & CmdMan::rettype::close) {
-				/* TODO i dunno */
-				mainmutex.lock();
-				runmain = false;
-				mainmutex.unlock();
-			}
+			handleInCmdResponse(cmdret);
 		}
 
 		// clean up local stuff
@@ -366,6 +388,33 @@ void IoMan::inputMain() {
 	}
 }
 
+void IoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
+	// determine wether to send something and do so if required
+	if (cmdret.type & CmdMan::rettype::print) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
+	}
+	if (cmdret.type & CmdMan::rettype::send) {
+		printMessage("IoMan::inputMain() sending json \"" + Json::writeString(wbuilder, cmdret.msg) + "\"", debug);
+		if (usessl)
+			boost::asio::write(*sslsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
+		else
+			boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
+		if (errcode) {
+			printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", error);
+			return;
+		}
+	}
+	if (cmdret.type & CmdMan::rettype::error) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
+	}
+	if (cmdret.type & CmdMan::rettype::close) {
+		/* TODO i dunno */
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+	}
+}
+
 /* loop to handle responses that have been fetched by netMain and possibly add
  * new commands to be handled by inputMain */
 void IoMan::responseMain() {
@@ -411,39 +460,7 @@ void IoMan::responseMain() {
 		// process jsons
 		for (Json::Value root : toprocess) {
 			cmdret = cmdman.handle(root);
-			if (cmdret.type & CmdMan::rettype::close) {
-				/* TODO i dunno */
-				mainmutex.lock();
-				runmain = false;
-				mainmutex.unlock();
-			}
-			if (cmdret.type & CmdMan::rettype::error) {
-				initmutex.lock();
-				if (versionstatus == off)
-					versionstatus = err;
-				else if (loginstatus == off)
-					loginstatus = err;
-				initmutex.unlock();
-				initcv.notify_all();
-				printMessage(Json::writeString(wbuilder, cmdret.msg), error);
-			}
-			if (cmdret.type & CmdMan::rettype::seton) {
-				initmutex.lock();
-				if (versionstatus == off)
-					versionstatus = on;
-				else
-					loginstatus = on;
-				initmutex.unlock();
-				initcv.notify_all();
-			}
-			if (cmdret.type & CmdMan::rettype::print) {
-				printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
-			}
-			if (cmdret.type & CmdMan::rettype::send) {
-				if (cmdret.nextcommand.size()) {
-					toput.push_back(cmdret.nextcommand);
-				}
-			}
+			handleOutCmdResponse(cmdret, toput);
 		}
 
 		if (toput.size()) {
@@ -463,6 +480,42 @@ void IoMan::responseMain() {
 	}
 }
 
+void IoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, vector<string> &toput) {
+	if (cmdret.type & CmdMan::rettype::close) {
+		/* TODO i dunno */
+		mainmutex.lock();
+		runmain = false;
+		mainmutex.unlock();
+	}
+	if (cmdret.type & CmdMan::rettype::error) {
+		initmutex.lock();
+		if (versionstatus == off)
+			versionstatus = err;
+		else if (loginstatus == off)
+			loginstatus = err;
+		initmutex.unlock();
+		initcv.notify_all();
+		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
+	}
+	if (cmdret.type & CmdMan::rettype::seton) {
+		initmutex.lock();
+		if (versionstatus == off)
+			versionstatus = on;
+		else
+			loginstatus = on;
+		initmutex.unlock();
+		initcv.notify_all();
+	}
+	if (cmdret.type & CmdMan::rettype::print) {
+		printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
+	}
+	if (cmdret.type & CmdMan::rettype::send) {
+		if (cmdret.nextcommand.size()) {
+			toput.push_back(cmdret.nextcommand);
+		}
+	}
+}
+
 /* this is the handler that readlines alternative interface will use to process
  * user input */
 void ioman_readlineHandler(char *line) {
@@ -492,9 +545,9 @@ void ioman_readlineHandler(char *line) {
 /* main user input loop */
 void IoMan::run() {
 	printMessage("IoMan::run() begin", debug);
-	struct pollfd inPipeStatus;
-	inPipeStatus.fd = STDIN_FILENO;
-	inPipeStatus.events = POLLIN;
+	struct pollfd inpipestatus;
+	inpipestatus.fd = STDIN_FILENO;
+	inpipestatus.events = POLLIN;
 
 	runmain = true;
 
@@ -505,9 +558,9 @@ void IoMan::run() {
 	while (runmain) {
 		mainmutex.unlock();
 
-		poll(&inPipeStatus, 1, 100);
+		poll(&inpipestatus, 1, 100);
 
-		if (inPipeStatus.revents & POLLIN) {
+		if (inpipestatus.revents & POLLIN) {
 			rl_callback_read_char();
 		}
 		if (!connected)

+ 4 - 14
cli/src/machineioman.cpp

@@ -1,14 +1,9 @@
 #include "../include/machineioman.h"
 
-#include <boost/algorithm/string.hpp>
-#include <fstream>
 #include <iostream>
-#include <limits>
 #include <vector>
 
-#include <readline/readline.h>
-
-using boost::asio::buffer;
+MachineIoMan::MachineIoMan(char *ipcstring, bool usessl, bool beverbose) : IoMan(ipcstring, usessl) { verbose = beverbose; }
 
 void MachineIoMan::printMessage(std::string msg, OutMsgType type) {
 	switch (type) {
@@ -21,18 +16,13 @@ void MachineIoMan::printMessage(std::string msg, OutMsgType type) {
 		break;
 	}
 	case debug: {
-		//~ std::cerr << msg << std::endl;
+		if (verbose)
+			std::cerr << msg << std::endl;
 		break;
 	}
 	}
 }
 
-void MachineIoMan::printWelcomeMessage() {
-	//~ std::cout << "please enter user and password" << std::endl;
-}
+void MachineIoMan::printWelcomeMessage() {}
 
 std::string MachineIoMan::getCmdPrompt() { return ""; }
-
-// currently not in use:
-//~ std::string MachineIoMan::getUserPrompt() { return ""; }
-//~ std::string MachineIoMan::getPassPrompt() { return ""; }

+ 19 - 9
cli/src/main.cpp

@@ -1,3 +1,4 @@
+#include "../include/batchioman.h"
 #include "../include/machineioman.h"
 #include "../include/userioman.h"
 
@@ -17,12 +18,13 @@ void show_help(char *exec) {
 }
 
 int main(int argc, char **argv) {
-	bpo::options_description desc{"Options"};
+	bpo::options_description desc("Options");
 	desc.add_options()("help", "show this help")("machine", "switch to machine mode for I/O (not designed for user interaction)")(
-	    "batch", bpo::value<std::string>(), "run operations from arg as batch file");
+	    "batch", bpo::value<std::string>(), "run operations from arg as batch file")("usessl", "enable ssl for connection to server")("verbose",
+	                                                                                                                                  "enable debug output");
 
 	bpo::variables_map vm;
-	unsigned int machine = 0;
+	bool machine = false, usessl = false, batch = false, verbose = false;
 	const char *file = NULL;
 	IoMan *ioman;
 
@@ -43,21 +45,29 @@ int main(int argc, char **argv) {
 		}
 		if (vm.count("machine")) {
 			// enable machine/gui mode
-			machine = 1;
+			machine = true;
 		}
 		if (vm.count("batch")) {
 			// handle batch file mode
 			file = vm["batch"].as<std::string>().c_str();
+			batch = true;
+		}
+		if (vm.count("usessl")) {
+			usessl = true;
+		}
+		if (vm.count("verbose")) {
+			verbose = true;
 		}
 	} catch (const bpo::error &ex) {
 		std::fprintf(stderr, "%s\n", ex.what());
 	}
-	std::printf("ip %s machine mode is %d file is %s\n", argv[1], machine, file ? file : "");
-
-	if (machine) {
-		ioman = new MachineIoMan(argv[1]);
+	std::printf("ip %s machine mode is %d file is %s enablessl is %d verbose is %d\n", argv[1], machine, file ? file : "", usessl, verbose);
+	if (batch) {
+		ioman = new BatchIoMan(argv[1], usessl, verbose, file);
+	} else if (machine) {
+		ioman = new MachineIoMan(argv[1], usessl, verbose);
 	} else {
-		ioman = new UserIoMan(argv[1]);
+		ioman = new UserIoMan(argv[1], usessl, verbose);
 	}
 	gIOMAN = ioman;
 	if (ioman->init()) {

+ 43 - 19
cli/src/userioman.cpp

@@ -1,21 +1,15 @@
 #include "../include/userioman.h"
 
-#include <boost/algorithm/string.hpp>
-#include <fstream>
 #include <iostream>
-#include <vector>
-
-#include <readline/history.h>
 #include <readline/readline.h>
+#include <vector>
 
-using boost::asio::buffer;
-
-UserIoMan::UserIoMan(char *ipcstring) : IoMan(ipcstring) {
-
+UserIoMan::UserIoMan(char *ipcstring, bool usessl, bool beverbose) : IoMan(ipcstring, usessl) {
 	/* setup json stuff */
 	Json::CharReaderBuilder rbuilder;
 	wbuilder.settings_["indentation"] = "";
 	reader = rbuilder.newCharReader();
+	verbose = beverbose;
 
 	/* initialize print command map */
 	printmap["error"] = &UserIoMan::printError;
@@ -32,7 +26,10 @@ UserIoMan::UserIoMan(char *ipcstring) : IoMan(ipcstring) {
 	printmap["putdata"] = &UserIoMan::printPutdata;
 	printmap["getdata"] = &UserIoMan::printGetdata;
 	printmap["head"] = &UserIoMan::printHead;
+	printmap["deletefile"] = &UserIoMan::printDeletefile;
 	printmap["deleteme"] = &UserIoMan::printDeleteme;
+	printmap["keyfile"] = &UserIoMan::printKeyfile;
+	printmap["closekey"] = &UserIoMan::printClosekey;
 }
 
 UserIoMan::~UserIoMan() { delete reader; }
@@ -40,6 +37,13 @@ UserIoMan::~UserIoMan() { delete reader; }
 void UserIoMan::printMessage(std::string msg, OutMsgType type) {
 	Json::Value root;
 	msgmutex.lock();
+
+	char *savedline = rl_copy_text(0, rl_end);
+	int savedpoint = rl_point;
+	rl_set_prompt("");
+	rl_replace_line("", 0);
+	rl_redisplay();
+
 	switch (type) {
 	case normal:
 	case error: {
@@ -51,16 +55,19 @@ void UserIoMan::printMessage(std::string msg, OutMsgType type) {
 		}
 		break;
 	}
-	//~ case error: {
-	//~ std::cout << msg << std::endl;
-	//~ break;
-	//~ }
 	case debug: {
-		std::cerr << msg << std::endl;
+		if (verbose)
+			std::cerr << msg << std::endl;
 		break;
 	}
 	}
+
+	rl_set_prompt(getCmdPrompt().c_str());
+	rl_replace_line(savedline, 0);
+	rl_point = savedpoint;
 	rl_redisplay();
+	free(savedline);
+
 	msgmutex.unlock();
 }
 
@@ -72,10 +79,6 @@ void UserIoMan::printWelcomeMessage() {
 
 std::string UserIoMan::getCmdPrompt() { return "ccats> "; }
 
-// currently not in use:
-//~ std::string UserIoMan::getUserPrompt() { return "User: "; }
-//~ std::string UserIoMan::getPassPrompt() { return "Pass: "; }
-
 void UserIoMan::printJson(Json::Value root) {
 	map<string, void (UserIoMan::*)(Json::Value)>::iterator it = printmap.find(root["command"].asString());
 	if (it == printmap.end()) {
@@ -181,7 +184,28 @@ void UserIoMan::printListdata(Json::Value root) {}
 
 void UserIoMan::printHead(Json::Value root) {
 	if (!root["accept"].asBool())
-		std::cout << "Request of the first four bytes failed, server reports: " << root["error"].asString() << std::endl;
+		std::cout << "Request of the first four bytes failed. " << root["error"].asString() << std::endl;
 	else
 		std::cout << "First four bytes of file " << root["file"].asString() << " are: " << root["data"].asString() << std::endl;
 }
+
+void UserIoMan::printDeletefile(Json::Value root) {
+	if (!root["accept"].asBool())
+		std::cout << "Deletion of file " << root["file"] << " failed. " << root["error"].asString() << std::endl;
+	else
+		std::cout << "File " << root["file"] << " deleted succesfully" << std::endl;
+}
+
+void UserIoMan::printKeyfile(Json::Value root) {
+	if (!root["accept"].asBool())
+		std::cout << "Couldnt select keyfile " << root["file"].asString() << ": " << root["error"].asString() << std::endl;
+	else
+		std::cout << "Using keyfile " << root["file"].asString() << std::endl;
+}
+
+void UserIoMan::printClosekey(Json::Value root) {
+	if (!root["accept"].asBool())
+		std::cout << "Failed to close key: " << root["error"].asString() << std::endl;
+	else
+		std::cout << "Key closed." << std::endl;
+}

+ 220 - 0
cli/test/cryptotest-gcm.c

@@ -0,0 +1,220 @@
+#include <openssl/conf.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <stdio.h>
+#include <string.h>
+
+// based on OpenSSL sample
+// refer to https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
+
+void handleErrors(void);
+int gcm_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, int iv_len, unsigned char *ciphertext, unsigned char *tag);
+int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *tag, unsigned char *key, unsigned char *iv, int iv_len, unsigned char *plaintext);
+
+int main(int argc, char **argv) {
+	/*
+	 * Set up the key and iv. Do I need to say to not hard code these in a
+	 * real application? :-)
+	 */
+
+	FILE *f, *o;
+	unsigned char signature[4] = {'C', 'C', 'A', 'T'};
+	unsigned char key[32];
+	unsigned char iv[12];
+	unsigned char tag[16];
+	unsigned char *plain;
+	unsigned char *cipher;
+	unsigned insize, mode;
+	int decryptedtext_len, ciphertext_len;
+	if (argc < 4) {
+		printf("not enough args. use %s mode key in out\n", argv[0]);
+		return 1;
+	}
+
+	mode = strtoul(argv[1], NULL, 10);
+
+	if (!(f = fopen(argv[2], "rb"))) {
+		printf("cannot open key\n");
+		return 1;
+	}
+	fread(key, sizeof(key), 1, f);
+	fclose(f);
+
+	if (!(f = fopen("/dev/urandom", "rb"))) {
+		printf("cannot open urandom\n");
+		return 1;
+	}
+	fread(iv, sizeof(iv), 1, f);
+	fclose(f);
+
+	if (!(f = fopen(argv[3], "rb"))) {
+		printf("cannot open in\n");
+		return 1;
+	}
+	if (!(o = fopen(argv[4], "wb"))) {
+		printf("cannot open out\n");
+		return 1;
+	}
+
+	if (mode) {
+		fseek(f, 0, SEEK_END);
+		insize = ftell(f) + 4;
+		fseek(f, 0, SEEK_SET);
+		plain = malloc(insize);
+		cipher = malloc(insize);
+		fread(plain + 4, insize, 1, f);
+		fclose(f);
+		/* prepend signature */
+		memcpy(plain, signature, 4);
+		/* Encrypt the plaintext */
+		ciphertext_len = gcm_encrypt(plain, insize, key, iv, sizeof(iv), cipher, tag);
+		fwrite(iv, sizeof(iv), 1, o);
+		fwrite(tag, sizeof(tag), 1, o);
+		fwrite(cipher, ciphertext_len, 1, o);
+		fclose(o);
+	} else {
+		fseek(f, 0, SEEK_END);
+		insize = ftell(f) - sizeof(iv) - sizeof(tag);
+		fseek(f, 0, SEEK_SET);
+		cipher = malloc(insize);
+		plain = malloc(insize);
+		fread(iv, sizeof(iv), 1, f);
+		fread(tag, sizeof(tag), 1, f);
+		fread(cipher, insize, 1, f);
+		fclose(f);
+
+		decryptedtext_len = gcm_decrypt(cipher, insize, tag, key, iv, sizeof(iv), plain);
+		if (decryptedtext_len < 0) {
+			printf("decrypt failed\n");
+			fclose(o);
+			remove(argv[4]);
+		} else if (memcmp(plain, signature, 4)) {
+			printf("signature mismatch, expected ");
+			for (int i = 0; i < sizeof(signature); i++)
+				printf("%02x ", signature[i]);
+			printf("but got ");
+			for (int i = 0; i < sizeof(signature); i++)
+				printf("%02x ", plain[i]);
+			printf("\n");
+			fclose(o);
+			remove(argv[4]);
+		} else {
+			fwrite(plain + 4, decryptedtext_len - 4, 1, o);
+			fclose(o);
+		}
+	}
+	free(cipher);
+	free(plain);
+	printf("done\n");
+	return 0;
+}
+
+void handleErrors(void) {
+	ERR_print_errors_fp(stderr);
+	abort();
+}
+
+int gcm_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, int iv_len, unsigned char *ciphertext, unsigned char *tag) {
+	EVP_CIPHER_CTX *ctx;
+
+	int len;
+
+	int ciphertext_len;
+
+	/* Create and initialise the context */
+	if (!(ctx = EVP_CIPHER_CTX_new()))
+		handleErrors();
+
+	/* Initialise the encryption operation. */
+	if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
+		handleErrors();
+
+	/*
+	 * Set IV length if default 12 bytes (96 bits) is not appropriate
+	 */
+	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL))
+		handleErrors();
+
+	/* Initialise key and IV */
+	if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
+		handleErrors();
+
+	/*
+	 * Provide the message to be encrypted, and obtain the encrypted output.
+	 * EVP_EncryptUpdate can be called multiple times if necessary
+	 */
+	if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
+		handleErrors();
+	ciphertext_len = len;
+
+	/*
+	 * Finalise the encryption. Normally ciphertext bytes may be written at
+	 * this stage, but this does not occur in GCM mode
+	 */
+	if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len))
+		handleErrors();
+	ciphertext_len += len;
+
+	/* Get the tag */
+	if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
+		handleErrors();
+
+	/* Clean up */
+	EVP_CIPHER_CTX_free(ctx);
+
+	return ciphertext_len;
+}
+
+int gcm_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *tag, unsigned char *key, unsigned char *iv, int iv_len,
+                unsigned char *plaintext) {
+	EVP_CIPHER_CTX *ctx;
+	int len;
+	int plaintext_len;
+	int ret;
+
+	/* Create and initialise the context */
+	if (!(ctx = EVP_CIPHER_CTX_new()))
+		handleErrors();
+
+	/* Initialise the decryption operation. */
+	if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
+		handleErrors();
+
+	/* Set IV length. Not necessary if this is 12 bytes (96 bits) */
+	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL))
+		handleErrors();
+
+	/* Initialise key and IV */
+	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv))
+		handleErrors();
+
+	/*
+	 * Provide the message to be decrypted, and obtain the plaintext output.
+	 * EVP_DecryptUpdate can be called multiple times if necessary
+	 */
+	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
+		handleErrors();
+	plaintext_len = len;
+
+	/* Set expected tag value. Works in OpenSSL 1.0.1d and later */
+	if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
+		handleErrors();
+
+	/*
+	 * Finalise the decryption. A positive return value indicates success,
+	 * anything else is a failure - the plaintext is not trustworthy.
+	 */
+	ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
+
+	/* Clean up */
+	EVP_CIPHER_CTX_free(ctx);
+
+	if (ret > 0) {
+		/* Success */
+		plaintext_len += len;
+		return plaintext_len;
+	} else {
+		/* Verify failed */
+		return -1;
+	}
+}

+ 2 - 0
cli/test/samplekey1.bin

@@ -0,0 +1,2 @@
+�¯]w¿¸{7•ַ´�ֲצ�׀¼gbs€ֱה
+…רee³O7

+ 1 - 0
cli/test/samplekey2.bin

@@ -0,0 +1 @@
+èP`\æS÷Ž”�®¯ÜmÓR‘”tHbjÜ _«]ž}

+ 38 - 3
daemon/Daemon-Config-Reference.md

@@ -1,17 +1,52 @@
 # Daemon configuration
 
 The daemon is configurable by config.txt.
-This file must be in the same directory as the binary.
+The config file must be in the same directory from where you run the binary.
 
 ### Configuration Values
 `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
+`SSLprivatekey` : The private key file to use for SSL connections
+`SSLdhparams` : The diffie-hellman file to use for SSL connections
+
+### Notes about SSL
+To use SSL, certificates, keys and diffie-hellman parameters are required. To generate these, a convenience script `createsslfiles.sh` is provided.
+The names of the output files are controlled with variables at the top of the script, modify these if desired.
+Assuming default names, place the `user.crt`, `user.key` and `dh2048.pem` files somewhere convenient and configure the server accordingly.
+Place the `rootca.crt` certificate in the directory you intend to run the client from.
+
+If you get an error about SSL related files not being found despite them existing, shorten the names of the files.
+If you cannot connect and the server prints a error related to TLSv1, ensure your version of boost and OpenSSL are up to date.
+
+#### Covert Channel options
+`covertChannelMode`: Sets the covert channel mode. To deactiveate don't set it or set it to none or false.<br/>
+`innerInterface`: The interface of your inner network<br/>
+`outerInterface`: The interface of your outer network<br/>
+
+##### Covert Channel Mode `forward`
+There no further config needed. Forward should work out of the pox
+
+##### Covert Channel Mode `proxy`
+`ownIP`: IP of this server<br/>
+`partnerIP`: IP of the partner server. If this is a relay the other cannot be a relay.<br/>
+`targetIP`: IP of the target server of teh covert channel<br/>
+`targetPort`: Port of the target server of the covert channel<br/>
+`relay`: Bool which sets the mode: true - just relays the covert channel | false - uses it's own covert channel<br/>
 
 ### Example for config.txt
 ```
 port=1234
-interface=lo
-userdatabase=user Storage.txt
+userdatabase=userStorage.txt
 filedirectory=./files/
+deleteAllowed=true
+SSLenabled=true
+SSLcertificate=user.crt
+SSLprivatekey=user.key
+SSLdhparams=dh2048.pem
+activateCovertChannel=false
 ```

+ 26 - 0
daemon/createsslfiles.sh

@@ -0,0 +1,26 @@
+#!/bin/bash
+ROOTNAME=rootca
+USERNAME=user
+DIFFIENAME=dh2048
+echo "Creating ROOTCA key"
+openssl genrsa -out $ROOTNAME.key 2048
+echo "Creating ROOTCA cert"
+openssl req -x509 -new -nodes -key $ROOTNAME.key -days 20000 -out $ROOTNAME.crt
+echo "Creating USER key"
+openssl genrsa -out $USERNAME.key 2048
+echo "Creating USER base cert"
+openssl req -new -key $USERNAME.key -out $USERNAME.csr
+echo "Signing USER base cert using ROOT key and cert"
+openssl x509 -req -in $USERNAME.csr -CA $ROOTNAME.crt -CAkey $ROOTNAME.key -CAcreateserial -out $USERNAME.crt -days 20000
+echo "Creating DIFFIE params. This might take a while"
+openssl dhparam -out $DIFFIENAME.pem 2048
+
+# should OK
+echo "This should OK"
+openssl verify -CAfile $ROOTNAME.crt $ROOTNAME.crt
+# should OK
+echo "This should OK"
+openssl verify -CAfile $ROOTNAME.crt $USERNAME.crt
+# should FAIL
+echo "This should FAIL"
+openssl verify -CAfile $USERNAME.crt $USERNAME.crt

+ 174 - 0
daemon/include/CovertChannel/CovertChannel.h

@@ -0,0 +1,174 @@
+#ifndef COVERTCHANNEL_H
+#define COVERTCHANNEL_H
+
+#include <tins/tins.h>
+
+/**
+ * @class CovertChannel
+ *
+ * Sniffs the network, redirects traffic and handles filtered traffic.
+ *
+ * CovertChannel class which will sniff on two network interfacees. It handles filtered traffic with a virtual handler
+ * function.
+ */
+class CovertChannel {
+public:
+	/**
+	 * Sets up a CovertChannel.
+	 *
+	 * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
+	 *
+	 * @param innerInterface name of the interface of the inner network
+	 * @param outerInterface name of the interface of the outer network
+	 * @param innerForwardFilter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 * @param outerForwardFilter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 * @param innerChannelFilter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 * @param outerChannelFilter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 * @param outerPartnerFilter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 */
+	CovertChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &innerForwardFilter = "",
+	              const std::string &outerForwardFilter = "", const std::string &innerChannelFilter = "", const std::string &outerChannelFilter = "",
+	              const std::string &outerPartnerFilter = "");
+
+	/**
+	 * Destroys the CovertChannel.
+	 */
+	virtual ~CovertChannel();
+
+	/**
+	 * Start sniffing on the interface.
+	 *
+	 * Starts a sniffing loop which calls handle. The loop will only be stopped if
+	 * handle returns false.
+	 */
+	void startSniffing();
+
+	/**
+	 * Sets a filter for the sniffers.
+	 *
+	 * Sets the filter for the forward and channel sniffers with a pcap filter string. E.g. "host 8.8.8.8".
+	 * The forward filter is the negated filter.
+	 *
+	 * @param filter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 */
+	void setFilter(const std::string &innerForwardFilter = "", const std::string &outerForwardFilter = "", const std::string &innerChannelFilter = "",
+	               const std::string &outerChannelFilter = "", const std::string &outerPartnerFilter = "");
+
+protected:
+	/**
+	 * Handler for sniffed packets filterd to forward from the outer network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	bool handleForwardFromOuter(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the inner network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	bool handleForwardFromInner(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to use as channel from the outer network.
+	 *
+	 * Handles incoming packets and redirets them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromOuter(Tins::PDU &pdu) = 0;
+
+	/**
+	 * Handler for sniffed packets filterd to use as channel from the outer network.
+	 *
+	 * Handles incoming packets and redirets them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromInner(Tins::PDU &pdu) = 0;
+
+	/**
+	 * Handler for sniffed packets filterd to use as channel from the outer network.
+	 *
+	 * Handles incoming packets and redirets them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handlePartnerFromOuter(Tins::PDU &pdu) = 0;
+
+	/**
+	 * Starts the sniffing loop of the inner forward sniffer.
+	 */
+	void startInnerForwardSniffing();
+
+	/**
+	 * Starts the sniffing loop of the outer forward sniffer.
+	 */
+	void startOuterForwardSniffing();
+
+	/**
+	 * Starts the sniffing loop of the inner channel sniffer.
+	 */
+	void startInnerChannelSniffing();
+
+	/**
+	 * Starts the sniffing loop of the outer channel sniffer.
+	 */
+	void startOuterChannelSniffing();
+
+	/**
+	 * Starts the sniffing loop of the outer partner sniffer.
+	 */
+	void startOuterPartnerSniffing();
+
+	/**
+	 * Tins Sniffer to filter packets to which should be forwarded
+	 */
+	Tins::Sniffer *innerForwardSniffer;
+
+	/**
+	 * Tins Sniffer to filter packets to which should be forwarded
+	 */
+	Tins::Sniffer *outerForwardSniffer;
+
+	/**
+	 * Tins Sniffer to filter packets to which should be used for the covert channel
+	 */
+	Tins::Sniffer *innerChannelSniffer;
+
+	/**
+	 * Tins Sniffer to filter packets to which should be used for the covert channel
+	 */
+	Tins::Sniffer *outerChannelSniffer;
+
+	/**
+	 * Tins Sniffer to filter packets to which should be used for the covert channel
+	 */
+	Tins::Sniffer *outerPartnerSniffer;
+
+	/**
+	 * Tins PacketSender which sends packets to the inner network
+	 */
+	Tins::PacketSender innerSender;
+
+	/**
+	 * Tins PacketSender which sends packets to the outer network
+	 */
+	Tins::PacketSender outerSender;
+};
+
+#endif

+ 64 - 0
daemon/include/CovertChannel/ForwardChannel.h

@@ -0,0 +1,64 @@
+#ifndef FORWARDCHANNEL_H
+#define FORWARDCHANNEL_H
+
+#include "CovertChannel.h"
+
+/**
+ * @class ForwardChannel
+ *
+ * A CovertChannel which forwards the traffic it captures.
+ */
+class ForwardChannel : public CovertChannel {
+public:
+	/**
+	 * Sets up a CovertChannel.
+	 *
+	 * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
+	 *
+	 * @param innerInterface name of the interface of the inner network
+	 * @param outerInterface name of the interface of the outer network
+	 * @param filter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 */
+	ForwardChannel(const std::string &innerInterface, const std::string &outerInterface);
+
+	/**
+	 * Destroys the CovertChannel.
+	 */
+	virtual ~ForwardChannel();
+
+protected:
+	/**
+	 * Handler for sniffed packets filterd to forward from the outer network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromOuter(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the inner network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromInner(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to use as channel from the outer network.
+	 *
+	 * Handles incoming packets and redirets them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handlePartnerFromOuter(Tins::PDU &pdu);
+};
+
+#endif

+ 82 - 0
daemon/include/CovertChannel/ProxyChannel.h

@@ -0,0 +1,82 @@
+#ifndef PROXYCHANNEL_H
+#define PROXYCHANNEL_H
+
+#include "CovertChannel.h"
+
+/**
+ * @class ForwardChannel
+ *
+ * A CovertChannel which forwards the traffic it captures.
+ */
+class ProxyChannel : public CovertChannel {
+public:
+	/**
+	 * Sets up a CovertChannel.
+	 *
+	 * Creates a CovertChannel, sets the network interfaces for sniffing and sending and sets the filter.
+	 *
+	 * @param innerInterface name of the interface of the inner network
+	 * @param outerInterface name of the interface of the outer network
+	 * @param filter pcap filter string which will be set for the channel sniffers and negated for the forward sniffers
+	 * @param relayOnly true - server only relays traffic | false - server redirects traffic over another relay
+	 */
+	ProxyChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &partnerIP,
+	             const std::string &originIP, const std::string &targetIP, const std::string &targetPort, const std::string &ownMAC,
+	             const std::string &originMAC, const std::string &channelGatewayMAC, const std::string &gatewayMAC, const bool relayOnly);
+
+	/**
+	 * Destroys the CovertChannel.
+	 */
+	virtual ~ProxyChannel();
+
+protected:
+	/**
+	 * Handler for sniffed packets filterd to forward from the outer network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromOuter(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to forward from the inner network.
+	 *
+	 * Handles incoming packets and forwards them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handleChannelFromInner(Tins::PDU &pdu);
+
+	/**
+	 * Handler for sniffed packets filterd to use as channel from the outer network.
+	 *
+	 * Handles incoming packets and redirets them.
+	 *
+	 * @param pdu sniffed packet
+	 *
+	 * @return false = stop loop | true = continue loop
+	 */
+	virtual bool handlePartnerFromOuter(Tins::PDU &pdu);
+
+	/**
+	 * Relay option which activates relay only mode
+	 */
+	const bool relayOnly;
+
+	const Tins::IPv4Address ownAddress;
+	const Tins::IPv4Address partnerAddress;
+	const Tins::IPv4Address originAddress;
+	const Tins::IPv4Address targetAddress;
+
+	const Tins::HWAddress<6> ownMAC;
+	const Tins::HWAddress<6> channelGatewayMAC;
+	const Tins::HWAddress<6> gatewayMAC;
+	const Tins::HWAddress<6> originMAC;
+};
+
+#endif

+ 25 - 13
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
 	 */
@@ -52,7 +60,14 @@ private:
 	std::vector<std::vector<std::string>> list;
 
 public:
-	enum { max_data_length = 512 };
+	enum { max_data_length = 4096 };
+
+	/**
+	 * 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

+ 8 - 2
daemon/include/JsonCommander.h

@@ -56,12 +56,13 @@ public:
 	 */
 	Response checkLogin(const Json::Value &message);
 
-private:
 	/**
 	 * protocol version of the client server protocol
 	 */
-	const std::string protocolVersion = "0.2";
+	const int protocolMajorVersion = 0;
+	const int protocolMinorVersion = 3;
 
+private:
 	/**
 	 * Contains the name of the user which uses the current connection.
 	 * Is set at checkLogin. Used for deleteUser.
@@ -140,6 +141,11 @@ private:
 	 * Executes the deleteme command
 	 */
 	Response executeDeleteMe(const Json::Value &message);
+
+	/**
+	 * Executes the deletefile command
+	 */
+	Response executeDeleteFile(const Json::Value &message);
 };
 
 #endif

+ 32 - 3
daemon/include/Server.h

@@ -2,6 +2,7 @@
 #define SERVER_H
 
 #include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
 #include <boost/bind.hpp>
 #include <boost/enable_shared_from_this.hpp>
 
@@ -21,12 +22,39 @@ using ip::tcp;
  */
 class con_handler : public boost::enable_shared_from_this<con_handler> {
 private:
+	/**
+	 * boost tcp socket
+	 */
 	tcp::socket sock;
 
+	/**
+	 * ssl stream socket
+	 */
+	ssl::stream<tcp::socket &> sslsock;
+
+	/**
+	 * ssl enable state
+	 *
+	 * true - ssl is enabled | false ssl is disabled
+	 */
+	const bool usessl;
+
+	/**
+	 * Performs SSL handshake.
+	 *
+	 * @return true - success | false - failure
+	 */
+	bool handshake();
+
+	/**
+	 * Closes socket
+	 */
+	void close_sock();
+
 	/**
 	 * max buffer length
 	 */
-	enum { max_length = 1024 };
+	enum { max_length = 16384 };
 
 	/**
 	 * data buffer
@@ -87,7 +115,7 @@ public:
 	 *
 	 * @param io_service connection info
 	 */
-	con_handler(basic_socket_acceptor<ip::tcp>::executor_type &io_service);
+	con_handler(basic_socket_acceptor<ip::tcp>::executor_type &io_service, boost::asio::ssl::context &context);
 
 	/**
 	 * con_handler destructor.
@@ -103,7 +131,7 @@ public:
 	 *
 	 * @return con_handler pointer
 	 */
-	static pointer create(basic_socket_acceptor<ip::tcp>::executor_type &io_service);
+	static pointer create(basic_socket_acceptor<ip::tcp>::executor_type &io_service, boost::asio::ssl::context &context);
 
 	/**
 	 * socket getter
@@ -172,6 +200,7 @@ public:
 class Server {
 private:
 	tcp::acceptor acceptor_;
+	boost::asio::ssl::context context_;
 
 	/**
 	 * Accepts traffic

+ 0 - 70
daemon/include/Sniffer.h

@@ -1,70 +0,0 @@
-#ifndef SNIFFER_H
-#define SNIFFER_H
-
-#include <tins/tins.h>
-
-/**
- * @class Sniffer
- *
- * Sniffs the network.
- *
- * Sniffer class which will sniff on a network interface. It is supposed to
- * forward the packets to an analyzer or modifyer so we can hide data in the
- * traffic.
- */
-class Sniffer {
-public:
-	/**
-	 * Creates a Sniffer.
-	 *
-	 * Creates a Sniffer and sets the network interface for sniffing.
-	 *
-	 * @param interface name of the interface for sniffing
-	 */
-	Sniffer(const std::string &interfaceName);
-
-	/**
-	 * Destroys the Sniffer.
-	 *
-	 * Destructor of the Sniffer.
-	 */
-	~Sniffer();
-
-	/**
-	 * Start sniffing on the interface.
-	 *
-	 * Starts a sniffing loop which calls handle. The loop will only be stopped if
-	 * handle returns false.
-	 */
-	void startSniffing();
-
-	/**
-	 * Sets a filter for the sniffer.
-	 *
-	 * Sets the filter for a sniffer with a pcap filter string. E.g. "ip
-	 * dst 8.8.8.8".
-	 *
-	 * @param filterString pcap filter string
-	 */
-	void setFilter(const std::string &filterString);
-
-private:
-	/**
-	 * Handler for sniffed packets.
-	 *
-	 * Handles incoming connections and provides data for the package analyzer and
-	 * modifyer.
-	 *
-	 * @param pdu sniffed packet
-	 *
-	 * @return false = stop loop | true = continue loop
-	 */
-	bool handle(Tins::PDU &pdu);
-
-	/**
-	 * Tins sniffer object.
-	 */
-	Tins::Sniffer *sniffer;
-};
-
-#endif

+ 4 - 21
daemon/include/base64.h

@@ -10,36 +10,19 @@ namespace base64 {
  *
  * @param val base64 encoded string
  *
- * @return normal string
+ * @return decoded data
  */
-std::string decode(const std::string &val);
-
-/**
- * Decodes base64 encoded strings.
- *
- * @param val base64 encoded string
- *
- * @return char vector
- */
-std::vector<char> decodeVector(const std::string &val);
+template <typename T> T decode(const std::string &val);
 
 /**
  * Encodes a string to base64.
  *
- * @param val normal string
+ * @param val data
  *
  * @return base64 encoded string
  */
-std::string encode(const std::string &val);
+template <typename T> std::string encode(const T &val);
 
-/**
- * Encodes a vector to base64.
- *
- * @param val char vector
- *
- * @return base64 encoded string
- */
-std::string encodeVector(const std::vector<char> &val);
 } // namespace base64
 
 #endif

+ 3 - 2
daemon/src/CMakeLists.txt

@@ -2,10 +2,11 @@ cmake_minimum_required(VERSION 2.8)
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
 
-add_executable(ccats src/main.cpp src/Sniffer.cpp src/Server.cpp src/base64.cpp src/JsonCommander.cpp src/FileManager.cpp src/UserManager.cpp src/Config.cpp)
+add_executable(ccats src/main.cpp src/Server.cpp src/base64.cpp src/JsonCommander.cpp src/FileManager.cpp src/UserManager.cpp src/Config.cpp src/CovertChannel/CovertChannel.cpp src/CovertChannel/ForwardChannel.cpp src/CovertChannel/ProxyChannel.cpp)
 
 # dependencies used by server only
 find_package(libtins 4.2 REQUIRED)
+find_package(OpenSSL REQUIRED)
 
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS})
-target_link_libraries(ccats PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${LIBTINS_LIBRARIES} ${JSONCPP_LIBRARIES})
+target_link_libraries(ccats PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${LIBTINS_LIBRARIES} ${JSONCPP_LIBRARIES})

+ 86 - 0
daemon/src/CovertChannel/CovertChannel.cpp

@@ -0,0 +1,86 @@
+#include "../../include/CovertChannel/CovertChannel.h"
+#include <cstdlib>
+#include <iostream>
+#include <thread>
+
+CovertChannel::CovertChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &innerForwardFilter,
+                             const std::string &outerForwardFilter, const std::string &innerChannelFilter, const std::string &outerChannelFilter,
+                             const std::string &outerPartnerFilter)
+    : innerSender(innerInterface), outerSender(outerInterface) {
+	Tins::SnifferConfiguration config;
+	config.set_promisc_mode(true);
+	config.set_immediate_mode(true);
+	config.set_direction(PCAP_D_IN);
+	config.set_snap_len(1500);
+
+	try {
+		innerForwardSniffer = new Tins::Sniffer(innerInterface, config);
+		outerForwardSniffer = new Tins::Sniffer(outerInterface, config);
+		innerChannelSniffer = new Tins::Sniffer(innerInterface, config);
+		outerChannelSniffer = new Tins::Sniffer(outerInterface, config);
+		outerPartnerSniffer = new Tins::Sniffer(outerInterface, config);
+
+		setFilter(innerForwardFilter, outerForwardFilter, innerChannelFilter, outerChannelFilter, outerPartnerFilter);
+	} catch (const Tins::pcap_error &e) {
+		std::cerr << "An error accured setting up the sniffer: " << e.what() << std::endl;
+		std::exit(EXIT_FAILURE);
+	}
+}
+
+CovertChannel::~CovertChannel() {
+	innerForwardSniffer->stop_sniff();
+	outerForwardSniffer->stop_sniff();
+	innerChannelSniffer->stop_sniff();
+	outerChannelSniffer->stop_sniff();
+	outerPartnerSniffer->stop_sniff();
+	delete (innerForwardSniffer);
+	delete (outerForwardSniffer);
+	delete (innerChannelSniffer);
+	delete (outerChannelSniffer);
+	delete (outerPartnerSniffer);
+}
+
+void CovertChannel::setFilter(const std::string &innerForwardFilter, const std::string &outerForwardFilter, const std::string &innerChannelFilter,
+                              const std::string &outerChannelFilter, const std::string &outerPartnerFilter) {
+	innerForwardSniffer->set_filter(innerForwardFilter);
+	outerForwardSniffer->set_filter(outerForwardFilter);
+	innerChannelSniffer->set_filter(innerChannelFilter);
+	outerChannelSniffer->set_filter(outerChannelFilter);
+	outerPartnerSniffer->set_filter(outerPartnerFilter);
+}
+
+void CovertChannel::startSniffing() {
+	std::thread innerForwardSnifferThread(&CovertChannel::startInnerForwardSniffing, this);
+	std::thread outerForwardSnifferThread(&CovertChannel::startOuterForwardSniffing, this);
+	std::thread innerChannelSnifferThread(&CovertChannel::startInnerChannelSniffing, this);
+	std::thread outerChannelSnifferThread(&CovertChannel::startOuterChannelSniffing, this);
+	std::thread outerPartnerSnifferThread(&CovertChannel::startOuterPartnerSniffing, this);
+
+	innerForwardSnifferThread.detach();
+	outerForwardSnifferThread.detach();
+	innerChannelSnifferThread.detach();
+	outerChannelSnifferThread.detach();
+	outerPartnerSnifferThread.detach();
+}
+
+void CovertChannel::startInnerForwardSniffing() { innerForwardSniffer->sniff_loop(make_sniffer_handler(this, &CovertChannel::handleForwardFromInner)); }
+
+void CovertChannel::startOuterForwardSniffing() { outerForwardSniffer->sniff_loop(make_sniffer_handler(this, &CovertChannel::handleForwardFromOuter)); }
+
+void CovertChannel::startInnerChannelSniffing() { innerChannelSniffer->sniff_loop(make_sniffer_handler(this, &CovertChannel::handleChannelFromInner)); }
+
+void CovertChannel::startOuterChannelSniffing() { outerChannelSniffer->sniff_loop(make_sniffer_handler(this, &CovertChannel::handleChannelFromOuter)); }
+
+void CovertChannel::startOuterPartnerSniffing() { outerPartnerSniffer->sniff_loop(make_sniffer_handler(this, &CovertChannel::handlePartnerFromOuter)); }
+
+bool CovertChannel::handleForwardFromOuter(Tins::PDU &pdu) {
+	innerSender.send(pdu);
+
+	return true;
+}
+
+bool CovertChannel::handleForwardFromInner(Tins::PDU &pdu) {
+	outerSender.send(pdu);
+
+	return true;
+}

+ 11 - 0
daemon/src/CovertChannel/ForwardChannel.cpp

@@ -0,0 +1,11 @@
+#include "../../include/CovertChannel/ForwardChannel.h"
+
+ForwardChannel::ForwardChannel(const std::string &innerInterface, const std::string &outerInterface) : CovertChannel(innerInterface, outerInterface) {}
+
+ForwardChannel::~ForwardChannel() {}
+
+bool ForwardChannel::handleChannelFromOuter(Tins::PDU &pdu) { return false; }
+
+bool ForwardChannel::handleChannelFromInner(Tins::PDU &pdu) { return false; }
+
+bool ForwardChannel::handlePartnerFromOuter(Tins::PDU &pdu) { return false; }

+ 82 - 0
daemon/src/CovertChannel/ProxyChannel.cpp

@@ -0,0 +1,82 @@
+#include "../../include/CovertChannel/ProxyChannel.h"
+#include <iostream>
+
+ProxyChannel::ProxyChannel(const std::string &innerInterface, const std::string &outerInterface, const std::string &ownIP, const std::string &partnerIP,
+                           const std::string &originIP, const std::string &targetIP, const std::string &targetPort, const std::string &ownMAC,
+                           const std::string &originMAC, const std::string &channelGatewayMAC, const std::string &gatewayMAC, const bool relayOnly)
+    : CovertChannel(innerInterface, outerInterface,
+                    "(not (tcp and src host " + originIP + " and dst host " + targetIP + " and dst port " + targetPort + ")) and (not (dst host " + ownIP +
+                        "))",
+                    "(not (tcp and src host " + targetIP + " and dst host " + ownIP + " and src port " + targetPort + ")) and (not (dst host " + ownIP + "))",
+                    "tcp and src host " + originIP + " and dst host " + targetIP + " and dst port " + targetPort,
+                    "tcp and src host " + targetIP + " and dst host " + ownIP + " and src port " + targetPort,
+                    "tcp and src host " + partnerIP + " and dst host " + ownIP + " and port " + targetPort),
+
+      relayOnly(relayOnly), ownAddress(ownIP), partnerAddress(partnerIP), originAddress(originIP), targetAddress(targetIP), ownMAC(ownMAC),
+      channelGatewayMAC(channelGatewayMAC), gatewayMAC(gatewayMAC), originMAC(originMAC) {}
+
+ProxyChannel::~ProxyChannel() {}
+
+bool ProxyChannel::handleChannelFromOuter(Tins::PDU &pdu) {
+	// TODO: check in a list how to route it and who send the request for this answer
+
+	Tins::EthernetII &eth = pdu.rfind_pdu<Tins::EthernetII>();
+	Tins::IP &ip = pdu.rfind_pdu<Tins::IP>();
+	if (relayOnly) {
+		// redirect to partner
+		eth.src_addr(ownMAC);
+		eth.dst_addr(channelGatewayMAC);
+		ip.src_addr(ownAddress);
+		ip.dst_addr(partnerAddress);
+		outerSender.send(pdu);
+	} else {
+		// Just forward it
+		// eth.src_addr(gatewayMAC);
+		// eth.dst_addr(originMAC);
+		// ip.src_addr(targetAddress);
+		// ip.dst_addr(originAddress);
+		innerSender.send(pdu);
+		std::cout << "channel from outer" << std::endl;
+	}
+	return true;
+}
+
+bool ProxyChannel::handleChannelFromInner(Tins::PDU &pdu) {
+	Tins::EthernetII &eth = pdu.rfind_pdu<Tins::EthernetII>();
+	Tins::IP &ip = pdu.rfind_pdu<Tins::IP>();
+	if (relayOnly) {
+		std::cerr << "Fixme: packet cannot be routed back so it's dropped here!!!" << std::endl;
+		// outerSender.send(pdu);
+		// TODO: add pdu to a list to check later how to route it
+	} else {
+		eth.src_addr(ownMAC);
+		eth.dst_addr(channelGatewayMAC);
+		ip.src_addr(ownAddress);
+		ip.dst_addr(partnerAddress);
+		outerSender.send(pdu);
+		std::cout << "channel from inner" << std::endl;
+	}
+	return true;
+}
+
+bool ProxyChannel::handlePartnerFromOuter(Tins::PDU &pdu) {
+	Tins::EthernetII &eth = pdu.rfind_pdu<Tins::EthernetII>();
+	Tins::IP &ip = pdu.rfind_pdu<Tins::IP>();
+	if (relayOnly) {
+		// redirect to target
+		eth.src_addr(ownMAC);
+		eth.dst_addr(gatewayMAC);
+		ip.src_addr(ownAddress);
+		ip.dst_addr(targetAddress);
+		outerSender.send(pdu);
+		std::cout << "relay" << std::endl;
+	} else {
+		eth.src_addr(gatewayMAC);
+		eth.dst_addr(originMAC);
+		ip.src_addr(targetAddress);
+		ip.dst_addr(originAddress);
+		innerSender.send(pdu);
+		std::cout << "partner" << std::endl;
+	}
+	return true;
+}

+ 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;
 }

+ 64 - 11
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() {}
@@ -209,7 +210,7 @@ JsonCommander::Response JsonCommander::executePutdata(const Json::Value &message
 			response.json["cancel"] = false;
 			response.json["error"] = "";
 
-			const std::vector<char> data = base64::decodeVector(message["data"].asString());
+			const std::vector<char> data = base64::decode<std::vector<char>>(message["data"].asString());
 
 			fileManager.writePut(data);
 
@@ -323,7 +324,7 @@ JsonCommander::Response JsonCommander::executeGetdata(const Json::Value &message
 			response.json["error"] = "";
 
 			const std::vector<char> data = fileManager.readGet();
-			response.json["data"] = base64::encodeVector(data);
+			response.json["data"] = base64::encode<std::vector<char>>(data);
 
 			fileManager.writePut(data);
 
@@ -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::encodeVector(res.first);
+			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!!!";
 		}
 	}
 
@@ -392,7 +403,7 @@ JsonCommander::Response JsonCommander::executeDeleteMe(const Json::Value &messag
 	response.json["command"] = "deleteme";
 
 	if (!message["pass"].isString()) {
-		response.action = send;
+		response.action = closeAndSend;
 		response.json["accept"] = false;
 		response.json["error"] = "incorrect deleteme command request";
 	} else if (UserManager::deleteUser(currentUser, message["pass"].asString())) {
@@ -411,10 +422,13 @@ JsonCommander::Response JsonCommander::executeDeleteMe(const Json::Value &messag
 
 JsonCommander::Response JsonCommander::testVersion(const Json::Value &message) {
 	JsonCommander::Response response;
-	response.json["version"] = this->protocolVersion;
+	response.json["major"] = this->protocolMajorVersion;
+	response.json["minor"] = this->protocolMinorVersion;
 
-	// check version string is the same
-	if (message["version"].asString().compare(this->protocolVersion) == 0) {
+	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 {
@@ -463,3 +477,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;
+}

+ 58 - 25
daemon/src/Server.cpp

@@ -11,7 +11,8 @@ using ip::tcp;
  * con_handler *
  ***************/
 
-con_handler::con_handler(basic_socket_acceptor<ip::tcp>::executor_type &io_service) : sock(io_service), buf(max_length), jsonCommander(fileManager) {
+con_handler::con_handler(basic_socket_acceptor<ip::tcp>::executor_type &io_service, boost::asio::ssl::context &context)
+    : sock(io_service), sslsock(sock, context), usessl(Config::getValue("SSLenabled") == "true"), buf(max_length), jsonCommander(fileManager) {
 	// disable indentation for json
 	this->jsonStringBuilder.settings_["indentation"] = "";
 
@@ -22,11 +23,32 @@ con_handler::con_handler(basic_socket_acceptor<ip::tcp>::executor_type &io_servi
 
 con_handler::~con_handler() {}
 
-con_handler::pointer con_handler::create(basic_socket_acceptor<ip::tcp>::executor_type &io_service) { return pointer(new con_handler(io_service)); }
+con_handler::pointer con_handler::create(basic_socket_acceptor<ip::tcp>::executor_type &io_service, boost::asio::ssl::context &context) {
+	return pointer(new con_handler(io_service, context));
+}
 
 tcp::socket &con_handler::socket() { return sock; }
 
-void con_handler::start() { read(&con_handler::handle_read_version); }
+bool con_handler::handshake() {
+	boost::system::error_code err;
+	sslsock.handshake(boost::asio::ssl::stream_base::server, err);
+	if (err) {
+		std::cerr << "SSL handshake failed: " << err.message() << std::endl;
+		close_sock();
+		return false;
+	}
+	return true;
+}
+
+void con_handler::close_sock() { sock.close(); }
+
+void con_handler::start() {
+	if (usessl) {
+		if (handshake())
+			read(&con_handler::handle_read_version);
+	} else
+		read(&con_handler::handle_read_version);
+}
 
 void con_handler::handle_read_version(const boost::system::error_code &err, size_t bytes_transferred) {
 	if (!err) {
@@ -43,12 +65,12 @@ void con_handler::handle_read_version(const boost::system::error_code &err, size
 		case JsonCommander::Action::closeAndSend:
 			sendJson(response.json);
 		default:
-			sock.close();
+			close_sock();
 		}
 
 	} else {
-		std::cerr << "error: " << err.message() << std::endl;
-		sock.close();
+		std::cerr << __PRETTY_FUNCTION__ << " error: " << err.message() << std::endl;
+		close_sock();
 	}
 }
 
@@ -67,12 +89,12 @@ void con_handler::handle_read_login(const boost::system::error_code &err, size_t
 		case JsonCommander::Action::closeAndSend:
 			sendJson(response.json);
 		default:
-			sock.close();
+			close_sock();
 		}
 
 	} else {
-		std::cerr << "error: " << err.message() << std::endl;
-		sock.close();
+		std::cerr << __PRETTY_FUNCTION__ << " error: " << err.message() << std::endl;
+		close_sock();
 	}
 }
 
@@ -91,12 +113,12 @@ void con_handler::handle_read_command(const boost::system::error_code &err, size
 		case JsonCommander::Action::closeAndSend:
 			sendJson(response.json);
 		default:
-			sock.close();
+			close_sock();
 		}
 
 	} else {
-		std::cerr << "error: " << err.message() << std::endl;
-		sock.close();
+		std::cerr << __PRETTY_FUNCTION__ << " error: " << err.message() << std::endl;
+		close_sock();
 	}
 }
 
@@ -104,26 +126,28 @@ void con_handler::handle_write(const boost::system::error_code &err, size_t byte
 	if (!err) {
 		std::cout << "Hello World!" << std::endl;
 	} else {
-		std::cerr << "error: " << err.message() << std::endl;
-		sock.close();
+		std::cerr << __PRETTY_FUNCTION__ << " error: " << err.message() << std::endl;
+		close_sock();
 	}
 }
 
 void con_handler::read(void (con_handler::*handler)(const boost::system::error_code &err, size_t bytes_transferred)) {
-	/*sock.async_read_some(buffer(data, max_length),
-	                     boost::bind(handler, shared_from_this(),
-	                                 placeholders::error,
-	                                 placeholders::bytes_transferred));*/
-
-	async_read_until(sock, buf, '\n', bind(handler, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
+	if (usessl)
+		async_read_until(sslsock, buf, '\n', bind(handler, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
+	else
+		async_read_until(sock, buf, '\n', bind(handler, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
 }
 
 void con_handler::sendJson(const Json::Value &json) {
 	std::string jsonString = Json::writeString(jsonStringBuilder, json);
 	jsonString.append("\n");
 
-	sock.async_write_some(buffer(jsonString, max_length),
-	                      boost::bind(&con_handler::handle_write, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
+	if (usessl)
+		sslsock.async_write_some(buffer(jsonString, max_length),
+		                         boost::bind(&con_handler::handle_write, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
+	else
+		sock.async_write_some(buffer(jsonString, max_length),
+		                      boost::bind(&con_handler::handle_write, shared_from_this(), placeholders::error, placeholders::bytes_transferred));
 }
 
 Json::Value con_handler::parseMessage() {
@@ -138,7 +162,7 @@ Json::Value con_handler::parseMessage() {
 	// parse data
 	if (!this->jsonReader->parse(data, data + lineEnd, &root, &err)) {
 		std::cerr << "Json error: " << err << std::endl << "data: " << data;
-		sock.close();
+		close_sock();
 	}
 
 	buf.consume(lineEnd + 1);
@@ -152,11 +176,20 @@ Json::Value con_handler::parseMessage() {
 
 void Server::start_accept() {
 	auto executor = acceptor_.get_executor();
-	con_handler::pointer connection = con_handler::create(executor);
+	con_handler::pointer connection = con_handler::create(executor, context_);
 	acceptor_.async_accept(connection->socket(), boost::bind(&Server::handle_accept, this, connection, placeholders::error));
 }
 
-Server::Server(io_service &io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), std::stoi(Config::getValue("port")))) { start_accept(); }
+Server::Server(io_service &io_service)
+    : acceptor_(io_service, tcp::endpoint(tcp::v4(), std::stoi(Config::getValue("port")))), context_(boost::asio::ssl::context::sslv23) {
+	if (Config::getValue("SSLenabled") == "true") {
+		context_.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
+		context_.use_certificate_chain_file(Config::getValue("SSLcertificate"));
+		context_.use_private_key_file(Config::getValue("SSLprivatekey"), boost::asio::ssl::context::pem);
+		context_.use_tmp_dh_file(Config::getValue("SSLdhparams"));
+	}
+	start_accept();
+}
 
 Server::~Server() {}
 

+ 0 - 33
daemon/src/Sniffer.cpp

@@ -1,33 +0,0 @@
-#include "../include/Sniffer.h"
-#include <cstdlib>
-#include <iostream>
-
-Sniffer::Sniffer(const std::string &interfaceName) {
-	Tins::SnifferConfiguration config;
-	config.set_promisc_mode(true);
-
-	try {
-		sniffer = new Tins::Sniffer(interfaceName, config);
-	} catch (const Tins::pcap_error &e) {
-		std::cerr << "An error accured setting up the sniffer: " << e.what() << std::endl;
-		std::exit(EXIT_FAILURE);
-	}
-}
-
-Sniffer::~Sniffer() {
-	sniffer->stop_sniff();
-	delete (sniffer);
-}
-
-void Sniffer::startSniffing() { sniffer->sniff_loop(make_sniffer_handler(this, &Sniffer::handle)); }
-
-void Sniffer::setFilter(const std::string &filterString) { sniffer->set_filter(filterString); }
-
-bool Sniffer::handle(Tins::PDU &pdu) {
-	// TODO implement handler for sniffed traffic
-
-	std::cout << "packet sniffed" << std::endl;
-
-	return false; // will stop sniffing after the first packet because this
-	              // handler returns false
-}

+ 4 - 4
daemon/src/base64.cpp

@@ -2,7 +2,7 @@
 
 #include <boost/beast.hpp>
 
-std::string base64::decode(const std::string &val) {
+template <> std::string base64::decode<std::string>(const std::string &val) {
 	std::string ret;
 	ret.resize(boost::beast::detail::base64::decoded_size(val.size()));
 
@@ -12,7 +12,7 @@ std::string base64::decode(const std::string &val) {
 	return ret;
 }
 
-std::vector<char> base64::decodeVector(const std::string &val) {
+template <> std::vector<char> base64::decode<std::vector<char>>(const std::string &val) {
 	std::vector<char> ret;
 	ret.resize(boost::beast::detail::base64::decoded_size(val.size()));
 
@@ -22,7 +22,7 @@ std::vector<char> base64::decodeVector(const std::string &val) {
 	return ret;
 }
 
-std::string base64::encode(const std::string &val) {
+template <> std::string base64::encode<std::string>(const std::string &val) {
 	std::string ret;
 	ret.resize(boost::beast::detail::base64::encoded_size(val.size()));
 
@@ -32,7 +32,7 @@ std::string base64::encode(const std::string &val) {
 	return ret;
 }
 
-std::string base64::encodeVector(const std::vector<char> &val) {
+template <> std::string base64::encode<std::vector<char>>(const std::vector<char> &val) {
 	std::string ret;
 	ret.resize(boost::beast::detail::base64::encoded_size(val.size()));
 

+ 35 - 8
daemon/src/main.cpp

@@ -1,9 +1,10 @@
 #include <iostream>
-#include <thread>
 
 #include "../include/Config.h"
+#include "../include/CovertChannel/CovertChannel.h"
+#include "../include/CovertChannel/ForwardChannel.h"
+#include "../include/CovertChannel/ProxyChannel.h"
 #include "../include/Server.h"
-#include "../include/Sniffer.h"
 #include "../include/UserManager.h"
 
 using namespace std;
@@ -15,15 +16,39 @@ int main(int argc, char *argv[]) {
 		exit(EXIT_FAILURE);
 	}
 
-	const string interface = Config::getValue("interface");
+	CovertChannel *covertchannel = nullptr;
+	const std::string covertChannelMode = Config::getValue("covertChannelMode");
+	if (covertChannelMode == "proxy") {
+		const string innerInterface = Config::getValue("innerInterface");
+		const string outerInterface = Config::getValue("outerInterface");
+
+		const string ownIP = Config::getValue("ownIP");
+		const string partnerIP = Config::getValue("partnerIP");
+		const string originIP = Config::getValue("originIP");
+		const string targetIP = Config::getValue("targetIP");
+		const string targetPort = Config::getValue("targetPort");
+		const string relayMode = Config::getValue("relayMode");
+		const string ownMAC = Config::getValue("ownMAC");
+		const string originMAC = Config::getValue("originMAC");
+		const string gatewayMAC = Config::getValue("gatewayMAC");
+		const string channelGatewayMAC = Config::getValue("channelGatewayMAC");
+		covertchannel = new ProxyChannel(innerInterface, outerInterface, ownIP, partnerIP, originIP, targetIP, targetPort, ownMAC, originMAC, channelGatewayMAC,
+		                                 gatewayMAC, relayMode == "true");
+
+		// covertchannel = new ForwardChannel(innerInterface, outerInterface);
+		covertchannel->startSniffing();
+	} else if (covertChannelMode == "forward") {
+		const string innerInterface = Config::getValue("innerInterface");
+		const string outerInterface = Config::getValue("outerInterface");
+
+		covertchannel = new ForwardChannel(innerInterface, outerInterface);
+		covertchannel->startSniffing();
+	}
 
 	// check if userStorage is add specified location
 	// if not create one
 	UserManager::init(Config::getValue("userdatabase"));
 
-	Sniffer *sniffer = new Sniffer(interface);
-	thread snifferThread(&Sniffer::startSniffing, sniffer);
-
 	try {
 		io_service io_service;
 		Server server(io_service);
@@ -32,7 +57,9 @@ int main(int argc, char *argv[]) {
 		cerr << e.what() << endl;
 	}
 
-	snifferThread.join();
-	delete (sniffer);
+	if (covertchannel == nullptr) {
+		delete (covertchannel);
+	}
+
 	return 0;
 }

+ 1 - 1
daemon/test/CMakeLists.txt

@@ -12,7 +12,7 @@ find_package(GMock REQUIRED)
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS} ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS})
 
 # Add test cpp file
-add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp src/UserManager.cpp test/ConfigMock.cpp)
+add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp test/ConfigMock.cpp test/UserManagerMock.cpp)
 target_link_libraries(jsonCommanderTest ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${JSONCPP_LIBRARIES} ${GTEST_BOTH_LIBRARIES} ${GMOCK_BOTH_LIBRARIES})
 
 add_test(

+ 17 - 14
daemon/test/FileManagerMock.h

@@ -1,8 +1,8 @@
 #ifndef FILEMANAGERMOCK_H
 #define FILEMANAGERMOCK_H
 
-#include <gmock/gmock.h>
 #include "../include/FileManager.h"
+#include <gmock/gmock.h>
 
 /**
  * @class FileManagerMock
@@ -11,24 +11,27 @@
  */
 class FileManagerMock : public FileManager {
 public:
-  MOCK_METHOD((std::pair<bool, int>), openGetFile, (const std::string &filename), (override));
-  MOCK_METHOD(bool, openPutFile, (const std::string &filename), (override));
+	MOCK_METHOD((std::pair<bool, int>), openGetFile, (const std::string &filename), (override));
+	MOCK_METHOD(bool, openPutFile, (const std::string &filename), (override));
+
+	MOCK_METHOD(bool, isDownloading, (), (override));
+	MOCK_METHOD(bool, isUploading, (), (override));
 
-  MOCK_METHOD(bool, isDownloading, (), (override));
-  MOCK_METHOD(bool, isUploading, (), (override));
+	MOCK_METHOD(void, cancelPut, (), (override));
 
-  MOCK_METHOD(void, cancelPut, (), (override));
+	MOCK_METHOD(std::string, getGetBaseFileName, (), (override));
+	MOCK_METHOD(std::string, getPutBaseFileName, (), (override));
 
-  MOCK_METHOD(std::string, getGetBaseFileName, (), (override));
-  MOCK_METHOD(std::string, getPutBaseFileName, (), (override));
+	MOCK_METHOD(void, writePut, (const std::vector<char> &data), (override));
+	MOCK_METHOD(std::vector<char>, readGet, (), (override));
 
-  MOCK_METHOD(void, writePut, (const std::vector<char> &data), (override));
-  MOCK_METHOD(std::vector<char>, readGet, (), (override));
+	MOCK_METHOD(int, openList, (), (override));
+	MOCK_METHOD(int, getRemainingListChunks, (), (override));
+	MOCK_METHOD(int, getListSize, (), (override));
+	MOCK_METHOD(std::vector<std::string>, getNextChunkFromList, (), (override));
 
-  MOCK_METHOD(int, openList,(), (override));
-  MOCK_METHOD(int, getRemainingListChunks, (), (override));
-  MOCK_METHOD(int, getListSize, (), (override));
-  MOCK_METHOD(std::vector<std::string>, getNextChunkFromList, (), (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

+ 336 - 11
daemon/test/JsonCommanderTest.cpp

@@ -6,34 +6,84 @@
 
 namespace {
 /* Version tests */
+TEST(testVersion, PositiveAllEqual) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+	Json::Value message;
+	message["major"] = 0;
+	message["minor"] = 1;
+
+	JsonCommander::Response response = jsonCommander.testVersion(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["major"].asInt(), jsonCommander.protocolMajorVersion);
+	EXPECT_EQ(response.json["minor"].asInt(), jsonCommander.protocolMinorVersion);
+}
+
 TEST(testVersion, Positive) {
 	FileManagerMock fileManager;
 
 	JsonCommander jsonCommander(fileManager);
+	Json::Value message;
+	message["major"] = jsonCommander.protocolMajorVersion;
+	message["minor"] = jsonCommander.protocolMinorVersion - 1;
 
-	const std::string versionString = "0.2";
-	Json::Value version;
-	version["version"] = versionString;
+	JsonCommander::Response response = jsonCommander.testVersion(message);
 
-	JsonCommander::Response response = jsonCommander.testVersion(version);
 	EXPECT_TRUE(response.action == JsonCommander::Action::send);
 	EXPECT_TRUE(response.json["accept"].asBool());
-	EXPECT_EQ(response.json["version"].asString(), versionString);
+	EXPECT_EQ(response.json["major"].asInt(), jsonCommander.protocolMajorVersion);
+	EXPECT_EQ(response.json["minor"].asInt(), jsonCommander.protocolMinorVersion);
 }
 
-TEST(testVersion, Negative) {
+TEST(testVersion, InvalidRequest) {
 	FileManagerMock fileManager;
 
 	JsonCommander jsonCommander(fileManager);
+	Json::Value message;
+	message["major"] = "def";
+	message["minor"] = "abc";
 
-	const std::string versionString = "0.1";
-	Json::Value version;
-	version["version"] = versionString;
+	JsonCommander::Response response = jsonCommander.testVersion(message);
 
-	JsonCommander::Response response = jsonCommander.testVersion(version);
 	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
 	EXPECT_FALSE(response.json["accept"].asBool());
-	EXPECT_FALSE(response.json["version"].asString().compare(versionString) == 0);
+	EXPECT_EQ(response.json["major"].asInt(), jsonCommander.protocolMajorVersion);
+	EXPECT_EQ(response.json["minor"].asInt(), jsonCommander.protocolMinorVersion);
+}
+
+TEST(testVersion, NotEqualMajorNumber) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+	Json::Value message;
+	message["major"] = jsonCommander.protocolMajorVersion + 1;
+	message["minor"] = jsonCommander.protocolMinorVersion;
+
+	JsonCommander::Response response = jsonCommander.testVersion(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["major"].asInt(), jsonCommander.protocolMajorVersion);
+	EXPECT_EQ(response.json["minor"].asInt(), jsonCommander.protocolMinorVersion);
+}
+
+TEST(testVersion, BiggerMinorNumber) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+	Json::Value message;
+	message["major"] = jsonCommander.protocolMajorVersion;
+	message["minor"] = jsonCommander.protocolMinorVersion + 1;
+
+	JsonCommander::Response response = jsonCommander.testVersion(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["major"].asInt(), jsonCommander.protocolMajorVersion);
+	EXPECT_EQ(response.json["minor"].asInt(), jsonCommander.protocolMinorVersion);
 }
 
 /* Status tests */
@@ -871,4 +921,279 @@ TEST(Listdata, InvalidRequest) {
 	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
 }
 
+TEST(Head, Positive) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "head";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	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::Error::no_error)));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_EQ(response.json["data"].asString(), bytesAsString);
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(Head, InvalidRequest) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "head";
+	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::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["file"].asString(), "");
+	EXPECT_EQ(response.json["data"].asString(), "");
+	EXPECT_NE(response.json["error"].asString(), "");
+}
+
+TEST(Head, NoSuchFile) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "head";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	std::vector<char> bytes;
+	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, 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_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_EQ(response.json["data"].asString(), "");
+	EXPECT_NE(response.json["error"].asString(), "");
+}
+
+TEST(Head, FileTooSmall) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "head";
+	const std::string file = "asdf.txt";
+
+	Json::Value message;
+	message["command"] = command;
+	message["file"] = file;
+
+	std::vector<char> bytes;
+	EXPECT_CALL(fileManager, getBytesFromFile(testing::_, testing::_)).WillOnce(testing::Return(std::make_pair(bytes, FileManager::Error::file_too_small)));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["file"].asString(), file);
+	EXPECT_EQ(response.json["data"].asString(), "");
+	EXPECT_NE(response.json["error"].asString(), "");
+}
+
+TEST(Deleteme, Positive) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	// need to set currentUser in jsonCommander via calling checkLogin
+	Json::Value login;
+	login["login"] = true;
+	login["user"] = "positive";
+	login["pass"] = "positive";
+	login["cancel"] = false;
+
+	JsonCommander::Response loginRes = jsonCommander.checkLogin(login);
+	EXPECT_TRUE(loginRes.json["accept"].asBool());
+	EXPECT_EQ(loginRes.json["error"].asString(), "");
+
+	// now the actual test
+	const std::string command = "deleteme";
+
+	Json::Value message;
+	message["command"] = command;
+	message["pass"] = "positive";
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_EQ(response.action, JsonCommander::Action::closeAndSend);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(Deleteme, Negative) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	// need to set currentUser in jsonCommander via calling checkLogin
+	Json::Value login;
+	login["login"] = true;
+	login["user"] = "positive";
+	login["pass"] = "positive";
+	login["cancel"] = false;
+
+	JsonCommander::Response loginRes = jsonCommander.checkLogin(login);
+	EXPECT_TRUE(loginRes.json["accept"].asBool());
+	EXPECT_EQ(loginRes.json["error"].asString(), "");
+
+	// now the actual test
+	const std::string command = "deleteme";
+
+	Json::Value message;
+	message["command"] = command;
+	message["pass"] = "negative";
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_EQ(response.action, JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_NE(response.json["error"].asString(), "");
+}
+
+TEST(Deleteme, InvalidRequest) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	// need to set currentUser in jsonCommander via calling checkLogin
+	Json::Value login;
+	login["login"] = true;
+	login["user"] = "positive";
+	login["pass"] = "positive";
+	login["cancel"] = false;
+
+	JsonCommander::Response loginRes = jsonCommander.checkLogin(login);
+	EXPECT_TRUE(loginRes.json["accept"].asBool());
+	EXPECT_EQ(loginRes.json["error"].asString(), "");
+
+	// now the actual test
+	const std::string command = "deleteme";
+
+	Json::Value message;
+	message["command"] = command;
+	message["pass"] = 123;
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_EQ(response.action, JsonCommander::Action::closeAndSend);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	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

+ 13 - 0
daemon/test/UserManagerMock.cpp

@@ -0,0 +1,13 @@
+#include "../include/UserManager.h"
+
+void UserManager::init(const std::string &file) {}
+
+bool UserManager::isAllowed(const std::string &name, const std::string &pw) { return name == "positive" && pw == "positive"; }
+
+bool UserManager::addUser(const std::string &name, const std::string &pw) { return name == "positive" && pw == "positive"; }
+
+bool UserManager::deleteUser(const std::string &name, const std::string &pw) { return name == "positive" && pw == "positive"; }
+
+void UserManager::readFromFile(std::map<std::string, std::string> &user_map) {}
+
+void UserManager::writeToFile(std::map<std::string, std::string> &user_map) {}

+ 8 - 0
daemon/test/test-dh2048.pem

@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAkn1yM22zNYKpIdmWyhpyrs6lMNQyRk+BM7AzS/cgu5jr790SOeG9
++MSDGWlUUcC2uAAFFevg+XDD6Bk5qAGjJsbWKQyUBeTQdKURhdQh4cNfSPtKIh93
+ub1ViSQEwbekqQYpKWDGsogHj5MvKBN9y4Ywv5cfV0snaZIR278yDt7h0MuFNRUj
+BZpolcwLHNUjuSm3gBpNBEA3GUvWkGm9QATPVyNsmBGUW2+pVcdiRNV+zVbDceqR
+Jgsfz3Y8tO9yuXghUfmI6STOLERSWrzge161MH1QhRfWWOMGjKIrurVSUzheFT7d
+jTzCW014/7P0e85N5fwtWoQuGr0G0WficwIBAg==
+-----END DH PARAMETERS-----

+ 4 - 4
gui/CMakeLists.txt

@@ -9,14 +9,14 @@ set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 find_package(Threads)
-find_package(Boost 1.67 REQUIRED COMPONENTS system)
+find_package(Boost 1.67 REQUIRED COMPONENTS system filesystem)
 find_package(Qt5 COMPONENTS Core Quick REQUIRED)
 find_package(PkgConfig REQUIRED)
 pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
-add_executable(${PROJECT_NAME} src/main.cpp src/qmlhandler.cpp src/qml.qrc)
+add_definitions(-DQT_NO_DEBUG_OUTPUT)
+
+add_executable(${PROJECT_NAME} src/main.cpp src/cmdmanager.cpp src/qmlhandler.cpp src/jsonhandler.cpp src/qml.qrc src/config.cpp include/qmlhandler.h)
 
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} include)
 target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${JSONCPP_LIBRARIES} ${Boost_LIBRARIES} Qt5::Core Qt5::Quick)
-
-

+ 33 - 0
gui/include/cmdmanager.h

@@ -0,0 +1,33 @@
+#ifndef CMDMANAGER_H
+#define CMDMANAGER_H
+
+#include "qmlhandler.h"
+
+#include <json/json.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace CmdManager {
+void init();
+void setQmlHandler(QMLHandler *q);
+
+void executeCmd(std::string cmd, Json::Value root);
+
+void handleStatus(Json::Value root);
+void handleClose(Json::Value root);
+void handleList(Json::Value root);
+void handleConnect(Json::Value root);
+void handleVersion(Json::Value root);
+void handleLogin(Json::Value root);
+void handleSignup(Json::Value root);
+void handlePut(Json::Value root);
+void handlePutData(Json::Value root);
+void handleGet(Json::Value root);
+void handleGetData(Json::Value root);
+void handleDeleteMe(Json::Value root);
+void handleDeleteFile(Json::Value root);
+} // namespace CmdManager
+
+#endif // CMDMANAGER_H

+ 18 - 0
gui/include/config.h

@@ -0,0 +1,18 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <vector>
+
+namespace Config {
+void setupDefaultConfig();
+bool checkConfig();
+bool loadFile();
+void saveFile();
+std::string getValue(const std::string &key);
+void setValue(const std::string &key, const std::string &value);
+} // namespace Config
+
+#endif // CONFIG_H

+ 11 - 0
gui/include/jsonhandler.h

@@ -0,0 +1,11 @@
+#ifndef JSONHANDLER_H
+#define JSONHANDLER_H
+
+#include "qmlhandler.h"
+#include <json/json.h>
+
+namespace JsonHandler {
+void parseJSON(std::string buffer);
+} // namespace JsonHandler
+
+#endif // JSONHANDLER_H

+ 138 - 0
gui/include/qmlhandler.h

@@ -0,0 +1,138 @@
+#ifndef QMLHANDLER_H
+#define QMLHANDLER_H
+
+#include <QObject>
+#include <QUrl>
+
+extern bool _RESTART;
+
+class QMLHandler : public QObject {
+	Q_OBJECT
+
+private:
+	void readPipeLoop();
+	void fileExists(std::string name);
+
+public:
+	explicit QMLHandler(QObject *parent = 0);
+	void onExit();
+	void setProgramActive(bool active);
+	void reopenCLI(QString ip);
+	void closeCLI();
+	void writeToCLI(QString command);
+	void loadSettingsToGUI();
+	QString getIP();
+	void setRestart(bool restart);
+
+	// C++ -> QML
+signals:
+	// No Config Found Popup
+	void noConfigFoundPopupOpen();
+	void noConfigFoundPopupClose();
+
+	// Invalid Cli Path Popup
+	void invalidCliPathPopupOpen();
+	void invalidCliPathPopupClose();
+
+	// Invalid Config Popup
+	void invalidConfigPopupOpen();
+	void invalidConfigPopupClose();
+
+	// Sending
+	void sendingSetFileUrlText(QString signalText);
+	void sendingEnableSendButton();
+	void sendingDisableSendButton();
+
+	// Receiving
+	void receivingClearFileList();
+	void receivingListFile(QString fileName, bool existsLocally);
+	void receivingDisableDownloadButton(QString fileName);
+	void receivingCloseConfirmDeletePopup();
+
+	// Messages
+	void message(QString msg);
+
+	// Settings
+	void closeWindow();
+	void loadSettings(int covertMethod, bool saveIP, bool saveUsername, QString cliPath);
+
+	// Delete Me Popup
+	void deleteMePopupSetStatus(QString status);
+
+	// Ip Popup
+	void ipPopupSetStatus(QString status);
+	void ipPopupSetIP(QString default_ip);
+	void ipPopupClose();
+	void ipPopupOpen();
+	void ipPopupEnableConnectButton();
+	void ipPopupDisableConnectButton();
+	void ipPopupCheckSaveCheckbox();
+
+	// Login Signup Popup
+	void loginSignupPopupClose();
+	void loginSignupPopupOpen();
+	void loginSignupCheckSaveCheckbox();
+
+	// Login
+	void loginSetStatus(QString status);
+	void loginEnableLoginButton();
+	void loginDisableLoginButton();
+	void loginSetUsername(QString username);
+
+	// Signup
+	void signupSetStatus(QString status);
+	void signupEnableRegisterButton();
+	void signupDisableRegisterButton();
+
+	// Footer
+	void log(QString logText);
+	void footerSetStatus(QString status);
+
+	// QML -> C++
+public slots:
+	void onStart();
+
+	// No Config Found Popup
+	void onNoConfigFoundPopupContinueButton(QString cli_path);
+
+	// Invalid Cli Path Popup
+	void onInvalidCliPathPopupContinueButton(QString cli_path);
+	void onInvalidCliPathPopupQuitButton();
+
+	// Invalid Config Popup
+	void onInvalidConfigPopupCreateDefaultButton();
+	void onInvalidConfigPopupQuitButton();
+
+	// Sending
+	void onSendingSelectFileButton(QUrl url);
+	void onSendingSendFileButton();
+	void onSendingClearSelectionButton();
+
+	// Receiving
+	void onReceivingListFilesButton();
+	void onReceivingDownloadFileButton(QString fileName);
+	void onReceivingConfirmDeleteFileButton(QString fileName);
+
+	// Messages
+	void onMessagesSendButton(QString msg);
+
+	// Settings
+	void onSettingsDeleteMeButton(QString password);
+	void onSettingsRevertChangesButton();
+	void onSettingsResetButton();
+	void onSettingsSaveButton(int covertMethod, bool saveIP, bool saveUsername, QString cliPath);
+
+	// Ip Popup
+	void onIpPopupConnectButton(QString ip, bool saveAsDefault);
+
+	// Login
+	void onLoginLoginButton(QString username, QString password, bool saveAsDefault);
+
+	// Signup
+	void onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo, bool saveAsDefault);
+
+	// Footer
+	void onFooterGetStatusButton();
+};
+
+#endif // QMLHANDLER_H

+ 28 - 5
gui/src/IpPopup.ui.qml → gui/src/Forms/Connect/IpPopup.ui.qml

@@ -4,25 +4,39 @@ import QtQuick.Layouts 1.3
 
 Popup {
     id: popup
-    height: 200
+    height: 250
     dim: true
     clip: false
     width: 400
     modal: true
     focus: true
     closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
 
     Connections {
         target: _qmlHandler
+        onIpPopupSetIP: {
+            ipPopupIpInput.text = default_ip
+            ipPopupConnectButton.enabled = ipPopupIpInput.acceptableInput
+        }
         onIpPopupClose: {
             popup.close()
         }
         onIpPopupOpen: {
-          popup.open();
+            popup.open();
         }
         onIpPopupSetStatus: {
             ipPopupStatusText.text = status
         }
+        onIpPopupEnableConnectButton: {
+            ipPopupConnectButton.enabled = true
+        }
+        onIpPopupDisableConnectButton: {
+            ipPopupConnectButton.enabled = false
+        }
+        onIpPopupCheckSaveCheckbox: {
+            ipPopupSetDefaultCheckbox.checked = true
+        }
     }
 
     ColumnLayout {
@@ -53,6 +67,15 @@ Popup {
             Keys.onReturnPressed: ipPopupConnectButton.activate()
             // @disable-check M222
             Keys.onEnterPressed: ipPopupConnectButton.activate()
+
+            onTextEdited: ipPopupConnectButton.enabled = ipPopupIpInput.acceptableInput
+        }
+
+        CheckBox {
+            id: ipPopupSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default IP"
         }
 
         Text {
@@ -71,7 +94,7 @@ Popup {
             text: qsTr("Connect")
             rightPadding: 8
             padding: 12
-            enabled: ipPopupIpInput.acceptableInput
+            enabled: false
             font.pointSize: 16
             // @disable-check M223
             onClicked: {
@@ -82,9 +105,9 @@ Popup {
             // @disable-check M222
             function activate() {
                 // @disable-check M223
-                if (ipPopupIpInput.acceptableInput) {
+                if (ipPopupConnectButton.enabled) {
                     // @disable-check M222
-                    _qmlHandler.onIpPopupConnectButton(ipPopupIpInput.text)
+                    _qmlHandler.onIpPopupConnectButton(ipPopupIpInput.text, ipPopupSetDefaultCheckbox.checked)
                 }
             }
         }

+ 30 - 3
gui/src/LoginForm.ui.qml → gui/src/Forms/Connect/LoginForm.ui.qml

@@ -8,9 +8,21 @@ Page {
 
     Connections {
         target: _qmlHandler
+        onLoginSetUsername: {
+            loginUsernameInput.text = username
+        }
         onLoginSetStatus: {
             loginStatusText.text = status
         }
+        onLoginEnableLoginButton: {
+            loginLoginButton.enabled = true
+        }
+        onLoginDisableLoginButton: {
+            loginLoginButton.enabled = false
+        }
+        onLoginSignupCheckSaveCheckbox: {
+            loginSetDefaultCheckbox.checked = true
+        }
     }
 
     ColumnLayout {
@@ -38,6 +50,9 @@ Page {
             Keys.onReturnPressed: loginLoginButton.activate()
             // @disable-check M222
             Keys.onEnterPressed: loginLoginButton.activate()
+
+            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != ""
+                                                      && loginPasswordInput.text != "")
         }
 
         TextField {
@@ -52,6 +67,16 @@ Page {
             // @disable-check M222
             Keys.onEnterPressed: loginLoginButton.activate()
             echoMode: TextInput.Password
+
+            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != ""
+                                                      && loginPasswordInput.text != "")
+        }
+
+        CheckBox {
+            id: loginSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default user"
         }
 
         Text {
@@ -68,8 +93,7 @@ Page {
             Layout.alignment: Qt.AlignCenter
             id: loginLoginButton
             text: qsTr("Login")
-            enabled: (loginUsernameInput.text != ""
-                      && loginPasswordInput.text != "")
+            enabled: false
             font.pointSize: 16
             // @disable-check M223
             onClicked: {
@@ -82,7 +106,10 @@ Page {
                 // @disable-check M223
                 if (loginLoginButton.enabled) {
                     // @disable-check M222
-                    _qmlHandler.onLoginLoginButton(loginUsernameInput.text, loginPasswordInput.text)
+                    _qmlHandler.onLoginLoginButton(
+                                loginUsernameInput.text,
+                                loginPasswordInput.text,
+                                loginSetDefaultCheckbox.checked)
                 }
             }
         }

+ 3 - 4
gui/src/LoginSignupPopup.ui.qml → gui/src/Forms/Connect/LoginSignupPopup.ui.qml

@@ -11,6 +11,7 @@ Popup {
     modal: true
     focus: true
     closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
 
     Connections {
         target: _qmlHandler
@@ -18,7 +19,7 @@ Popup {
             popup.close()
         }
         onLoginSignupPopupOpen: {
-          popup.open()
+            popup.open()
         }
     }
 
@@ -46,16 +47,14 @@ Popup {
             clip: true
 
             LoginForm {
-
             }
 
             SignupForm {
-
             }
         }
     }
 
     Component.onCompleted: {
-      swipeView.interactive = false
+        swipeView.interactive = false
     }
 }

+ 146 - 0
gui/src/Forms/Connect/SignupForm.ui.qml

@@ -0,0 +1,146 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.3
+
+Page {
+    width: 400
+    height: 400
+
+    Connections {
+        target: _qmlHandler
+        onSignupSetStatus: {
+            signupStatusText.text = status
+        }
+        onSignupEnableRegisterButton: {
+            signupRegisterButton.enabled = true
+        }
+        onSignupDisableRegisterButton: {
+            signupRegisterButton.enabled = false
+        }
+        onLoginSignupCheckSaveCheckbox: {
+            signupSetDefaultCheckbox.checked = true
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            id: signupTitle
+            color: "#ffffff"
+            text: qsTr("Signup")
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupUsernameInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Username"
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
+
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
+
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupPasswordOneInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Password"
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
+            echoMode: TextInput.Password
+
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
+
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupPasswordTwoInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Repeat Passw."
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
+            echoMode: TextInput.Password
+
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
+
+        CheckBox {
+            id: signupSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default user"
+        }
+
+        Text {
+            id: signupStatusText
+            color: "#df3f3f"
+            text: qsTr("")
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            Layout.alignment: Qt.AlignCenter
+            font.pixelSize: 20
+        }
+
+        Button {
+            Layout.alignment: Qt.AlignCenter
+            id: signupRegisterButton
+            text: qsTr("Register")
+            enabled: false
+            font.pointSize: 16
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                signupRegisterButton.activate()
+            }
+
+            // @disable-check M222
+            function activate() {
+                // @disable-check M223
+                if (signupRegisterButton.enabled) {
+                    // @disable-check M222
+                    _qmlHandler.onSignupRegisterButton(
+                                signupUsernameInput.text,
+                                signupPasswordOneInput.text,
+                                signupPasswordTwoInput.text,
+                                signupSetDefaultCheckbox.checked)
+                }
+            }
+        }
+    }
+}

+ 0 - 0
gui/src/HelpForm.ui.qml → gui/src/Forms/Help/HelpForm.ui.qml


+ 0 - 0
gui/src/FooterForm.ui.qml → gui/src/Forms/Main/FooterForm.ui.qml


+ 128 - 0
gui/src/Forms/Main/InvalidCliPathPopup.ui.qml

@@ -0,0 +1,128 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 200
+    dim: true
+    clip: false
+    width: 800
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onInvalidCliPathPopupClose: {
+            popup.close()
+        }
+        onInvalidCliPathPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: invalidCliPathPopupText
+            color: "#ffffff"
+            text: qsTr("Invalid CLI-Path: CLI could not be found on the location specified in the config file.\nPlease specify the new location of the CLI:")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Text {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 600
+                id: invalidCliPathPopupCliPath
+                color: "#ffffff"
+                text: qsTr("Select CLI Path >>>")
+                horizontalAlignment: Text.AlignLeft
+                font.italic: true
+                verticalAlignment: Text.AlignVCenter
+                font.pixelSize: 20
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignLeft
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                id: invalicCliPathPopupSelectCliButton
+                height: 50
+                text: qsTr("Select CLI")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    invalidCliPathPopupDialog.open()
+                }
+            }
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupQuitButton
+                text: qsTr("Quit")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidCliPathPopupQuitButton()
+                }
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupContinueButton
+                text: qsTr("Continue")
+                enabled: invalidCliPathPopupText.text == "Select CLI Path >>>" ? false : true
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidCliPathPopupContinueButton(
+                                invalidCliPathPopupCliPath.text)
+                    popup.close()
+                }
+            }
+        }
+    }
+
+    FileDialog {
+        id: invalidCliPathPopupDialog
+        nameFilters: ["CLI file (ccats-cli)", "Any file (*)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = invalidCliPathPopupDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            invalidCliPathPopupCliPath.text = path
+        }
+    }
+}

+ 78 - 0
gui/src/Forms/Main/InvalidConfigPopup.ui.qml

@@ -0,0 +1,78 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 150
+    dim: true
+    clip: false
+    width: 650
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onInvalidConfigPopupClose: {
+            popup.close()
+        }
+        onInvalidConfigPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: invalidConfigPopupText
+            color: "#ffffff"
+            text: qsTr("Configuration file invalid!\nWarning: Creating the default config will overwrite your config file!")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidConfigPopupQuitButton
+                text: qsTr("Quit")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidConfigPopupQuitButton()
+                }
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupContinueButton
+                text: qsTr("Create Default")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidConfigPopupCreateDefaultButton()
+                    popup.close()
+                }
+            }
+        }
+    }
+}

+ 108 - 0
gui/src/Forms/Main/NoConfigFoundPopup.ui.qml

@@ -0,0 +1,108 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 200
+    dim: true
+    clip: false
+    width: 800
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onNoConfigFoundPopupClose: {
+            popup.close()
+        }
+        onNoConfigFoundPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: noConfigFoundPopupText
+            color: "#ffffff"
+            text: qsTr("No configuration file found. Default config will be created.\nPlease specify the location of the CLI:")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Text {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 600
+                id: noConfigFoundPopupCliPath
+                color: "#ffffff"
+                text: qsTr("Select CLI Path >>>")
+                horizontalAlignment: Text.AlignLeft
+                font.italic: true
+                verticalAlignment: Text.AlignVCenter
+                font.pixelSize: 20
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignLeft
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                id: noConfigFoundPopupSelectCliButton
+                height: 50
+                text: qsTr("Select CLI")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    noConfigFoundPopupCliDialog.open()
+                }
+            }
+        }
+
+        Button {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: 50
+            Layout.preferredWidth: 200
+            id: noConfigFoundPopupContinueButton
+            text: qsTr("Continue")
+            enabled: noConfigFoundPopupCliPath.text == "Select CLI Path >>>" ? false : true
+            font.pointSize: 16
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                _qmlHandler.onNoConfigFoundPopupContinueButton(
+                            noConfigFoundPopupCliPath.text)
+                popup.close()
+            }
+        }
+    }
+
+    FileDialog {
+        id: noConfigFoundPopupCliDialog
+        nameFilters: ["CLI file (ccats-cli)", "Any file (*)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = noConfigFoundPopupCliDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            noConfigFoundPopupCliPath.text = path
+        }
+    }
+}

+ 20 - 5
gui/src/main.qml → gui/src/Forms/Main/main.qml

@@ -1,5 +1,11 @@
 import QtQuick 2.12
 import QtQuick.Controls 2.5
+import "../Sending"
+import "../Receiving"
+import "../Messages"
+import "../Settings"
+import "../Help"
+import "../Connect"
 
 ApplicationWindow {
     id: window
@@ -8,6 +14,7 @@ ApplicationWindow {
     height: 720
     maximumHeight: height
     maximumWidth: width
+    font.capitalization: Font.MixedCase
 
     minimumHeight: height
     minimumWidth: width
@@ -71,19 +78,27 @@ ApplicationWindow {
 
     LoginSignupPopup {
         id: loginSignupPopup
-        x: Math.round((parent.width - width) / 2)
-        y: Math.round((parent.height - height) / 2)
     }
 
     IpPopup {
         id: ipPopup
-        x: Math.round((parent.width - width) / 2)
-        y: Math.round((parent.height - height) / 2)
-        onClosed: loginSignupPopup.open()
+    }
+
+    NoConfigFoundPopup {
+        id: noConfigFoundPopup
+    }
+
+    InvalidConfigPopup {
+        id: invalidConfigPopup
+    }
+
+    InvalidCliPathPopup {
+        id: invalidCliPathPopup
     }
 
     Component.onCompleted: {
       swipeView.interactive = false
       ipPopup.open()
+      _qmlHandler.onStart()
     }
 }

+ 0 - 0
gui/src/MessagesForm.ui.qml → gui/src/Forms/Messages/MessagesForm.ui.qml


+ 87 - 0
gui/src/Forms/Receiving/ReceivingFileTemplate.ui.qml

@@ -0,0 +1,87 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.3
+
+Item {
+    width: 1250
+    height: 50
+    property string fileNameText: "Name Placeholder"
+    property string fileSizeText: "Size Placeholder"
+    property string fileDecryptableText: "Decryptable Placeholder"
+    property bool fileExists: false
+
+    Connections {
+        target: _qmlHandler
+        onReceivingDisableDownloadButton: {
+            if (fileNameText == fileName) {
+                fileExists = true
+            }
+        }
+
+        onReceivingCloseConfirmDeletePopup: {
+            confirmDeletePopup.close()
+        }
+    }
+
+    RowLayout {
+        id: rowLayout
+        anchors.fill: parent
+
+        Text {
+            id: fileTemplateFileName
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 400
+            verticalAlignment: Text.AlignVCenter
+            text: fileNameText
+            color: "#ffffff"
+        }
+
+        Text {
+            id: fileTemplateFileSize
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 200
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+            text: fileSizeText
+            color: "#ffffff"
+        }
+
+        Text {
+            id: fileTemplateFileDecryptable
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 200
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHCenter
+            text: fileDecryptableText
+            color: "#ffffff"
+        }
+
+        Button {
+            id: fileTemplateDownloadButton
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 200
+            enabled: !fileExists
+            text: fileExists ? qsTr("Already Downloaded") : qsTr("Download")
+
+            onClicked: _qmlHandler.onReceivingDownloadFileButton(fileNameText)
+        }
+
+        Button {
+            id: fileTemplateDeleteButton
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 200
+            text: qsTr("Delete from server")
+
+            onClicked: confirmDeletePopup.open()
+        }
+    }
+
+    ReceivingFileTemplateDeletePopup {
+        id: confirmDeletePopup
+    }
+}

+ 56 - 0
gui/src/Forms/Receiving/ReceivingFileTemplateDeletePopup.ui.qml

@@ -0,0 +1,56 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+
+Popup {
+    id: confirmDeletePopup
+    height: 150
+    dim: true
+    clip: false
+    width: 400
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            id: confirmDeletePopupConfirmationText
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: 80
+            Layout.preferredWidth: parent.width
+            text: "Are you sure you want to delete this file from the Server?"
+            verticalAlignment: Text.AlignTop
+            horizontalAlignment: Text.AlignHCenter
+            wrapMode: Text.WordWrap
+            font.pointSize: 15
+            color: "#ffffff"
+        }
+        RowLayout {
+            Layout.preferredHeight: 80
+            Layout.preferredWidth: parent.width
+            Button {
+                id: confirmDeleteButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 100
+                text: qsTr("Delete")
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onReceivingConfirmDeleteFileButton(fileNameText)
+                }
+            }
+
+            Button {
+                id: cancelDeleteButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 100
+                text: qsTr("Cancel")
+                // @disable-check M222
+                onClicked: confirmDeletePopup.close()
+            }
+        }
+    }
+}

+ 69 - 0
gui/src/Forms/Receiving/ReceivingForm.ui.qml

@@ -0,0 +1,69 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.3
+
+Page {
+    width: 1280
+    height: 470
+    id: receivingForm
+
+    font.capitalization: Font.MixedCase
+
+    Connections {
+        target: _qmlHandler
+        onReceivingListFile: {
+            fileList.append({"fileName" : fileName,
+                           "fileSize" : "42 kb",
+                           "fileDecryptable" : "Decryptable?: Yes",
+                           "fileExistsLocally" : existsLocally})
+        }
+
+        onReceivingClearFileList: {
+            fileList.clear()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        ScrollView {
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 400
+
+            ListView {
+                anchors.fill: parent
+                model: fileList
+                clip: true
+
+                delegate: ReceivingFileTemplate {
+                    fileNameText: fileName
+                    fileSizeText: fileSize
+                    fileDecryptableText: fileDecryptable
+                    fileExists: fileExistsLocally
+                }
+            }
+        }
+
+        ListModel {
+            id: fileList
+        }
+
+        RowLayout {
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 70
+
+            Button {
+                id: receivingListFilesButton
+                Layout.preferredWidth: 180
+                Layout.preferredHeight: parent.height
+                text: qsTr("Refresh File List")
+
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M223
+                    _qmlHandler.onReceivingListFilesButton()
+                }
+            }
+        }
+    }
+}

+ 0 - 0
gui/src/SendingForm.ui.qml → gui/src/Forms/Sending/SendingForm.ui.qml


+ 107 - 0
gui/src/Forms/Settings/DeleteMePopup.ui.qml

@@ -0,0 +1,107 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+
+Popup {
+    id: popup
+    height: 300
+    dim: true
+    clip: false
+    width: 400
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    signal resetStatus
+
+    onClosed: {
+        resetStatus()
+    }
+
+    Connections {
+        target: _qmlHandler
+        onDeleteMePopupSetStatus: {
+            deleteMePopupStatusText.text = status
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 100
+            color: "#ffffff"
+            text: qsTr("Are you sure you want to delete your account on the server?\nThe application will restart.")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        Text {
+            id: deleteMePopupStatusText
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            color: "#df3f3f"
+            text: ""
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+            Connections {
+                target: popup
+                onResetStatus: deleteMePopupStatusText.text = ""
+            }
+        }
+
+        TextField {
+            id: deleteMePopupPasswordInput
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: 300
+            Layout.preferredHeight: 50
+            selectByMouse: true
+            echoMode: TextInput.Password
+            text: ""
+            horizontalAlignment: Text.AlignHCenter
+            placeholderText: "Enter password to confirm"
+        }
+
+        RowLayout {
+            spacing: 0
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                text: "Confirm"
+                enabled: !(deleteMePopupPasswordInput.text == "")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onSettingsDeleteMeButton(
+                                deleteMePopupPasswordInput.text)
+                }
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                text: "Cancel"
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    popup.close()
+                }
+            }
+        }
+    }
+}

+ 244 - 0
gui/src/Forms/Settings/SettingsForm.ui.qml

@@ -0,0 +1,244 @@
+import QtQuick 2.12
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.5
+import QtQuick.Controls.Material 2.3
+import QtQuick.Dialogs 1.0
+
+Page {
+    width: 1280
+    height: 470
+    id: page
+
+    font.capitalization: Font.MixedCase
+
+    Connections {
+        target: _qmlHandler
+        onCloseWindow: {
+            window.close()
+        }
+        onLoadSettings: {
+            settingsCovertMethodPicker.currentIndex = covertMethod
+            settingsSaveIpSwitch.checked = saveIP
+            settingsSaveUsernameSwitch.checked = saveUsername
+            settingsCliPath.text = "CLI-Path:     " + cliPath
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 400
+            Layout.bottomMargin: 20
+
+            ColumnLayout {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 500
+                Layout.preferredHeight: parent.height
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Covert Channel Method:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Autofill default IP on start:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Autofill default username on start:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    id: settingsCliPath
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "CLI-Path:   "
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Delete my account:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+            }
+
+            ColumnLayout {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 500
+                Layout.preferredHeight: parent.height
+
+                ComboBox {
+                    id: settingsCovertMethodPicker
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+
+                    model: ListModel {
+                        ListElement {
+                            text: "Method 1"
+                        }
+                        ListElement {
+                            text: "Method 2"
+                        }
+                        ListElement {
+                            text: "Method 3"
+                        }
+                    }
+                }
+
+                Switch {
+                    id: settingsSaveIpSwitch
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+                    text: ""
+                    checked: false
+                    display: AbstractButton.IconOnly
+                }
+
+                Switch {
+                    id: settingsSaveUsernameSwitch
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+                    text: ""
+                    checked: false
+                    display: AbstractButton.IconOnly
+                }
+
+                Button {
+                    id: settingsChangeCliPathButton
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 220
+                    text: "Change (req. restart)"
+                    font.pixelSize: 20
+                    // @disable-check M223
+                    onClicked: {
+                        // @disable-check M222
+                        settingsCliDialog.open()
+                    }
+                }
+
+                Button {
+                    id: settingsDeleteMeButton
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 150
+                    text: "Delete Me"
+                    font.pixelSize: 20
+                    // @disable-check M223
+                    onClicked: {
+                        // @disable-check M222
+                        deleteMePopup.open()
+                    }
+                }
+            }
+        }
+
+        Rectangle {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 2
+            Layout.bottomMargin: 30
+            color: Material.accent
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            Layout.bottomMargin: 20
+
+            Button {
+                id: settingsResetButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Select defaults"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsResetButton()
+                }
+            }
+
+            Button {
+                id: settingsSaveButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Save Changes"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsSaveButton(settingsCovertMethodPicker.currentIndex,
+                                                     settingsSaveIpSwitch.checked,
+                                                     settingsSaveUsernameSwitch.checked,
+                                                     settingsCliPath.text.replace("CLI-Path:     ", ""))
+                }
+            }
+
+            Button {
+                id: settingsRevertChangesButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Revert Changes"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsRevertChangesButton()
+                }
+            }
+        }
+    }
+
+    FileDialog {
+        id: settingsCliDialog
+        nameFilters: ["CLI file (ccats-cli)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = settingsCliDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            settingsCliPath.text = "CLI-Path:   " + path
+        }
+    }
+    
+    DeleteMePopup {
+        id: deleteMePopup
+    }
+}

+ 0 - 93
gui/src/ReceivingForm.ui.qml

@@ -1,93 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.5
-import QtQuick.Layouts 1.3
-
-Page {
-    width: 1280
-    height: 470
-
-    font.capitalization: Font.MixedCase
-
-    Connections {
-        target: _qmlHandler
-        onReceivingListFile: {
-            receivingFileList.append(fileName)
-            receivingFlickable.contentY = receivingFileList.height - receivingFlickable.height
-        }
-
-        onReceivingClearFileList: {
-            receivingFileList.text = ""
-        }
-    }
-
-    ColumnLayout {
-        anchors.fill: parent
-
-        Flickable {
-            id: receivingFlickable
-            flickableDirection: Flickable.VerticalFlick
-            Layout.preferredHeight: 400
-            Layout.preferredWidth: parent.width
-
-            TextArea.flickable: TextArea {
-                selectByMouse: true
-                readOnly: true
-                id: receivingFileList
-                wrapMode: TextArea.Wrap
-                text: qsTr("")
-                font.pointSize: 15
-            }
-
-            ScrollBar.vertical: ScrollBar {
-            }
-        }
-
-        RowLayout {
-            Layout.preferredWidth: parent.width
-            Layout.preferredHeight: 70
-
-            TextField {
-                id: receivingFileNameField
-                selectByMouse: true
-                Layout.preferredWidth: 880
-                Layout.preferredHeight: parent.height
-                placeholderText: "Enter File Name to download..."
-                text: qsTr("")
-                font.pixelSize: 20
-            }
-
-            Button {
-                id: receivingListFilesButton
-                Layout.preferredWidth: 180
-                Layout.preferredHeight: parent.height
-                text: qsTr("List Files")
-
-                // @disable-check M223
-                onClicked: {
-                    // @disable-check M223
-                    _qmlHandler.onReceivingListFilesButton()
-                }
-            }
-
-            Button {
-                id: receivingGetFileButton
-                Layout.preferredWidth: 180
-                Layout.preferredHeight: parent.height
-                text: qsTr("Download")
-
-                enabled: receivingFileNameField.text != ""
-
-                // @disable-check M223
-                onClicked: {
-                    // @disable-check M223
-                    if (receivingFileNameField.text != "") {
-                        // @disable-check M222
-                        _qmlHandler.onReceivingGetFileButton(
-                                    receivingFileNameField.text)
-                        receivingFileNameField.text = ""
-                    }
-                }
-            }
-        }
-    }
-}

+ 0 - 80
gui/src/SettingsForm.ui.qml

@@ -1,80 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.5
-
-Page {
-    width: 1280
-    height: 470
-    id: page
-
-    font.capitalization: Font.MixedCase
-
-    Connections {
-        target: _qmlHandler
-        onCloseWindow: {
-            window.close()
-        }
-    }
-
-    ComboBox {
-        id: settingsCovertMethodPicker
-        x: 328
-        y: 59
-        width: 285
-        height: 48
-
-        model: ListModel {
-            ListElement {
-                text: "Method 1"
-            }
-            ListElement {
-                text: "Method 2"
-            }
-            ListElement {
-                text: "Method 3"
-            }
-        }
-    }
-
-    Text {
-        id: settingsCovertMethodText
-        x: 56
-        y: 71
-        color: "#ffffff"
-        text: qsTr("Covert Channel Method:")
-        font.pixelSize: 20
-    }
-
-    Button {
-        id: settingsDeleteMeButton
-        x: 549
-        y: 343
-        width: 182
-        height: 71
-        text: qsTr("Delete Me")
-        font.pointSize: 15
-        // @disable-check M223
-        onClicked: {
-            // @disable-check M222
-            _qmlHandler.onSettingsDeleteMeButton()
-
-        }
-    }
-
-    Text {
-        id: settingsSaveIpText
-        x: 56
-        y: 154
-        color: "#ffffff"
-        text: qsTr("Save last IP:")
-        font.pixelSize: 20
-    }
-
-    Switch {
-        id: settingsSaveIpSwitch
-        x: 328
-        y: 142
-        text: qsTr("")
-        checked: false
-        display: AbstractButton.IconOnly
-    }
-}

+ 0 - 111
gui/src/SignupForm.ui.qml

@@ -1,111 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.5
-import QtQuick.Layouts 1.3
-
-Page {
-  width: 400
-  height: 400
-
-  Connections {
-      target: _qmlHandler
-      onSignupSetStatus: {
-          signupStatusText.text = status
-      }
-  }
-
-  ColumnLayout {
-      anchors.fill: parent
-
-      Text {
-          Layout.alignment: Qt.AlignCenter
-          id: signupTitle
-          color: "#ffffff"
-          text: qsTr("Signup")
-          horizontalAlignment: Text.AlignHCenter
-          verticalAlignment: Text.AlignVCenter
-          font.pixelSize: 20
-      }
-
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupUsernameInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Username"
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
-      }
-
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupPasswordOneInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Password"
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
-          echoMode: TextInput.Password
-
-          onTextEdited: signupStatusText.text = ""
-      }
-
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupPasswordTwoInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Repeat Passw."
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
-          echoMode: TextInput.Password
-
-          onTextEdited: signupStatusText.text = ""
-      }
-
-      Text {
-          id: signupStatusText
-          color: "#df3f3f"
-          text: qsTr("")
-          horizontalAlignment: Text.AlignHCenter
-          verticalAlignment: Text.AlignVCenter
-          Layout.alignment: Qt.AlignCenter
-          font.pixelSize: 20
-      }
-
-      Button {
-          Layout.alignment: Qt.AlignCenter
-          id: signupRegisterButton
-          text: qsTr("Register")
-          enabled: (signupUsernameInput.text != ""
-                    && signupPasswordOneInput.text != ""
-                    && signupPasswordTwoInput.text != "")
-          font.pointSize: 16
-          // @disable-check M223
-          onClicked: {
-              // @disable-check M222
-              signupRegisterButton.activate()
-          }
-
-          // @disable-check M222
-          function activate() {
-              // @disable-check M223
-              if (signupRegisterButton.enabled) {
-                  // @disable-check M222
-                  _qmlHandler.onSignupRegisterButton(signupUsernameInput.text, signupPasswordOneInput.text, signupPasswordTwoInput.text);
-              }
-          }
-      }
-   }
-}

+ 146 - 0
gui/src/cmdmanager.cpp

@@ -0,0 +1,146 @@
+#include <QDebug>
+#include <QGuiApplication>
+
+#include "../include/cmdmanager.h"
+#include "../include/config.h"
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <json/json.h>
+
+using boost::lexical_cast;
+using boost::asio::buffer;
+using namespace std;
+
+namespace CmdManager {
+QMLHandler *qmlHandler;
+map<string, void (*)(Json::Value root)> cmdmap;
+} // namespace CmdManager
+
+void CmdManager::init() {
+	cmdmap["status"] = &CmdManager::handleStatus;
+	cmdmap["close"] = &CmdManager::handleClose;
+	cmdmap["list"] = &CmdManager::handleList;
+	cmdmap["connect"] = &CmdManager::handleConnect;
+	cmdmap["version"] = &CmdManager::handleVersion;
+	cmdmap["login"] = &CmdManager::handleLogin;
+	cmdmap["signup"] = &CmdManager::handleSignup;
+	cmdmap["put"] = &CmdManager::handlePut;
+	cmdmap["putdata"] = &CmdManager::handlePutData;
+	cmdmap["get"] = &CmdManager::handleGet;
+	cmdmap["getdata"] = &CmdManager::handleGetData;
+	cmdmap["deleteme"] = &CmdManager::handleDeleteMe;
+	cmdmap["deletefile"] = &CmdManager::handleDeleteFile;
+}
+
+void CmdManager::setQmlHandler(QMLHandler *q) { qmlHandler = q; }
+
+void CmdManager::executeCmd(string cmd, Json::Value root) { cmdmap[cmd](root); }
+
+void CmdManager::handleStatus(Json::Value root) { emit qmlHandler->footerSetStatus(root["response"].asString().c_str()); }
+
+void CmdManager::handleClose(Json::Value root) { qmlHandler->setProgramActive(false); }
+
+void CmdManager::handleList(Json::Value root) {
+	if (root["accept"] == true) {
+		emit qmlHandler->receivingClearFileList();
+
+		// Get the array of file Names
+		auto fileNames = root["names"];
+		for (int i = 0; i < fileNames.size(); i++) {
+			emit qmlHandler->receivingListFile(QString::fromStdString(fileNames[i].asString().c_str()), boost::filesystem::exists(fileNames[i].asString()));
+		}
+	} else {
+		emit qmlHandler->log(root["error"].asString().c_str());
+	}
+}
+
+void CmdManager::handleConnect(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		emit qmlHandler->ipPopupSetStatus(root["error"].asString().c_str());
+		qmlHandler->closeCLI();
+		emit qmlHandler->ipPopupEnableConnectButton();
+	}
+}
+
+void CmdManager::handleVersion(Json::Value root) {
+	if (root["accept"] == true) {
+		emit qmlHandler->ipPopupClose();
+		emit qmlHandler->loginSignupPopupOpen();
+	} else {
+		QString errorMessage =
+		    QString::fromStdString(string("Version mismatch: \nClient: " + root["clientversion"].asString() + "\nServer: " + root["serverversion"].asString()));
+		emit qmlHandler->ipPopupSetStatus(errorMessage);
+		qmlHandler->closeCLI();
+		emit qmlHandler->ipPopupEnableConnectButton();
+	}
+}
+
+void CmdManager::handleLogin(Json::Value root) {
+	if (root["accept"] == true) {
+		emit qmlHandler->loginSignupPopupClose();
+		qmlHandler->writeToCLI("list\n");
+		qmlHandler->loadSettingsToGUI();
+	} else {
+		emit qmlHandler->loginSetStatus(root["error"].asString().c_str());
+		qmlHandler->reopenCLI(qmlHandler->getIP());
+		emit qmlHandler->loginEnableLoginButton();
+	}
+}
+
+void CmdManager::handleSignup(Json::Value root) {
+	if (root["accept"] == true) {
+		emit qmlHandler->loginSignupPopupClose();
+	} else {
+		emit qmlHandler->signupSetStatus(root["error"].asString().c_str());
+		qmlHandler->reopenCLI(qmlHandler->getIP());
+		emit qmlHandler->signupEnableRegisterButton();
+	}
+}
+
+void CmdManager::handlePut(Json::Value root) {
+	if (root["accept"] == false) {
+		QString errorMessage = QString::fromStdString(string("Error when uploading file " + root["file"].asString() + ":\n" + root["error"].asString()));
+		emit qmlHandler->log(errorMessage);
+	}
+}
+
+void CmdManager::handlePutData(Json::Value root) {
+	// TODO: Show speed and handle Error
+}
+
+void CmdManager::handleGet(Json::Value root) {
+	if (root["accept"] == false) {
+		QString errorMessage = QString::fromStdString(string("Error when downloading file " + root["file"].asString() + ":\n" + root["error"].asString()));
+		emit qmlHandler->log(errorMessage);
+	} else {
+		string fileName = root["file"].asString();
+		// TODO: Only do this in getdata when remaining is 0 (when the file is fully downloaded) - maybe set text to "downloading.." in between
+		emit qmlHandler->receivingDisableDownloadButton(QString::fromStdString(fileName));
+	}
+}
+
+void CmdManager::handleGetData(Json::Value root) {
+	// TODO: Show speed and handle Error
+}
+
+void CmdManager::handleDeleteMe(Json::Value root) {
+	if (root["accept"] == true) {
+		qmlHandler->setRestart(true);
+		emit qmlHandler->closeWindow();
+	} else {
+		QString errorMessage = QString::fromStdString(root["error"].asString());
+		emit qmlHandler->deleteMePopupSetStatus(errorMessage);
+	}
+}
+
+void CmdManager::handleDeleteFile(Json::Value root) {
+	emit qmlHandler->receivingCloseConfirmDeletePopup();
+	if (root["accept"] == false) {
+		QString errorMessage = QString::fromStdString(string("Error when deleting file " + root["file"].asString() + ":\n" + root["error"].asString()));
+		emit qmlHandler->log(errorMessage);
+	} else {
+		QString message = QString::fromStdString(string("Deleted file " + root["file"].asString() + " from the server!"));
+		emit qmlHandler->log(message);
+	}
+}

+ 100 - 0
gui/src/config.cpp

@@ -0,0 +1,100 @@
+#include "config.h"
+#include <QDebug>
+#include <boost/lexical_cast.hpp>
+
+using boost::bad_lexical_cast;
+using boost::lexical_cast;
+
+namespace Config {
+std::map<std::string, std::string> configuration;
+bool configValid;
+std::string configPath = "config.txt";
+std::string configKeys[] = {"Autofill-IP", "Default-IP", "Autofill-Username", "Default-Username", "CLI-Path", "Covert-Channel-Method"};
+} // namespace Config
+
+void Config::setupDefaultConfig() {
+	configuration.clear();
+	setValue(configKeys[0], "0");
+	setValue(configKeys[1], "0.0.0.0");
+	setValue(configKeys[2], "0");
+	setValue(configKeys[3], "user");
+	setValue(configKeys[5], "0");
+}
+
+bool Config::checkConfig() {
+	if (!configValid || configuration.size() != (sizeof(configKeys) / sizeof(*configKeys)))
+		return false;
+	for (int i = 0; i < (sizeof(configKeys) / sizeof(*configKeys)); i++) {
+		if (getValue(configKeys[i]) == "")
+			return false;
+	}
+
+	std::string autofill_ip = getValue("Autofill-IP");
+	if (autofill_ip != "0" && autofill_ip != "1")
+		return false;
+
+	std::string autofill_user = getValue("Autofill-Username");
+	if (autofill_user != "0" && autofill_user != "1")
+		return false;
+
+	try {
+		int covert_method = lexical_cast<int>(getValue("Covert-Channel-Method"));
+	} catch (bad_lexical_cast &e) {
+		return false;
+	}
+
+	return true;
+}
+
+bool Config::loadFile() {
+	std::ifstream ifile(configPath);
+	std::string line;
+
+	if (ifile.is_open()) {
+		while (getline(ifile, line)) {
+			std::stringstream ss(line);
+			std::string segment;
+			std::vector<std::string> v;
+			while (getline(ss, segment, '=')) {
+				v.push_back(segment);
+			}
+			if (v.size() != 2) {
+				// One line doesn't have the format *=*
+				configValid = false;
+			} else {
+				configuration.insert(std::pair<std::string, std::string>(v.at(0), v.at(1)));
+			}
+		}
+		configValid = true;
+		return true;
+	}
+	return false;
+}
+
+void Config::saveFile() {
+	std::ofstream file;
+	file.open(configPath);
+	for (auto const &x : configuration) {
+		file << x.first << "=" << x.second << std::endl;
+	}
+	file.close();
+}
+
+std::string Config::getValue(const std::string &key) {
+	auto it = configuration.find(key);
+	if (it == configuration.end()) {
+		return "";
+	} else {
+		return it->second;
+	}
+}
+
+void Config::setValue(const std::string &key, const std::string &value) {
+	auto it = configuration.find(key);
+
+	if (it == configuration.end()) {
+		configuration.insert(std::pair<std::string, std::string>(key, value));
+	} else {
+		it->second = value;
+	}
+}

+ 26 - 0
gui/src/jsonhandler.cpp

@@ -0,0 +1,26 @@
+#include "../include/jsonhandler.h"
+#include "../include/cmdmanager.h"
+
+using namespace std;
+
+// This method gets a string and tries to read it as Json
+// If it fails to do so, return and do nothing, else handle the content
+void JsonHandler::parseJSON(string buffer) {
+	Json::Value root;
+	Json::CharReaderBuilder builder;
+	Json::CharReader *reader = builder.newCharReader();
+	string jsonError;
+
+	// Try to parse the string as Json and store the result of the pasring in a
+	// boolean
+	bool parsingSuccessful = reader->parse(buffer.c_str(), buffer.c_str() + buffer.size(), &root, &jsonError);
+
+	// If the string is not correct Json, return
+	if (!parsingSuccessful) {
+		return;
+	}
+
+	string cmd = root["command"].asString();
+
+	CmdManager::executeCmd(cmd, root);
+}

+ 6 - 2
gui/src/main.cpp

@@ -13,7 +13,9 @@
 #include <thread>
 #include <unistd.h>
 
-#include "qmlhandler.h"
+#include "../include/cmdmanager.h"
+#include "../include/jsonhandler.h"
+#include "../include/qmlhandler.h"
 
 using namespace std;
 
@@ -28,13 +30,15 @@ int main(int argc, char *argv[]) {
 	QQmlApplicationEngine engine;
 
 	QMLHandler qmlHandler;
+	CmdManager::setQmlHandler(&qmlHandler);
+	CmdManager::init();
 
 	// Set the context for the window, so that the qml files can be connected to
 	// the qmlHandler
 	engine.rootContext()->setContextProperty("_qmlHandler", &qmlHandler);
 
 	// Load the main window
-	QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
+	QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/Forms/Main/main.qml")));
 
 	QObject *object = component.create();
 

+ 24 - 11
gui/src/qml.qrc

@@ -1,16 +1,29 @@
 <RCC>
     <qresource prefix="/">
-        <file>main.qml</file>
-        <file>SendingForm.ui.qml</file>
-        <file>ReceivingForm.ui.qml</file>
-        <file>SettingsForm.ui.qml</file>
-        <file>MessagesForm.ui.qml</file>
         <file>qtquickcontrols2.conf</file>
-        <file>LoginSignupPopup.ui.qml</file>
-        <file>HelpForm.ui.qml</file>
-        <file>FooterForm.ui.qml</file>
-        <file>IpPopup.ui.qml</file>
-        <file>LoginForm.ui.qml</file>
-        <file>SignupForm.ui.qml</file>
+
+        <file>Forms/Main/main.qml</file>
+        <file>Forms/Main/FooterForm.ui.qml</file>
+        <file>Forms/Main/NoConfigFoundPopup.ui.qml</file>
+        <file>Forms/Main/InvalidCliPathPopup.ui.qml</file>
+        <file>Forms/Main/InvalidConfigPopup.ui.qml</file>
+
+        <file>Forms/Connect/LoginForm.ui.qml</file>
+        <file>Forms/Connect/IpPopup.ui.qml</file>
+        <file>Forms/Connect/LoginSignupPopup.ui.qml</file>
+        <file>Forms/Connect/SignupForm.ui.qml</file>
+
+        <file>Forms/Sending/SendingForm.ui.qml</file>
+
+        <file>Forms/Receiving/ReceivingForm.ui.qml</file>
+        <file>Forms/Receiving/ReceivingFileTemplate.ui.qml</file>
+        <file>Forms/Receiving/ReceivingFileTemplateDeletePopup.ui.qml</file>
+
+        <file>Forms/Messages/MessagesForm.ui.qml</file>
+
+        <file>Forms/Settings/DeleteMePopup.ui.qml</file>
+        <file>Forms/Settings/SettingsForm.ui.qml</file>
+
+        <file>Forms/Help/HelpForm.ui.qml</file>
     </qresource>
 </RCC>

+ 147 - 131
gui/src/qmlhandler.cpp

@@ -1,53 +1,68 @@
+#include <QDebug>
 #include <QGuiApplication>
 #include <csignal>
 #include <cstdio>
 #include <cstdlib>
 #include <iostream>
 #include <poll.h>
+#include <string>
 #include <sys/prctl.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <thread>
 #include <unistd.h>
 
-#include "qmlhandler.h"
+#include "../include/config.h"
+#include "../include/jsonhandler.h"
+#include "../include/qmlhandler.h"
 #include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
 #include <iostream>
 #include <json/json.h>
 
+using boost::lexical_cast;
 using boost::asio::buffer;
 
 using namespace std;
 
 int inpipefd[2];
 int outpipefd[2];
+
 char buf[1025];
 QUrl sendFileUrl = QUrl("");
 QString _IP = "";
 bool _CLI_RUNNING = false;
 bool _RESTART = false;
+pid_t childpid;
 
 bool programActive = true;
 
 QMLHandler::QMLHandler(QObject *parent) : QObject(parent) {}
 
-void QMLHandler::closeCLI() {
-	close(outpipefd[1]);
-	close(inpipefd[0]);
-	_CLI_RUNNING = false;
+void QMLHandler::closeCLI() { _CLI_RUNNING = false; }
+
+void QMLHandler::loadSettingsToGUI() {
+	int covertMethod = lexical_cast<int>(Config::getValue("Covert-Channel-Method"));
+	bool saveIP = lexical_cast<bool>(Config::getValue("Autofill-IP"));
+	bool saveUsername = lexical_cast<bool>(Config::getValue("Autofill-Username"));
+	QString cliPath = QString::fromStdString(Config::getValue("CLI-Path"));
+	emit loadSettings(covertMethod, saveIP, saveUsername, cliPath);
 }
 
 void QMLHandler::reopenCLI(QString ip) {
 	if (_CLI_RUNNING) {
+		waitpid(childpid, NULL, 0);
 		closeCLI();
 	}
 
 	_IP = ip;
-	pid_t pid = 0;
 
 	pipe(inpipefd);
 	pipe(outpipefd);
-	pid = fork();
-	if (pid == 0) {
+
+	childpid = fork();
+	if (childpid == 0) {
 		// Child
 		dup2(outpipefd[0], STDIN_FILENO);
 		dup2(inpipefd[1], STDOUT_FILENO);
@@ -58,7 +73,8 @@ void QMLHandler::reopenCLI(QString ip) {
 
 		// Set the path to the CLI - pass argument h (help) for now
 		// TODO: Change hardcoded path
-		execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
+		// execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
+		execl(Config::getValue("CLI-Path").c_str(), "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
 
 		exit(1);
 	}
@@ -67,7 +83,7 @@ void QMLHandler::reopenCLI(QString ip) {
 
 	close(outpipefd[0]);
 	close(inpipefd[1]);
-	std::thread(&QMLHandler::readPipeLoop, this, pid).detach();
+	std::thread(&QMLHandler::readPipeLoop, this).detach();
 }
 
 std::vector<std::string> tokenizeByNewlines(std::string in) {
@@ -85,122 +101,11 @@ std::vector<std::string> tokenizeByNewlines(std::string in) {
 
 void QMLHandler::onExit() { write(outpipefd[1], "disconnect\n", strlen("disconnect\n")); }
 
-// This method gets a string and tries to read it as Json
-// If it fails to do so, return and do nothing, else handle the content
-void QMLHandler::handleJSON(string buffer) {
-	Json::Value root;
-	Json::CharReaderBuilder builder;
-	Json::CharReader *reader = builder.newCharReader();
-	string jsonError;
-
-	// Try to parse the string as Json and store the result of the pasring in a
-	// boolean
-	bool parsingSuccessful = reader->parse(buffer.c_str(), buffer.c_str() + buffer.size(), &root, &jsonError);
-
-	// If the string is not correct Json, return
-	if (!parsingSuccessful) {
-		return;
-	}
-
-	const Json::Value command = root["command"];
-	string cmd = command.asString();
-	qInfo() << QString::fromStdString("Received command " + cmd);
-
-	if (!cmd.compare("status")) {
-		emit footerSetStatus(root["response"].asString().c_str());
-	}
-
-	else if (!cmd.compare("close")) {
-		programActive = false;
-	}
-
-	else if (!cmd.compare("list")) {
-		if (root["accept"] == true) {
-			emit receivingClearFileList();
-
-			// Get the array of file Names
-			auto fileNames = root["names"];
-			for (int i = 0; i < fileNames.size(); i++) {
-				emit receivingListFile(QString::fromStdString(fileNames[i].asString().c_str()));
-			}
-		} else {
-			emit log(root["error"].asString().c_str());
-		}
-	}
-
-	else if (!cmd.compare("connect")) {
-		if (root["accept"].asBool()) {
-		} else {
-			emit ipPopupSetStatus(root["error"].asString().c_str());
-			closeCLI();
-		}
-	}
-
-	else if (!cmd.compare("version")) {
-		if (root["accept"] == true) {
-			emit ipPopupClose();
-			emit loginSignupPopupOpen();
-		} else {
-			QString errorMessage = QString::fromStdString(
-			    string("Version mismatch: \nClient: " + root["clientversion"].asString() + "\nServer: " + root["serverversion"].asString()));
-			emit ipPopupSetStatus(errorMessage);
-			reopenCLI(_IP);
-		}
-	}
-
-	else if (!cmd.compare("login")) {
-		if (root["accept"] == true) {
-			emit loginSignupPopupClose();
-		} else {
-			emit loginSetStatus(root["error"].asString().c_str());
-			reopenCLI(_IP);
-		}
-	}
-
-	else if (!cmd.compare("signup")) {
-		if (root["accept"] == true) {
-			emit loginSignupPopupClose();
-		} else {
-			emit loginSetStatus(root["error"].asString().c_str());
-			reopenCLI(_IP);
-		}
-	}
-
-	else if (!cmd.compare("put")) {
-		if (root["accept"] == false) {
-			QString errorMessage = QString::fromStdString(string("Error when uploading file " + root["file"].asString() + ":\n" + root["error"].asString()));
-			emit log(errorMessage);
-		}
-	}
-
-	else if (!cmd.compare("putdata")) {
-		// TODO: Show speed and handle Error
-	}
-
-	else if (!cmd.compare("get")) {
-		if (root["accept"] == false) {
-			QString errorMessage = QString::fromStdString(string("Error when downloading file " + root["file"].asString() + ":\n" + root["error"].asString()));
-			emit log(errorMessage);
-		}
-	}
-
-	else if (!cmd.compare("getdata")) {
-		// TODO: Show speed and handle Error
-	}
-
-	else if (!cmd.compare("deleteme")) {
-		if (root["accept"] == true) {
-			_RESTART = true;
-			emit closeWindow();
-		}
-	}
-}
-
 // This method is a loop which runs in a seperate thread.
 // If will read the Input Pipe, which containts the string that get printed
 // on stdout by the CLI/Server, and calls handleJSON with the read content
 // one it reads a newline.
-void QMLHandler::readPipeLoop(pid_t childid) {
+void QMLHandler::readPipeLoop() {
 	unsigned int readOffset = 0;
 	unsigned int pollCount = 0;
 	struct pollfd inPipeStatus;
@@ -221,26 +126,26 @@ void QMLHandler::readPipeLoop(pid_t childid) {
 			pollCount++;
 		}
 
-		if (pollCount > 9 && (readOffset || pipeInput.size())) {
+		if (pollCount > 4 && (readOffset || pipeInput.size())) {
 			pipeInput.append(buf);
 			inputs = tokenizeByNewlines(pipeInput);
 			for (string s : inputs) {
 				emit log(QString::fromStdString(s));
 				qInfo() << QString::fromStdString(s);
-				handleJSON(s);
+				// handleJSON(s);
+				JsonHandler::parseJSON(s);
 			}
 			pipeInput = string();
 			memset(buf, 0, 1025);
 			pollCount = 0;
 			readOffset = 0;
-			if (waitpid(childid, NULL, WNOHANG)) {
+			if (waitpid(childpid, NULL, WNOHANG)) {
 				// nonzero means error or childid has changed state
 				// for us that means child has exited -> CLI is dead
 				break;
 			}
 		}
 
-		// Fixme
 		if (readOffset >= 1024) {
 			pipeInput.append(buf);
 			readOffset = 0;
@@ -252,6 +157,59 @@ void QMLHandler::readPipeLoop(pid_t childid) {
 
 // ### QML Handlers ###
 
+void QMLHandler::onStart() {
+	bool configExists = Config::loadFile();
+
+	if (configExists == true) {
+		// Config exists
+		if (Config::checkConfig() == true) {
+			// Config is valid
+			if (!boost::filesystem::is_regular_file(Config::getValue("CLI-Path")) ||
+			    boost::filesystem::path(Config::getValue("CLI-Path")).filename().compare("ccats-cli")) {
+				// Invalid CLI Path
+				emit invalidCliPathPopupOpen();
+			}
+			if (Config::getValue("Autofill-IP") == "1") {
+				emit ipPopupSetIP(QString::fromStdString(Config::getValue("Default-IP")));
+				emit ipPopupCheckSaveCheckbox();
+			}
+			if (Config::getValue("Autofill-Username") == "1") {
+				emit loginSetUsername(QString::fromStdString(Config::getValue("Default-Username")));
+				emit loginSignupCheckSaveCheckbox();
+			}
+		} else {
+			// Config is invalid
+			emit invalidConfigPopupOpen();
+		}
+	} else {
+		// Config doesn't exist
+		Config::setupDefaultConfig();
+		emit noConfigFoundPopupOpen();
+	}
+}
+
+// No Config Found Popup
+void QMLHandler::onNoConfigFoundPopupContinueButton(QString cli_path) {
+	Config::setValue("CLI-Path", cli_path.toUtf8().constData());
+	Config::saveFile();
+}
+
+// Invalid Cli Path Popup
+void QMLHandler::onInvalidCliPathPopupContinueButton(QString cli_path) {
+	Config::setValue("CLI-Path", cli_path.toUtf8().constData());
+	Config::saveFile();
+}
+
+void QMLHandler::onInvalidCliPathPopupQuitButton() { emit closeWindow(); }
+
+// Invalid Config Popup
+void QMLHandler::onInvalidConfigPopupQuitButton() { emit closeWindow(); }
+
+void QMLHandler::onInvalidConfigPopupCreateDefaultButton() {
+	Config::setupDefaultConfig();
+	emit noConfigFoundPopupOpen();
+}
+
 // Sending
 void QMLHandler::onSendingSelectFileButton(QUrl url) {
 	sendFileUrl = url.toLocalFile();
@@ -275,35 +233,93 @@ void QMLHandler::onSendingClearSelectionButton() {
 // Receiving
 void QMLHandler::onReceivingListFilesButton() { write(outpipefd[1], "list\n", strlen("list\n")); }
 
-void QMLHandler::onReceivingGetFileButton(QString fileName) {
+void QMLHandler::onReceivingDownloadFileButton(QString fileName) {
 	QString command = "get " + fileName + "\n";
 	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
 }
 
+void QMLHandler::onReceivingConfirmDeleteFileButton(QString fileName) {
+	QString command = "deletefile " + fileName + "\n";
+	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
+}
+
 // Messages
 void QMLHandler::onMessagesSendButton(QString msg) { emit message(msg); }
 
 // Settings
-void QMLHandler::onSettingsDeleteMeButton() { write(outpipefd[1], "deleteme\n", strlen("deleteme\n")); }
+void QMLHandler::onSettingsDeleteMeButton(QString password) {
+	QString command = "deleteme " + password + "\n";
+	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
+}
+
+void QMLHandler::onSettingsRevertChangesButton() {
+	loadSettingsToGUI();
+	emit log("Settings changes reverted.");
+}
+
+void QMLHandler::onSettingsResetButton() {
+	string cli_path = Config::getValue("CLI-Path");
+	Config::setupDefaultConfig();
+	Config::setValue("CLI-Path", cli_path);
+	loadSettingsToGUI();
+	emit log("Settings resetted to default.");
+}
+
+void QMLHandler::onSettingsSaveButton(int covertMethod, bool saveIP, bool saveUsername, QString cliPath) {
+	Config::setValue("Covert-Channel-Method", lexical_cast<string>(covertMethod));
+	Config::setValue("Autofill-IP", lexical_cast<string>(saveIP));
+	Config::setValue("Autofill-Username", lexical_cast<string>(saveUsername));
+	Config::setValue("CLI-Path", cliPath.toUtf8().constData());
+	Config::saveFile();
+	emit log("Settings saved.");
+}
 
 // Ip Popup
-void QMLHandler::onIpPopupConnectButton(QString ip) { reopenCLI(ip); }
+void QMLHandler::onIpPopupConnectButton(QString ip, bool saveAsDefault) {
+	reopenCLI(ip);
+	emit ipPopupDisableConnectButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-IP", ip.toUtf8().constData());
+		Config::setValue("Autofill-IP", "1");
+		Config::saveFile();
+	}
+}
 
 // Login
-void QMLHandler::onLoginLoginButton(QString username, QString password) {
+void QMLHandler::onLoginLoginButton(QString username, QString password, bool saveAsDefault) {
 	QString command = "login " + username + " " + password + "\n";
 	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
+	emit loginDisableLoginButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-Username", username.toUtf8().constData());
+		Config::setValue("Autofill-Username", "1");
+		Config::saveFile();
+	}
 }
 
 // Signup
-void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo) {
+void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo, bool saveAsDefault) {
 	if (QString::compare(passwordOne, passwordTwo, Qt::CaseSensitive)) {
 		emit signupSetStatus("Passwords don't match");
 		return;
 	}
 	QString command = "signup " + username + " " + passwordOne + "\n";
 	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
+	emit signupDisableRegisterButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-Username", username.toUtf8().constData());
+		Config::setValue("Autofill-Username", "1");
+		Config::saveFile();
+	}
 }
 
 // Footer
 void QMLHandler::onFooterGetStatusButton() { write(outpipefd[1], "status\n", strlen("status\n")); }
+
+void QMLHandler::setProgramActive(bool active) { programActive = active; }
+
+void QMLHandler::writeToCLI(QString command) { write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); }
+
+QString QMLHandler::getIP() { return _IP; }
+
+void QMLHandler::setRestart(bool restart) { _RESTART = restart; }

+ 0 - 93
gui/src/qmlhandler.h

@@ -1,93 +0,0 @@
-#ifndef QMLHANDLER_H
-#define QMLHANDLER_H
-
-
-#include <QDebug>
-#include <QObject>
-#include <QUrl>
-#include <string>
-
-extern bool _RESTART;
-
-class QMLHandler : public QObject {
-  Q_OBJECT
-
-private:
-  void handleJSON(std::string buffer);
-  void readPipeLoop(pid_t childid);
-  void reopenCLI(QString ip);
-  void closeCLI();
-
-public:
-  explicit QMLHandler(QObject *parent = 0);
-  void onExit();
-
-
-// C++ -> QML
-signals:
-  // Sending
-  void sendingSetFileUrlText(QString signalText);
-  void sendingEnableSendButton();
-  void sendingDisableSendButton();
-
-  // Receiving
-  void receivingClearFileList();
-  void receivingListFile(QString fileName);
-
-  // Messages
-  void message(QString msg);
-
-  // Settings
-  void closeWindow();
-
-  // Ip Popup
-  void ipPopupSetStatus(QString status);
-  void ipPopupClose();
-  void ipPopupOpen();
-
-  // Login Signup Popup
-  void loginSignupPopupClose();
-  void loginSignupPopupOpen();
-
-  // Login
-  void loginSetStatus(QString status);
-
-  // Signup
-  void signupSetStatus(QString status);
-
-  // Footer
-  void log(QString logText);
-  void footerSetStatus(QString status);
-
-
-// QML -> C++
-public slots:
-  // Sending
-  void onSendingSelectFileButton(QUrl url);
-  void onSendingSendFileButton();
-  void onSendingClearSelectionButton();
-
-  // Receiving
-  void onReceivingListFilesButton();
-  void onReceivingGetFileButton(QString fileName);
-
-  // Messages
-  void onMessagesSendButton(QString msg);
-
-  // Settings
-  void onSettingsDeleteMeButton();
-
-  // Ip Popup
-  void onIpPopupConnectButton(QString ip);
-
-  // Login
-  void onLoginLoginButton(QString username, QString password);
-
-  // Signup
-  void onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo);
-
-  // Footer
-  void onFooterGetStatusButton();
-};
-
-#endif // QMLHANDLER_H