batchioman.cpp 18 KB

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