Browse Source

Merge branch 'cli-connect-only-when-not-connected' into 'develop'

CLI feature fixes

Closes #97

See merge request tobias.wach/ccats!107
Sander, Paul 4 years ago
parent
commit
fe8c65893b

+ 2 - 2
cli/README.md

@@ -17,8 +17,8 @@ Batch mode. A batch file for the CLI must contain commands (as used in regular u
 
 ### Additional arguments
 
-`--usessl`: <br/>
-Used to enable SSL communication with the server. Needs to be passed if server is configured to use SSL encryption.
+`--usessl <certfile>`: <br/>
+Used to enable SSL communication with the server using the certificate file specified by the file path <i>&lt;certfile&gt;</i>. Needs to be passed if server is configured to use SSL encryption.
 
 `--verbose`: <br/>
 Prints additional output for debugging purposes.

+ 1 - 1
cli/include/batchioman.h

@@ -81,7 +81,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	BatchIoMan(bool usessl, bool beverbose, string batchpath);
+	BatchIoMan(bool usessl, const char *certfile, bool beverbose, string batchpath);
 	~BatchIoMan();
 
 	bool init();

+ 23 - 7
cli/include/cmdman.h

@@ -24,13 +24,24 @@ class CmdMan {
 public:
 	/**
 	 * Flags for type of message returned in CmdRet.
-	 * print	- print something to the user
-	 * send	- send something to the server
-	 * error	- an error occured, do not send to the server
-	 * close	- terminate the connection
-	 * seton	- contextually change state, used for version check and login
-	 */
-	enum rettype { none = 0, print = (1 << 1), send = (1 << 2), error = (1 << 3), close = (1 << 4), connect = (1 << 5), exit = (1 << 6) };
+	 * print			- print something to the user
+	 * send				- send something to the server
+	 * error			- an error occured, do not send to the server
+	 * close			- terminate the connection
+	 * connect			- connect to the server
+	 * exit				- exit the program
+	 * noanswerexpected	- do not expect an answer from the server
+	 */
+	enum rettype {
+		none = 0,
+		print = (1 << 1),
+		send = (1 << 2),
+		error = (1 << 3),
+		close = (1 << 4),
+		connect = (1 << 5),
+		exit = (1 << 6),
+		noanswerexpected = (1 << 7)
+	};
 	/**
 	 * Response to user or command input
 	 *
@@ -62,7 +73,12 @@ public:
 	 */
 	Json::CharReader *reader;
 
+	/**
+	 * Used to inform the CmdMan that the CLI is now (dis-)connected to the server.
+	 * Sets the internal state of the CmdMan accordingly.
+	 */
 	void stateSetConnectionOk();
+	void stateSetDisconnected();
 
 protected:
 	/**

+ 11 - 1
cli/include/ioman.h

@@ -123,11 +123,21 @@ private:
 	 */
 	std::vector<std::string> tokenizeInput(std::string in);
 
+	/**
+	 * Timestamp saves the sending time of the oldest request to the server that was not followed by an answer.
+	 * If the timestamp is not valid, since the last request to the server, there was an answer
+	 * (or the cli is disconnected, or there was no request yet).
+	 * Mutex used for access to the timestamps.
+	 */
+	time_t sendtimestamp;
+	bool sendtimestampValid;
+	std::mutex timestampmutex;
+
 public:
 	/**
 	 * Constructor and destructor
 	 */
-	IoMan(bool enablessl);
+	IoMan(bool enablessl, const char *certfile);
 	virtual ~IoMan();
 
 	/**

+ 1 - 1
cli/include/machineioman.h

@@ -14,7 +14,7 @@ private:
 	bool verbose;
 
 public:
-	MachineIoMan(bool usessl, bool beverbose);
+	MachineIoMan(bool usessl, const char *certfile, bool beverbose);
 
 protected:
 	/**

+ 1 - 1
cli/include/userioman.h

@@ -69,7 +69,7 @@ public:
 	/**
 	 * Constructor and destructor
 	 */
-	UserIoMan(bool usessl, bool verbose);
+	UserIoMan(bool usessl, const char *certfile, bool verbose);
 	~UserIoMan();
 
 protected:

+ 1 - 1
cli/src/batchioman.cpp

@@ -4,7 +4,7 @@
 #include <string>
 #include <vector>
 
-BatchIoMan::BatchIoMan(bool usessl, bool beverbose, std::string batchpath) : IoMan(usessl) {
+BatchIoMan::BatchIoMan(bool usessl, const char *certfile, bool beverbose, std::string batchpath) : IoMan(usessl, certfile) {
 	/* setup json stuff */
 	Json::CharReaderBuilder rbuilder;
 	wbuilder.settings_["indentation"] = "";

+ 45 - 34
cli/src/cmdman.cpp

@@ -94,6 +94,13 @@ CmdMan::~CmdMan() { delete reader; }
 
 void CmdMan::stateSetConnectionOk() { currentState = versionpossible; }
 
+void CmdMan::stateSetDisconnected() {
+	currentState = connectionpossible;
+	fileman.cancelGet();
+	fileman.cancelPut();
+	fileman.cancelList();
+}
+
 CmdMan::CmdRet CmdMan::cmdHelp(vector<string> args) {
 	CmdRet retval;
 	Json::Value root, arr;
@@ -142,7 +149,7 @@ CmdMan::CmdRet CmdMan::cmdDisconnect(vector<string> args) {
 		root["user"] = "";
 		root["pass"] = "";
 		root["cancel"] = true;
-		retval.type |= close;
+		retval.type |= close | noanswerexpected;
 		if (currentState == disconnecttoexitearly) {
 			retval.nextcommand = "exit";
 		}
@@ -159,13 +166,13 @@ CmdMan::CmdRet CmdMan::cmdPut(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "put";
 
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, put requires at least 1 argument.";
 	} else {
+		root["command"] = "put";
 		if (fileman.isPutting()) {
 			retval.type = error;
 			root["file"] = args[0];
@@ -216,14 +223,13 @@ CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "get";
 
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, get requires at least 1 argument.";
 	} else {
-
+		root["command"] = "get";
 		if (fileman.isGetting()) {
 			retval.type = error;
 			root["file"] = args[0];
@@ -350,13 +356,14 @@ CmdMan::CmdRet CmdMan::cmdHead(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "head";
 
 	if (args.size() < 1) {
 		retval.type = error;
+		root["command"] = "error";
 		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["error"] = "Not enough arguments, head requires at least 1 argument.";
 	} else {
+		root["command"] = "head";
 		root["file"] = args[0];
 		retval.type = send;
 	}
@@ -369,13 +376,14 @@ CmdMan::CmdRet CmdMan::cmdDeletefile(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "deletefile";
 
 	if (args.size() < 1) {
 		retval.type = error;
+		root["command"] = "error";
 		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["error"] = "Not enough arguments, deletefile requires at least 1 argument.";
 	} else {
+		root["command"] = "deletefile";
 		root["file"] = args[0];
 		retval.type = send;
 	}
@@ -389,17 +397,23 @@ CmdMan::CmdRet CmdMan::cmdConnect(vector<string> args) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
 
-	root["command"] = "connect";
-
 	if (args.size() < 1) {
 		retval.type = error;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["accept"] = false;
+		root["error"] = "Not enough arguments, connect requires at least 1 argument.";
+	} else if (currentState != connectionpossible) {
+		retval.type = error;
+		root["command"] = "error"; // analogous to execute() method when command unavailable
+		root["error"] = "Connecting not possible, you are already connected to a server.";
 	} else if (args.size() < 2) {
 		retval.type = connect;
+		root["command"] = "connect";
 		root["address"] = args[0];
 		root["port"] = 1234;
 	} else {
 		retval.type = connect;
+		root["command"] = "connect";
 		root["address"] = args[0];
 		root["port"] = (unsigned int)stoul(args[1]);
 	}
@@ -488,14 +502,13 @@ CmdMan::CmdRet CmdMan::cmdDeleteme(vector<string> args) {
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
 
-	root["command"] = "deleteme";
-
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, deleteme requires at least 1 argument.";
 	} else {
 		retval.type = send;
+		root["command"] = "deleteme";
 		root["pass"] = args[0];
 	}
 	retval.msg = root;
@@ -507,13 +520,13 @@ CmdMan::CmdRet CmdMan::cmdKeyfile(vector<string> args) {
 	CmdRet retval;
 	Json::Value root;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
-	root["command"] = "keyfile";
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, keyfile requires at least 1 argument.";
 	} else {
 		DEBUGPRINT(string(__PRETTY_FUNCTION__) + " haveargs");
+		root["command"] = "keyfile";
 		if (!fileman.openKey(args[0])) {
 			DEBUGPRINT(string(__PRETTY_FUNCTION__) + " openkey fail");
 			root["accept"] = false;
@@ -576,9 +589,8 @@ CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
 
 	if (args.size() < 2) {
 		retval.type = error;
-		root["command"] = "login";
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 2 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, login requires at least 2 arguments.";
 	} else {
 		if (currentState == loginpossible) {
 			currentState = dologin;
@@ -607,9 +619,8 @@ CmdMan::CmdRet CmdMan::cmdSignup(vector<string> args) {
 
 	if (args.size() < 2) {
 		retval.type = error;
-		root["command"] = "signup";
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 2 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, signup requires at least 2 arguments.";
 	} else {
 		if (currentState == loginpossible) {
 			currentState = dosignup;
@@ -635,13 +646,13 @@ CmdMan::CmdRet CmdMan::cmdQueue(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "queue";
 
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, queue requires at least 1 argument.";
 	} else {
+		root["command"] = "queue";
 		root["file"] = args[0];
 		retval.type = send;
 	}
@@ -654,13 +665,13 @@ CmdMan::CmdRet CmdMan::cmdDequeue(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
 	Json::Value root;
-	root["command"] = "dequeue";
 
 	if (args.size() < 1) {
 		retval.type = error;
-		root["accept"] = false;
-		root["error"] = "not enough arguments, at least 1 argument required";
+		root["command"] = "error";
+		root["error"] = "Not enough arguments, dequeue requires at least 1 argument.";
 	} else {
+		root["command"] = "dequeue";
 		root["file"] = args[0];
 		retval.type = send;
 	}

+ 41 - 2
cli/src/ioman.cpp

@@ -25,7 +25,7 @@ extern IoMan *gIOMAN;
 
 void ioman_externalDebugPrint(string msg) { gIOMAN->printMessage(msg, gIOMAN->OutMsgType::debug); }
 
-IoMan::IoMan(bool enablessl) : cmdman(fileman, &ioman_externalDebugPrint), recvbuf(16384) {
+IoMan::IoMan(bool enablessl, const char *certfile) : cmdman(fileman, &ioman_externalDebugPrint), recvbuf(16384) {
 	ipstring = "";
 	port = 0;
 	tcpsock = new tcp::socket(ios);
@@ -45,7 +45,10 @@ IoMan::IoMan(bool enablessl) : cmdman(fileman, &ioman_externalDebugPrint), recvb
 		sslctx = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23);
 		sslctx->set_verify_mode(boost::asio::ssl::verify_peer);
 		sslctx->set_options(boost::asio::ssl::context::no_sslv2);
-		sslctx->load_verify_file("rootca.crt");
+		sslctx->load_verify_file(string(certfile), errcode);
+		if (errcode) {
+			throw std::runtime_error("Couldn't initialize SSL, Boost reports: " + errcode.message());
+		}
 		sslsock = new boost::asio::ssl::stream<tcp::socket &>(*tcpsock, *sslctx);
 	}
 }
@@ -173,6 +176,7 @@ bool IoMan::connect() {
 		if (errcode) {
 			root["error"] = errcode.message();
 			connected = false;
+			disconnect();
 		} else {
 			connected = true;
 			root["error"] = "";
@@ -186,6 +190,7 @@ bool IoMan::connect() {
 		if (errcode) {
 			root["error"] = errcode.message();
 			connected = false;
+			disconnect();
 		} else {
 			connected = true;
 			root["error"] = "";
@@ -205,6 +210,7 @@ void IoMan::disconnect() {
 	if (errcode)
 		printMessage(string(__PRETTY_FUNCTION__) + string("tcp shutdown says ") + errcode.message(), debug);
 	connected = false;
+	cmdman.stateSetDisconnected();
 }
 
 bool IoMan::init() {
@@ -257,6 +263,11 @@ 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);
+
+		timestampmutex.lock();
+		sendtimestampValid = false;
+		timestampmutex.unlock();
+
 		if (readsize < 1) {
 			break;
 		}
@@ -374,10 +385,23 @@ void IoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
 	}
 	if (cmdret.type & CmdMan::rettype::send) {
 		printMessage("IoMan::inputMain() sending json \"" + Json::writeString(wbuilder, cmdret.msg) + "\"", debug);
+
+		timestampmutex.lock();
+		if (!sendtimestampValid) {
+			if (cmdret.type & CmdMan::rettype::noanswerexpected) {
+				// there will be no answer from the server, do not set a timestamp
+			} else {
+				sendtimestampValid = true;
+				time(&sendtimestamp); // set timestamp
+			}
+		}
+		timestampmutex.unlock();
+
 		if (usessl)
 			boost::asio::write(*sslsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
 		else
 			boost::asio::write(*tcpsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
+
 		if (errcode) {
 			printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", debug);
 			return;
@@ -563,6 +587,21 @@ void IoMan::run() {
 	while (runmain) {
 		mainmutex.unlock();
 
+		timestampmutex.lock();
+		if (sendtimestampValid && std::difftime(time(NULL), sendtimestamp) > 15) {
+			// answer took more than 15 seconds
+			disconnect();
+			// cmdman gets informed inside disconnect method
+			// inform the user by giving output
+			Json::Value root;
+			root["command"] = "error";
+			root["error"] = "The server does not respond. You are now disconnected.";
+			printMessage(Json::writeString(wbuilder, root), normal);
+
+			sendtimestampValid = false;
+		}
+		timestampmutex.unlock();
+
 		poll(&inpipestatus, 1, 100);
 
 		if (inpipestatus.revents & POLLIN) {

+ 1 - 1
cli/src/machineioman.cpp

@@ -3,7 +3,7 @@
 #include <iostream>
 #include <vector>
 
-MachineIoMan::MachineIoMan(bool usessl, bool beverbose) : IoMan(usessl) { verbose = beverbose; }
+MachineIoMan::MachineIoMan(bool usessl, const char *certfile, bool beverbose) : IoMan(usessl, certfile) { verbose = beverbose; }
 
 void MachineIoMan::printMessage(std::string msg, OutMsgType type) {
 	switch (type) {

+ 10 - 6
cli/src/main.cpp

@@ -19,12 +19,13 @@ void show_help(char *exec) {
 int main(int argc, char **argv) {
 	bpo::options_description desc("Options");
 	desc.add_options()("help", "show this help")("machine", "switch to machine mode for I/O (not designed for user interaction)")(
-	    "batch", bpo::value<std::string>(), "run operations from arg as batch file")("usessl", "enable ssl for connection to server")("verbose",
-	                                                                                                                                  "enable debug output");
+	    "batch", bpo::value<std::string>(), "run operations from arg as batch file")("usessl", bpo::value<std::string>(),
+	                                                                                 "enable ssl for connection to server")("verbose", "enable debug output");
 
 	bpo::variables_map vm;
 	bool machine = false, usessl = false, batch = false, verbose = false;
 	const char *file = NULL;
+	const char *certfile = NULL;
 	IoMan *ioman;
 
 	try {
@@ -46,21 +47,23 @@ int main(int argc, char **argv) {
 			batch = true;
 		}
 		if (vm.count("usessl")) {
+			certfile = vm["usessl"].as<std::string>().c_str();
 			usessl = true;
 		}
 		if (vm.count("verbose")) {
 			verbose = true;
 		}
 	} catch (const bpo::error &ex) {
-		std::fprintf(stderr, "%s\n", ex.what());
+		std::cerr << ex.what() << std::endl;
+		return 1;
 	}
 	std::printf("machine mode is %d file is %s enablessl is %d verbose is %d\n", machine, file ? file : "", usessl, verbose);
 	if (batch) {
-		ioman = new BatchIoMan(usessl, verbose, file);
+		ioman = new BatchIoMan(usessl, certfile, verbose, file);
 	} else if (machine) {
-		ioman = new MachineIoMan(usessl, verbose);
+		ioman = new MachineIoMan(usessl, certfile, verbose);
 	} else {
-		ioman = new UserIoMan(usessl, verbose);
+		ioman = new UserIoMan(usessl, certfile, verbose);
 	}
 	gIOMAN = ioman;
 	if (ioman->init()) {
@@ -69,4 +72,5 @@ int main(int argc, char **argv) {
 	delete ioman;
 	gIOMAN = NULL;
 	std::printf("done\n");
+	return 0;
 }

+ 1 - 1
cli/src/userioman.cpp

@@ -4,7 +4,7 @@
 #include <readline/readline.h>
 #include <vector>
 
-UserIoMan::UserIoMan(bool usessl, bool beverbose) : IoMan(usessl) {
+UserIoMan::UserIoMan(bool usessl, const char *certfile, bool beverbose) : IoMan(usessl, certfile) {
 	/* setup json stuff */
 	Json::CharReaderBuilder rbuilder;
 	wbuilder.settings_["indentation"] = "";

+ 65 - 26
cli/test/cmdman_test.cpp

@@ -65,8 +65,7 @@ TEST(testConnect, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "connect");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isNotConnected());
@@ -144,6 +143,56 @@ TEST(testConnect, Negative) {
 	EXPECT_TRUE(cm.isNotConnected());
 }
 
+TEST(testConnect, AlreadyConnected) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initConnected();
+
+	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::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isConnected());
+}
+
+TEST(testConnect, AlreadyLoggedIn) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	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::error);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
+	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
 /* =====================================
  * tests for
  * version check, signup, login
@@ -244,7 +293,6 @@ TEST(testVersion, AlreadyLoggedIn) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	// TODO: check some more things maybe
 
 	EXPECT_TRUE(cm.isLoggedIn());
 }
@@ -337,8 +385,7 @@ TEST(testLogin, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "login");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isVersionChecked());
@@ -498,8 +545,7 @@ TEST(testSignup, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "signup");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isVersionChecked());
@@ -560,7 +606,7 @@ TEST(testDisconnect, NotLoggedIn) {
 	retvalCmd = cm.execute(cmd, args);
 
 	// check things
-	EXPECT_EQ(retvalCmd.type, CmdMan::send | CmdMan::close);
+	EXPECT_EQ(retvalCmd.type, CmdMan::send | CmdMan::close | CmdMan::noanswerexpected);
 	EXPECT_FALSE(retvalCmd.msg["login"].asBool());
 	EXPECT_EQ(retvalCmd.msg["user"].asString(), "");
 	EXPECT_EQ(retvalCmd.msg["pass"].asString(), "");
@@ -641,7 +687,7 @@ TEST(testExit, NotLoggedIn) {
 
 	retvalCmdDisconnect = cm.execute("disconnect", args);
 
-	EXPECT_EQ(retvalCmdDisconnect.type, CmdMan::send | CmdMan::close);
+	EXPECT_EQ(retvalCmdDisconnect.type, CmdMan::send | CmdMan::close | CmdMan::noanswerexpected);
 	EXPECT_FALSE(retvalCmdDisconnect.msg["login"].asBool());
 	EXPECT_EQ(retvalCmdDisconnect.msg["user"].asString(), "");
 	EXPECT_EQ(retvalCmdDisconnect.msg["pass"].asString(), "");
@@ -697,8 +743,7 @@ TEST(testPut, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "put");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -1169,7 +1214,7 @@ TEST(testHead, TooFewArgs) {
 	retvalCmd = cm.execute(cmd, args);
 
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "head");
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	retvalHdl = cm.handle(root);
@@ -1273,8 +1318,7 @@ TEST(testGet, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "get");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -2337,8 +2381,7 @@ TEST(testDeleteme, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deleteme");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -2431,8 +2474,7 @@ TEST(testDeletefile, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "deletefile");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -2772,8 +2814,7 @@ TEST(testKeyfile, TooFewArgs) {
 
 	// 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["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -2984,8 +3025,7 @@ TEST(testQueue, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "queue");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -3079,8 +3119,7 @@ TEST(testDequeue, TooFewArgs) {
 
 	// check things
 	EXPECT_EQ(retvalCmd.type, CmdMan::error);
-	EXPECT_EQ(retvalCmd.msg["command"].asString(), "dequeue");
-	EXPECT_FALSE(retvalCmd.msg["accept"].asBool());
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "error");
 	EXPECT_NE(retvalCmd.msg["error"].asString(), "");
 
 	EXPECT_TRUE(cm.isLoggedIn());
@@ -3167,8 +3206,8 @@ TEST(testCommandsWithRequiredLogin, NotLoggedInOrNotConnected) {
 	CmdMan::CmdRet retval1, retval2, retval3;
 
 	// prepare cmd/args
-	std::vector<std::string> cmds = {"put",  "get",        "list",     "extendedlist",  "putdata", "getdata", "listdata", "extendedlistdata",
-	                                 "head", "deletefile", "deleteme", "extendedstatus"};
+	std::vector<std::string> cmds = {"put",  "get",        "list",     "extendedlist",   "putdata",       "getdata", "listdata", "extendedlistdata",
+	                                 "head", "deletefile", "deleteme", "extendedstatus", "notifications", "queue",   "dequeue"};
 	// 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"};