Browse Source

switch internal messaging to json, switch rettype to flag model, change UserIoManager to prettyprint json

Missingmew 4 years ago
parent
commit
c12e92f2c0
8 changed files with 489 additions and 253 deletions
  1. 13 7
      cli/include/cmdman.h
  2. 8 5
      cli/include/ioman.h
  3. 1 0
      cli/include/machineioman.h
  4. 47 1
      cli/include/userioman.h
  5. 172 117
      cli/src/cmdman.cpp
  6. 121 114
      cli/src/ioman.cpp
  7. 0 7
      cli/src/main.cpp
  8. 127 2
      cli/src/userioman.cpp

+ 13 - 7
cli/include/cmdman.h

@@ -22,22 +22,28 @@ using std::vector;
 class CmdMan {
 public:
   /**
-   * Type of message returned in CmdRet.
-   * notsend	- do not send anything to the server
-   * send		- send something to the server
+   * 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 { notsend, send, error, close, seton };
+  enum rettype {
+    print = (1 << 0),
+    send = (1 << 1),
+    error = (1 << 2),
+    close = (1 << 3),
+    seton = (1 << 4)
+  };
   /**
    * Response to user or command input
    *
-   * string is to be handled depending on type
+   * msg is to be handled depending on type
    */
   struct CmdRet {
-    rettype type;
-    string msg;
+    unsigned int type;
+    Json::Value msg;
   };
   /**
    * Constructor and destructor

+ 8 - 5
cli/include/ioman.h

@@ -6,6 +6,7 @@
 
 #include <boost/asio.hpp>
 #include <condition_variable>
+#include <json/json.h>
 #include <mutex>
 #include <string>
 #include <thread>
@@ -61,7 +62,7 @@ private:
    * Flag telling wether one is connected
    */
   std::string ipstring;
-  int port;
+  unsigned short port;
   bool connected;
   /**
    * Instances of CmdMan and FileMan to process user input and handle File I/O
@@ -108,12 +109,14 @@ private:
   std::mutex initmutex;
   std::condition_variable initcv;
 
-protected:
   /**
-   * mutex
+   * Tokenizes input based on space as seperator
+   * Respects double-quoted tokens
+   * Returns a vector with the tokens as elements in order
    */
-  std::mutex msgmutex;
+  std::vector<std::string> tokenizeInput(std::string in);
 
+protected:
   /**
    * Prompt messages for readline prompt
    */
@@ -127,7 +130,7 @@ public:
    * Constructor and destructor
    */
   IoMan(char *ipcstring);
-  ~IoMan();
+  virtual ~IoMan();
 
   /**
    * Establish connection to server and perform vesion check

+ 1 - 0
cli/include/machineioman.h

@@ -13,6 +13,7 @@ class MachineIoMan : public IoMan {
 public:
   using IoMan::IoMan;
 
+protected:
   /**
    * Specific implementations for printing messages
    */

+ 47 - 1
cli/include/userioman.h

@@ -10,9 +10,55 @@
  * for interactive user sessions
  */
 class UserIoMan : public IoMan {
+private:
+  /**
+   * Map containing pointers to the appropriate member functions for printing
+   * formatted json output
+   */
+  map<string, void (UserIoMan::*)(Json::Value)> printmap;
+
+  /**
+   * Class-wide json functionality
+   */
+  Json::CharReader *reader;
+  Json::StreamWriterBuilder wbuilder;
+  string jsonerror;
+
+  /**
+   * Format and pretty print json for terminal output
+   */
+  void printJson(Json::Value root);
+
+  /**
+   * Method prototypes for printing json output
+   */
+  /* printing commands go here */
+  void printConnect(Json::Value root);
+  void printHelp(Json::Value root);
+  void printStatus(Json::Value root);
+  void printDisconnect(Json::Value root);
+  void printPut(Json::Value root);
+  void printGet(Json::Value root);
+  void printList(Json::Value root);
+  void printVersion(Json::Value root);
+  void printLogin(Json::Value root);
+  void printPutdata(Json::Value root);
+  void printGetdata(Json::Value root);
+  void printListdata(Json::Value root);
+
+  /**
+   * Mutex for synchronized message output
+   */
+  std::recursive_mutex msgmutex;
+
 public:
-  using IoMan::IoMan;
+  /**
+   * Constructor and destructor
+   */
+  UserIoMan(char *ipcstring);
+  ~UserIoMan();
 
+protected:
   /**
    * Specific implementations for printing messages
    */

+ 172 - 117
cli/src/cmdman.cpp

@@ -3,6 +3,9 @@
 
 #include <iostream>
 
+//~ #define DEBUGPRINT(x) std::cerr << x
+#define DEBUGPRINT(x)
+
 CmdMan::CmdMan(FileMan &fm) : fileman(fm) {
 
   /* setup json stuff */
@@ -49,64 +52,68 @@ CmdMan::~CmdMan() { delete reader; }
 
 CmdMan::CmdRet CmdMan::cmdHelp(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value root, arr;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   map<string, string>::iterator it;
+  root["command"] = "help";
   for (it = helpmap.begin(); it != helpmap.end(); it++) {
-    retval.msg.append(it->first);
-    retval.msg.append(" - ");
-    retval.msg.append(it->second);
-    retval.msg.append("\n");
+    arr.append(it->first + " - " + it->second);
   }
-  retval.type = notsend;
+  root["names"] = arr;
+  retval.type = print;
+  retval.msg = root;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdStatus(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
   root["command"] = "status";
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdDisconnect(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
   root["command"] = "close";
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdPut(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
 
   bool opened = fileman.openPut(args[0]);
+  root["command"] = "put";
+  root["file"] = fileman.getPutName();
   if (opened) {
     root["command"] = "put";
     root["file"] = fileman.getPutName();
     root["size"] = fileman.getPutSize();
     root["chunks"] = fileman.getPutChunks();
     retval.type = send;
-    retval.msg = Json::writeString(wbuilder, root);
   } else {
-    retval.type = error;
-    retval.msg = "couldnt open local file \"" + args[0] + "\"";
+    retval.type = print | error;
+    root["accept"] = false;
+    root["error"] = "couldnt open local file \"" + args[0] + "\"";
   }
 
+  retval.msg = root;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdPutdata(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
 
   root["command"] = "putdata";
@@ -117,33 +124,34 @@ CmdMan::CmdRet CmdMan::cmdPutdata(vector<string> args) {
       fileman
           .getPutRemainingChunks(); // number already decremented by readBase64
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
 
   bool opened = fileman.openGet(args[0]);
+  root["command"] = "get";
+  root["file"] = fileman.getGetName();
   if (opened) {
-    root["command"] = "get";
-    root["file"] = fileman.getGetName();
     retval.type = send;
-    retval.msg = Json::writeString(wbuilder, root);
   } else {
     retval.type = error;
-    retval.msg = "local file \"" + args[0] + "\" already exists";
+    root["accept"] = false;
+    root["error"] = "local file \"" + args[0] + "\" already exists";
   }
 
+  retval.msg = root;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdGetdata(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
 
   root["command"] = "getdata";
@@ -151,24 +159,26 @@ CmdMan::CmdRet CmdMan::cmdGetdata(vector<string> args) {
   root["chunk"] = fileman.getGetRemainingChunks();
   root["cancel"] = false;
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::cmdList(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
 
   bool opened = fileman.openList();
+  root["command"] = "list";
   if (opened) {
-    root["command"] = "list";
     retval.type = send;
-    retval.msg = Json::writeString(wbuilder, root);
+    retval.msg = root;
   } else {
     retval.type = error;
-    retval.msg = "cannot list, already listing";
+    root["accept"] = false;
+    root["names"] = "";
+    root["error"] = "cannot list, already listing";
   }
 
   return retval;
@@ -176,24 +186,24 @@ CmdMan::CmdRet CmdMan::cmdList(vector<string> args) {
 
 CmdMan::CmdRet CmdMan::cmdListdata(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
   root["command"] = "listdata";
   root["chunk"] = fileman.getListRemainingChunks();
   root["cancel"] = false;
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
-  std::cerr << __PRETTY_FUNCTION__ << " using command \"" << cmd
-            << "\" with arguments [ ";
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " using command \"" << cmd
+                                 << "\" with arguments [ ");
   for (string s : args)
-    std::cerr << s << " ";
-  std::cerr << "]" << std::endl;
+    DEBUGPRINT(s << " ");
+  DEBUGPRINT("]" << std::endl);
   map<string, CmdRet (CmdMan::*)(vector<string>)>::iterator it =
       execmap.find(cmd);
   string retmsg;
@@ -207,11 +217,11 @@ CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
 /* internal commands */
 CmdMan::CmdRet CmdMan::cmdVersion(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
   root["version"] = protocolVersion;
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   doversion = true;
 
@@ -220,14 +230,14 @@ CmdMan::CmdRet CmdMan::cmdVersion(vector<string> args) {
 
 CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   Json::Value root;
   root["user"] = args[0];
   root["pass"] = args[1];
   root["login"] = true;
   root["cancel"] = false;
   retval.type = send;
-  retval.msg = Json::writeString(wbuilder, root);
+  retval.msg = root;
 
   dologin = true;
 
@@ -245,13 +255,13 @@ CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
 */
 
 CmdMan::CmdRet CmdMan::handle(Json::Value root) {
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   if (doversion)
     root["command"] = "version";
   else if (dologin)
     root["command"] = "login";
-  std::cerr << __PRETTY_FUNCTION__ << " using json" << std::endl
-            << root << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " using json" << std::endl
+                                 << root << std::endl);
   string retmsg;
   map<string, CmdRet (CmdMan::*)(Json::Value)>::iterator it =
       handlemap.find(root["command"].asString());
@@ -265,231 +275,276 @@ CmdMan::CmdRet CmdMan::handle(Json::Value root) {
 
 CmdMan::CmdRet CmdMan::handleStatus(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
-  retval.type = notsend;
-  retval.msg = root["response"].asString();
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+  retval.type = print;
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleClose(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   retval.type = close;
-  retval.msg = root["response"].asString();
+  retval.msg = root;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handlePut(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output;
+  output["command"] = "put";
+  output["file"] = fileman.getPutName();
+  output["accept"] = false;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
 
   if (!root["accept"].asBool()) {
     retval.type = error;
-    retval.msg = "File upload request failed: Server reports: " +
-                 root["error"].asString();
+    output["error"] = "Server reports: " + root["error"].asString();
+    fileman.cancelPut();
   } else if (root["file"].asString() != fileman.getPutName()) {
     retval.type = error;
-    retval.msg = "File upload request failed: Server reports filename " +
-                 root["file"].asString() + " but actual filename is " +
-                 fileman.getPutName();
+    output["error"] = "Server reports filename " + root["file"].asString() +
+                      " but actual filename is " + fileman.getPutName();
+    fileman.cancelPut();
   } else {
-    retval.type = send;
-    retval.msg = "putdata";
+    output["accept"] = true;
+    output["error"] = "";
+    retval.type = print | send;
+    output["nextcommand"] = "putdata";
   }
+  retval.msg = output;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handlePutdata(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+  output["command"] = "putdata";
+  output["file"] = fileman.getPutName();
+  output["speed"] = 0.0f; // TODO
+  output["cancel"] = true;
 
   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;
-    retval.msg = std::string("File upload failed: Server reports number of "
-                             "remaining chunks as ") +
-                 std::to_string(root["received"].asInt()) +
-                 " but actual number is " +
-                 std::to_string(fileman.getPutRemainingChunks());
+    output["error"] = std::string("Server reports number of "
+                                  "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;
-    retval.msg =
-        "File upload cancelles: Server reports: " + root["error"].asString();
+    output["error"] = "Server reports: " + root["error"].asString();
     fileman.cancelPut();
   } else if (root["file"].asString() != fileman.getPutName()) {
     retval.type = error;
-    retval.msg = "File upload request failed: Server reports filename " +
-                 root["file"].asString() + " but actual filename is " +
-                 fileman.getPutName();
+    output["error"] = "Server reports filename " + root["file"].asString() +
+                      " but actual filename is " + fileman.getPutName();
     fileman.cancelPut();
   } else {
+    output["cancel"] = false;
+    output["error"] = "";
     // sent successfully
     if (!root["received"].asInt()) {
       // everything sent
-      retval.type = notsend;
-      retval.msg = "succesfully uploaded file " + fileman.getPutName();
+      retval.type = print;
+      // TODO
+      //~ retval.msg = "succesfully uploaded file " + fileman.getPutName();
       fileman.closePut();
     } else {
-      retval.type = send;
-      retval.msg = "putdata";
+      retval.type = print | send;
+      output["nextcommand"] = "putdata";
     }
   }
+  retval.msg = output;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleGet(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output;
+  output["command"] = "get";
+  output["file"] = fileman.getGetName();
+  output["accept"] = false;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
 
   if (!root["accept"].asBool()) {
     retval.type = error;
-    retval.msg = "File download request failed: Server reports: " +
-                 root["error"].asString();
+    output["error"] = "Server reports: " + root["error"].asString();
   } else if (root["file"].asString() != fileman.getGetName()) {
     retval.type = error;
-    retval.msg = "File download request failed: Server reports filename " +
-                 root["file"].asString() + " but actual filename is " +
-                 fileman.getGetName();
+    output["error"] = "Server reports filename " + root["file"].asString() +
+                      " but actual filename is " + fileman.getGetName();
   } else {
     fileman.setGetChunks(root["chunks"].asInt());
-    retval.type = send;
-    retval.msg = "getdata";
+    output["accept"] = true;
+    output["error"] = "";
+    retval.type = print | send;
+    output["nextcommand"] = "getdata";
   }
+  retval.msg = output;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleGetdata(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+  output["command"] = "getdata";
+  output["file"] = fileman.getGetName();
+  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()) {
     retval.type = error;
-    retval.msg = std::string("File download failed: Server reports number of "
-                             "remaining chunks as ") +
-                 std::to_string(root["remaining"].asInt()) +
-                 " but actual number is " +
-                 std::to_string(fileman.getGetRemainingChunks());
+    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["cancel"].asBool()) {
     retval.type = error;
-    retval.msg =
-        "File download cancelled: Server reports: " + root["error"].asString();
+    output["error"] = "Server reports: " + root["error"].asString();
     fileman.cancelGet();
   } else if (root["file"].asString() != fileman.getGetName()) {
     retval.type = error;
-    retval.msg = "File download failed: Server reports filename " +
-                 root["file"].asString() + " but actual filename is " +
-                 fileman.getGetName();
+    output["error"] = "Server reports filename " + root["file"].asString() +
+                      " but actual filename is " + fileman.getGetName();
     fileman.cancelGet();
   } else {
+    output["cancel"] = false;
+    output["error"] = "";
     fileman.writeBase64(root["data"].asString());
     // loaded successfully
     if (fileman.getGetRemainingChunks() < 0) {
       // everything sent
-      retval.type = notsend;
-      retval.msg = "succesfully downloaded file " + fileman.getGetName();
+      retval.type = print;
+      //~ retval.msg = "succesfully downloaded file " + fileman.getGetName();
       fileman.closeGet();
     } else {
-      retval.type = send;
-      retval.msg = "getdata";
+      retval.type = print | send;
+      output["nextcommand"] = "getdata";
     }
   }
+  retval.msg = output;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleList(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output; // LOCALOUTPUT
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+
+  output["command"] = "list";
+  output["names"] = "";
 
   if (!root["accept"].asBool()) {
     retval.type = error;
-    retval.msg = "File listing request failed: Server reports: " +
-                 root["error"].asString();
+    output["accept"] = false;
+    output["error"] = "Server reports: " + root["error"].asString();
+    fileman.cancelList();
   } else {
     fileman.setListChunks(root["chunks"].asInt());
     retval.type = send;
-    retval.msg = "listdata";
+    output["nextcommand"] = "listdata";
   }
 
+  retval.msg = output;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleListdata(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output, arr;
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
   vector<string> toadd;
 
+  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()) {
     retval.type = error;
-    retval.msg = std::string("File listing failed: Server reports number of "
-                             "remaining chunks as ") +
-                 std::to_string(root["remaining"].asInt()) +
-                 " but actual number is " +
-                 std::to_string(fileman.getListRemainingChunks());
+    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;
-    retval.msg =
-        "File listing cancelled: Server reports: " + root["error"].asString();
+    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()) {
       // everything sent
-      retval.type = notsend;
-      retval.msg = "Files on server:\n";
+      retval.type = print;
       for (string s : fileman.getListData())
-        retval.msg += s + "\n";
+        arr.append(s);
+      output["names"] = arr;
       fileman.closeList();
     } else {
-      retval.type = send;
-      retval.msg = "listdata";
+      retval.type = print | send;
+      output["nextcommand"] = "listdata";
     }
   }
+  retval.msg = output;
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleVersion(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output; // LOCALOUTPUT
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+
+  output["command"] = "version";
+  output["serverversion"] = root["version"].asString();
+  output["clientversion"] = protocolVersion;
 
   if (!root["accept"].asBool()) {
     retval.type = error;
-    retval.msg = "Version check failed: Server reports " +
-                 root["version"].asString() + " but client is version " +
-                 protocolVersion;
+    output["accept"] = false;
   } else {
     retval.type = seton;
-    retval.msg = "Version check ok.";
+    output["accept"] = true;
     doversion = false;
   }
+  retval.msg = output;
 
   return retval;
 }
 
 CmdMan::CmdRet CmdMan::handleLogin(Json::Value root) {
   CmdRet retval;
-  std::cerr << __PRETTY_FUNCTION__ << " begin" << std::endl;
+  Json::Value output; // LOCALOUTPUT
+  DEBUGPRINT(__PRETTY_FUNCTION__ << " begin" << std::endl);
+
+  output["command"] = "login";
 
   if (!root["accept"].asBool()) {
     retval.type = error;
-    retval.msg = "Login failed: " + root["error"].asString();
+    output["error"] = root["error"].asString();
+    output["accept"] = false;
   } else {
     retval.type = seton;
-    retval.msg = "Login ok.";
+    output["error"] = "";
+    output["accept"] = true;
     dologin = false;
   }
+  retval.msg = output;
 
   return retval;
 }

+ 121 - 114
cli/src/ioman.cpp

@@ -17,6 +17,7 @@ using std::string;
 using std::vector;
 
 using boost::asio::buffer;
+using boost::asio::ip::address;
 using boost::asio::ip::tcp;
 
 /* this will be provided by main.cpp for the readline callback */
@@ -42,23 +43,27 @@ IoMan::IoMan(char *ipcstring) : cmdman(fileman) {
 }
 
 IoMan::~IoMan() {
-  networkmutex.lock();
-  inputmutex.lock();
-  responsemutex.lock();
-  runnetwork = false;
-  runinput = false;
-  runresponse = false;
-  networkmutex.unlock();
-  inputmutex.unlock();
-  responsemutex.unlock();
-
   initcv.notify_all();
-  localcv.notify_all();
-  netcv.notify_all();
-
-  tnetwork.join();
-  tinput.join();
-  tresponse.join();
+  if (runnetwork) {
+    networkmutex.lock();
+    runnetwork = false;
+    networkmutex.unlock();
+    tnetwork.join();
+  }
+  if (runinput) {
+    inputmutex.lock();
+    runinput = false;
+    inputmutex.unlock();
+    localcv.notify_all();
+    tinput.join();
+  }
+  if (runresponse) {
+    responsemutex.lock();
+    runresponse = false;
+    responsemutex.unlock();
+    netcv.notify_all();
+    tresponse.join();
+  }
 
   if (connected) {
     disconnect();
@@ -70,24 +75,89 @@ IoMan::~IoMan() {
 
 void IoMan::printMessage(string nouse, OutMsgType nouse2) {}
 
-bool IoMan::connect() {
-  tcp::endpoint *ep;
+vector<string> IoMan::tokenizeInput(string in) {
+  size_t prev, index, quot;
+  vector<string> args;
+  /* tokenize string into command and arguments vector*/
+  if ((index = in.find(" ")) == string::npos) {
+    // only command no args
+    args.push_back(in);
+  } else {
+    args.push_back(in.substr(0, index));
+    index++;
+
+    bool end_tokenizing = false;
+    while (!end_tokenizing) {
+      // find first char thats not a space
+      while (in[index] == ' ') {
+        index++;
+
+        // bounds check
+        if (index == in.size())
+          end_tokenizing = true;
+      }
+      if (end_tokenizing)
+        break;
+
+      in = in.substr(index);
+
+      if (in[0] == '\"') {
+        // quoted string
+        in = in.substr(1);
+        index = in.find("\"");
+        args.push_back(in.substr(0, index));
+        index++;
 
-  ep = new tcp::endpoint(boost::asio::ip::address::from_string(ipstring), 1234);
+        /*
+        tokens.push_back(in.substr(0, ++index));
+        */
 
-  // establish connection
-  printMessage("IoMan::connect() connecting to " + ipstring, debug);
-  tcpsock->connect(*ep, errcode);
+        // char after closing quote should be space while within bounds
+        if (index == in.size())
+          end_tokenizing = true;
+      } else {
+        // non-quoted string
+        index = in.find(" ");
+        if (index == string::npos) { // no spaces, last arg
+          args.push_back(in);
+          end_tokenizing = true;
+        } else {
+          args.push_back(in.substr(0, index));
+        }
+      }
+    }
+  }
+  return args;
+}
+
+bool IoMan::connect() {
+  tcp::endpoint *ep = NULL;
+  address addr;
+  Json::Value root;
+  root["command"] = "connect";
+  root["address"] = ipstring;
+  root["port"] = port;
+  addr = address::from_string(ipstring, errcode);
   if (errcode) {
+    root["error"] = errcode.message();
+    connected = false;
+  } else {
+    // establish connection
+    printMessage("IoMan::connect() connecting to " + ipstring, debug);
+    ep = new tcp::endpoint(addr, port);
+    tcpsock->connect(*ep, errcode);
+    if (errcode) {
+      root["error"] = errcode.message();
+      connected = false;
+    } else {
+      connected = true;
+      root["error"] = "";
+    }
     delete ep;
-    printMessage("IoMan::connect() couldnt connect to " + ipstring + "\n" +
-                     errcode.message(),
-                 error);
-    return false;
   }
-  connected = true;
-  delete ep;
-  return true;
+  root["accept"] = connected;
+  printMessage(Json::writeString(wbuilder, root), normal);
+  return connected;
 }
 
 void IoMan::disconnect() {
@@ -241,8 +311,6 @@ void IoMan::inputMain() {
   CmdMan::CmdRet cmdret;
   std::unique_lock<std::mutex> ulock;
 
-  size_t prev, index, quot;
-
   printMessage("IoMan::inputMain() begin", debug);
   inputmutex.lock();
   while (runinput) {
@@ -278,86 +346,31 @@ void IoMan::inputMain() {
 
     // process
     for (string cmd : toprocess) {
-      args = vector<string>();
-
-      /* tokenize string into command and arguments vector*/
-      if ((index = cmd.find(" ")) == string::npos) {
-        // only command no args
-        command = cmd;
-      } else {
-        command = cmd.substr(0, index);
-        index++;
-
-        bool end_tokenizing = false;
-        while (!end_tokenizing) {
-          // find first char thats not a space
-          while (cmd[index] == ' ') {
-            index++;
-
-            // bounds check
-            if (index == cmd.size())
-              end_tokenizing = true;
-          }
-          if (end_tokenizing)
-            break;
-
-          cmd = cmd.substr(index);
-
-          if (cmd[0] == '\"') {
-            // quoted string
-            cmd = cmd.substr(1);
-            index = cmd.find("\"");
-            args.push_back(cmd.substr(0, index));
-            index++;
-
-            /*
-            tokens.push_back(cmd.substr(0, ++index));
-            */
-
-            // char after closing quote should be space while within bounds
-            if (index == cmd.size())
-              end_tokenizing = true;
-          } else {
-            // non-quoted string
-            index = cmd.find(" ");
-            if (index == string::npos) { // no spaces, last arg
-              args.push_back(cmd);
-              end_tokenizing = true;
-            } else {
-              args.push_back(cmd.substr(0, index));
-            }
-          }
-        }
-      }
-
+      args = tokenizeInput(cmd);
+      command = args.front();
+      args.erase(args.begin());
       cmdret = cmdman.execute(command, args);
 
       // determine wether to send something and do so if required
-      switch (cmdret.type) {
-      case CmdMan::rettype::send: {
-        printMessage("IoMan::inputMain() sending json \"" + cmdret.msg + "\"",
+      if (cmdret.type & CmdMan::rettype::print) {
+        printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
+      }
+      if (cmdret.type & CmdMan::rettype::send) {
+        printMessage("IoMan::inputMain() sending json \"" +
+                         Json::writeString(wbuilder, cmdret.msg) + "\"",
                      debug);
-        boost::asio::write(*tcpsock, buffer(cmdret.msg + "\n"), errcode);
+        boost::asio::write(
+            *tcpsock, buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"),
+            errcode);
         if (errcode) {
           printMessage("IoMan::inputMain() couldnt send json data\n" +
                            errcode.message() + "\n",
                        error);
           continue;
         }
-        break;
-      }
-      case CmdMan::rettype::notsend: {
-        printMessage(cmdret.msg, normal);
-        break;
-      }
-      case CmdMan::rettype::error: {
-        printMessage(cmdret.msg, error);
-        break;
-      }
-      default: {
-        printMessage(string(__PRETTY_FUNCTION__) + string(" unknown rettype"),
-                     debug);
       }
+      if (cmdret.type & CmdMan::rettype::error) {
+        printMessage(Json::writeString(wbuilder, cmdret.msg), error);
       }
     }
 
@@ -413,15 +426,13 @@ void IoMan::responseMain() {
     // process jsons
     for (Json::Value root : toprocess) {
       cmdret = cmdman.handle(root);
-      switch (cmdret.type) {
-      case CmdMan::rettype::close: {
+      if (cmdret.type & CmdMan::rettype::close) {
         /* TODO i dunno */
         mainmutex.lock();
         runmain = false;
         mainmutex.unlock();
-        break;
       }
-      case CmdMan::rettype::error: {
+      if (cmdret.type & CmdMan::rettype::error) {
         initmutex.lock();
         if (versionstatus == off)
           versionstatus = err;
@@ -429,10 +440,9 @@ void IoMan::responseMain() {
           loginstatus = err;
         initmutex.unlock();
         initcv.notify_all();
-        printMessage(cmdret.msg, error);
-        break;
+        printMessage(Json::writeString(wbuilder, cmdret.msg), error);
       }
-      case CmdMan::rettype::seton: {
+      if (cmdret.type & CmdMan::rettype::seton) {
         initmutex.lock();
         if (versionstatus == off)
           versionstatus = on;
@@ -441,14 +451,11 @@ void IoMan::responseMain() {
         initmutex.unlock();
         initcv.notify_all();
       }
-      case CmdMan::rettype::notsend: {
-        printMessage(cmdret.msg, normal);
-        break;
-      }
-      case CmdMan::rettype::send: {
-        toput.push_back(cmdret.msg);
-        break;
+      if (cmdret.type & CmdMan::rettype::print) {
+        printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
       }
+      if (cmdret.type & CmdMan::rettype::send) {
+        toput.push_back(cmdret.msg["nextcommand"].asString());
       }
     }
 

+ 0 - 7
cli/src/main.cpp

@@ -35,13 +35,6 @@ int main(int argc, char **argv) {
     return 1;
   }
 
-  if (!isdigit(argv[1][0])) {
-    std::printf("invalid ip\n");
-    show_help(argv[0]);
-    std::cout << desc;
-    return 1;
-  }
-
   try {
     store(parse_command_line(argc - 1, argv + 1, desc), vm);
     notify(vm);

+ 127 - 2
cli/src/userioman.cpp

@@ -10,11 +10,43 @@
 
 using boost::asio::buffer;
 
+UserIoMan::UserIoMan(char *ipcstring) : IoMan(ipcstring) {
+
+  /* setup json stuff */
+  Json::CharReaderBuilder rbuilder;
+  wbuilder.settings_["indentation"] = "";
+  reader = rbuilder.newCharReader();
+
+  /* initialize print command map */
+  printmap["connect"] = &UserIoMan::printConnect;
+  printmap["help"] = &UserIoMan::printHelp;
+  printmap["status"] = &UserIoMan::printStatus;
+  printmap["disconnect"] = &UserIoMan::printDisconnect;
+  printmap["put"] = &UserIoMan::printPut;
+  printmap["get"] = &UserIoMan::printGet;
+  printmap["list"] = &UserIoMan::printList;
+  printmap["version"] = &UserIoMan::printVersion;
+  printmap["login"] = &UserIoMan::printLogin;
+  printmap["putdata"] = &UserIoMan::printPutdata;
+  printmap["getdata"] = &UserIoMan::printGetdata;
+}
+
+UserIoMan::~UserIoMan() { delete reader; }
+
 void UserIoMan::printMessage(std::string msg, OutMsgType type) {
+  Json::Value root;
   msgmutex.lock();
   switch (type) {
   case normal: {
-    std::cout << msg << std::endl;
+    // this should never happen outside of development
+    if (!reader->parse(msg.c_str(), msg.c_str() + msg.size(), &root,
+                       &jsonerror)) {
+      printMessage(string(__PRETTY_FUNCTION__) +
+                       " couldnt parse json data: " + jsonerror,
+                   error);
+    } else {
+      printJson(root);
+    }
     break;
   }
   case error: {
@@ -22,7 +54,7 @@ void UserIoMan::printMessage(std::string msg, OutMsgType type) {
     break;
   }
   case debug: {
-    std::cerr << msg << std::endl;
+    //~ std::cerr << msg << std::endl;
     break;
   }
   }
@@ -38,3 +70,96 @@ std::string UserIoMan::getCmdPrompt() { return "ccats> "; }
 
 std::string UserIoMan::getUserPrompt() { return "User: "; }
 std::string UserIoMan::getPassPrompt() { return "Pass: "; }
+
+void UserIoMan::printJson(Json::Value root) {
+  map<string, void (UserIoMan::*)(Json::Value)>::iterator it =
+      printmap.find(root["command"].asString());
+  if (it == printmap.end()) {
+    // this should never happen outside of development
+    printMessage(string(__PRETTY_FUNCTION__) + " unknown command \"" +
+                     root["command"].asString() +
+                     "\".\nensure code is implemented.",
+                 error);
+    return;
+  }
+  (this->*(printmap[root["command"].asString()]))(root);
+}
+
+void UserIoMan::printConnect(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Couldnt connect to " << root["address"].asString() << ":"
+              << root["port"].asUInt() << std::endl
+              << "Reason: " << root["error"].asString() << std::endl;
+  }
+}
+
+void UserIoMan::printHelp(Json::Value root) {
+  std::cout << "Available commands are: " << std::endl;
+  for (Json::Value i : root["names"])
+    std::cout << i.asString() << std::endl;
+}
+
+void UserIoMan::printStatus(Json::Value root) {
+  std::cout << "Server reports status: " << root["response"].asString()
+            << std::endl;
+}
+
+void UserIoMan::printDisconnect(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Disconnect failed." << std::endl;
+  } else {
+    std::cout << "Disconnect successful." << std::endl;
+  }
+}
+
+void UserIoMan::printPut(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Upload request for file " << root["file"].asString()
+              << " failed: " << root["error"].asString() << std::endl;
+  } else
+    std::cout << "Begin uploading file " << root["file"].asString()
+              << std::endl;
+}
+
+void UserIoMan::printGet(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Download request for file " << root["file"].asString()
+              << " failed: " << root["error"].asString() << std::endl;
+  } else
+    std::cout << "Begin downloading file " << root["file"].asString()
+              << std::endl;
+}
+
+void UserIoMan::printList(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Listing files failed: " << root["error"].asString()
+              << std::endl;
+  } else {
+    std::cout << "Listing files stored on server: " << std::endl;
+    for (Json::Value i : root["names"])
+      std::cout << i.asString() << std::endl;
+    std::cout << "End of list." << std::endl;
+  }
+}
+
+void UserIoMan::printVersion(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Version check failed. Server reports "
+              << root["serverversion"].asString() << " but client is "
+              << root["clientversion"].asString() << std::endl;
+  } else
+    std::cout << "Version check ok." << std::endl;
+}
+
+void UserIoMan::printLogin(Json::Value root) {
+  if (!root["accept"].asBool()) {
+    std::cout << "Login failed: " << root["error"].asString() << std::endl;
+  } else
+    std::cout << "Login ok." << std::endl;
+}
+
+void UserIoMan::printPutdata(Json::Value root) {}
+
+void UserIoMan::printGetdata(Json::Value root) {}
+
+void UserIoMan::printListdata(Json::Value root) {}