batchioman.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. #include "../include/batchioman.h"
  2. #include <iostream>
  3. #include <string>
  4. #include <vector>
  5. BatchIoMan::BatchIoMan(bool usessl, const char *certfile, bool beverbose, std::string batchpath) : IoMan(usessl, certfile) {
  6. /* setup json stuff */
  7. Json::CharReaderBuilder rbuilder;
  8. wbuilder.settings_["indentation"] = "";
  9. reader = rbuilder.newCharReader();
  10. /* initialize print command map */
  11. printmap["error"] = &BatchIoMan::printError;
  12. printmap["connect"] = &BatchIoMan::printConnect;
  13. printmap["help"] = &BatchIoMan::printHelp;
  14. printmap["status"] = &BatchIoMan::printStatus;
  15. printmap["extendedstatus"] = &BatchIoMan::printExtendedstatus;
  16. printmap["disconnect"] = &BatchIoMan::printDisconnect;
  17. printmap["put"] = &BatchIoMan::printPut;
  18. printmap["get"] = &BatchIoMan::printGet;
  19. printmap["list"] = &BatchIoMan::printList;
  20. printmap["extendedlist"] = &BatchIoMan::printExtendedlist;
  21. printmap["version"] = &BatchIoMan::printVersion;
  22. printmap["login"] = &BatchIoMan::printLogin;
  23. printmap["signup"] = &BatchIoMan::printSignup;
  24. printmap["putdata"] = &BatchIoMan::printPutdata;
  25. printmap["getdata"] = &BatchIoMan::printGetdata;
  26. printmap["head"] = &BatchIoMan::printHead;
  27. printmap["deletefile"] = &BatchIoMan::printDeletefile;
  28. printmap["deleteme"] = &BatchIoMan::printDeleteme;
  29. printmap["queue"] = &BatchIoMan::printQueue;
  30. printmap["dequeue"] = &BatchIoMan::printDequeue;
  31. printmap["keyfile"] = &BatchIoMan::printKeyfile;
  32. printmap["closekey"] = &BatchIoMan::printClosekey;
  33. getnextline = true;
  34. verbose = beverbose;
  35. filepath = batchpath;
  36. }
  37. bool BatchIoMan::init() {
  38. batchin.open(filepath);
  39. normalout.open(filepath + ".out");
  40. if (verbose)
  41. debugout.open(filepath + ".debug");
  42. errorout.open(filepath + ".err");
  43. if (!batchin.is_open() || !normalout.is_open() || (verbose && !debugout.is_open()) || !errorout.is_open())
  44. return false;
  45. return IoMan::init();
  46. }
  47. BatchIoMan::~BatchIoMan() {
  48. batchin.close();
  49. normalout.close();
  50. if (verbose)
  51. debugout.close();
  52. errorout.close();
  53. delete reader;
  54. }
  55. void BatchIoMan::printMessage(std::string msg, OutMsgType type) {
  56. Json::Value root;
  57. msgmutex.lock();
  58. switch (type) {
  59. case normal: {
  60. // this should never happen outside of development
  61. if (!reader->parse(msg.c_str(), msg.c_str() + msg.size(), &root, &jsonerror)) {
  62. printMessage(string(__PRETTY_FUNCTION__) + " couldnt parse json data: " + jsonerror, debug);
  63. } else {
  64. normalout << printJson(root) << std::endl;
  65. }
  66. break;
  67. }
  68. case error: {
  69. // this should never happen outside of development
  70. if (!reader->parse(msg.c_str(), msg.c_str() + msg.size(), &root, &jsonerror)) {
  71. printMessage(string(__PRETTY_FUNCTION__) + " couldnt parse json data: " + jsonerror, debug);
  72. } else {
  73. errorout << printJson(root) << std::endl;
  74. }
  75. break;
  76. }
  77. case debug: {
  78. if (verbose)
  79. debugout << msg << std::endl;
  80. break;
  81. }
  82. }
  83. msgmutex.unlock();
  84. }
  85. void BatchIoMan::printWelcomeMessage() {}
  86. std::string BatchIoMan::getCmdPrompt() { return ""; }
  87. /* modified handleInCmdResponse to abort on error */
  88. void BatchIoMan::handleInCmdResponse(CmdMan::CmdRet cmdret) {
  89. // determine wether to send something and do so if required
  90. if (cmdret.type & CmdMan::rettype::print) {
  91. printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
  92. if (!(cmdret.type ^ CmdMan::rettype::print)) {
  93. // xor here works because flag is set at this point
  94. // if we only printed something get the next line
  95. linemutex.lock();
  96. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  97. getnextline = true;
  98. linemutex.unlock();
  99. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  100. linecv.notify_all();
  101. }
  102. }
  103. if (cmdret.type & CmdMan::rettype::send) {
  104. printMessage("IoMan::inputMain() sending json \"" + Json::writeString(wbuilder, cmdret.msg) + "\"", debug);
  105. if (usessl)
  106. boost::asio::write(*sslsock, boost::asio::buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
  107. else
  108. boost::asio::write(*tcpsock, boost::asio::buffer(Json::writeString(wbuilder, cmdret.msg) + "\n"), errcode);
  109. if (errcode) {
  110. printMessage("IoMan::inputMain() couldnt send json data\n" + errcode.message() + "\n", error);
  111. mainmutex.lock();
  112. runmain = false;
  113. mainmutex.unlock();
  114. linemutex.lock();
  115. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  116. getnextline = true;
  117. linemutex.unlock();
  118. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  119. linecv.notify_all();
  120. return;
  121. }
  122. }
  123. if (cmdret.type & CmdMan::rettype::error) {
  124. printMessage(Json::writeString(wbuilder, cmdret.msg), error);
  125. mainmutex.lock();
  126. runmain = false;
  127. mainmutex.unlock();
  128. linemutex.lock();
  129. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  130. getnextline = true;
  131. linemutex.unlock();
  132. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  133. linecv.notify_all();
  134. }
  135. if (cmdret.type & CmdMan::rettype::close) {
  136. /* TODO i dunno */
  137. mainmutex.lock();
  138. runmain = false;
  139. mainmutex.unlock();
  140. linemutex.lock();
  141. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  142. getnextline = true;
  143. linemutex.unlock();
  144. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  145. linecv.notify_all();
  146. }
  147. if (cmdret.type & CmdMan::rettype::connect) {
  148. ipstring = cmdret.msg["address"].asString();
  149. port = cmdret.msg["port"].asUInt();
  150. if (connect()) {
  151. runnetwork = true;
  152. tnetwork = std::thread(&BatchIoMan::networkMain, this);
  153. // put new commands into global vector
  154. localmutex.lock();
  155. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
  156. localinput.push_back("version");
  157. cmdman.stateSetConnectionOk();
  158. localmutex.unlock();
  159. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
  160. localcv.notify_all();
  161. }
  162. }
  163. if (cmdret.type & CmdMan::rettype::exit) {
  164. mainmutex.lock();
  165. runmain = false;
  166. mainmutex.unlock();
  167. linemutex.lock();
  168. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  169. getnextline = true;
  170. linemutex.unlock();
  171. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  172. linecv.notify_all();
  173. return;
  174. }
  175. if (cmdret.nextcommand.size()) {
  176. localmutex.lock();
  177. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
  178. localinput.push_back(cmdret.nextcommand);
  179. localmutex.unlock();
  180. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
  181. localcv.notify_all();
  182. }
  183. }
  184. /* modified handleOutCmdResponse to fetch next command and abort on error */
  185. void BatchIoMan::handleOutCmdResponse(CmdMan::CmdRet cmdret, std::vector<std::string> &toput) {
  186. if (cmdret.type & CmdMan::rettype::close) {
  187. // connection closed, stop network thread and shutdown any operations remaining
  188. networkmutex.lock();
  189. runnetwork = false;
  190. networkmutex.unlock();
  191. disconnect();
  192. tnetwork.join();
  193. if (cmdret.nextcommand.size()) {
  194. toput.push_back(cmdret.nextcommand);
  195. }
  196. }
  197. if (cmdret.type & CmdMan::rettype::error) {
  198. printMessage(Json::writeString(wbuilder, cmdret.msg), error);
  199. mainmutex.lock();
  200. runmain = false;
  201. mainmutex.unlock();
  202. linemutex.lock();
  203. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  204. getnextline = true;
  205. linemutex.unlock();
  206. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  207. linecv.notify_all();
  208. return;
  209. }
  210. if (cmdret.type & CmdMan::rettype::print) {
  211. printMessage(Json::writeString(wbuilder, cmdret.msg), normal);
  212. }
  213. if (cmdret.type & CmdMan::rettype::send) {
  214. printMessage(string(__PRETTY_FUNCTION__) + string(" send new cmd"), debug);
  215. if (cmdret.nextcommand.size()) {
  216. toput.push_back(cmdret.nextcommand);
  217. }
  218. }
  219. if (!(cmdret.type & CmdMan::rettype::send)) {
  220. // only fetch next line if we did not send a new command on our own
  221. // if we managed to get here, get next command from file
  222. linemutex.lock();
  223. printMessage(string(__PRETTY_FUNCTION__) + string(" get linemutex"), debug);
  224. getnextline = true;
  225. linemutex.unlock();
  226. printMessage(string(__PRETTY_FUNCTION__) + string(" release linemutex"), debug);
  227. linecv.notify_all();
  228. }
  229. }
  230. /* main user input loop */
  231. void BatchIoMan::run() {
  232. std::string line;
  233. printMessage(string(__PRETTY_FUNCTION__) + " begin", debug);
  234. std::unique_lock<std::mutex> ulock;
  235. runmain = true;
  236. mainmutex.lock();
  237. while (runmain) {
  238. mainmutex.unlock();
  239. line.erase();
  240. ulock = std::unique_lock<std::mutex>(linemutex);
  241. while (!getnextline && runmain) {
  242. linecv.wait(ulock);
  243. }
  244. if (!runmain)
  245. break;
  246. printMessage(string(__PRETTY_FUNCTION__) + " fetch next line", debug);
  247. while (!line.size()) {
  248. // skip empty lines until either eof or non-empty line
  249. if (batchin.eof()) {
  250. line = "exit";
  251. } else
  252. std::getline(batchin, line);
  253. }
  254. getnextline = false;
  255. linemutex.unlock();
  256. localmutex.lock();
  257. printMessage(string(__PRETTY_FUNCTION__) + string(" get localmutex"), debug);
  258. localinput.push_back(line);
  259. localmutex.unlock();
  260. printMessage(string(__PRETTY_FUNCTION__) + string(" release localmutex"), debug);
  261. localcv.notify_all();
  262. mainmutex.lock();
  263. }
  264. mainmutex.unlock();
  265. }
  266. std::string BatchIoMan::printJson(Json::Value root) {
  267. map<string, std::string (BatchIoMan::*)(Json::Value)>::iterator it = printmap.find(root["command"].asString());
  268. if (it == printmap.end()) {
  269. // this should never happen outside of development
  270. printMessage(string(__PRETTY_FUNCTION__) + " unknown command \"" + root["command"].asString() + "\".\nEnsure code is implemented.", debug);
  271. return "";
  272. }
  273. return (this->*(printmap[root["command"].asString()]))(root);
  274. }
  275. std::string BatchIoMan::printError(Json::Value root) { return std::string("Error: ") + root["error"].asString(); }
  276. std::string BatchIoMan::printConnect(Json::Value root) {
  277. if (!root["accept"].asBool()) {
  278. return std::string("Couldnt connect to ") + root["address"].asString() + ":" + std::to_string(root["port"].asUInt()) + "\n" +
  279. "Reason: " + root["error"].asString();
  280. }
  281. return "";
  282. }
  283. std::string BatchIoMan::printHelp(Json::Value root) {
  284. std::string ret = std::string("Available commands are: ") + "\n";
  285. for (Json::Value i : root["names"])
  286. ret += i.asString() + "\n";
  287. return ret;
  288. }
  289. std::string BatchIoMan::printStatus(Json::Value root) { return std::string("Server reports status: ") + root["response"].asString(); }
  290. std::string BatchIoMan::printExtendedstatus(Json::Value root) {
  291. if (!root["accept"].asBool()) {
  292. return "Listing transfers failed. Server reports: " + root["error"].asString();
  293. } else {
  294. std::string retval = "";
  295. if (!root["transfersclientserver"].empty()) {
  296. retval += "\nTransfers between clients and server:\n";
  297. /*
  298. * EXAMPLE:
  299. * type progress file
  300. * download 99% foobar.txt
  301. * upload 1% baz.html
  302. */
  303. retval += "type progress file\n";
  304. for (Json::Value val : root["transfersclientserver"]) {
  305. std::string type = val["upload"].asBool() ? "upload" : "download";
  306. std::string progress = std::to_string(val["progress"].asInt());
  307. std::string file = val["file"].asString();
  308. char output[21];
  309. std::snprintf(output, 21, "%-8s %7.7s%% ", type.c_str(), progress.c_str());
  310. retval += output + file + "\n";
  311. }
  312. }
  313. if (!root["transfersserverserver"].empty()) {
  314. retval += "\nTransfers between different servers:\n";
  315. /*
  316. * EXAMPLE:
  317. * type progress method bytes/sec file
  318. * download 99% method0000001 9000.01 foobar.txt
  319. * queued 1% urgent field 42.00 baz.html
  320. *
  321. * Too long method strings get truncated, unexpectedly high speeds are shown (with less pretty format), e.g.:
  322. *
  323. * download 95% too long stri 340282346638528859811704183484516925440.00 filename.zip
  324. */
  325. retval += "type progress method bytes/sec file\n";
  326. for (Json::Value val : root["transfersclientserver"]) {
  327. std::string type = val["type"].asString();
  328. std::string progress = std::to_string(val["progress"].asInt());
  329. std::string method = val["method"].asString();
  330. float speed = val["speed"].asFloat();
  331. std::string file = val["file"].asString();
  332. // size of 80 is just enough for maximum possible float value to fit as string
  333. char output[80];
  334. std::snprintf(output, 80, "%-8s %7.7s%% %-13.13s %15.2f ", type.c_str(), progress.c_str(), method.c_str(), speed);
  335. std::cout << output << file << std::endl;
  336. retval += output + file + "\n";
  337. }
  338. }
  339. if (root["transfersclientserver"].empty() && root["transfersserverserver"].empty()) {
  340. retval += "No transfers running.";
  341. }
  342. return retval;
  343. }
  344. }
  345. std::string BatchIoMan::printDisconnect(Json::Value root) {
  346. if (!root["accept"].asBool()) {
  347. return "Disconnect failed.";
  348. } else {
  349. return "Disconnect successful.";
  350. }
  351. }
  352. std::string BatchIoMan::printPut(Json::Value root) {
  353. if (!root["accept"].asBool()) {
  354. if (root.isMember("file")) {
  355. return std::string("Upload request for file ") + root["file"].asString() + " failed: " + root["error"].asString();
  356. } else {
  357. return std::string("Upload request failed: ") + root["error"].asString();
  358. }
  359. } else
  360. return std::string("Begin uploading file ") + root["file"].asString();
  361. }
  362. std::string BatchIoMan::printGet(Json::Value root) {
  363. if (!root["accept"].asBool()) {
  364. if (root.isMember("file")) {
  365. return std::string("Download request for file ") + root["file"].asString() + " failed: " + root["error"].asString();
  366. } else {
  367. return std::string("Download request failed: ") + root["error"].asString();
  368. }
  369. } else
  370. return std::string("Begin downloading file ") + root["file"].asString();
  371. }
  372. std::string BatchIoMan::printList(Json::Value root) {
  373. std::string ret;
  374. if (!root["accept"].asBool()) {
  375. ret = std::string("Listing files failed: ") + root["error"].asString();
  376. } else {
  377. ret = std::string("Listing files stored on server: ") + "\n";
  378. for (Json::Value i : root["names"])
  379. ret += i.asString() + "\n";
  380. ret += "End of list.";
  381. }
  382. return ret;
  383. }
  384. std::string BatchIoMan::printExtendedlist(Json::Value root) {
  385. std::string ret;
  386. if (!root["accept"].asBool()) {
  387. ret = "Listing files failed: " + root["error"].asString();
  388. } else {
  389. if (!root["files"].empty()) {
  390. ret = "Files stored on server: \n";
  391. /*
  392. * EXAMPLE:
  393. * size (kBytes) decryptable file
  394. * 9000.01 yes foo.txt
  395. * 42.00 no baz.html
  396. * 0.02 plaintext bar.zip
  397. */
  398. ret += "size (kBytes) decryptable file\n";
  399. for (Json::Value val : root["files"]) {
  400. float size = val["size"].asFloat();
  401. std::string encrypted = val["encrypted"].asString();
  402. std::string decryptable;
  403. if (encrypted == "decryptable") {
  404. decryptable = "yes ";
  405. } else if (encrypted == "undecryptable") {
  406. decryptable = "no ";
  407. } else if (encrypted == "unknown") {
  408. decryptable = "unknown ";
  409. } else {
  410. decryptable = "plaintext ";
  411. }
  412. std::string progress = std::to_string(val["progress"].asInt());
  413. std::string file = val["name"].asString();
  414. char sizeString[44];
  415. std::snprintf(sizeString, 44, "%13.2f ", size);
  416. ret += std::string(sizeString) + decryptable + file + "\n";
  417. }
  418. } else {
  419. std::cout << "No files stored on server." << std::endl;
  420. }
  421. }
  422. return ret;
  423. }
  424. std::string BatchIoMan::printVersion(Json::Value root) {
  425. if (!root["accept"].asBool()) {
  426. return std::string("Version check failed. Server reports version ") + root["serverversion"].asString() + " but client is " +
  427. root["clientversion"].asString();
  428. } else
  429. return "Version check ok.";
  430. }
  431. std::string BatchIoMan::printLogin(Json::Value root) {
  432. if (!root["accept"].asBool()) {
  433. return std::string("Login failed: ") + root["error"].asString();
  434. } else
  435. return "Login ok.";
  436. }
  437. std::string BatchIoMan::printSignup(Json::Value root) {
  438. if (!root["accept"].asBool()) {
  439. return std::string("Signup failed: ") + root["error"].asString();
  440. } else
  441. return "Signup ok. You are now logged in.";
  442. }
  443. std::string BatchIoMan::printDeleteme(Json::Value root) {
  444. if (!root["accept"].asBool()) {
  445. return std::string("User deletion failed: ") + root["error"].asString();
  446. } else
  447. return "User deletion ok. You are now disconnected from the server.";
  448. }
  449. std::string BatchIoMan::printPutdata(Json::Value root) { return ""; }
  450. std::string BatchIoMan::printGetdata(Json::Value root) { return ""; }
  451. std::string BatchIoMan::printListdata(Json::Value root) { return ""; }
  452. std::string BatchIoMan::printHead(Json::Value root) {
  453. if (!root["accept"].asBool())
  454. return std::string("Request of the first few bytes failed. ") + root["error"].asString();
  455. else
  456. return std::string("First few bytes of file ") + root["file"].asString() + " are: " + root["data"].asString();
  457. }
  458. std::string BatchIoMan::printDeletefile(Json::Value root) {
  459. if (!root["accept"].asBool())
  460. return std::string("Deletion of file ") + root["file"].asString() + " failed. " + root["error"].asString();
  461. else
  462. return std::string("File ") + root["file"].asString() + " deleted succesfully";
  463. }
  464. std::string BatchIoMan::printKeyfile(Json::Value root) {
  465. if (!root["accept"].asBool())
  466. return std::string("Couldnt select keyfile ") + root["file"].asString() + ": " + root["error"].asString();
  467. else
  468. return std::string("Using keyfile ") + root["file"].asString();
  469. }
  470. std::string BatchIoMan::printClosekey(Json::Value root) {
  471. if (!root["accept"].asBool())
  472. return std::string("Failed to close key: ") + root["error"].asString();
  473. else
  474. return "Key closed.";
  475. }
  476. std::string BatchIoMan::printQueue(Json::Value root) {
  477. if (!root["accept"].asBool())
  478. return std::string("Queueing of file ") + root["file"].asString() + " failed. " + root["error"].asString();
  479. else
  480. return std::string("File ") + root["file"].asString() + " queued succesfully.";
  481. }
  482. std::string BatchIoMan::printDequeue(Json::Value root) {
  483. if (!root["accept"].asBool())
  484. return std::string("Dequeueing of file ") + root["file"].asString() + " failed. " + root["error"].asString();
  485. else
  486. return std::string("File ") + root["file"].asString() + " dequeued succesfully.";
  487. }
  488. std::string BatchIoMan::printNotifications(Json::Value root) {
  489. std::string ret;
  490. if (!root["accept"].asBool()) {
  491. return std::string("Failed to get notifications: ") + root["error"].asString();
  492. } else {
  493. ret = "New notifications:\n";
  494. for (Json::Value i : root["messages"])
  495. ret += i.asString() + "\n";
  496. }
  497. return ret;
  498. }