Browse Source

rework CMakeLists into one per directory with one top-level file to build all. get boilerplate for cmdman_test up, also test for version command.

Sander, Paul 4 years ago
parent
commit
5e1a4565bd

+ 49 - 0
.cmake_modules/FindReadline.cmake

@@ -0,0 +1,49 @@
+# Code copied from sethhall@github
+#
+# - Try to find readline include dirs and libraries 
+#
+# Usage of this module as follows:
+#
+#     find_package(Readline)
+#
+# Variables used by this module, they can change the default behaviour and need
+# to be set before calling find_package:
+#
+#  Readline_ROOT_DIR         Set this variable to the root installation of
+#                            readline if the module has problems finding the
+#                            proper installation path.
+#
+# Variables defined by this module:
+#
+#  READLINE_FOUND            System has readline, include and lib dirs found
+#  Readline_INCLUDE_DIR      The readline include directories. 
+#  Readline_LIBRARY          The readline library.
+
+find_path(Readline_ROOT_DIR
+    NAMES include/readline/readline.h
+)
+
+find_path(Readline_INCLUDE_DIR
+    NAMES readline/readline.h
+    HINTS ${Readline_ROOT_DIR}/include
+)
+
+find_library(Readline_LIBRARY
+    NAMES readline
+    HINTS ${Readline_ROOT_DIR}/lib
+)
+
+if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+  set(READLINE_FOUND TRUE)
+else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+  FIND_LIBRARY(Readline_LIBRARY NAMES readline)
+  include(FindPackageHandleStandardArgs)
+  FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
+  MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
+endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+
+mark_as_advanced(
+    Readline_ROOT_DIR
+    Readline_INCLUDE_DIR
+    Readline_LIBRARY
+)

+ 44 - 6
.gitlab-ci.yml

@@ -12,10 +12,10 @@ build-image:
   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"
+    - docker build --pull -t "$CI_REGISTRY_IMAGE:testing" -f Dockerfile.testing .
+    - docker push "$CI_REGISTRY_IMAGE:testing"
      
-jsonCommaderTest:
+daemon-jsonCommaderTest:
   stage: test
   before_script:
   - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
@@ -23,7 +23,45 @@ jsonCommaderTest:
   - 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
+  - docker pull "$CI_REGISTRY_IMAGE:testing"
+  - docker run -d --name cont "$CI_REGISTRY_IMAGE:testing"
+  - docker exec cont daemon/build/test/jsonCommanderTest
     
+cli-cmdman_test:
+  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:testing"
+  - docker run -d --name cont "$CI_REGISTRY_IMAGE:testing"
+  - docker exec cont cli/build/test/cmdman_test
+  
+cli-logintest:
+  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:testing"
+  - docker run -d --name cont "$CI_REGISTRY_IMAGE:testing"
+  - docker exec cont cli/build/test/logintest_pos.sh
+  - docker exec cont cli/build/test/logintest_neg.sh
+  
+cli-cryptotest:
+  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:testing"
+  - docker run -d --name cont "$CI_REGISTRY_IMAGE:testing"
+  - docker exec cont cli/build/test/cryptoput.sh /root/build/files
+  - docker exec cont cli/build/test/cryptoget.sh /root/build/files
+  

+ 5 - 5
Dockerfile

@@ -10,7 +10,7 @@ RUN wget https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.
     && rm boost_1_72_0.tar.gz \
     && cd boost_1_72_0 \
     && ./bootstrap.sh --prefix=/usr/local \
-    && ./b2 install \
+    && ./b2 -j$(nproc) install \
     && cd .. \
     && rm -rf boost_1_72_0
 
@@ -18,14 +18,14 @@ RUN wget https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.tar.
 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 \
+    && cmake ../ -DLIBTINS_ENABLE_CXX11=1 && make -j$(nproc) && 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 \
+    && cmake .. && make -j$(nproc) && make install \
     && cd ../.. && rm -rf googletest
 
 RUN mkdir /root/build/files \
@@ -33,7 +33,7 @@ RUN mkdir /root/build/files \
     && 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
+    && echo "filedirectory=./files/" >> /root/build/config.txt
 
 # Copy all required data into image
 COPY .cmake_modules/ /root/.cmake_modules
@@ -43,7 +43,7 @@ COPY /daemon/test /root/build/test
 COPY /daemon/CMakeLists.txt /root/build
 
 # Compile ccats with tests
-RUN cmake -DENABLE_TESTS=true . && make \
+RUN cmake . && make -j$(nproc) \
     && rm -rf src include CMakeFiles \
     && rm Makefile CMakeCache.txt cmake_install.cmake CMakeLists.txt \
     CTestTestfile.cmake

+ 69 - 0
Dockerfile.testing

@@ -0,0 +1,69 @@
+FROM debian
+
+RUN apt-get update -y && apt-get install -y \
+    build-essential git cmake libpcap-dev libjsoncpp-dev wget libssl-dev libreadline-dev pkg-config
+
+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 -j$(nproc) 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 -j$(nproc) && 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 -j$(nproc) && make install \
+    && cd ../.. && rm -rf googletest
+
+# Copy all required data into image
+COPY .cmake_modules/ /root/build/.cmake_modules
+RUN mkdir -p /root/build/daemon/build /root/build/cli/build
+
+# Daemon
+COPY /daemon/include	/root/build/daemon/include
+COPY /daemon/src	/root/build/daemon/src
+COPY /daemon/test	/root/build/daemon/test
+COPY /daemon/CMakeLists.txt /root/build/daemon/
+
+# CLI
+COPY /cli/include	/root/build/cli/include
+COPY /cli/src		/root/build/cli/src
+COPY /cli/test		/root/build/cli/test
+COPY /cli/CMakeLists.txt /root/build/cli/
+
+# Create Daemon config
+# must be located in WORKDIR because running the container will set this as default dir, so all other scripts are also run from there
+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 \
+    && echo "activateCovertChannel=false" >> /root/build/config.txt
+
+# Compile daemon
+RUN cd daemon/build && cmake -DENABLE_TESTS=true .. && make -j$(nproc) \
+    && rm -rf ../src ../include ../test ../CMakeLists.txt \
+    && rm -rf CMakeFiles Makefile CMakeCache.txt cmake_install.cmake \
+    CTestTestfile.cmake
+
+# Compile ccats with tests
+RUN cd cli/build && cmake -DENABLE_TESTS=true .. && make -j$(nproc) \
+    && rm -rf ../src ../include ../test ../CMakeLists.txt \
+    && rm -rf CMakeFiles Makefile CMakeCache.txt cmake_install.cmake \
+    CTestTestfile.cmake
+
+ENTRYPOINT /root/build/daemon/build/bin/ccats

+ 7 - 11
cli/CMakeLists.txt

@@ -1,21 +1,17 @@
 cmake_minimum_required(VERSION 2.8)
 
-# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/../.cmake_modules/")
 
 project(ccats-cli)
 
-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)
-pkg_check_modules(READLINE REQUIRED readline)
-pkg_check_modules(JSONCPP REQUIRED jsoncpp)
-
 find_package(Threads)
 find_package(OpenSSL REQUIRED)
 find_package(Boost 1.70 REQUIRED COMPONENTS system program_options)
+find_package(Jsoncpp REQUIRED)
 
+include(src/CMakeLists.txt)
 
-include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} include)
-
-target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${READLINE_LIBRARIES} ${JSONCPP_LIBRARIES})
+if(ENABLE_TESTS)
+  include(test/CMakeLists.txt)
+endif()

+ 9 - 8
cli/include/cmdman.h

@@ -63,6 +63,15 @@ public:
 
 	void stateSetConnectionOk();
 
+protected:
+	/**
+	 * State used to internally format received json to allow easy handling using
+	 * handlemap and to disallow the use of certain commands when logged in/not
+	 * logged in.
+	 */
+	enum state { connectionpossible, versionpossible, doversion, loginpossible, dologin, dosignup, normal, disconnecttoexit, disconnecttoexitearly };
+	state currentState;
+
 private:
 	/**
 	 * internal json writer and error string member
@@ -88,14 +97,6 @@ private:
 	 */
 	map<string, string> helpmap;
 
-	/**
-	 * State used to internally format received json to allow easy handling using
-	 * handlemap and to disallow the use of certain commands when logged in/not
-	 * logged in.
-	 */
-	enum state { connectionpossible, versionpossible, doversion, loginpossible, dologin, dosignup, normal, disconnecttoexit, disconnecttoexitearly };
-	state currentState;
-
 	/**
 	 * Help strings and method prototypes for commands to be used by a user
 	 */

+ 34 - 34
cli/include/fileman.h

@@ -77,19 +77,19 @@ public:
 	 *
 	 * Return true if the corresponding action is being performed, false otherwise
 	 */
-	bool isGetting();
-	bool isPutting();
-	bool isListing();
-	bool isEncrypted();
+	virtual bool isGetting();
+	virtual bool isPutting();
+	virtual bool isListing();
+	virtual bool isEncrypted();
 
 	/**
 	 * Check for and prepare state and streams for reading/writing
 	 *
 	 * Return true if successful, false otherwise
 	 */
