cmdman.cpp 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. #include "../include/cmdman.h"
  2. #include "../include/global.h"
  3. #include <iostream>
  4. #define DEBUGPRINT(x) debugprintfunc(x)
  5. //~ #define DEBUGPRINT(x) std::cerr << x
  6. //~ #define DEBUGPRINT(x)
  7. CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
  8. /* setup json stuff */
  9. Json::CharReaderBuilder rbuilder;
  10. wbuilder.settings_["indentation"] = "";
  11. reader = rbuilder.newCharReader();
  12. currentState = connectionpossible;
  13. /* initialize execute command map */
  14. execmap["help"] = &CmdMan::cmdHelp;
  15. execmap["status"] = &CmdMan::cmdStatus;
  16. execmap["extendedstatus"] = &CmdMan::cmdExtendedstatus;
  17. execmap["disconnect"] = &CmdMan::cmdDisconnect;
  18. execmap["put"] = &CmdMan::cmdPut;
  19. execmap["get"] = &CmdMan::cmdGet;
  20. execmap["list"] = &CmdMan::cmdList;
  21. execmap["extendedlist"] = &CmdMan::cmdExtendedlist;
  22. execmap["version"] = &CmdMan::cmdVersion;
  23. execmap["login"] = &CmdMan::cmdLogin;
  24. execmap["signup"] = &CmdMan::cmdSignup;
  25. execmap["putdata"] = &CmdMan::cmdPutdata;
  26. execmap["getdata"] = &CmdMan::cmdGetdata;
  27. execmap["listdata"] = &CmdMan::cmdListdata;
  28. execmap["extendedlistdata"] = &CmdMan::cmdExtendedlistdata;
  29. execmap["head"] = &CmdMan::cmdHead;
  30. execmap["deletefile"] = &CmdMan::cmdDeletefile;
  31. execmap["deleteme"] = &CmdMan::cmdDeleteme;
  32. execmap["keyfile"] = &CmdMan::cmdKeyfile;
  33. execmap["closekey"] = &CmdMan::cmdClosekey;
  34. execmap["queue"] = &CmdMan::cmdQueue;
  35. execmap["dequeue"] = &CmdMan::cmdDequeue;
  36. execmap["notifications"] = &CmdMan::cmdNotifications;
  37. execmap["connect"] = &CmdMan::cmdConnect;
  38. execmap["exit"] = &CmdMan::cmdExit;
  39. /* initialize description map */
  40. helpmap["help"] = descHelp;
  41. helpmap["status"] = descStatus;
  42. helpmap["extendedstatus"] = descExtendedstatus;
  43. helpmap["disconnect"] = descDisconnect;
  44. helpmap["put"] = descPut;
  45. helpmap["get"] = descGet;
  46. helpmap["list"] = descList;
  47. helpmap["extendedlist"] = descExtendedlist;
  48. helpmap["head"] = descHead;
  49. helpmap["login"] = descLogin;
  50. helpmap["signup"] = descSignup;
  51. helpmap["deletefile"] = descDeletefile;
  52. helpmap["deleteme"] = descDeleteme;
  53. helpmap["keyfile"] = descKeyfile;
  54. helpmap["closekey"] = descClosekey;
  55. helpmap["queue"] = descQueue;
  56. helpmap["dequeue"] = descDequeue;
  57. helpmap["notifications"] = descNotifications;
  58. helpmap["connect"] = descConnect;
  59. helpmap["exit"] = descExit;
  60. /* initialize handle command map */
  61. handlemap["status"] = &CmdMan::handleStatus;
  62. handlemap["extendedstatus"] = &CmdMan::handleExtendedstatus;
  63. handlemap["close"] = &CmdMan::handleClose;
  64. handlemap["put"] = &CmdMan::handlePut;
  65. handlemap["get"] = &CmdMan::handleGet;
  66. handlemap["putdata"] = &CmdMan::handlePutdata;
  67. handlemap["getdata"] = &CmdMan::handleGetdata;
  68. handlemap["list"] = &CmdMan::handleList;
  69. handlemap["extendedlist"] = &CmdMan::handleExtendedlist;
  70. handlemap["version"] = &CmdMan::handleVersion;
  71. handlemap["login"] = &CmdMan::handleLogin;
  72. handlemap["signup"] = &CmdMan::handleSignup;
  73. handlemap["listdata"] = &CmdMan::handleListdata;
  74. handlemap["extendedlistdata"] = &CmdMan::handleExtendedlistdata;
  75. handlemap["head"] = &CmdMan::handleHead;
  76. handlemap["deletefile"] = &CmdMan::handleDeletefile;
  77. handlemap["deleteme"] = &CmdMan::handleDeleteme;
  78. handlemap["queue"] = &CmdMan::handleQueue;
  79. handlemap["dequeue"] = &CmdMan::handleDequeue;
  80. handlemap["notifications"] = &CmdMan::handleNotifications;
  81. debugprintfunc = dpf;
  82. }
  83. CmdMan::~CmdMan() { delete reader; }
  84. void CmdMan::stateSetConnectionOk() { currentState = versionpossible; }
  85. void CmdMan::stateSetDisconnected() {
  86. currentState = connectionpossible;
  87. fileman.cancelGet();
  88. fileman.cancelPut();
  89. fileman.cancelList();
  90. }
  91. CmdMan::CmdRet CmdMan::cmdHelp(vector<string> args) {
  92. CmdRet retval;
  93. Json::Value root, arr;
  94. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  95. map<string, string>::iterator it;
  96. root["command"] = "help";
  97. for (it = helpmap.begin(); it != helpmap.end(); it++) {
  98. arr.append(it->first + " - " + it->second);
  99. }
  100. root["names"] = arr;
  101. retval.type = print;
  102. retval.msg = root;
  103. return retval;
  104. }
  105. CmdMan::CmdRet CmdMan::cmdStatus(vector<string> args) {
  106. CmdRet retval;
  107. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  108. Json::Value root;
  109. root["command"] = "status";
  110. if (currentState == normal) {
  111. retval.type = send;
  112. } else if (currentState == connectionpossible || currentState == disconnecttoexit || currentState == disconnecttoexitearly) {
  113. retval.type = print;
  114. root["response"] = "You are not connected.";
  115. } else {
  116. retval.type = print;
  117. root["response"] = "Connected to " + ip + ":" + std::to_string(port) + ". Not logged in.";
  118. }
  119. retval.msg = root;
  120. return retval;
  121. }
  122. CmdMan::CmdRet CmdMan::cmdExtendedstatus(vector<string> args) {
  123. CmdRet retval;
  124. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  125. Json::Value root;
  126. root["command"] = "extendedstatus";
  127. retval.type = send;
  128. retval.msg = root;
  129. return retval;
  130. }
  131. CmdMan::CmdRet CmdMan::cmdDisconnect(vector<string> args) {
  132. CmdRet retval;
  133. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  134. Json::Value root;
  135. retval.type = send;
  136. if (currentState == loginpossible || currentState == disconnecttoexitearly) {
  137. // not logged in, send appropriate login message instead of normal close
  138. root["login"] = false;
  139. root["user"] = "";
  140. root["pass"] = "";
  141. root["cancel"] = true;
  142. retval.type |= close | noanswerexpected;
  143. if (currentState == disconnecttoexitearly) {
  144. retval.nextcommand = "exit";
  145. }
  146. currentState = connectionpossible;
  147. } else {
  148. root["command"] = "close";
  149. }
  150. retval.msg = root;
  151. return retval;
  152. }
  153. CmdMan::CmdRet CmdMan::cmdPut(vector<string> args) {
  154. CmdRet retval;
  155. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  156. Json::Value root;
  157. if (args.size() < 1) {
  158. retval.type = error;
  159. root["command"] = "error";
  160. root["error"] = "Not enough arguments, put requires at least 1 argument.";
  161. } else {
  162. root["command"] = "put";
  163. if (fileman.isPutting()) {
  164. retval.type = error;
  165. root["file"] = args[0];
  166. root["accept"] = false;
  167. root["error"] = "already putting file \"" + fileman.getPutName() + "\"";
  168. } else {
  169. bool opened = fileman.openPut(args[0]);
  170. if (opened) {
  171. root["file"] = fileman.getPutName();
  172. root["size"] = fileman.getPutSize();
  173. root["chunks"] = fileman.getPutChunks();
  174. retval.type = send;
  175. } else {
  176. retval.type = error;
  177. root["file"] = fileman.pathToFilename(args[0]);
  178. root["accept"] = false;
  179. root["error"] = "couldnt open local file \"" + args[0] + "\"";
  180. }
  181. }
  182. }
  183. retval.msg = root;
  184. return retval;
  185. }
  186. CmdMan::CmdRet CmdMan::cmdPutdata(vector<string> args) {
  187. CmdRet retval;
  188. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  189. Json::Value root;
  190. root["command"] = "putdata";
  191. if (!fileman.isPutting()) {
  192. root["error"] = "Client cannot handle input (received command \"putdata\").";
  193. retval.type = error;
  194. } else {
  195. root["file"] = fileman.getPutName();
  196. root["cancel"] = false;
  197. root["data"] = fileman.readBase64();
  198. root["remaining"] = fileman.getPutRemainingChunks(); // number already decremented by readBase64
  199. retval.type = send;
  200. }
  201. retval.msg = root;
  202. return retval;
  203. }
  204. CmdMan::CmdRet CmdMan::cmdGet(vector<string> args) {
  205. CmdRet retval;
  206. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  207. Json::Value root;
  208. if (args.size() < 1) {
  209. retval.type = error;
  210. root["command"] = "error";
  211. root["error"] = "Not enough arguments, get requires at least 1 argument.";
  212. } else {
  213. root["command"] = "get";
  214. if (fileman.isGetting()) {
  215. retval.type = error;
  216. root["file"] = args[0];
  217. root["accept"] = false;
  218. root["error"] = "already getting file \"" + fileman.getGetName() + "\"";
  219. } else {
  220. bool opened = fileman.openGet(args[0]);
  221. root["file"] = fileman.getGetName();
  222. if (opened) {
  223. root["file"] = fileman.getGetName();
  224. retval.type = send;
  225. } else {
  226. root["file"] = fileman.pathToFilename(args[0]);
  227. root["accept"] = false;
  228. root["error"] = "local file \"" + args[0] + "\" already exists";
  229. retval.type = error;
  230. }
  231. }
  232. }
  233. retval.msg = root;
  234. return retval;
  235. }
  236. CmdMan::CmdRet CmdMan::cmdGetdata(vector<string> args) {
  237. CmdRet retval;
  238. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  239. Json::Value root;
  240. root["command"] = "getdata";
  241. if (!fileman.isGetting()) {
  242. root["error"] = "Client cannot handle input (received command \"getdata\").";
  243. retval.type = error;
  244. } else {
  245. root["file"] = fileman.getGetName();
  246. root["chunk"] = fileman.getGetRemainingChunks();
  247. root["cancel"] = false;
  248. retval.type = send;
  249. }
  250. retval.msg = root;
  251. return retval;
  252. }
  253. CmdMan::CmdRet CmdMan::cmdList(vector<string> args) {
  254. CmdRet retval;
  255. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  256. Json::Value root;
  257. bool opened = fileman.openList(false);
  258. root["command"] = "list";
  259. if (opened) {
  260. retval.type = send;
  261. } else {
  262. retval.type = error;
  263. root["accept"] = false;
  264. root["names"] = "";
  265. root["error"] = "cannot list, already listing";
  266. }
  267. retval.msg = root;
  268. return retval;
  269. }
  270. CmdMan::CmdRet CmdMan::cmdListdata(vector<string> args) {
  271. CmdRet retval;
  272. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  273. Json::Value root;
  274. if (!fileman.isListingSimple()) {
  275. root["command"] = "list";
  276. root["error"] = "Client cannot handle input (received command \"listdata\").";
  277. retval.type = error;
  278. } else {
  279. root["command"] = "listdata";
  280. root["chunk"] = fileman.getListRemainingChunks();
  281. root["cancel"] = false;
  282. retval.type = send;
  283. }
  284. retval.msg = root;
  285. return retval;
  286. }
  287. CmdMan::CmdRet CmdMan::cmdExtendedlist(vector<string> args) {
  288. CmdRet retval;
  289. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  290. Json::Value root;
  291. bool opened = fileman.openList(true);
  292. root["command"] = "extendedlist";
  293. if (opened) {
  294. retval.type = send;
  295. } else {
  296. retval.type = error;
  297. root["accept"] = false;
  298. root["names"] = "";
  299. root["error"] = "cannot list, already listing";
  300. }
  301. retval.msg = root;
  302. return retval;
  303. }
  304. CmdMan::CmdRet CmdMan::cmdExtendedlistdata(vector<string> args) {
  305. CmdRet retval;
  306. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  307. Json::Value root;
  308. if (!fileman.isListingExtended()) {
  309. root["command"] = "extendedlist";
  310. root["error"] = "Client cannot handle input (received command \"listdata\").";
  311. retval.type = error;
  312. } else {
  313. root["command"] = "extendedlistdata";
  314. root["chunk"] = fileman.getListRemainingChunks();
  315. root["cancel"] = false;
  316. retval.type = send;
  317. }
  318. retval.msg = root;
  319. return retval;
  320. }
  321. CmdMan::CmdRet CmdMan::cmdHead(vector<string> args) {
  322. CmdRet retval;
  323. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  324. Json::Value root;
  325. if (args.size() < 1) {
  326. retval.type = error;
  327. root["command"] = "error";
  328. root["accept"] = false;
  329. root["error"] = "Not enough arguments, head requires at least 1 argument.";
  330. } else {
  331. root["command"] = "head";
  332. root["file"] = args[0];
  333. retval.type = send;
  334. }
  335. retval.msg = root;
  336. return retval;
  337. }
  338. CmdMan::CmdRet CmdMan::cmdDeletefile(vector<string> args) {
  339. CmdRet retval;
  340. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  341. Json::Value root;
  342. if (args.size() < 1) {
  343. retval.type = error;
  344. root["command"] = "error";
  345. root["accept"] = false;
  346. root["error"] = "Not enough arguments, deletefile requires at least 1 argument.";
  347. } else {
  348. root["command"] = "deletefile";
  349. root["file"] = args[0];
  350. retval.type = send;
  351. }
  352. retval.msg = root;
  353. return retval;
  354. }
  355. CmdMan::CmdRet CmdMan::cmdConnect(vector<string> args) {
  356. CmdRet retval;
  357. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  358. Json::Value root;
  359. if (args.size() < 1) {
  360. retval.type = error;
  361. root["command"] = "error";
  362. root["accept"] = false;
  363. root["error"] = "Not enough arguments, connect requires at least 1 argument.";
  364. } else if (currentState != connectionpossible) {
  365. retval.type = error;
  366. root["command"] = "error"; // analogous to execute() method when command unavailable
  367. root["error"] = "Connecting not possible, you are already connected to a server.";
  368. } else {
  369. // set internal ip and port fields first
  370. ip = args[0];
  371. if (args.size() < 2) {
  372. port = 1234;
  373. } else {
  374. port = (unsigned int)stoul(args[1]);
  375. }
  376. // construct json
  377. retval.type = connect;
  378. root["command"] = "connect";
  379. root["address"] = ip;
  380. root["port"] = port;
  381. }
  382. retval.msg = root;
  383. return retval;
  384. }
  385. CmdMan::CmdRet CmdMan::execute(string cmd, vector<string> args) {
  386. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  387. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " using command \"" + cmd + "\" with arguments [ ");
  388. for (string s : args)
  389. DEBUGPRINT(s + " ");
  390. DEBUGPRINT("]");
  391. cmdmutex.lock();
  392. map<string, CmdRet (CmdMan::*)(vector<string>)>::iterator execit = execmap.find(cmd);
  393. vector<string>::const_iterator alwaysit;
  394. vector<string>::const_iterator connectit;
  395. for (alwaysit = cmdAllowAlways.cbegin(); alwaysit != cmdAllowAlways.cend(); alwaysit++)
  396. if (*alwaysit == cmd)
  397. break;
  398. for (connectit = cmdAllowAfterConnect.cbegin(); connectit != cmdAllowAfterConnect.cend(); connectit++)
  399. if (*connectit == cmd)
  400. break;
  401. CmdRet retval;
  402. Json::Value root;
  403. root["command"] = cmd;
  404. if (execit == execmap.end()) {
  405. retval.type = error;
  406. root["command"] = "error";
  407. root["error"] = string(__PRETTY_FUNCTION__) + " unknown command \"" + cmd + "\".\ntype help to list available commands.";
  408. retval.msg = root;
  409. cmdmutex.unlock();
  410. return retval;
  411. } else if (alwaysit != cmdAllowAlways.cend()) {
  412. // Command should be usable in all cases
  413. } else if (currentState == loginpossible || currentState == dologin || currentState == dosignup) {
  414. DEBUGPRINT("execute does login");
  415. DEBUGPRINT(string("cmd is in usable commands ") + std::to_string(connectit != cmdAllowAfterConnect.end()));
  416. if (connectit == cmdAllowAfterConnect.cend()) {
  417. // Command was NOT in list of usable commands after login
  418. string allowedCommands;
  419. for (string s : cmdAllowAlways)
  420. allowedCommands += s + " ";
  421. for (string s : cmdAllowAfterConnect)
  422. allowedCommands += s + " ";
  423. retval.type = error;
  424. root["command"] = "error";
  425. root["error"] = string("Not logged in. Available commands are limited to ") + allowedCommands + "\n" + "Use help for usage of these commands.";
  426. retval.msg = root;
  427. cmdmutex.unlock();
  428. return retval;
  429. }
  430. } else if (currentState == versionpossible || currentState == doversion) {
  431. DEBUGPRINT("execute does version");
  432. DEBUGPRINT(string("comparison is ") + std::to_string(cmd.compare("version")));
  433. if (cmd.compare("version")) {
  434. retval.type = error;
  435. root["command"] = "error";
  436. root["error"] = string("Version not checked yet. No commands avalable.");
  437. retval.msg = root;
  438. cmdmutex.unlock();
  439. return retval;
  440. }
  441. } else if (currentState == connectionpossible) {
  442. DEBUGPRINT("execute does connect");
  443. DEBUGPRINT(string("comparison is ") + std::to_string(cmd.compare("connect")));
  444. if (cmd.compare("version") && cmd.compare("connect")) {
  445. retval.type = error;
  446. root["command"] = "error";
  447. root["error"] = "Not connected. Please connect.";
  448. retval.msg = root;
  449. cmdmutex.unlock();
  450. return retval;
  451. }
  452. }
  453. retval = (this->*(execmap[cmd]))(args);
  454. cmdmutex.unlock();
  455. return retval;
  456. }
  457. CmdMan::CmdRet CmdMan::cmdDeleteme(vector<string> args) {
  458. CmdRet retval;
  459. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  460. Json::Value root;
  461. if (args.size() < 1) {
  462. retval.type = error;
  463. root["command"] = "error";
  464. root["error"] = "Not enough arguments, deleteme requires at least 1 argument.";
  465. } else {
  466. retval.type = send;
  467. root["command"] = "deleteme";
  468. root["pass"] = args[0];
  469. }
  470. retval.msg = root;
  471. return retval;
  472. }
  473. CmdMan::CmdRet CmdMan::cmdKeyfile(vector<string> args) {
  474. CmdRet retval;
  475. Json::Value root;
  476. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  477. if (args.size() < 1) {
  478. retval.type = error;
  479. root["command"] = "error";
  480. root["error"] = "Not enough arguments, keyfile requires at least 1 argument.";
  481. } else {
  482. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " haveargs");
  483. root["command"] = "keyfile";
  484. if (!fileman.openKey(args[0])) {
  485. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " openkey fail");
  486. root["accept"] = false;
  487. root["file"] = args[0];
  488. root["error"] = string("couldnt open keyfile, openssl reports: ") + fileman.getOpensslError();
  489. retval.type = error;
  490. } else {
  491. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " openkey good");
  492. root["accept"] = true;
  493. root["file"] = args[0];
  494. retval.type = print;
  495. }
  496. }
  497. retval.msg = root;
  498. return retval;
  499. }
  500. CmdMan::CmdRet CmdMan::cmdClosekey(vector<string> args) {
  501. CmdRet retval;
  502. Json::Value root;
  503. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  504. root["command"] = "closekey";
  505. if (!fileman.closeKey()) {
  506. root["accept"] = false;
  507. root["error"] = "couldnt close keyfile. ensure no put or get is running";
  508. retval.type = error;
  509. } else {
  510. root["accept"] = true;
  511. retval.type = print;
  512. }
  513. retval.msg = root;
  514. return retval;
  515. }
  516. CmdMan::CmdRet CmdMan::cmdExit(vector<string> args) {
  517. CmdRet retval;
  518. Json::Value root;
  519. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  520. root["command"] = "exit";
  521. if (currentState != connectionpossible) {
  522. // we are connected, disconnect first
  523. retval.nextcommand = "disconnect";
  524. if (currentState == loginpossible)
  525. currentState = disconnecttoexitearly;
  526. else
  527. currentState = disconnecttoexit;
  528. retval.type = none;
  529. } else {
  530. retval.type = exit;
  531. }
  532. retval.msg = root;
  533. return retval;
  534. }
  535. /* login and signup commands */
  536. CmdMan::CmdRet CmdMan::cmdLogin(vector<string> args) {
  537. CmdRet retval;
  538. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  539. Json::Value root;
  540. if (args.size() < 2) {
  541. retval.type = error;
  542. root["command"] = "error";
  543. root["error"] = "Not enough arguments, login requires at least 2 arguments.";
  544. } else {
  545. if (currentState == loginpossible) {
  546. currentState = dologin;
  547. root["user"] = username = args[0];
  548. root["pass"] = args[1];
  549. root["login"] = true;
  550. root["cancel"] = false;
  551. retval.type = send;
  552. } else {
  553. root["command"] = "login";
  554. root["error"] = "Login not possible, because you already requested a login "
  555. "or you are logged in";
  556. root["accept"] = false;
  557. retval.type = error;
  558. }
  559. }
  560. retval.msg = root;
  561. return retval;
  562. }
  563. CmdMan::CmdRet CmdMan::cmdSignup(vector<string> args) {
  564. CmdRet retval;
  565. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  566. Json::Value root;
  567. if (args.size() < 2) {
  568. retval.type = error;
  569. root["command"] = "error";
  570. root["error"] = "Not enough arguments, signup requires at least 2 arguments.";
  571. } else {
  572. if (currentState == loginpossible) {
  573. currentState = dosignup;
  574. root["user"] = username = args[0];
  575. root["pass"] = args[1];
  576. root["login"] = false;
  577. root["cancel"] = false;
  578. retval.type = send;
  579. } else {
  580. root["command"] = "signup";
  581. root["error"] = "Signup not possible, because you already requested a "
  582. "login or you are logged in";
  583. root["accept"] = false;
  584. retval.type = error;
  585. }
  586. }
  587. retval.msg = root;
  588. return retval;
  589. }
  590. CmdMan::CmdRet CmdMan::cmdQueue(vector<string> args) {
  591. CmdRet retval;
  592. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  593. Json::Value root;
  594. if (args.size() < 1) {
  595. retval.type = error;
  596. root["command"] = "error";
  597. root["error"] = "Not enough arguments, queue requires at least 1 argument.";
  598. } else {
  599. root["command"] = "queue";
  600. root["file"] = args[0];
  601. retval.type = send;
  602. }
  603. retval.msg = root;
  604. return retval;
  605. }
  606. CmdMan::CmdRet CmdMan::cmdDequeue(vector<string> args) {
  607. CmdRet retval;
  608. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  609. Json::Value root;
  610. if (args.size() < 1) {
  611. retval.type = error;
  612. root["command"] = "error";
  613. root["error"] = "Not enough arguments, dequeue requires at least 1 argument.";
  614. } else {
  615. root["command"] = "dequeue";
  616. root["file"] = args[0];
  617. retval.type = send;
  618. }
  619. retval.msg = root;
  620. return retval;
  621. }
  622. /* internal commands */
  623. CmdMan::CmdRet CmdMan::cmdVersion(vector<string> args) {
  624. CmdRet retval;
  625. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  626. Json::Value root;
  627. if (currentState == versionpossible) {
  628. root["major"] = protocolMajorVersion;
  629. root["minor"] = protocolMinorVersion;
  630. retval.type = send;
  631. currentState = doversion;
  632. } else {
  633. retval.type = error;
  634. root["command"] = "error";
  635. root["error"] = "Executing version command not possible. Type help to list available commands.";
  636. }
  637. retval.msg = root;
  638. return retval;
  639. }
  640. CmdMan::CmdRet CmdMan::cmdNotifications(vector<string> args) {
  641. CmdRet retval;
  642. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  643. Json::Value root;
  644. root["command"] = "notifications";
  645. retval.type = send;
  646. retval.msg = root;
  647. return retval;
  648. }
  649. CmdMan::CmdRet CmdMan::handle(Json::Value root) {
  650. CmdRet retval;
  651. Json::Value output;
  652. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  653. cmdmutex.lock();
  654. if (currentState == doversion)
  655. root["command"] = "version";
  656. else if (currentState == dosignup)
  657. root["command"] = "signup";
  658. else if (currentState == dologin)
  659. root["command"] = "login";
  660. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " using json\n" + Json::writeString(wbuilder, root) + "\n");
  661. string retmsg;
  662. map<string, CmdRet (CmdMan::*)(Json::Value)>::iterator it = handlemap.find(root["command"].asString());
  663. if (it == handlemap.end()) {
  664. retval.type = error;
  665. output["command"] = "error";
  666. output["error"] = string(__PRETTY_FUNCTION__) + " unknown command \"" + root["command"].asString() + "\".\nEnsure code is implemented.";
  667. retval.msg = output;
  668. cmdmutex.unlock();
  669. return retval;
  670. }
  671. retval = (this->*(handlemap[root["command"].asString()]))(root);
  672. cmdmutex.unlock();
  673. return retval;
  674. }
  675. CmdMan::CmdRet CmdMan::handleStatus(Json::Value root) {
  676. CmdRet retval;
  677. Json::Value output;
  678. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  679. retval.type = print;
  680. output["command"] = "status";
  681. output["response"] = username + "@" + ip + ":" + std::to_string(port) + " - Status: " + root["response"].asString();
  682. retval.msg = output;
  683. return retval;
  684. }
  685. CmdMan::CmdRet CmdMan::handleExtendedstatus(Json::Value root) {
  686. CmdRet retval;
  687. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  688. if (!root["accept"].asBool()) {
  689. retval.type = error;
  690. } else {
  691. retval.type = print;
  692. }
  693. retval.msg = root;
  694. return retval;
  695. }
  696. CmdMan::CmdRet CmdMan::handleClose(Json::Value root) {
  697. CmdRet retval;
  698. Json::Value output;
  699. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  700. output["command"] = "disconnect";
  701. output["accept"] = true;
  702. retval.type = close | print;
  703. retval.msg = output;
  704. if (currentState == disconnecttoexit) {
  705. retval.nextcommand = "exit";
  706. }
  707. currentState = connectionpossible;
  708. return retval;
  709. }
  710. CmdMan::CmdRet CmdMan::handlePut(Json::Value root) {
  711. CmdRet retval;
  712. Json::Value output;
  713. output["command"] = "put";
  714. output["file"] = fileman.getPutName();
  715. output["accept"] = false;
  716. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  717. if (!root["accept"].asBool()) {
  718. retval.type = error;
  719. output["error"] = "Server reports: " + root["error"].asString();
  720. fileman.cancelPut();
  721. } else if (!fileman.isPutting()) {
  722. retval.type = error;
  723. output["error"] = "Server responds to put message which was never sent.";
  724. } else if (root["file"].asString() != fileman.getPutName()) {
  725. retval.type = error;
  726. output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getPutName();
  727. fileman.cancelPut();
  728. } else {
  729. output["accept"] = true;
  730. output["error"] = "";
  731. retval.type = print | send;
  732. retval.nextcommand = "putdata";
  733. }
  734. retval.msg = output;
  735. return retval;
  736. }
  737. CmdMan::CmdRet CmdMan::handlePutdata(Json::Value root) {
  738. CmdRet retval;
  739. Json::Value output;
  740. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  741. output["command"] = "putdata";
  742. output["file"] = fileman.getPutName();
  743. output["cancel"] = true;
  744. if (root["cancel"].asBool()) {
  745. retval.type = error;
  746. output["error"] = "Server reports: " + root["error"].asString();
  747. fileman.cancelPut();
  748. } else if (!fileman.isPutting()) {
  749. retval.type = error;
  750. output["error"] = "Server responds to put message which was never sent.";
  751. } else if (root["received"].asInt() != fileman.getPutRemainingChunks()) {
  752. // the number of remaining chunks received from the daemon does not equal
  753. // the number stored at the client side
  754. retval.type = error;
  755. output["error"] = std::string("Server reports number of "
  756. "remaining chunks as ") +
  757. std::to_string(root["received"].asInt()) + " but actual number is " + std::to_string(fileman.getPutRemainingChunks());
  758. fileman.cancelPut();
  759. } else if (root["file"].asString() != fileman.getPutName()) {
  760. retval.type = error;
  761. output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getPutName();
  762. fileman.cancelPut();
  763. } else {
  764. output["cancel"] = false;
  765. output["error"] = "";
  766. // sent successfully
  767. if (!root["received"].asInt()) {
  768. // everything sent
  769. retval.type = print;
  770. fileman.closePut();
  771. } else {
  772. retval.type = print | send;
  773. retval.nextcommand = "putdata";
  774. }
  775. }
  776. retval.msg = output;
  777. return retval;
  778. }
  779. CmdMan::CmdRet CmdMan::handleGet(Json::Value root) {
  780. CmdRet retval;
  781. Json::Value output;
  782. output["command"] = "get";
  783. output["file"] = fileman.getGetName();
  784. output["accept"] = false;
  785. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  786. if (!root["accept"].asBool()) {
  787. retval.type = error;
  788. output["error"] = "Server reports: " + root["error"].asString();
  789. fileman.cancelGet();
  790. } else if (!fileman.isGetting()) {
  791. retval.type = error;
  792. output["error"] = "Server responds to get message which was never sent.";
  793. } else if (root["file"].asString() != fileman.getGetName()) {
  794. retval.type = error;
  795. output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getGetName();
  796. fileman.cancelGet();
  797. } else {
  798. fileman.setGetChunks(root["chunks"].asInt());
  799. output["accept"] = true;
  800. output["error"] = "";
  801. retval.type = print | send;
  802. retval.nextcommand = "getdata";
  803. }
  804. retval.msg = output;
  805. return retval;
  806. }
  807. CmdMan::CmdRet CmdMan::handleGetdata(Json::Value root) {
  808. CmdRet retval;
  809. Json::Value output;
  810. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  811. output["command"] = "getdata";
  812. output["file"] = fileman.getGetName();
  813. output["cancel"] = true;
  814. if (root["cancel"].asBool()) {
  815. retval.type = error;
  816. output["error"] = "Server reports: " + root["error"].asString();
  817. fileman.cancelGet();
  818. } else if (!fileman.isGetting()) {
  819. retval.type = error;
  820. output["error"] = "Server responds to get message which was never sent.";
  821. } else if (root["remaining"].asInt() != fileman.getGetRemainingChunks()) {
  822. retval.type = error;
  823. output["error"] = std::string("Server reports number of remaining chunks as ") + std::to_string(root["remaining"].asInt()) + " but actual number is " +
  824. std::to_string(fileman.getGetRemainingChunks());
  825. fileman.cancelGet();
  826. } else if (root["file"].asString() != fileman.getGetName()) {
  827. retval.type = error;
  828. output["error"] = "Server reports filename " + root["file"].asString() + " but actual filename is " + fileman.getGetName();
  829. fileman.cancelGet();
  830. } else {
  831. output["cancel"] = false;
  832. output["error"] = "";
  833. fileman.writeBase64(root["data"].asString());
  834. // loaded successfully
  835. if (!root["remaining"].asInt()) {
  836. // everything received
  837. retval.type = print;
  838. fileman.closeGet();
  839. } else {
  840. retval.type = print | send;
  841. retval.nextcommand = "getdata";
  842. }
  843. }
  844. retval.msg = output;
  845. return retval;
  846. }
  847. CmdMan::CmdRet CmdMan::handleList(Json::Value root) {
  848. CmdRet retval;
  849. Json::Value output; // LOCALOUTPUT
  850. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  851. output["command"] = "list";
  852. output["names"] = "";
  853. if (!root["accept"].asBool()) {
  854. retval.type = error;
  855. output["accept"] = false;
  856. output["error"] = "Server reports: " + root["error"].asString();
  857. fileman.cancelList();
  858. } else if (root["items"].asInt() == 0) {
  859. retval.type = print;
  860. output["accept"] = false;
  861. output["error"] = "There are no files stored on the server.";
  862. fileman.closeList();
  863. } else if (!fileman.isListingSimple()) {
  864. retval.type = error;
  865. output["accept"] = false;
  866. output["error"] = "Server responds to list message which was never sent.";
  867. } else {
  868. fileman.setListChunks(root["chunks"].asInt());
  869. retval.type = send;
  870. output["accept"] = true;
  871. retval.nextcommand = "listdata";
  872. }
  873. retval.msg = output;
  874. return retval;
  875. }
  876. CmdMan::CmdRet CmdMan::handleListdata(Json::Value root) {
  877. CmdRet retval;
  878. Json::Value output, arr;
  879. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  880. vector<Json::Value> toadd;
  881. output["command"] = "list";
  882. output["names"] = "";
  883. output["accept"] = false;
  884. if (root["cancel"].asBool()) {
  885. retval.type = error;
  886. output["error"] = "Server reports: " + root["error"].asString();
  887. fileman.cancelList();
  888. } else if (!fileman.isListingSimple()) {
  889. retval.type = error;
  890. output["error"] = "Server responds to list message which was never sent.";
  891. } else if (root["remaining"].asInt() != fileman.getListRemainingChunks()) {
  892. // the passed number of recieved chunks should equal the number of sent chunks
  893. retval.type = error;
  894. output["error"] = std::string("Server reports number of "
  895. "remaining chunks as ") +
  896. std::to_string(root["remaining"].asInt()) + " but actual number is " + std::to_string(fileman.getListRemainingChunks());
  897. fileman.cancelList();
  898. } else {
  899. output["accept"] = true;
  900. for (Json::Value i : root["names"])
  901. toadd.push_back(i);
  902. fileman.putListData(toadd);
  903. // loaded successfully
  904. if (root["remaining"] <= 0) {
  905. // everything sent
  906. retval.type = print;
  907. for (Json::Value s : fileman.getListData())
  908. arr.append(s.asString());
  909. output["names"] = arr;
  910. fileman.closeList();
  911. } else {
  912. retval.type = send;
  913. retval.nextcommand = "listdata";
  914. }
  915. }
  916. retval.msg = output;
  917. return retval;
  918. }
  919. CmdMan::CmdRet CmdMan::handleExtendedlist(Json::Value root) {
  920. CmdRet retval;
  921. Json::Value output, files; // LOCALOUTPUT
  922. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  923. output["command"] = "extendedlist";
  924. output["files"] = files;
  925. if (!root["accept"].asBool()) {
  926. retval.type = error;
  927. output["accept"] = false;
  928. output["error"] = "Server reports: " + root["error"].asString();
  929. fileman.cancelList();
  930. } else if (root["items"].asInt() == 0) {
  931. retval.type = print;
  932. output["accept"] = false;
  933. output["error"] = "There are no files stored on the server.";
  934. fileman.closeList();
  935. } else if (!fileman.isListingExtended()) {
  936. retval.type = error;
  937. output["accept"] = false;
  938. output["error"] = "Server responds to list message which was never sent.";
  939. } else {
  940. fileman.setListChunks(root["chunks"].asInt());
  941. retval.type = send;
  942. output["accept"] = true;
  943. retval.nextcommand = "extendedlistdata";
  944. }
  945. retval.msg = output;
  946. return retval;
  947. }
  948. CmdMan::CmdRet CmdMan::handleExtendedlistdata(Json::Value root) {
  949. CmdRet retval;
  950. Json::Value output, arr, files;
  951. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  952. vector<Json::Value> toadd;
  953. output["command"] = "extendedlist";
  954. output["files"] = files;
  955. output["accept"] = false;
  956. if (root["cancel"].asBool()) {
  957. retval.type = error;
  958. output["error"] = "Server reports: " + root["error"].asString();
  959. fileman.cancelList();
  960. } else if (!fileman.isListingExtended()) {
  961. retval.type = error;
  962. output["error"] = "Server responds to list message which was never sent.";
  963. } else if (root["remaining"].asInt() != fileman.getListRemainingChunks()) {
  964. // the passed number of recieved chunks should equal the number of sent chunks
  965. retval.type = error;
  966. output["error"] = std::string("Server reports number of "
  967. "remaining chunks as ") +
  968. std::to_string(root["remaining"].asInt()) + " but actual number is " + std::to_string(fileman.getListRemainingChunks());
  969. fileman.cancelList();
  970. } else {
  971. output["accept"] = true;
  972. for (Json::Value i : root["files"])
  973. toadd.push_back(i);
  974. fileman.putListData(toadd);
  975. // loaded successfully
  976. if (root["remaining"] <= 0) {
  977. // everything sent
  978. retval.type = print;
  979. for (Json::Value s : fileman.getListData())
  980. arr.append(s);
  981. output["files"] = arr;
  982. fileman.closeList();
  983. } else {
  984. retval.type = send;
  985. retval.nextcommand = "extendedlistdata";
  986. }
  987. }
  988. retval.msg = output;
  989. return retval;
  990. }
  991. CmdMan::CmdRet CmdMan::handleVersion(Json::Value root) {
  992. CmdRet retval;
  993. Json::Value output; // LOCALOUTPUT
  994. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  995. output["command"] = "version";
  996. output["serverversion"] = std::to_string(root["major"].asInt()) + "." + std::to_string(root["minor"].asInt());
  997. output["clientversion"] = std::to_string(protocolMajorVersion) + "." + std::to_string(protocolMinorVersion);
  998. if (!root["accept"].asBool()) {
  999. retval.type = error | close;
  1000. output["accept"] = false;
  1001. currentState = connectionpossible;
  1002. } else {
  1003. retval.type = print;
  1004. output["accept"] = true;
  1005. currentState = loginpossible;
  1006. }
  1007. retval.msg = output;
  1008. return retval;
  1009. }
  1010. CmdMan::CmdRet CmdMan::handleLogin(Json::Value root) {
  1011. CmdRet retval;
  1012. Json::Value output; // LOCALOUTPUT
  1013. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1014. output["command"] = "login";
  1015. if (!root["accept"].asBool()) {
  1016. retval.type = error | close;
  1017. output["error"] = root["error"].asString();
  1018. output["accept"] = false;
  1019. currentState = connectionpossible;
  1020. } else {
  1021. retval.type = print;
  1022. output["error"] = "";
  1023. output["accept"] = true;
  1024. currentState = normal;
  1025. }
  1026. retval.msg = output;
  1027. return retval;
  1028. }
  1029. CmdMan::CmdRet CmdMan::handleSignup(Json::Value root) {
  1030. CmdRet retval;
  1031. Json::Value output; // LOCALOUTPUT
  1032. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1033. output["command"] = "signup";
  1034. if (!root["accept"].asBool()) {
  1035. retval.type = error;
  1036. output["error"] = root["error"].asString();
  1037. output["accept"] = false;
  1038. currentState = loginpossible;
  1039. } else {
  1040. retval.type = print;
  1041. output["error"] = "";
  1042. output["accept"] = true;
  1043. currentState = normal;
  1044. }
  1045. retval.msg = output;
  1046. return retval;
  1047. }
  1048. CmdMan::CmdRet CmdMan::handleHead(Json::Value root) {
  1049. CmdRet retval;
  1050. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1051. if (!root["accept"].asBool()) {
  1052. Json::Value output;
  1053. output["command"] = "head";
  1054. output["file"] = root["file"];
  1055. output["error"] = "Server reports: " + root["error"].asString();
  1056. output["accept"] = false;
  1057. retval.type = error;
  1058. retval.msg = output;
  1059. } else {
  1060. retval.type = print;
  1061. retval.msg = root;
  1062. }
  1063. return retval;
  1064. }
  1065. CmdMan::CmdRet CmdMan::handleDeletefile(Json::Value root) {
  1066. CmdRet retval;
  1067. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1068. if (!root["accept"].asBool()) {
  1069. Json::Value output;
  1070. output["command"] = "deletefile";
  1071. output["file"] = root["file"];
  1072. output["error"] = "Server reports: " + root["error"].asString();
  1073. output["accept"] = false;
  1074. retval.type = error;
  1075. retval.msg = output;
  1076. } else {
  1077. retval.type = print;
  1078. retval.msg = root;
  1079. }
  1080. return retval;
  1081. }
  1082. CmdMan::CmdRet CmdMan::handleDeleteme(Json::Value root) {
  1083. CmdRet retval;
  1084. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1085. if (!root["accept"].asBool()) {
  1086. retval.type = error;
  1087. } else {
  1088. retval.type = close | print;
  1089. currentState = connectionpossible;
  1090. }
  1091. retval.msg = root;
  1092. return retval;
  1093. }
  1094. CmdMan::CmdRet CmdMan::handleQueue(Json::Value root) {
  1095. CmdRet retval;
  1096. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1097. if (root["accept"].asBool()) {
  1098. retval.type = print;
  1099. } else {
  1100. retval.type = error;
  1101. }
  1102. retval.msg = root;
  1103. return retval;
  1104. }
  1105. CmdMan::CmdRet CmdMan::handleDequeue(Json::Value root) {
  1106. CmdRet retval;
  1107. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1108. if (root["accept"].asBool()) {
  1109. retval.type = print;
  1110. } else {
  1111. retval.type = error;
  1112. }
  1113. retval.msg = root;
  1114. return retval;
  1115. }
  1116. CmdMan::CmdRet CmdMan::handleNotifications(Json::Value root) {
  1117. CmdRet retval;
  1118. DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
  1119. if (root["accept"].asBool()) {
  1120. retval.type = print;
  1121. } else {
  1122. retval.type = error;
  1123. }
  1124. retval.msg = root;
  1125. return retval;
  1126. }