batchioman.cpp 17 KB

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