-	bool openPut(const std::string &path);
-	bool openGet(const std::string &path);
-	bool openList();
+	virtual bool openPut(const std::string &path);
+	virtual bool openGet(const std::string &path);
+	virtual bool openList();
 
 	/**
 	 * Open file and read a hex string from it as key and initialize the IV
@@ -97,14 +97,14 @@ public:
 	 *
 	 * Return true if successful, false otherwise. After this put and get will en/decrypt data
 	 */
-	bool openKey(const std::string &path);
+	virtual bool openKey(const std::string &path);
 
 	/**
 	 * Close the respective filestream
 	 */
-	void closePut();
-	void closeGet();
-	void closeList();
+	virtual void closePut();
+	virtual void closeGet();
+	virtual void closeList();
 
 	/**
 	 * Reset internal key state and disable en/decryption of data
@@ -112,68 +112,68 @@ public:
 	 *
 	 * Return true if key was reset, false otherwise. After this put and get will use unencrypted data
 	 */
-	bool closeKey();
+	virtual bool closeKey();
 
 	/**
 	 * Query the names of the file currently being put or get
 	 */
-	std::string getPutName();
-	std::string getGetName();
+	virtual std::string getPutName();
+	virtual std::string getGetName();
 
 	/**
 	 * Cancel a put, get or list, depreparing internal state (closing streams if
 	 * required)
 	 */
-	void cancelPut();
-	void cancelGet();
-	void cancelList();
+	virtual void cancelPut();
+	virtual void cancelGet();
+	virtual void cancelList();
 
 	/**
 	 * Read max_rea_len bytes from the current file opened for put
 	 */
-	std::vector<char> readPut();
+	virtual std::vector<char> readPut();
 	/**
 	 * Write the provided vector to the current file opened for get
 	 */
-	void writeGet(std::vector<char> data);
+	virtual void writeGet(std::vector<char> data);
 
 	/**
 	 * Wrapper methods for reading and writing base64 encoded data instead of raw
 	 * bytes
 	 */
-	std::string readBase64();
-	void writeBase64(std::string data);
+	virtual std::string readBase64();
+	virtual void writeBase64(std::string data);
 
 	/**
 	 * read and write emulating methods for list
 	 */
-	void putListData(std::vector<std::string> names);
-	std::vector<std::string> getListData();
+	virtual void putListData(std::vector<std::string> names);
+	virtual std::vector<std::string> getListData();
 
 	/**
 	 * Query internal state, requesting the corresponding size
 	 */
-	int getPutChunks();
-	int getGetChunks();
-	int getListChunks();
-	int getPutRemainingChunks();
-	int getGetRemainingChunks();
-	int getListRemainingChunks();
-	int getPutSize();
+	virtual int getPutChunks();
+	virtual int getGetChunks();
+	virtual int getListChunks();
+	virtual int getPutRemainingChunks();
+	virtual int getGetRemainingChunks();
+	virtual int getListRemainingChunks();
+	virtual int getPutSize();
 
 	/**
 	 * Set internal state, adjusting the chunks as well as chunks remaining for
 	 * get and list
 	 */
-	void setGetChunks(int chunks);
-	void setListChunks(int chunks);
+	virtual void setGetChunks(int chunks);
+	virtual void setListChunks(int chunks);
 
 	/**
 	 * Returns the filename of the passed (relative) path of a file
 	 */
-	std::string pathToFilename(std::string path);
+	virtual std::string pathToFilename(std::string path);
 
-	std::string getOpensslError();
+	virtual std::string getOpensslError();
 };
 
 #endif

+ 24 - 23
cli/include/ioman.h

@@ -59,8 +59,32 @@ protected:
 	virtual void printWelcomeMessage() = 0;
 	virtual std::string getCmdPrompt() = 0;
 
+	/**
+	 * The IP and port to connect to
+	 * Flag telling wether one is connected
+	 */
+	std::string ipstring;
+	unsigned short port;
 	bool connected;
 
+	/**
+	 * Thread handles for processing local and network input as well as generating
+	 * responses to both Matching mutexes for the flags wether the threads should
+	 * keep running Function prototypes for the main thread functions
+	 */
+	std::thread tinput, tnetwork, tresponse;
+	std::mutex inputmutex, networkmutex, responsemutex;
+	bool runinput, runnetwork, runresponse;
+	void networkMain();
+	void inputMain();
+	void responseMain();
+
+	/**
+	 * Instances of CmdMan and FileMan to process user input and handle File I/O
+	 */
+	CmdMan cmdman;
+	FileMan fileman;
+
 	virtual void handleInCmdResponse(CmdMan::CmdRet cmdret);
 	virtual void handleOutCmdResponse(CmdMan::CmdRet cmdret, vector<string> &toput);
 
@@ -75,17 +99,6 @@ private:
 	boost::asio::io_service ios;
 	boost::asio::streambuf recvbuf;
 	boost::asio::ssl::context *sslctx;
-	/**
-	 * The IP and port to connect to
-	 * Flag telling wether one is connected
-	 */
-	std::string ipstring;
-	unsigned short port;
-	/**
-	 * Instances of CmdMan and FileMan to process user input and handle File I/O
-	 */
-	CmdMan cmdman;
-	FileMan fileman;
 
 	/**
 	 * Class-wide json functionality
@@ -94,18 +107,6 @@ private:
 	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
-	 * keep running Function prototypes for the main thread functions
-	 */
-	std::thread tinput, tnetwork, tresponse;
-	std::mutex inputmutex, networkmutex, responsemutex;
-	bool runinput, runnetwork, runresponse;
-	void networkMain();
-	void inputMain();
-	void responseMain();
-
 	/**
 	 * Vector to hold preprocessed input from the network
 	 * Matching condition variable to wake up waiting threads

+ 9 - 0
cli/src/CMakeLists.txt

@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 2.8)
+
+find_package(Readline REQUIRED)
+
+include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS} ${Readline_INCLUDE_DIR} include)
+
+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)
+
+target_link_libraries(ccats-cli PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${Readline_LIBRARY} ${JSONCPP_LIBRARIES})

+ 42 - 10
cli/src/batchioman.cpp

@@ -30,7 +30,7 @@ BatchIoMan::BatchIoMan(bool usessl, bool beverbose, std::string batchpath) : IoM
 	printmap["keyfile"] = &BatchIoMan::printKeyfile;
 	printmap["closekey"] = &BatchIoMan::printClosekey;
 
-	getnextline = false;
+	getnextline = true;
 	verbose = beverbose;
 	filepath = batchpath;
 }
@@ -150,12 +150,24 @@ void BatchIoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
 		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 */
+	if (cmdret.type & CmdMan::rettype::connect) {
+		ipstring = cmdret.msg["address"].asString();
+		port = cmdret.msg["port"].asUInt();
+		if (connect()) {
+			runnetwork = true;
+			tnetwork = std::thread(&BatchIoMan::networkMain, this);
+
+			// put new commands into global vector
+			localmutex.lock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
+			localinput.push_back("version");
+			cmdman.stateSetConnectionOk();
+			localmutex.unlock();
+			printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
+			localcv.notify_all();
+		}
+	}
+	if (cmdret.type & CmdMan::rettype::exit) {
 		mainmutex.lock();
 		runmain = false;
 		mainmutex.unlock();
@@ -167,6 +179,29 @@ void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::st
 		linecv.notify_all();
 		return;
 	}
+	if (cmdret.nextcommand.size()) {
+		localmutex.lock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
+		localinput.push_back(cmdret.nextcommand);
+		localmutex.unlock();
+		printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
+		localcv.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) {
+		// connection closed, stop network thread and shutdown any operations remaining
+		networkmutex.lock();
+		runnetwork = false;
+		networkmutex.unlock();
+		disconnect();
+		tnetwork.join();
+		if (cmdret.nextcommand.size()) {
+			toput.push_back(cmdret.nextcommand);
+		}
+	}
 	if (cmdret.type & CmdMan::rettype::error) {
 		printMessage(Json::writeString(wbuilder, cmdret.msg), error);
 		mainmutex.lock();
@@ -240,9 +275,6 @@ void BatchIoMan::run() {
 		printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
 		localcv.notify_all();
 
-		if (!connected)
-			break;
-
 		mainmutex.lock();
 	}
 	mainmutex.unlock();

+ 77 - 36
cli/src/cmdman.cpp

@@ -145,13 +145,14 @@ CmdMan::CmdRet CmdMan::cmdPut(vector<string> args) {
 			root["error"] = "already putting file \"" + fileman.getPutName() + "\"";
 		} else {
 			bool opened = fileman.openPut(args[0]);
-			root["file"] = fileman.getPutName();
 			if (opened) {
+				root["file"] = fileman.getPutName();
 				root["size"] = fileman.getPutSize();
 				root["chunks"] = fileman.getPutChunks();
 				retval.type = send;
 			} else {
 				retval.type = error;
+				root["file"] = fileman.pathToFilename(args[0]);
 				root["accept"] = false;
 				root["error"] = "couldnt open local file \"" + args[0] + "\"";
 			}
@@ -168,11 +169,16 @@ CmdMan::CmdRet CmdMan::cmdPutdata(vector<string> args) {
 	Json::Value root;
 
 	root["command"] = "putdata";
-	root["file"] = fileman.getPutName();
-	root["cancel"] = false;
-	root["data"] = fileman.readBase64();
-	root["remaining"] = fileman.getPutRemainingChunks(); // number already decremented by readBase64
-	retval.type = send;
+	if (!fileman.isPutting()) {
+		root["error"] = "Client cannot handle input (received command \"putdata\").";
+		retval.type = error;
+	} else {
+		root["file"] = fileman.getPutName();
+		root["cancel"] = false;
+		root["data"] = fileman.readBase64();
+		root["remaining"] = fileman.getPutRemainingChunks(); // number already decremented by readBase64
+		retval.type = send;
+	}
 	retval.msg = root;
 
 	return retval;
@@ -189,6 +195,7 @@ CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
 		root["accept"] = false;
 		root["error"] = "not enough arguments, at least 1 argument required";
 	} else {
+
 		if (fileman.isGetting()) {
 			retval.type = error;
 			root["file"] = args[0];
@@ -198,11 +205,13 @@ CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
 			bool opened = fileman.openGet(args[0]);
 			root["file"] = fileman.getGetName();
 			if (opened) {
+				root["file"] = fileman.getGetName();
 				retval.type = send;
 			} else {
-				retval.type = error;
+				root["file"] = fileman.pathToFilename(args[0]);
 				root["accept"] = false;
 				root["error"] = "local file \"" + args[0] + "\" already exists";
+				retval.type = error;
 			}
 		}
 	}
@@ -217,10 +226,15 @@ CmdMan::CmdRet CmdMan::cmdGetdata(vector<string> args) {
 	Json::Value root;
 
 	root["command"] = "getdata";
-	root["file"] = fileman.getGetName();
-	root["chunk"] = fileman.getGetRemainingChunks();
-	root["cancel"] = false;
-	retval.type = send;
+	if (!fileman.isGetting()) {
+		root["error"] = "Client cannot handle input (received command \"getdata\").";
+		retval.type = error;
+	} else {
+		root["file"] = fileman.getGetName();
+		root["chunk"] = fileman.getGetRemainingChunks();
+		root["cancel"] = false;
+		retval.type = send;
+	}
 	retval.msg = root;
 
 	return retval;
@@ -250,12 +264,18 @@ CmdMan::CmdRet CmdMan::cmdListdata(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "listdata";
-	root["chunk"] = fileman.getListRemainingChunks();
-	root["cancel"] = false;
-	retval.type = send;
-	retval.msg = root;
+	if (!fileman.isListing()) {
+		root["command"] = "list";
+		root["error"] = "Client cannot handle input (received command \"listdata\").";
+		retval.type = error;
+	} else {
+		root["command"] = "listdata";
+		root["chunk"] = fileman.getListRemainingChunks();
+		root["cancel"] = false;
+		retval.type = send;
+	}
 
+	retval.msg = root;
 	return retval;
 }
 
@@ -605,6 +625,9 @@ CmdMan::CmdRet CmdMan::handlePut(Json::Value root) {
 		retval.type = error;
 		output["error"] = "Server reports: " + root["error"].asString();
 		fileman.cancelPut();
+	} else if (!fileman.isPutting()) {
+		retval.type = error;
+		output["error"] = "Server responds to put message which was never sent.";
 	} else if (root["file"].asString() != fileman.getPutName()) {
 		retval.type = error;
 		output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getPutName();
@@ -629,7 +652,14 @@ CmdMan::CmdRet CmdMan::handlePutdata(Json::Value root) {
 	output["speed"] = 0.0f; // TODO
 	output["cancel"] = true;
 
-	if (root["received"].asInt() != fileman.getPutRemainingChunks()) {
+	if (root["cancel"].asBool()) {
+		retval.type = error;
+		output["error"] = "Server reports: " + root["error"].asString();
+		fileman.cancelPut();
+	} else if (!fileman.isPutting()) {
+		retval.type = error;
+		output["error"] = "Server responds to put message which was never sent.";
+	} else if (root["received"].asInt() != fileman.getPutRemainingChunks()) {
 		// the number of remaining chunks received from the daemon does not equal
 		// the number stored at the client side
 		retval.type = error;
@@ -637,10 +667,6 @@ CmdMan::CmdRet CmdMan::handlePutdata(Json::Value root) {
 		                              "remaining chunks as ") +
 		                  std::to_string(root["received"].asInt()) + " but actual number is " + std::to_string(fileman.getPutRemainingChunks());
 		fileman.cancelPut();
-	} else if (root["cancel"].asBool()) {
-		retval.type = error;
-		output["error"] = "Server reports: " + root["error"].asString();
-		fileman.cancelPut();
 	} else if (root["file"].asString() != fileman.getPutName()) {
 		retval.type = error;
 		output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getPutName();
@@ -675,9 +701,14 @@ CmdMan::CmdRet CmdMan::handleGet(Json::Value root) {
 	if (!root["accept"].asBool()) {
 		retval.type = error;
 		output["error"] = "Server reports: " + root["error"].asString();
+		fileman.cancelGet();
+	} else if (!fileman.isGetting()) {
+		retval.type = error;
+		output["error"] = "Server responds to get message which was never sent.";
 	} else if (root["file"].asString() != fileman.getGetName()) {
 		retval.type = error;
 		output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getGetName();
+		fileman.cancelGet();
 	} else {
 		fileman.setGetChunks(root["chunks"].asInt());
 		output["accept"] = true;
@@ -699,15 +730,17 @@ CmdMan::CmdRet CmdMan::handleGetdata(Json::Value root) {
 	output["speed"] = 0.0f; // TODO
 	output["cancel"] = true;
 
-	// the passed number of recieved chunks should equal the number of sent chunks
-	if (root["remaining"].asInt() != fileman.getGetRemainingChunks()) {
+	if (root["cancel"].asBool()) {
 		retval.type = error;
-		output["error"] = std::string("Server reports number of remaining chunks as ") + std::to_string(root["remaining"].asInt()) + " but actual number is " +
-		                  std::to_string(fileman.getGetRemainingChunks());
+		output["error"] = "Server reports: " + root["error"].asString();
 		fileman.cancelGet();
-	} else if (root["cancel"].asBool()) {
+	} else if (!fileman.isGetting()) {
 		retval.type = error;
-		output["error"] = "Server reports: " + root["error"].asString();
+		output["error"] = "Server responds to get message which was never sent.";
+	} else if (root["remaining"].asInt() != fileman.getGetRemainingChunks()) {
+		retval.type = error;
+		output["error"] = std::string("Server reports number of remaining chunks as ") + std::to_string(root["remaining"].asInt()) + " but actual number is " +
+		                  std::to_string(fileman.getGetRemainingChunks());
 		fileman.cancelGet();
 	} else if (root["file"].asString() != fileman.getGetName()) {
 		retval.type = error;
@@ -718,8 +751,8 @@ CmdMan::CmdRet CmdMan::handleGetdata(Json::Value root) {
 		output["error"] = "";
 		fileman.writeBase64(root["data"].asString());
 		// loaded successfully
-		if (fileman.getGetRemainingChunks() < 0) {
-			// everything sent
+		if (!root["remaining"].asInt()) {
+			// everything received
 			retval.type = print;
 			//~ retval.msg = "succesfully downloaded file " + fileman.getGetName();
 			fileman.closeGet();
@@ -745,6 +778,10 @@ CmdMan::CmdRet CmdMan::handleList(Json::Value root) {
 		output["accept"] = false;
 		output["error"] = "Server reports: " + root["error"].asString();
 		fileman.cancelList();
+	} else if (!fileman.isListing()) {
+		retval.type = error;
+		output["accept"] = false;
+		output["error"] = "Server responds to list message which was never sent.";
 	} else {
 		fileman.setListChunks(root["chunks"].asInt());
 		retval.type = send;
@@ -765,24 +802,28 @@ CmdMan::CmdRet CmdMan::handleListdata(Json::Value root) {
 	output["command"] = "list";
 	output["names"] = "";
 	output["accept"] = false;
-	// the passed number of recieved chunks should equal the number of sent chunks
-	if (root["remaining"].asInt() != fileman.getListRemainingChunks()) {
+
+	if (root["cancel"].asBool()) {
+		retval.type = error;
+		output["error"] = "Server reports: " + root["error"].asString();
+		fileman.cancelList();
+	} else if (!fileman.isListing()) {
+		retval.type = error;
+		output["error"] = "Server responds to list message which was never sent.";
+	} else if (root["remaining"].asInt() != fileman.getListRemainingChunks()) {
+		// the passed number of recieved chunks should equal the number of sent chunks
 		retval.type = error;
 		output["error"] = std::string("Server reports number of "
 		                              "remaining chunks as ") +
 		                  std::to_string(root["remaining"].asInt()) + " but actual number is " + std::to_string(fileman.getListRemainingChunks());
 		fileman.cancelList();
-	} else if (root["cancel"].asBool()) {
-		retval.type = error;
-		output["error"] = "Server reports: " + root["error"].asString();
-		fileman.cancelList();
 	} else {
 		output["accept"] = true;
 		for (Json::Value i : root["names"])
 			toadd.push_back(i.asString());
 		fileman.putListData(toadd);
 		// loaded successfully
-		if (fileman.getListRemainingChunks() < 0) {
+		if (root["remaining"] <= 0) {
 			// everything sent
 			retval.type = print;
 			for (string s : fileman.getListData())

+ 8 - 7
cli/src/ioman.cpp

@@ -156,7 +156,8 @@ bool IoMan::connect() {
 		root["error"] = errcode.message();
 		connected = false;
 	} else {
-		if(!ios.stopped()) ios.stop();
+		if (!ios.stopped())
+			ios.stop();
 		ios.restart();
 		// establish connection
 		printMessage(string(__PRETTY_FUNCTION__) + string(" connecting to ") + ipstring, debug);
@@ -254,13 +255,13 @@ void IoMan::networkMain() {
 		printMessage(string(__PRETTY_FUNCTION__) + string(" asio::read() ok ") + std::to_string(readsize), debug);
 		// printMessage(string("have ") + std::to_string(toprocess.size()) +
 		// string(" commands"), debug);
-		if (errcode && errcode != boost::asio::error::eof) {
-			printMessage("IoMan::networkMain() couldnt read json data\n" + errcode.message(), error);
-			continue;
-		}
 		if (readsize < 1) {
 			break;
 		}
+		if (errcode && errcode != boost::asio::error::eof) {
+			printMessage("IoMan::networkMain() couldnt read json data\n" + errcode.message(), debug);
+			continue;
+		}
 		recvjson = (char *)(boost::asio::buffer_cast<const char *>(recvbuf.data()));
 		recvjson[readsize] = 0;
 		while (strchr(recvjson, '\n')) {
@@ -270,7 +271,7 @@ void IoMan::networkMain() {
 			printMessage(string(__PRETTY_FUNCTION__) + string(" found jsondata ") + string(recvjson), debug);
 
 			if (!reader->parse(recvjson, recvjson + jsonsize, &root, &jsonerror)) {
-				printMessage("IoMan::networkMain() couldnt parse json data: " + jsonerror, error);
+				printMessage("IoMan::networkMain() couldnt parse json data: " + jsonerror, debug);
 				recvbuf.consume(jsonsize);
 				recvjson += jsonsize;
 				continue;
@@ -370,7 +371,7 @@ void IoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
 		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);
+			printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", debug);
 			return;
 		}
 	}

+ 44 - 0
cli/test/CMakeLists.txt

@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 2.8)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/test)
+
+# Setup testing
+enable_testing()
+
+find_package(GTest REQUIRED)
+find_package(GMock REQUIRED)
+
+find_program(SHELL bash)
+
+include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR})
+
+# Add test cpp file
+add_executable(cmdman_test test/cmdman_test.cpp src/cmdman.cpp src/fileman.cpp src/base64.cpp)
+target_link_libraries(cmdman_test ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} ${JSONCPP_LIBRARIES} ${GMOCK_LIBRARIES} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY})
+
+add_executable(cryptotest_gcm test/cryptotest_gcm.c)
+target_link_libraries(cryptotest_gcm ${OPENSSL_LIBRARIES})
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/samplekey1.bin ${CMAKE_BINARY_DIR}/test/samplekey1.bin COPYONLY)
+
+add_test(logintest_pos ${SHELL} ${CMAKE_BINARY_DIR}/test/logintest_pos.sh)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/logintest_pos.sh ${CMAKE_BINARY_DIR}/test/logintest_pos.sh COPYONLY)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/logintest_pos.txt ${CMAKE_BINARY_DIR}/test/logintest_pos.txt COPYONLY)
+add_test(logintest_neg ${SHELL} ${CMAKE_BINARY_DIR}/test/logintest_neg.sh)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/logintest_neg.sh ${CMAKE_BINARY_DIR}/test/logintest_neg.sh COPYONLY)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/logintest_neg.txt ${CMAKE_BINARY_DIR}/test/logintest_neg.txt COPYONLY)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/samplefile.txt ${CMAKE_BINARY_DIR}/test/samplefile.txt COPYONLY)
+
+add_test(cryptoput ${SHELL} ${CMAKE_BINARY_DIR}/test/cryptoput.sh)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/cryptoput.sh ${CMAKE_BINARY_DIR}/test/cryptoput.sh COPYONLY)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/cryptoput.txt ${CMAKE_BINARY_DIR}/test/cryptoput.txt COPYONLY)
+
+add_test(cryptoget ${SHELL} ${CMAKE_BINARY_DIR}/test/cryptoget.sh)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/cryptoget.sh ${CMAKE_BINARY_DIR}/test/cryptoget.sh COPYONLY)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/test/cryptoget.txt ${CMAKE_BINARY_DIR}/test/cryptoget.txt COPYONLY)
+
+add_test(
+  NAME cmdman_test
+  COMMAND cmdman_test
+)

+ 31 - 0
cli/test/CmdManForTest.cpp

@@ -0,0 +1,31 @@
+#include "../include/cmdman.h"
+
+class CmdManForTest : public CmdMan {
+public:
+	CmdManForTest(FileMan &fm, void (*dpf)(string)) : CmdMan(fm, dpf) {}
+
+	/*
+	 * initialize state
+	 */
+	void initNotConnected() { currentState = connectionpossible; }
+
+	void initConnected() { currentState = versionpossible; }
+
+	void initVersionChecked() { currentState = loginpossible; }
+
+	void initLoggedIn() { currentState = normal; }
+
+	/*
+	 * check state
+	 */
+
+	bool isNotConnected() { return currentState == connectionpossible; }
+
+	bool isConnected() { return currentState == versionpossible; }
+
+	bool isVersionChecked() { return currentState == loginpossible; }
+
+	bool isLoggedIn() { return currentState == normal; }
+
+	// connectionpossible, versionpossible, doversion, loginpossible, dologin, dosignup, normal, disconnecttoexit, disconnecttoexitearly
+};

+ 2412 - 0
cli/test/cmdman_test.cpp

@@ -0,0 +1,2412 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "../include/cmdman.h"
+#include "../include/global.h"
+#include "fileman_mock.h"
+
+#include "CmdManForTest.cpp"
+
+/* template
+
+TEST(testVersion, Positive) {
+        FileManMock fm;
+        CmdManForTest cm(fm, dummyDebugPrint);
+
+        std::string cmd;
+        std::vector<std::string> args;
+        Json::Value root;
+        CmdMan::CmdRet retval;
+
+        // prepare root/cmd/args
+        cmd = "";
+        args = {"", ""};
+        root[""] = "";
+
+        // stick into cmdman
+        cm.execute(cmd, args);
+        cm.handle(root);
+
+        // check things
+        EXPECT_EQ(retval.type, sometypes);
+        EXPECT_EQ(retval.nextcommand, somestring);
+        EXPECT_TRUE(retval.msg[""].asBool());
+        EXPECT_FALSE(retval.msg[""].asBool());
+        ON_CALL(fm, methodname()).WillByDefault(testing::Return(true));
+}
+
+*/
+
+/* holds all tests */
+namespace {
+
+void dummyDebugPrint(string x) {
+	// do nothing
+}
+
+/*
+ * =====================================
+ * tests for connect
+ */
+TEST(testConnect, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "connect";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "connect");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+TEST(testConnect, Positive_NoPortSpecified) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "connect";
+	args = {"1.222.33.4"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	cm.stateSetConnectionOk();
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::connect);
+	EXPECT_EQ(retvalCmd.msg["address"].asString(), "1.222.33.4");
+	EXPECT_EQ(retvalCmd.msg["port"].asString(), "1234");
+
+	EXPECT_TRUE(cm.isConnected());
+}
+
+TEST(testConnect, Positive_PortSpecified) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "connect";
+	args = {"1.222.33.4", "1337"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	cm.stateSetConnectionOk();
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::connect);
+	EXPECT_EQ(retvalCmd.msg["address"].asString(), "1.222.33.4");
+	EXPECT_EQ(retvalCmd.msg["port"].asString(), "1337");
+
+	EXPECT_TRUE(cm.isConnected());
+}
+
+TEST(testConnect, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "connect";
+	args = {"1.222.33.4", "1337"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	// cm.stateSetConnectionOk() not called
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::connect);
+	EXPECT_EQ(retvalCmd.msg["address"].asString(), "1.222.33.4");
+	EXPECT_EQ(retvalCmd.msg["port"].asString(), "1337");
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+/* =====================================
+ * tests for
+ * version check, signup, login
+ */
+
+TEST(testVersion, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initConnected();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "version";
+	args = {};
+	root["major"] = protocolMajorVersion;
+	root["minor"] = protocolMinorVersion;
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["major"], protocolMajorVersion);
+	EXPECT_EQ(retvalCmd.msg["minor"], protocolMinorVersion);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "version");
+	EXPECT_EQ(retvalHdl.msg["serverversion"].asString(), std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion));
+	EXPECT_EQ(retvalHdl.msg["clientversion"].asString(), std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion));
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isVersionChecked());
+}
+
+TEST(testVersion, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initConnected();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+	int servermajor, serverminor;
+
+	// prepare root/cmd/args
+	cmd = "version";
+	args = {};
+	servermajor = (protocolMajorVersion == 0) ? 42 : 0;
+	serverminor = (protocolMinorVersion == 3) ? 42 : 3;
+	root["major"] = servermajor;
+	root["minor"] = serverminor;
+	root["accept"] = false;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["major"], protocolMajorVersion);
+	EXPECT_EQ(retvalCmd.msg["minor"], protocolMinorVersion);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error | CmdMan::close);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "version");
+	EXPECT_EQ(retvalHdl.msg["serverversion"].asString(), std::to_string(servermajor) + "." + std::to_string(serverminor));
+	EXPECT_EQ(retvalHdl.msg["clientversion"].asString(), std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion));
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+TEST(testVersion, AlreadyLoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args
+	cmd = "version";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	// TODO: check some more things maybe
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testLogin, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "login";
+	args = {"usernem", "paswod"};
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_TRUE(retvalCmd.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd.msg["user"].asString(), "usernem");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "paswod");
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "login");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testLogin, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "login";
+	args = {"usernem", "paswod"};
+	root["accept"] = false;
+	root["error"] = "fancy error string";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_TRUE(retvalCmd.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd.msg["user"].asString(), "usernem");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "paswod");
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "login");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("fancy error string"));
+
+	EXPECT_TRUE(cm.isVersionChecked());
+}
+
+TEST(testLogin, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "login";
+	args = {"usernem"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "login");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isVersionChecked());
+}
+
+TEST(testLogin, AlreadyLoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare root/cmd/args
+	cmd = "login";
+	args = {"usernem", "paswod"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "login");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testLogin, TwoRequestsInRow) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd1;
+	CmdMan::CmdRet retvalCmd2;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "login";
+	args = {"usernem", "paswod"};
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd1 = cm.execute(cmd, args);
+	retvalCmd2 = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd1.type, CmdMan::send);
+	EXPECT_TRUE(retvalCmd1.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd1.msg["user"].asString(), "usernem");
+	EXPECT_EQ(retvalCmd1.msg["pass"].asString(), "paswod");
+	EXPECT_FALSE(retvalCmd1.msg["cancel"].asBool());
+
+	EXPECT_EQ(retvalCmd2.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd2.msg["command"].asString(), "login");
+	EXPECT_FALSE(retvalCmd2.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd2.msg["error"].asString(), "");
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "login");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testSignup, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "signup";
+	args = {"usernem", "paswod"};
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_FALSE(retvalCmd.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd.msg["user"].asString(), "usernem");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "paswod");
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "signup");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testSignup, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "signup";
+	args = {"usernem", "paswod"};
+	root["accept"] = false;
+	root["error"] = "fancy error string";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_FALSE(retvalCmd.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd.msg["user"].asString(), "usernem");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "paswod");
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "signup");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("fancy error string"));
+
+	EXPECT_TRUE(cm.isVersionChecked());
+}
+
+TEST(testSignup, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "signup";
+	args = {"usernem"};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "signup");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isVersionChecked());
+}
+
+/*
+ * =====================================
+ * tests for disconnect and exit
+ */
+TEST(testDisconnect, LoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "disconnect";
+	args = {};
+	root["command"] = "close";
+	root["response"] = "bye";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "close");
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::close | CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "disconnect");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+TEST(testDisconnect, NotLoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "disconnect";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send | CmdMan::close);
+	EXPECT_FALSE(retvalCmd.msg["login"].asBool());
+	EXPECT_EQ(retvalCmd.msg["user"].asString(), "");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "");
+	EXPECT_TRUE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+TEST(testExit, LoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmdExit;
+	CmdMan::CmdRet retvalCmdDisconnect;
+	CmdMan::CmdRet retvalHdl;
+	CmdMan::CmdRet retvalLastExit;
+
+	// prepare root/cmd/args
+	cmd = "exit";
+	args = {};
+	root["command"] = "close";
+	root["response"] = "bye";
+
+	// stick into cmdman and check things
+	retvalCmdExit = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmdExit.type, CmdMan::none);
+	EXPECT_EQ(retvalCmdExit.nextcommand, "disconnect");
+
+	retvalCmdDisconnect = cm.execute("disconnect", args);
+
+	EXPECT_EQ(retvalCmdDisconnect.type, CmdMan::send);
+	EXPECT_EQ(retvalCmdDisconnect.msg["command"].asString(), "close");
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::close | CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "disconnect");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.nextcommand, "exit");
+
+	EXPECT_TRUE(cm.isNotConnected());
+
+	retvalLastExit = cm.execute("exit", args);
+
+	EXPECT_EQ(retvalLastExit.type, CmdMan::exit);
+	EXPECT_EQ(retvalLastExit.msg["command"].asString(), "exit");
+}
+
+TEST(testExit, NotLoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initVersionChecked();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmdExit;
+	CmdMan::CmdRet retvalCmdDisconnect;
+	CmdMan::CmdRet retvalHdl;
+	CmdMan::CmdRet retvalLastExit;
+
+	// prepare root/cmd/args
+	cmd = "exit";
+	args = {};
+	root["command"] = "close";
+	root["response"] = "bye";
+
+	// stick into cmdman and check things
+	retvalCmdExit = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmdExit.type, CmdMan::none);
+	EXPECT_EQ(retvalCmdExit.nextcommand, "disconnect");
+
+	retvalCmdDisconnect = cm.execute("disconnect", args);
+
+	EXPECT_EQ(retvalCmdDisconnect.type, CmdMan::send | CmdMan::close);
+	EXPECT_FALSE(retvalCmdDisconnect.msg["login"].asBool());
+	EXPECT_EQ(retvalCmdDisconnect.msg["user"].asString(), "");
+	EXPECT_EQ(retvalCmdDisconnect.msg["pass"].asString(), "");
+	EXPECT_TRUE(retvalCmdDisconnect.msg["cancel"].asBool());
+	EXPECT_EQ(retvalCmdDisconnect.nextcommand, "exit");
+
+	EXPECT_TRUE(cm.isNotConnected());
+
+	retvalLastExit = cm.execute("exit", args);
+
+	EXPECT_EQ(retvalLastExit.type, CmdMan::exit);
+	EXPECT_EQ(retvalLastExit.msg["command"].asString(), "exit");
+}
+
+TEST(testExit, NotConnected) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare root/cmd/args
+	cmd = "exit";
+	args = {};
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::exit);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "exit");
+}
+
+/* =====================================
+ * tests for put[data]
+ */
+
+TEST(testPut, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "put";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "put");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPut, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "put";
+	args = {"../some/nice/filepath/filename.txt"};
+	root["command"] = "put";
+	root["file"] = "filename.txt";
+	root["accept"] = true;
+
+	EXPECT_CALL(fm, openPut(testing::_)).WillOnce(testing::Return(true));
+	ON_CALL(fm, getPutSize()).WillByDefault(testing::Return(5));
+	ON_CALL(fm, getPutChunks()).WillByDefault(testing::Return(1));
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+
+	EXPECT_CALL(fm, cancelGet).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "put");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalCmd.msg["size"].asInt(), 5);
+	EXPECT_EQ(retvalCmd.msg["chunks"].asInt(), 1);
+
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print | CmdMan::send);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "put");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.nextcommand, "putdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Positive_LastChunk) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	/**logically continuation of TEST(testPut, Positive)**/
+
+	// prepare root/cmd/args
+	cmd = "putdata";
+	args = {};
+	root["command"] = "putdata";
+	root["file"] = "filename.txt";
+	root["received"] = 0;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, readBase64()).WillByDefault(testing::Return("cool Base64 string"));
+	ON_CALL(fm, getPutRemainingChunks()).WillByDefault(testing::Return(0));
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelPut).Times(0); // not called
+	EXPECT_CALL(fm, closePut);
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "putdata");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalCmd.msg["data"].asString(), "cool Base64 string");
+	EXPECT_EQ(retvalCmd.msg["remaining"].asInt(), 0);
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	EXPECT_FALSE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["speed"].asInt(), 0);
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Positive_ChunksRemaining) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "putdata";
+	args = {};
+	root["command"] = "putdata";
+	root["file"] = "filename.txt";
+	root["received"] = 42;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, readBase64()).WillByDefault(testing::Return("cool Base64 string"));
+	ON_CALL(fm, getPutRemainingChunks()).WillByDefault(testing::Return(42));
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelGet).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "putdata");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalCmd.msg["data"].asString(), "cool Base64 string");
+	EXPECT_EQ(retvalCmd.msg["remaining"].asInt(), 42);
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print | CmdMan::send);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	EXPECT_FALSE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["speed"].asInt(), 0);
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.msg["error"].asString(), "");
+	EXPECT_EQ(retvalHdl.nextcommand, "putdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPut, Negative_FileNotExisting) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "put";
+	args = {"../some/nice/filepath/filename.txt"};
+
+	EXPECT_CALL(fm, openPut(testing::_)).WillOnce(testing::Return(false));
+	ON_CALL(fm, pathToFilename("../some/nice/filepath/filename.txt")).WillByDefault(testing::Return("filename.txt"));
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "put");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPut, Negative_ServerError) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "put";
+	root["file"] = "filename.txt";
+	root["accept"] = false;
+	root["error"] = "foobar";
+
+	ON_CALL(fm, getPutSize()).WillByDefault(testing::Return(5));
+	ON_CALL(fm, getPutChunks()).WillByDefault(testing::Return(1));
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+
+	EXPECT_CALL(fm, cancelPut);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "put");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("foobar"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPut, Negative_ServerWrongFile) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "put";
+	root["file"] = "completely_different_filename.txt";
+	root["accept"] = true;
+
+	ON_CALL(fm, getPutSize()).WillByDefault(testing::Return(5));
+	ON_CALL(fm, getPutChunks()).WillByDefault(testing::Return(1));
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fm, cancelPut);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "put");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Negative_ServerError) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "putdata";
+	root["file"] = "filename.txt";
+	root["received"] = 42;
+	root["cancel"] = true;
+	root["error"] = "cool foobar";
+
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getPutRemainingChunks()).WillByDefault(testing::Return(42));
+	ON_CALL(fm, isPutting()).WillByDefault(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_CALL(fm, cancelPut);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("cool foobar"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Negative_ServerWrongFile) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "putdata";
+	root["file"] = "completely_different_filename.txt";
+	root["received"] = 42;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getPutRemainingChunks()).WillByDefault(testing::Return(42));
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelPut);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Negative_ServerWrongChunkNumber) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "putdata";
+	root["file"] = "filename.txt";
+	root["received"] = 23;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getPutName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getPutRemainingChunks()).WillByDefault(testing::Return(42));
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelPut);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPut, Negative_NoFileOpen_ServerMessage) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "put";
+	root["file"] = "filename.txt";
+	root["accept"] = true;
+
+	// handle reply
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(false));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "put");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Negative_NoFileOpen_ServerMessage) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "putdata";
+	root["file"] = "filename.txt";
+	root["received"] = 23;
+	root["cancel"] = false;
+
+	// handle reply
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(false));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "putdata");
+	//	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt"); // no guarantee should be given...
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testPutdata, Negative_NoFileOpen_UserRequest) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	cmd = "putdata";
+	args = {};
+
+	EXPECT_CALL(fm, isPutting()).WillOnce(testing::Return(false));
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "putdata");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for head
+ */
+TEST(testHead, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args
+	cmd = "head";
+	args = {};
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "head");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testHead, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "head";
+	args = {"filename.txt"};
+	root["command"] = "head";
+	root["file"] = "filename.txt";
+	root["accept"] = true;
+	root["data"] = "this string is 4 bytes (obviously)";
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "head");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "head");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.msg["data"].asString(), "this string is 4 bytes (obviously)");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testHead, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "head";
+	args = {"filename.txt"};
+	root["command"] = "head";
+	root["file"] = "filename.txt";
+	root["accept"] = false;
+	root["error"] = "this is a fancy error message";
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "head");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "head");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("this is a fancy error message"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for get[data]
+ */
+
+TEST(testGet, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "get";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "get");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGet, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "get";
+	args = {"../some/nice/filepath/filename.txt"};
+	root["command"] = "get";
+	root["file"] = "filename.txt";
+	root["accept"] = true;
+	root["chunks"] = 42;
+
+	EXPECT_CALL(fm, openGet("../some/nice/filepath/filename.txt")).WillOnce(testing::Return(true));
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+
+	EXPECT_CALL(fm, cancelGet).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "get");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fm, setGetChunks(42));
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print | CmdMan::send);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "get");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.nextcommand, "getdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGetdata, Positive_LastChunk) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	/**logically continuation of TEST(testGet, Positive)**/
+
+	// prepare root/cmd/args
+	cmd = "getdata";
+	args = {};
+	root["command"] = "getdata";
+	root["file"] = "filename.txt";
+	root["remaining"] = 0;
+	root["cancel"] = false;
+	root["data"] = "cool Base64 string";
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	EXPECT_CALL(fm, writeBase64("cool Base64 string"));
+	ON_CALL(fm, getGetRemainingChunks()).WillByDefault(testing::Return(0));
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelGet).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "getdata");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalCmd.msg["chunk"].asInt(), 0);
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "getdata");
+	EXPECT_FALSE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["speed"].asInt(), 0);
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGetdata, Positive_ChunksRemaining) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "getdata";
+	args = {};
+	root["command"] = "getdata";
+	root["file"] = "filename.txt";
+	root["remaining"] = 42;
+	root["cancel"] = false;
+	root["data"] = "cool Base64 string";
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	EXPECT_CALL(fm, writeBase64("cool Base64 string"));
+	ON_CALL(fm, getGetRemainingChunks()).WillByDefault(testing::Return(42));
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelGet).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "getdata");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalCmd.msg["chunk"].asInt(), 42);
+	EXPECT_FALSE(retvalCmd.msg["cancel"].asBool());
+
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print | CmdMan::send);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "getdata");
+	EXPECT_FALSE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["speed"].asInt(), 0);
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_EQ(retvalHdl.msg["error"].asString(), "");
+	EXPECT_EQ(retvalHdl.nextcommand, "getdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGet, Negative_FileAlreadyExisting) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "get";
+	args = {"../some/nice/filepath/filename.txt"};
+
+	EXPECT_CALL(fm, openGet(testing::_)).WillOnce(testing::Return(false));
+	ON_CALL(fm, pathToFilename("../some/nice/filepath/filename.txt")).WillByDefault(testing::Return("filename.txt"));
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "get");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGet, Negative_ServerError) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "get";
+	root["file"] = "filename.txt";
+	root["accept"] = false;
+	root["error"] = "foobar";
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	EXPECT_CALL(fm, cancelGet());
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "get");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGet, Negative_ServerWrongFile) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "get";
+	root["file"] = "completely_different_filename.txt";
+	root["accept"] = true;
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+
+	EXPECT_CALL(fm, isGetting()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fm, cancelGet);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "get");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGetdata, Negative_ServerError) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "getdata";
+	root["file"] = "filename.txt";
+	root["remaining"] = 42;
+	root["cancel"] = true;
+	root["error"] = "fancy foobar";
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getGetRemainingChunks()).WillByDefault(testing::Return(42));
+	ON_CALL(fm, isGetting()).WillByDefault(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_CALL(fm, cancelGet);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "getdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("fancy foobar"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGetdata, Negative_ServerWrongFile) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "getdata";
+	root["file"] = "completely_different_filename.txt";
+	root["remaining"] = 42;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getGetRemainingChunks()).WillByDefault(testing::Return(42));
+	ON_CALL(fm, isGetting()).WillByDefault(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_CALL(fm, cancelGet);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "getdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testGetdata, Negative_ServerWrongChunkNumber) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "getdata";
+	root["file"] = "filename.txt";
+	root["remaining"] = 23;
+	root["cancel"] = false;
+
+	ON_CALL(fm, getGetName()).WillByDefault(testing::Return("filename.txt"));
+	ON_CALL(fm, getGetRemainingChunks()).WillByDefault(testing::Return(42));
+	ON_CALL(fm, isGetting()).WillByDefault(testing::Return(true));
+	retvalHdl = cm.handle(root);
+
+	EXPECT_CALL(fm, cancelGet);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "getdata");
+	EXPECT_TRUE(retvalHdl.msg["cancel"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "filename.txt");
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for list[data]
+ */
+
+TEST(testList, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "list";
+	args = {};
+	root["command"] = "list";
+	root["accept"] = true;
+	root["items"] = 92;
+	root["chunks"] = 2;
+
+	EXPECT_CALL(fm, openList).WillOnce(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelList).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "list");
+
+	EXPECT_CALL(fm, isListing()).WillOnce(testing::Return(true));
+	EXPECT_CALL(fm, setListChunks(2));
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::send);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.nextcommand, "listdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testListdata, Positive_ChunksRemaining) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, namesArr;
+	vector<string> namevec = {"blue.txt", "red.pdf", "green.sh", "yellow.tgz"};
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "listdata";
+	args = {};
+	root["command"] = "listdata";
+	root["cancel"] = false;
+	root["remaining"] = 3;
+	for (string s : namevec)
+		namesArr.append(s);
+	root["names"] = namesArr;
+
+	EXPECT_CALL(fm, getListRemainingChunks).Times(2).WillRepeatedly(testing::Return(3));
+	EXPECT_CALL(fm, isListing).Times(2).WillRepeatedly(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelList).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "listdata");
+	EXPECT_EQ(retvalCmd.msg["chunk"].asInt(), 3);
+	EXPECT_EQ(retvalCmd.msg["cancel"].asBool(), false);
+
+	vector<string> vec{"blue.txt", "red.pdf", "green.sh", "yellow.tgz"};
+	EXPECT_CALL(fm, putListData(vec));
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::send);
+	EXPECT_EQ(retvalHdl.nextcommand, "listdata");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testListdata, Positive_LastChunk) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, namesArr;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "listdata";
+	args = {};
+	root["command"] = "listdata";
+	root["cancel"] = false;
+	root["remaining"] = 0;
+	namesArr.append("magenta.vcd");
+	root["names"] = namesArr;
+
+	EXPECT_CALL(fm, getListRemainingChunks).Times(2).WillRepeatedly(testing::Return(0));
+	EXPECT_CALL(fm, isListing).Times(2).WillRepeatedly(testing::Return(true));
+
+	EXPECT_CALL(fm, cancelList).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "listdata");
+	EXPECT_EQ(retvalCmd.msg["chunk"].asInt(), 0);
+	EXPECT_EQ(retvalCmd.msg["cancel"].asBool(), false);
+
+	vector<string> vec{"magenta.vcd"};
+	EXPECT_CALL(fm, putListData(vec));
+	EXPECT_CALL(fm, closeList);
+	vector<string> allFiles{"blue.txt", "red.pdf", "green.sh", "yellow.tgz", "cyan.exe", "white", "black", "magenta.vcd"};
+	EXPECT_CALL(fm, getListData).WillOnce(testing::Return(allFiles));
+
+	retvalHdl = cm.handle(root);
+	// convert returned json value into vector
+	vector<string> retval_msg_names;
+	for (Json::Value i : retvalHdl.msg["names"])
+		retval_msg_names.push_back(i.asString());
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retval_msg_names, allFiles);
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testList, Negative_ListEmpty) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	cmd = "list";
+	args = {};
+	root["command"] = "list";
+	root["accept"] = false;
+	root["error"] = "no chunks to send";
+
+	// stick into cmdman and check things
+	EXPECT_CALL(fm, openList).WillOnce(testing::Return(true));
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "list");
+
+	EXPECT_CALL(fm, cancelList);
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("no chunks to send"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testListdata, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	/* tests the handling of server reply only,
+	 * initial request assumed as correct (tested in other tests) */
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	root["command"] = "listdata";
+	root["cancel"] = true;
+	root["error"] = "some foobar";
+
+	EXPECT_CALL(fm, cancelList);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("some foobar"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testList, Negative_AlreadyListing) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "list";
+	args = {};
+
+	EXPECT_CALL(fm, openList).WillOnce(testing::Return(false));
+
+	EXPECT_CALL(fm, cancelList).Times(0); // not called
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "list");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testList, Negative_NotListing_ServerRequest) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, namesArr;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root
+	root["command"] = "listdata";
+	root["cancel"] = false;
+	root["remaining"] = 0;
+	namesArr.append("magenta.vcd");
+	root["names"] = namesArr;
+
+	EXPECT_CALL(fm, cancelList).Times(0);
+	EXPECT_CALL(fm, putListData).Times(0);
+	EXPECT_CALL(fm, closeList).Times(0);
+	EXPECT_CALL(fm, getListData).Times(0);
+	EXPECT_CALL(fm, isListing).WillOnce(testing::Return(false));
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testListdata, Negative_NotListing_UserRequest) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare root/cmd/args
+	cmd = "listdata";
+	args = {};
+
+	EXPECT_CALL(fm, isListing).WillOnce(testing::Return(false));
+
+	// stick into cmdman and check things
+	retvalCmd = cm.execute(cmd, args);
+
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "list");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+	EXPECT_EQ(retvalCmd.msg["accept"].asBool(), false);
+}
+
+TEST(testListdata, Negative_NotListing_ServerRequest) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, namesArr;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare root/cmd/args
+	root["command"] = "listdata";
+	root["cancel"] = false;
+	root["remaining"] = 0;
+	namesArr.append("magenta.vcd");
+	root["names"] = namesArr;
+
+	EXPECT_CALL(fm, cancelList).Times(0);
+
+	retvalHdl = cm.handle(root);
+
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "list");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_NE(retvalHdl.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for deleteme
+ */
+
+TEST(testDeleteme, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "deleteme";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deleteme");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testDeleteme, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "deleteme";
+	args = {"myFancyPasswd"};
+	root["command"] = "deleteme";
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deleteme");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "myFancyPasswd");
+	EXPECT_EQ(retvalHdl.type, CmdMan::print | CmdMan::close);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "deleteme");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+TEST(testDeleteme, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "deleteme";
+	args = {"myFancyPasswd"};
+	root["command"] = "deleteme";
+	root["accept"] = false;
+	root["error"] = "password incorrect";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deleteme");
+	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "myFancyPasswd");
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "deleteme");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("password incorrect"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for deletefile
+ */
+TEST(testDeletefile, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "deletefile";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deletefile");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testDeletefile, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "deletefile";
+	args = {"fancy_file.txt"};
+	root["command"] = "deletefile";
+	root["file"] = "fancy_file.txt";
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deletefile");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "fancy_file.txt");
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "deletefile");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "fancy_file.txt");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testDeletefile, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "deletefile";
+	args = {"fancy_file.txt"};
+	root["command"] = "deletefile";
+	root["file"] = "fancy_file.txt";
+	root["accept"] = false;
+	root["error"] = "file does not exist";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deletefile");
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "fancy_file.txt");
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "deletefile");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_EQ(retvalHdl.msg["file"].asString(), "fancy_file.txt");
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("file does not exist"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * test for status
+ */
+TEST(testStatus, Test) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "status";
+	args = {};
+	root["command"] = "status";
+	root["response"] = "fancy response";
+	root["accept"] = true;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "status");
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "status");
+	EXPECT_THAT(retvalHdl.msg["response"].asString(), testing::HasSubstr("fancy response"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+/* =====================================
+ * tests for help
+ */
+TEST(testHelp, LoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args/root
+	cmd = "help";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::print);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "help");
+	EXPECT_NE(retvalCmd.msg["names"], "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testHelp, NotConnected) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args/root
+	cmd = "help";
+	args = {};
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::print);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "help");
+	EXPECT_NE(retvalCmd.msg["names"], "");
+
+	EXPECT_TRUE(cm.isNotConnected());
+}
+
+/* =====================================
+ * tests for keyfile and closekey
+ */
+TEST(testKeyfile, TooFewArgs) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "keyfile";
+	args = {};
+
+	// stick into cmdman
+	EXPECT_CALL(fm, openKey).Times(0);
+	EXPECT_CALL(fm, getOpensslError).Times(0);
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "keyfile");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testKeyfile, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "keyfile";
+	args = {"./some/path"};
+
+	// stick into cmdman
+	EXPECT_CALL(fm, openKey).WillOnce(testing::Return(false));
+	EXPECT_CALL(fm, getOpensslError).WillOnce(testing::Return("some openssl error"));
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "keyfile");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "./some/path");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testKeyfile, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "keyfile";
+	args = {"./some/path"};
+
+	// stick into cmdman
+	EXPECT_CALL(fm, openKey).WillOnce(testing::Return(true));
+	EXPECT_CALL(fm, getOpensslError).Times(0);
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::print);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "keyfile");
+	EXPECT_TRUE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["file"].asString(), "./some/path");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testClosekey, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "closekey";
+	args = {};
+
+	// stick into cmdman
+	EXPECT_CALL(fm, closeKey).WillOnce(testing::Return(true));
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::print);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "closekey");
+	EXPECT_TRUE(retvalCmd.msg["accept"].asBool());
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testClosekey, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	CmdMan::CmdRet retvalCmd;
+
+	// prepare cmd/args
+	cmd = "closekey";
+	args = {};
+
+	// stick into cmdman
+	EXPECT_CALL(fm, closeKey).WillOnce(testing::Return(false));
+	retvalCmd = cm.execute(cmd, args);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "closekey");
+	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testCommandsWithRequiredLogin, NotLoggedInOrNotConnected) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	CmdMan::CmdRet retval1, retval2, retval3;
+
+	// prepare cmd/args
+	std::vector<std::string> cmds = {"put", "get", "list", "putdata", "getdata", "listdata", "head", "deletefile", "deleteme", "keyfile", "closekey"};
+	// as every command works fine with too many args, we will simply pass three to all of them
+	std::vector<std::string> args = {"arg1", "arg2", "arg3"};
+
+	for (std::string cmd : cmds) {
+		cm.initNotConnected();
+		retval1 = cm.execute(cmd, args);
+		cm.initConnected();
+		retval2 = cm.execute(cmd, args);
+		cm.initVersionChecked();
+		retval3 = cm.execute(cmd, args);
+
+		EXPECT_EQ(retval1.type, CmdMan::error);
+		EXPECT_EQ(retval1.msg["command"].asString(), "error");
+		EXPECT_NE(retval1.msg["error"].asString(), "");
+
+		EXPECT_EQ(retval2.type, CmdMan::error);
+		EXPECT_EQ(retval2.msg["command"].asString(), "error");
+		EXPECT_NE(retval2.msg["error"].asString(), "");
+
+		EXPECT_EQ(retval3.type, CmdMan::error);
+		EXPECT_EQ(retval3.msg["command"].asString(), "error");
+		EXPECT_NE(retval3.msg["error"].asString(), "");
+	}
+}
+
+} // namespace
+/* end of namespace */

+ 49 - 0
cli/test/cryptoget.sh

@@ -0,0 +1,49 @@
+#!/bin/bash
+
+SCRIPT_PATH=${0%/*}
+if [ "$0" != "$SCRIPT_PATH" ] && [ "$SCRIPT_PATH" != "" ]; then 
+    cd "$SCRIPT_PATH/.."
+fi
+
+DAEMONFILEPATH="../../daemon/build/files"
+
+if [ $# -eq 1 ];
+	then DAEMONFILEPATH="$1"
+fi
+
+echo "am in directory $PWD"
+echo "using file path $DAEMONFILEPATH"
+
+test/cryptotest_gcm 1 test/samplekey1.bin test/samplefile.txt $DAEMONFILEPATH/samplefile.txt
+
+./ccats-cli --batch test/cryptoget.txt
+
+if [ ! -f test/cryptoput.txt.out ];
+then
+	echo "running of batch file failed"
+	exit 1;
+fi
+
+if [ ! -f samplefile.txt ];
+then
+	echo "file didnt download"
+	echo "STDOUT is :"
+	cat test/cryptoput.txt.out
+	echo "STDERR is :"
+	cat test/cryptoput.txt.err
+	exit 1;
+fi
+
+diff samplefile.txt test/samplefile.txt
+DIFFRES=$?
+
+rm $DAEMONFILEPATH/samplefile.txt
+rm samplefile.txt
+
+if [ $DIFFRES -ne 0 ];
+then
+	echo "files are not equal after decryption: $DIFFRES"
+	exit 1;
+fi
+
+exit 0;

+ 4 - 0
cli/test/cryptoget.txt

@@ -0,0 +1,4 @@
+connect 0.0.0.0
+login user pass
+keyfile test/samplekey1.bin
+get samplefile.txt

+ 57 - 0
cli/test/cryptoput.sh

@@ -0,0 +1,57 @@
+#!/bin/bash
+
+SCRIPT_PATH=${0%/*}
+if [ "$0" != "$SCRIPT_PATH" ] && [ "$SCRIPT_PATH" != "" ]; then 
+    cd "$SCRIPT_PATH/.."
+fi
+
+DAEMONFILEPATH="../../daemon/build/files"
+
+if [ $# -eq 1 ];
+then
+	DAEMONFILEPATH="$1"
+fi
+
+echo "am in directory $PWD"
+echo "using file path $DAEMONFILEPATH"
+
+./ccats-cli --batch test/cryptoput.txt
+
+if [ ! -f test/cryptoput.txt.out ];
+then
+	echo "running of batch file failed"
+	exit 1;
+fi
+
+if [ ! -f $DAEMONFILEPATH/samplefile.txt ];
+then
+	echo "couldnt find file on server"
+	echo "STDOUT is :"
+	cat test/cryptoput.txt.out
+	echo "STDERR is :"
+	cat test/cryptoput.txt.err
+	exit 1;
+fi
+
+test/cryptotest_gcm 0 test/samplekey1.bin $DAEMONFILEPATH/samplefile.txt samplefile.txt
+CRYPTORES=$?
+
+diff samplefile.txt test/samplefile.txt
+DIFFRES=$?
+
+rm $DAEMONFILEPATH/samplefile.txt
+rm samplefile.txt
+
+if [ $CRYPTORES -ne 0 ];
+then
+	echo "decryption failed: $CRYPTORES"
+	exit 1;
+fi
+
+if [ $DIFFRES -ne 0 ];
+then
+	echo "files are not equal after decryption: $DIFFRES"
+	exit 1;
+fi
+
+exit 0;

+ 4 - 0
cli/test/cryptoput.txt

@@ -0,0 +1,4 @@
+connect 0.0.0.0
+login user pass
+keyfile test/samplekey1.bin
+put test/samplefile.txt

+ 0 - 0
cli/test/cryptotest-gcm.c → cli/test/cryptotest_gcm.c


+ 57 - 0
cli/test/fileman_mock.h

@@ -0,0 +1,57 @@
+#ifndef FILEMAN_MOCK_H
+#define FILEMAN_MOCK_H
+
+#include "../include/fileman.h"
+#include <gmock/gmock.h>
+
+class FileManMock : public FileMan {
+public:
+	MOCK_METHOD(bool, isGetting, (), (override));
+	MOCK_METHOD(bool, isPutting, (), (override));
+	MOCK_METHOD(bool, isListing, (), (override));
+
+	MOCK_METHOD(bool, openPut, (const std::string &path), (override));
+	MOCK_METHOD(bool, openGet, (const std::string &path), (override));
+	MOCK_METHOD(bool, openList, (), (override));
+
+	MOCK_METHOD(void, closePut, (), (override));
+	MOCK_METHOD(void, closeGet, (), (override));
+	MOCK_METHOD(void, closeList, (), (override));
+
+	MOCK_METHOD(std::string, getPutName, (), (override));
+	MOCK_METHOD(std::string, getGetName, (), (override));
+
+	MOCK_METHOD(void, cancelPut, (), (override));
+	MOCK_METHOD(void, cancelGet, (), (override));
+	MOCK_METHOD(void, cancelList, (), (override));
+
+	MOCK_METHOD(std::vector<char>, readPut, (), (override));
+
+	MOCK_METHOD(void, writeGet, (std::vector<char> data), (override));
+
+	MOCK_METHOD(std::string, readBase64, (), (override));
+	MOCK_METHOD(void, writeBase64, (std::string data), (override));
+
+	MOCK_METHOD(void, putListData, (std::vector<std::string> names), (override));
+	MOCK_METHOD(std::vector<std::string>, getListData, (), (override));
+
+	MOCK_METHOD(int, getPutChunks, (), (override));
+	MOCK_METHOD(int, getGetChunks, (), (override));
+	MOCK_METHOD(int, getListChunks, (), (override));
+	MOCK_METHOD(int, getPutRemainingChunks, (), (override));
+	MOCK_METHOD(int, getGetRemainingChunks, (), (override));
+	MOCK_METHOD(int, getListRemainingChunks, (), (override));
+	MOCK_METHOD(int, getPutSize, (), (override));
+
+	MOCK_METHOD(void, setGetChunks, (int chunks), (override));
+	MOCK_METHOD(void, setListChunks, (int chunks), (override));
+
+	MOCK_METHOD(std::string, pathToFilename, (std::string path), (override));
+
+	MOCK_METHOD(std::string, getOpensslError, (), (override));
+	MOCK_METHOD(bool, openKey, (const std::string &path), (override));
+
+	MOCK_METHOD(bool, closeKey, (), (override));
+};
+
+#endif

+ 22 - 0
cli/test/logintest_neg.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+SCRIPT_PATH=${0%/*}
+if [ "$0" != "$SCRIPT_PATH" ] && [ "$SCRIPT_PATH" != "" ]; then 
+    cd "$SCRIPT_PATH/.."
+fi
+
+if [ $# -eq 1 ];
+	then DAEMONFILEPATH="$1"
+fi
+
+./ccats-cli --batch test/logintest_neg.txt
+
+if [ ! -f test/logintest_neg.txt.err ];
+then
+	exit 1;
+fi
+if [ -z "$(grep "Login failed" test/logintest_neg.txt.err)" ];
+then
+	exit 1;
+fi
+exit 0;

+ 2 - 0
cli/test/logintest_neg.txt

@@ -0,0 +1,2 @@
+connect 0.0.0.0
+login user badpass

+ 18 - 0
cli/test/logintest_pos.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+
+SCRIPT_PATH=${0%/*}
+if [ "$0" != "$SCRIPT_PATH" ] && [ "$SCRIPT_PATH" != "" ]; then 
+    cd "$SCRIPT_PATH/.."
+fi
+
+./ccats-cli --batch test/logintest_pos.txt
+
+if [ ! -f test/logintest_pos.txt.out ];
+then
+	exit 1;
+fi
+if [ -z "$(grep "Login ok." test/logintest_pos.txt.out)" ];
+then
+	exit 1;
+fi
+exit 0;

+ 2 - 0
cli/test/logintest_pos.txt

@@ -0,0 +1,2 @@
+connect 0.0.0.0
+login user pass

+ 1 - 0
cli/test/samplefile.txt

@@ -0,0 +1 @@
+This is a ccats sample test file.