1 /**
2 * @file src/megacmdshell.cpp
3 * @brief MEGAcmd: Interactive CLI and service application
4 * This is the shell application
5 *
6 * (c) 2013 by Mega Limited, Auckland, New Zealand
7 *
8 * This file is distributed under the terms of the GNU General Public
9 * License, see http://www.gnu.org/copyleft/gpl.txt
10 * for details.
11 *
12 * This file is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 */
16
17 #include "megacmdshell.h"
18 #include "megacmdshellcommunications.h"
19 #include "megacmdshellcommunicationsnamedpipes.h"
20 #include "../megacmdcommonutils.h"
21
22 #define USE_VARARGS
23 #define PREFER_STDARG
24
25 #ifdef NO_READLINE
26 #include <megaconsole.h>
27 #else
28 #include <readline/readline.h>
29 #include <readline/history.h>
30 #endif
31
32 #include <iomanip>
33 #include <string>
34 #include <cstring>
35 #include <mutex>
36 #include <condition_variable>
37 #include <set>
38 #include <map>
39 #include <vector>
40 #include <sstream>
41 #include <algorithm>
42 #include <atomic>
43 #include <stdio.h>
44
45 #define PROGRESS_COMPLETE -2
46 #define SPROGRESS_COMPLETE "-2"
47 #define PROMPT_MAX_SIZE 128
48
49 #ifndef _WIN32
50 #include <signal.h>
51 #include <sys/types.h>
52 #include <errno.h>
53 #else
54 #include <fcntl.h>
55 #include <io.h>
56 #include <stdio.h>
57 #ifndef _O_U16TEXT
58 #define _O_U16TEXT 0x00020000
59 #endif
60 #ifndef _O_U8TEXT
61 #define _O_U8TEXT 0x00040000
62 #endif
63 #endif
64
65 #if defined(_WIN32)
66 #define strdup _strdup
67 #endif
68
69 #define SSTR( x ) static_cast< const std::ostringstream & >( \
70 ( std::ostringstream() << std::dec << x ) ).str()
71
72 using namespace mega;
73
74 namespace megacmd {
75 using namespace std;
76
77 #if defined(NO_READLINE) && defined(_WIN32)
78 CONSOLE_CLASS* console = NULL;
79 #endif
80
81
82 // utility functions
83 #ifndef NO_READLINE
getCurrentLine()84 string getCurrentLine()
85 {
86 char *saved_line = rl_copy_text(0, rl_point);
87 string toret(saved_line);
88 free(saved_line);
89 saved_line = NULL;
90 return toret;
91 }
92 #endif
93
94
95 // end utily functions
96
97 string clientID; //identifier for a registered state listener
98
99 // Console related functions:
console_readpwchar(char * pw_buf,int pw_buf_size,int * pw_buf_pos,char ** line)100 void console_readpwchar(char* pw_buf, int pw_buf_size, int* pw_buf_pos, char** line)
101 {
102 #ifdef _WIN32
103 char c;
104 DWORD cread;
105
106 if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &c, 1, &cread, NULL) == 1)
107 {
108 if ((c == 8) && *pw_buf_pos)
109 {
110 (*pw_buf_pos)--;
111 }
112 else if (c == 13)
113 {
114 *line = (char*)malloc(*pw_buf_pos + 1);
115 memcpy(*line, pw_buf, *pw_buf_pos);
116 (*line)[*pw_buf_pos] = 0;
117 }
118 else if (*pw_buf_pos < pw_buf_size)
119 {
120 pw_buf[(*pw_buf_pos)++] = c;
121 }
122 }
123 #else
124 // FIXME: UTF-8 compatibility
125
126 char c;
127
128 if (read(STDIN_FILENO, &c, 1) == 1)
129 {
130 if (c == 8 && *pw_buf_pos)
131 {
132 (*pw_buf_pos)--;
133 }
134 else if (c == 13)
135 {
136 *line = (char*) malloc(*pw_buf_pos + 1);
137 memcpy(*line, pw_buf, *pw_buf_pos);
138 (*line)[*pw_buf_pos] = 0;
139 }
140 else if (*pw_buf_pos < pw_buf_size)
141 {
142 pw_buf[(*pw_buf_pos)++] = c;
143 }
144 }
145 #endif
146 }
console_setecho(bool echo)147 void console_setecho(bool echo)
148 {
149 #ifdef _WIN32
150 HANDLE hCon = GetStdHandle(STD_INPUT_HANDLE);
151 DWORD mode;
152
153 GetConsoleMode(hCon, &mode);
154
155 if (echo)
156 {
157 mode |= ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
158 }
159 else
160 {
161 mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
162 }
163
164 SetConsoleMode(hCon, mode);
165 #else
166 //do nth
167 #endif
168 }
169
170 bool alreadyFinished = false; //flag to show progress
171 float percentDowloaded = 0.0; // to show progress
172
173 // password change-related state information
174 string oldpasswd;
175 string newpasswd;
176
177 bool doExit = false;
178 bool doReboot = false;
179 static std::atomic_bool handlerOverridenByExternalThread(false);
180 static std::mutex handlerInstallerMutex;
181
182 static std::atomic_bool requirepromptinstall(true);
183
184 bool procesingline = false;
185 bool promptreinstalledwhenprocessingline = false;
186
187 std::mutex promptLogReceivedMutex;
188 std::condition_variable promtpLogReceivedCV;
189 bool promtpLogReceivedBool = false;
190 bool serverTryingToLog = false;
191
192
193 static char dynamicprompt[PROMPT_MAX_SIZE];
194
195 static char* line;
196
197 static prompttype prompt = COMMAND;
198
199 static char pw_buf[256];
200 static int pw_buf_pos = 0;
201
202 string loginname;
203 bool signingup = false;
204 string signupline;
205 string passwdline;
206 string linktoconfirm;
207
208 bool confirminglink = false;
209 bool confirmingcancellink = false;
210
211 // communications with megacmdserver:
212 MegaCmdShellCommunications *comms;
213
214 std::mutex mutexPrompt;
215
216 void printWelcomeMsg(unsigned int width = 0);
217
218 #ifndef NO_READLINE
219 void install_rl_handler(const char *theprompt, bool external = true);
220 #endif
221
222 std::mutex lastMessageMutex;
223 std::string lastMessage;
notRepeatedMessage(const string & newMessage)224 bool notRepeatedMessage(const string &newMessage)
225 {
226 std::lock_guard<std::mutex> g(lastMessageMutex);
227 bool toret = lastMessage.compare(newMessage);
228 if (toret)
229 {
230 lastMessage = newMessage;
231 }
232 return toret;
233 }
cleanLastMessage()234 void cleanLastMessage()
235 {
236 std::lock_guard<std::mutex> g(lastMessageMutex);
237 lastMessage = string();
238 }
239
statechangehandle(string statestring)240 void statechangehandle(string statestring)
241 {
242 char statedelim[2]={(char)0x1F,'\0'};
243 size_t nextstatedelimitpos = statestring.find(statedelim);
244 static bool shown_partial_progress = false;
245
246 unsigned int width = getNumberOfCols(75);
247 if (width > 1 ) width--;
248
249 while (nextstatedelimitpos!=string::npos && statestring.size())
250 {
251 string newstate = statestring.substr(0,nextstatedelimitpos);
252 statestring=statestring.substr(nextstatedelimitpos+1);
253 nextstatedelimitpos = statestring.find(statedelim);
254 if (newstate.compare(0, strlen("prompt:"), "prompt:") == 0)
255 {
256 if (serverTryingToLog)
257 {
258 std::unique_lock<std::mutex> lk(MegaCmdShellCommunications::megaCmdStdoutputing);
259 printCenteredContentsCerr(string(" Server is still trying to log in. Still, some commands are available.\n"
260 "Type \"help\", to list them.").c_str(), width);
261 }
262 changeprompt(newstate.substr(strlen("prompt:")).c_str(),true);
263
264 std::unique_lock<std::mutex> lk(promptLogReceivedMutex);
265 promtpLogReceivedCV.notify_one();
266 promtpLogReceivedBool = true;
267 }
268 else if (newstate.compare(0, strlen("endtransfer:"), "endtransfer:") == 0)
269 {
270 string rest = newstate.substr(strlen("endtransfer:"));
271 if (rest.size() >=3)
272 {
273 bool isdown = rest.at(0) == 'D';
274 string path = rest.substr(2);
275 stringstream os;
276 if (shown_partial_progress)
277 {
278 os << endl;
279 }
280 os << (isdown?"Download":"Upload") << " finished: " << path << endl;
281
282 #ifdef _WIN32
283 wstring wbuffer;
284 stringtolocalw((const char*)os.str().data(),&wbuffer);
285 int oldmode;
286 MegaCmdShellCommunications::megaCmdStdoutputing.lock();
287 oldmode = _setmode(_fileno(stdout), _O_U8TEXT);
288 OUTSTREAM << wbuffer << flush;
289 _setmode(_fileno(stdout), oldmode);
290 MegaCmdShellCommunications::megaCmdStdoutputing.unlock();
291 #else
292 OUTSTREAM << os.str();
293 #endif
294 }
295 }
296 else if (newstate.compare(0, strlen("loged:"), "loged:") == 0)
297 {
298 serverTryingToLog = false;
299 }
300 else if (newstate.compare(0, strlen("login:"), "login:") == 0)
301 {
302 serverTryingToLog = true;
303 std::unique_lock<std::mutex> lk(MegaCmdShellCommunications::megaCmdStdoutputing);
304 printCenteredContentsCerr(string("Resuming session ... ").c_str(), width, false);
305 }
306 else if (newstate.compare(0, strlen("message:"), "message:") == 0)
307 {
308 if (notRepeatedMessage(newstate)) //to avoid repeating messages
309 {
310 MegaCmdShellCommunications::megaCmdStdoutputing.lock();
311 #ifdef _WIN32
312 int oldmode = _setmode(_fileno(stdout), _O_U8TEXT);
313 #endif
314 string contents = newstate.substr(strlen("message:"));
315 if (contents.find("-----") != 0)
316 {
317 if (!procesingline || promptreinstalledwhenprocessingline || shown_partial_progress)
318 {
319 OUTSTREAM << endl;
320 }
321 printCenteredContents(contents, width);
322 requirepromptinstall = true;
323 #ifndef NO_READLINE
324 if (prompt == COMMAND && promtpLogReceivedBool)
325 {
326 std::lock_guard<std::mutex> g(mutexPrompt);
327 redisplay_prompt();
328 }
329 #endif
330
331 }
332 else
333 {
334 requirepromptinstall = true;
335 OUTSTREAM << endl << contents << endl;
336 }
337 #ifdef _WIN32
338 _setmode(_fileno(stdout), oldmode);
339 #endif
340 MegaCmdShellCommunications::megaCmdStdoutputing.unlock();
341
342 }
343 }
344 else if (newstate.compare(0, strlen("clientID:"), "clientID:") == 0)
345 {
346 clientID = newstate.substr(strlen("clientID:")).c_str();
347 }
348 else if (newstate.compare(0, strlen("progress:"), "progress:") == 0)
349 {
350 string rest = newstate.substr(strlen("progress:"));
351
352 size_t nexdel = rest.find(":");
353 string received = rest.substr(0,nexdel);
354
355 rest = rest.substr(nexdel+1);
356 nexdel = rest.find(":");
357 string total = rest.substr(0,nexdel);
358
359 string title;
360 if ( (nexdel != string::npos) && (nexdel < rest.size() ) )
361 {
362 rest = rest.substr(nexdel+1);
363 nexdel = rest.find(":");
364 title = rest.substr(0,nexdel);
365 }
366
367 if (received!=SPROGRESS_COMPLETE)
368 {
369 shown_partial_progress = true;
370 }
371 else
372 {
373 shown_partial_progress = false;
374 }
375
376 MegaCmdShellCommunications::megaCmdStdoutputing.lock();
377 if (title.size())
378 {
379 if (received==SPROGRESS_COMPLETE)
380 {
381 printprogress(PROGRESS_COMPLETE, charstoll(total.c_str()),title.c_str());
382
383 }
384 else
385 {
386 printprogress(charstoll(received.c_str()), charstoll(total.c_str()),title.c_str());
387 }
388 }
389 else
390 {
391 if (received==SPROGRESS_COMPLETE)
392 {
393 printprogress(PROGRESS_COMPLETE, charstoll(total.c_str()));
394 }
395 else
396 {
397 printprogress(charstoll(received.c_str()), charstoll(total.c_str()));
398 }
399 }
400 MegaCmdShellCommunications::megaCmdStdoutputing.unlock();
401 }
402 else if (newstate == "ack")
403 {
404 // do nothing, all good
405 }
406 else if (newstate == "restart")
407 {
408 doExit = true;
409 doReboot = true;
410 if (!comms->updating)
411 {
412 comms->updating = true; // to avoid mensajes about server down
413 }
414 sleepSeconds(3); // Give a while for server to restart
415 changeprompt("RESTART REQUIRED BY SERVER (due to an update). Press any key to continue.", true);
416 }
417 else
418 {
419 if (shown_partial_progress)
420 {
421 OUTSTREAM << endl;
422 }
423 cerr << "received unrecognized state change: [" << newstate << "]" << endl;
424 //sleep a while to avoid continuous looping
425 sleepSeconds(1);
426 }
427
428
429 if (newstate.compare(0, strlen("progress:"), "progress:") != 0)
430 {
431 shown_partial_progress = false;
432 }
433 }
434 }
435
436
sigint_handler(int signum)437 void sigint_handler(int signum)
438 {
439 if (prompt != COMMAND)
440 {
441 setprompt(COMMAND);
442 }
443
444 #ifndef NO_READLINE
445 // reset position and print prompt
446 rl_replace_line("", 0); //clean contents of actual command
447 rl_crlf(); //move to nextline
448
449 if (RL_ISSTATE(RL_STATE_ISEARCH) || RL_ISSTATE(RL_STATE_ISEARCH) || RL_ISSTATE(RL_STATE_ISEARCH))
450 {
451 RL_UNSETSTATE(RL_STATE_ISEARCH);
452 RL_UNSETSTATE(RL_STATE_NSEARCH);
453 RL_UNSETSTATE( RL_STATE_SEARCH);
454 history_set_pos(history_length);
455 rl_restore_prompt(); // readline has stored it when searching
456 }
457 else
458 {
459 rl_reset_line_state();
460 }
461 rl_redisplay();
462 #endif
463 }
464
printprogress(long long completed,long long total,const char * title)465 void printprogress(long long completed, long long total, const char *title)
466 {
467 float oldpercent = percentDowloaded;
468 if (total == 0)
469 {
470 percentDowloaded = 0;
471 }
472 else
473 {
474 percentDowloaded = float(completed * 1.0 / total * 100.0);
475 }
476 if (completed != PROGRESS_COMPLETE && (alreadyFinished || ( ( percentDowloaded == oldpercent ) && ( oldpercent != 0 ) ) ))
477 {
478 return;
479 }
480 if (percentDowloaded < 0)
481 {
482 percentDowloaded = 0;
483 }
484
485 if (total < 0)
486 {
487 return; // after a 100% this happens
488 }
489 if (completed != PROGRESS_COMPLETE && completed < 0.001 * total)
490 {
491 return; // after a 100% this happens
492 }
493 if (completed == PROGRESS_COMPLETE)
494 {
495 alreadyFinished = true;
496 completed = total;
497 percentDowloaded = 100;
498 }
499 printPercentageLineCerr(title, completed, total, percentDowloaded, !alreadyFinished);
500 }
501
502
503 #ifdef _WIN32
CtrlHandler(DWORD fdwCtrlType)504 BOOL WINAPI CtrlHandler( DWORD fdwCtrlType )
505 {
506 cerr << "Reached CtrlHandler: " << fdwCtrlType << endl;
507
508 switch( fdwCtrlType )
509 {
510 // Handle the CTRL-C signal.
511 case CTRL_C_EVENT:
512 sigint_handler((int)fdwCtrlType);
513 return( TRUE );
514
515 default:
516 return FALSE;
517 }
518 }
519 #endif
520
getprompt()521 prompttype getprompt()
522 {
523 return prompt;
524 }
525
setprompt(prompttype p,string arg)526 void setprompt(prompttype p, string arg)
527 {
528 prompt = p;
529
530 #ifndef NO_READLINE
531 if (p == COMMAND)
532 {
533 console_setecho(true);
534 }
535 else
536 {
537 pw_buf_pos = 0;
538 if (arg.size())
539 {
540 OUTSTREAM << arg << flush;
541 }
542 else
543 {
544 OUTSTREAM << prompts[p] << flush;
545 }
546 console_setecho(false);
547 }
548 #else
549 console->setecho(p == COMMAND);
550
551 if (p != COMMAND)
552 {
553 pw_buf_pos = 0;
554 console->updateInputPrompt(arg.empty() ? prompts[p] : arg);
555 }
556 #endif
557 }
558
559 #ifndef NO_READLINE
560 // readline callback - exit if EOF, add to history unless password
store_line(char * l)561 static void store_line(char* l)
562 {
563 procesingline = true;
564 if (!l)
565 {
566 #ifndef _WIN32 // to prevent exit with Supr key
567 doExit = true;
568 rl_set_prompt("(CTRL+D) Exiting ...\n");
569 #ifndef NDEBUG
570 if (comms->serverinitiatedfromshell)
571 {
572 OUTSTREAM << " Forwarding exit command to the server, since this cmd shell (most likely) initiated it" << endl;
573 comms->executeCommand("exit", readresponse);
574 }
575 #endif
576 #endif
577 return;
578 }
579
580 if (*l && ( prompt == COMMAND ))
581 {
582 add_history(l);
583 }
584
585 line = l;
586 }
587 #endif
588
589 #ifdef _WIN32
590
validoptionforreadline(const string & string)591 bool validoptionforreadline(const string& string)
592 {// TODO: this has not been tested in 100% cases (perhaps it is too diligent or too strict)
593 int c,i,ix,n,j;
594 for (i=0, ix=int(string.length()); i < ix; i++)
595 {
596 c = (unsigned char) string[i];
597
598 //if (c>0xC0) return false;
599 //if (c==0x09 || c==0x0a || c==0x0d || (0x20 <= c && c <= 0x7e) ) n = 0; // is_printable_ascii
600 if (0x00 <= c && c <= 0x7f) n=0; // 0bbbbbbb
601 else if ((c & 0xE0) == 0xC0) n=1; // 110bbbbb
602 else if ( c==0xed && i<(ix-1) && ((unsigned char)string[i+1] & 0xa0)==0xa0) return false; //U+d800 to U+dfff
603 else if ((c & 0xF0) == 0xE0) {return false; n=2;} // 1110bbbb
604 else if ((c & 0xF8) == 0xF0) {return false; n=3;} // 11110bbb
605 //else if (($c & 0xFC) == 0xF8) n=4; // 111110bb //byte 5, unnecessary in 4 byte UTF-8
606 //else if (($c & 0xFE) == 0xFC) n=5; // 1111110b //byte 6, unnecessary in 4 byte UTF-8
607 else return false;
608 for (j=0; j<n && i<ix; j++) { // n bytes matching 10bbbbbb follow ?
609 if ((++i == ix) || (( (unsigned char)string[i] & 0xC0) != 0x80))
610 return false;
611 }
612 }
613 return true;
614 }
615
validwcharforeadline(const wchar_t thewchar)616 bool validwcharforeadline(const wchar_t thewchar)
617 {
618 wstring input;
619 input+=thewchar;
620 string output;
621 localwtostring(&input,&output);
622 return validoptionforreadline(output);
623 }
624
escapereadlinebreakers(const wchar_t * what)625 wstring escapereadlinebreakers(const wchar_t *what)
626 {
627 wstring output;
628 for( unsigned int i = 0; i < wcslen( what ) ; i++ )
629 {
630 if(validwcharforeadline(what[ i ] ))
631 {
632 output.reserve( output.size() + 1 );
633 output += what[ i ];
634 } else {
635 #ifndef __MINGW32__
636 wchar_t code[ 7 ];
637 swprintf( code, 7, L"\\u%0.4X", what[ i ] ); //while this does not work (yet) as what, at least it shows something and does not break
638 //TODO: ideally we would do the conversion from escaped unicode chars \uXXXX back to wchar_t in the server
639 // NOTICE: I was able to execute a command with a literl \x242ee (which correspond to \uD850\uDEEE in UTF16).
640 // So it'll be more interesting to output here the complete unicode char and in unescapeutf16escapedseqs revert it.
641 // or keep here the UTF16 escaped secs and revert them correctly in the unescapeutf16escapedseqs
642 output.reserve( output.size() + 7 ); // "\u"(2) + 5(uint max digits capacity)
643 output += code;
644 #endif
645 }
646 }
647 return output;
648 }
649 #endif
650
651 #ifndef NO_READLINE
install_rl_handler(const char * theprompt,bool external)652 void install_rl_handler(const char *theprompt, bool external)
653 {
654 std::lock_guard<std::mutex> lkrlhandler(handlerInstallerMutex);
655 if (procesingline)
656 {
657 promptreinstalledwhenprocessingline = true;
658 }
659
660 #ifdef _WIN32
661 wstring wswhat;
662 stringtolocalw(theprompt,&wswhat);
663 const wchar_t *what = wswhat.c_str();
664
665
666 // escape characters that break readline input (e.g. Chinese ones. e.g \x242ee)
667 wstring output = escapereadlinebreakers(what);
668
669 // give readline something it understands
670 what = output.c_str();
671 size_t buffer_size;
672 #ifdef _TRUNCATE
673 wcstombs_s(&buffer_size, NULL, 0, what, _TRUNCATE);
674 #else
675 buffer_size=output.size()*sizeof(wchar_t)*2;
676 #endif
677
678 if (buffer_size) //coversion is ok
679 {
680 // do the actual conversion
681 char *buffer = new char[buffer_size];
682 #ifdef _TRUNCATE
683 wcstombs_s(&buffer_size, buffer, buffer_size,what, _TRUNCATE);
684 #else
685 wcstombs(buffer, what, buffer_size);
686 #endif
687
688 rl_callback_handler_install(buffer, store_line);
689 }
690 else
691 {
692 rl_callback_handler_install("INVALID_PROMPT: ", store_line);
693 }
694
695 #else
696
697 rl_callback_handler_install(theprompt, store_line);
698 handlerOverridenByExternalThread = external;
699 requirepromptinstall = false;
700
701 #endif
702 }
703
redisplay_prompt()704 void redisplay_prompt()
705 {
706 int saved_point = rl_point;
707 char *saved_line = rl_copy_text(0, rl_end);
708
709 rl_clear_message();
710
711 // enter a new line if not processing sth (otherwise, the newline should already be there)
712 if (!procesingline)
713 {
714 rl_crlf();
715 }
716
717 if (prompt == COMMAND)
718 {
719 install_rl_handler(*dynamicprompt ? dynamicprompt : prompts[COMMAND]);
720 }
721
722 // restore line
723 if (saved_line)
724 {
725 rl_replace_line(saved_line, 0);
726 free(saved_line);
727 saved_line = NULL;
728 }
729 rl_point = saved_point;
730 rl_redisplay();
731 }
732
733 #endif
734
735
changeprompt(const char * newprompt,bool redisplay)736 void changeprompt(const char *newprompt, bool redisplay)
737 {
738 std::lock_guard<std::mutex> g(mutexPrompt);
739
740 if (*dynamicprompt)
741 {
742 if (!strcmp(newprompt,dynamicprompt))
743 return; //same prompt. do nth
744 }
745
746 strncpy(dynamicprompt, newprompt, sizeof( dynamicprompt ));
747
748 if (strlen(newprompt) >= PROMPT_MAX_SIZE)
749 {
750 strncpy(dynamicprompt, newprompt, PROMPT_MAX_SIZE/2-1);
751 dynamicprompt[PROMPT_MAX_SIZE/2-1] = '.';
752 dynamicprompt[PROMPT_MAX_SIZE/2] = '.';
753
754 strncpy(dynamicprompt+PROMPT_MAX_SIZE/2+1, newprompt+(strlen(newprompt)-PROMPT_MAX_SIZE/2+2), PROMPT_MAX_SIZE/2-2);
755 dynamicprompt[PROMPT_MAX_SIZE-1] = '\0';
756 }
757
758 #ifdef NO_READLINE
759 console->updateInputPrompt(newprompt);
760 #else
761 if (redisplay)
762 {
763 // save line
764 redisplay_prompt();
765 }
766
767 #endif
768
769 static bool firstime = true;
770 if (firstime)
771 {
772 firstime = false;
773 #if _WIN32
774 if( !SetConsoleCtrlHandler( CtrlHandler, TRUE ) )
775 {
776 cerr << "Control handler set failed" << endl;
777 }
778 #else
779 // prevent CTRL+C exit
780 signal(SIGINT, sigint_handler);
781 #endif
782 }
783 }
784
escapeEspace(string & orig)785 void escapeEspace(string &orig)
786 {
787 replaceAll(orig," ", "\\ ");
788 }
789
unescapeEspace(string & orig)790 void unescapeEspace(string &orig)
791 {
792 replaceAll(orig,"\\ ", " ");
793 }
794
795 #ifndef NO_READLINE
empty_completion(const char * text,int state)796 char* empty_completion(const char* text, int state)
797 {
798 // we offer 2 different options so that it doesn't complete (no space is inserted)
799 if (state == 0)
800 {
801 return strdup(" ");
802 }
803 if (state == 1)
804 {
805 return strdup(text);
806 }
807 return NULL;
808 }
809
generic_completion(const char * text,int state,vector<string> validOptions)810 char* generic_completion(const char* text, int state, vector<string> validOptions)
811 {
812 static size_t list_index, len;
813 static bool foundone;
814 string name;
815 if (!validOptions.size()) // no matches
816 {
817 return empty_completion(text,state); //dont fall back to filenames
818 }
819 if (!state)
820 {
821 list_index = 0;
822 foundone = false;
823 len = strlen(text);
824 }
825 while (list_index < validOptions.size())
826 {
827 name = validOptions.at(list_index);
828 if (!rl_completion_quote_character) {
829 escapeEspace(name);
830 }
831
832 list_index++;
833
834 if (!( strcmp(text, "")) || (( name.size() >= len ) && ( strlen(text) >= len ) && ( name.find(text) == 0 )))
835 {
836 if (name.size() && (( name.at(name.size() - 1) == '=' ) || ( name.at(name.size() - 1) == '\\' ) || ( name.at(name.size() - 1) == '/' )))
837 {
838 rl_completion_suppress_append = 1;
839 }
840 foundone = true;
841 return dupstr((char*)name.c_str());
842 }
843 }
844
845 if (!foundone)
846 {
847 return empty_completion(text,state); //dont fall back to filenames
848 }
849
850 return((char*)NULL );
851 }
852 #endif
853
local_completion(const char * text,int state)854 char* local_completion(const char* text, int state)
855 {
856 return((char*)NULL ); //matches will be NULL: readline will use local completion
857 }
858
pushvalidoption(vector<string> * validOptions,const char * beginopt)859 void pushvalidoption(vector<string> *validOptions, const char *beginopt)
860 {
861 #ifdef _WIN32
862 if (validoptionforreadline(beginopt))
863 {
864 validOptions->push_back(beginopt);
865 }
866 else
867 {
868 wstring input;
869 stringtolocalw(beginopt,&input);
870 wstring output = escapereadlinebreakers(input.c_str());
871
872 string soutput;
873 localwtostring(&output,&soutput);
874 validOptions->push_back(soutput.c_str());
875 }
876 #else
877 validOptions->push_back(beginopt);
878 #endif
879 }
880
changedir(const string & where)881 void changedir(const string& where)
882 {
883 #ifdef _WIN32
884 wstring wwhere;
885 stringtolocalw(where.c_str(), &wwhere);
886 wwhere.append(L"\\");
887 int r = SetCurrentDirectoryW((LPCWSTR)wwhere.data());
888 if (!r)
889 {
890 cerr << "Error at SetCurrentDirectoryW before local completion to " << where << ". errno: " << ERRNO << endl;
891 }
892 #else
893 chdir(where.c_str());
894 #endif
895 }
896
897
898 #ifndef NO_READLINE
remote_completion(const char * text,int state)899 char* remote_completion(const char* text, int state)
900 {
901 char *saved_line = strdup(getCurrentLine().c_str());
902
903 static vector<string> validOptions;
904 if (state == 0)
905 {
906 validOptions.clear();
907 string completioncommand("completionshell ");
908 completioncommand+=saved_line;
909
910 OUTSTRING s;
911 OUTSTRINGSTREAM oss(s);
912
913 comms->executeCommand(completioncommand, readresponse, oss);
914
915 string outputcommand;
916
917 #ifdef _WIN32
918 #ifdef __MINGW32__
919 wstring soss=oss.str();
920 localwtostring(&soss,&outputcommand);
921 #else
922 localwtostring(&oss.str(),&outputcommand);
923 #endif
924 #else
925 outputcommand = oss.str();
926 #endif
927
928 if (outputcommand == "MEGACMD_USE_LOCAL_COMPLETION")
929 {
930 free(saved_line);
931 return local_completion(text,state); //fallback to local path completion
932 }
933
934 if (outputcommand.find("MEGACMD_USE_LOCAL_COMPLETION") == 0)
935 {
936 string where = outputcommand.substr(strlen("MEGACMD_USE_LOCAL_COMPLETION"));
937 changedir(where);
938 free(saved_line);
939 return local_completion(text,state); //fallback to local path completion
940 }
941
942 char *ptr = (char *)outputcommand.c_str();
943
944 char *beginopt = ptr;
945 while (*ptr)
946 {
947 if (*ptr == 0x1F)
948 {
949 *ptr = '\0';
950 if (strcmp(beginopt," ")) //the server will give a " " for empty_completion (no matches)
951 {
952 pushvalidoption(&validOptions,beginopt);
953 }
954
955 beginopt=ptr+1;
956 }
957 ptr++;
958 }
959 if (*beginopt && strcmp(beginopt," "))
960 {
961 pushvalidoption(&validOptions,beginopt);
962 }
963 }
964
965 free(saved_line);
966 saved_line = NULL;
967
968 return generic_completion(text, state, validOptions);
969 }
970
getCompletionMatches(const char * text,int start,int end)971 static char** getCompletionMatches(const char * text, int start, int end)
972 {
973 rl_filename_quoting_desired = 1;
974
975 char **matches;
976
977 matches = (char**)NULL;
978
979 matches = rl_completion_matches((char*)text, remote_completion);
980
981 return( matches );
982 }
983
printHistory()984 void printHistory()
985 {
986 int length = history_length;
987 int offset = 1;
988 int rest = length;
989 while (rest >= 10)
990 {
991 offset++;
992 rest = rest / 10;
993 }
994
995 for (int i = 0; i < length; i++)
996 {
997 history_set_pos(i);
998 OUTSTREAM << setw(offset) << i << " " << current_history()->line << endl;
999 }
1000 }
1001
1002 #ifdef _WIN32
1003
1004 /**
1005 * @brief getcharacterreadlineUTF16support
1006 * while this works, somehow arrows and other readline stuff is disabled using this one.
1007 * @param stream
1008 * @return
1009 */
getcharacterreadlineUTF16support(FILE * stream)1010 int getcharacterreadlineUTF16support (FILE *stream)
1011 {
1012 int result;
1013 char b[10];
1014 memset(b,0,10);
1015
1016 while (1)
1017 {
1018 int oldmode = _setmode(_fileno(stream), _O_U16TEXT);
1019
1020 result = read (fileno (stream), &b, 10);
1021 _setmode(_fileno(stream), oldmode);
1022
1023 if (result == 0)
1024 {
1025 return (EOF);
1026 }
1027
1028 // convert the UTF16 string to widechar
1029 size_t wbuffer_size;
1030 #ifdef _TRUNCATE
1031 mbstowcs_s(&wbuffer_size, NULL, 0, b, _TRUNCATE);
1032 #else
1033 wbuffer_size=10;
1034 #endif
1035 wchar_t *wbuffer = new wchar_t[wbuffer_size];
1036
1037 #ifdef _TRUNCATE
1038 mbstowcs_s(&wbuffer_size, wbuffer, wbuffer_size, b, _TRUNCATE);
1039 #else
1040 mbstowcs(wbuffer, b, wbuffer_size);
1041 #endif
1042
1043 // convert the UTF16 widechar to UTF8 string
1044 string receivedutf8;
1045 utf16ToUtf8(wbuffer, wbuffer_size,&receivedutf8);
1046
1047 if (strlen(receivedutf8.c_str()) > 1) //multi byte utf8 sequence: place the UTF8 characters into rl buffer one by one
1048 {
1049 for (unsigned int i=0;i< strlen(receivedutf8.c_str());i++)
1050 {
1051 rl_line_buffer[rl_end++] = receivedutf8.c_str()[i];
1052 rl_point=rl_end;
1053 }
1054 rl_line_buffer[rl_end] = '\0';
1055
1056 return 0;
1057 }
1058
1059 if (result =! 0)
1060 {
1061 return (b[0]);
1062 }
1063
1064 /* If zero characters are returned, then the file that we are
1065 reading from is empty! Return EOF in that case. */
1066 if (result == 0)
1067 {
1068 return (EOF);
1069 }
1070 }
1071 }
1072 #endif
1073
wait_for_input(int readline_fd)1074 void wait_for_input(int readline_fd)
1075 {
1076 fd_set fds;
1077
1078 FD_ZERO(&fds);
1079 FD_SET(readline_fd, &fds);
1080
1081 int rc = select(FD_SETSIZE, &fds, NULL, NULL, NULL);
1082 if (rc < 0)
1083 {
1084 if (ERRNO != EINTR) //syscall
1085 {
1086 #ifdef _WIN32
1087 if (ERRNO != WSAENOTSOCK) // it enters here since it is not a socket. Alt: Use WaitForMultipleObjectsEx
1088 #endif
1089 cerr << "Error at select at wait_for_input errno: " << ERRNO << endl;
1090 return;
1091 }
1092 }
1093 }
1094 #else
1095
1096
remote_completion(string linetocomplete)1097 vector<autocomplete::ACState::Completion> remote_completion(string linetocomplete)
1098 {
1099 using namespace autocomplete;
1100 vector<ACState::Completion> result;
1101
1102 // normalize any partially or intermediately quoted strings, eg. `put c:\Program" Fi` or `/My" Documents/"`
1103 ACState acs = prepACState(linetocomplete, linetocomplete.size(), console->getAutocompleteStyle());
1104 string refactoredline;
1105 for (auto& s : acs.words)
1106 {
1107 refactoredline += (refactoredline.empty() ? "" : " ") + s.getQuoted();
1108 }
1109
1110 OUTSTRING s;
1111 OUTSTRINGSTREAM oss(s);
1112 comms->executeCommand(string("completionshell ") + refactoredline, readresponse, oss);
1113
1114 string outputcommand;
1115 auto ossstr=oss.str();
1116 localwtostring(&ossstr, &outputcommand);
1117
1118 ACState::quoted_word completionword = acs.words.size() ? acs.words[acs.words.size() - 1] : string();
1119
1120 if (outputcommand.find("MEGACMD_USE_LOCAL_COMPLETION") == 0)
1121 {
1122 string where;
1123 bool folders = false;
1124 if (outputcommand.find("MEGACMD_USE_LOCAL_COMPLETIONFOLDERS") == 0)
1125 {
1126 where = outputcommand.substr(strlen("MEGACMD_USE_LOCAL_COMPLETIONFOLDERS"));
1127 folders = true;
1128 }
1129 else
1130 {
1131 where = outputcommand.substr(strlen("MEGACMD_USE_LOCAL_COMPLETION"));
1132 }
1133 changedir(where);
1134
1135 if (acs.words.size())
1136 {
1137 string l = completionword.getQuoted();
1138 CompletionState cs = autoComplete(l, l.size(), folders ? localFSFolder() : localFSPath(), console->getAutocompleteStyle());
1139 result.swap(cs.completions);
1140 }
1141 return result;
1142 }
1143 else
1144 {
1145 char *ptr = (char *)outputcommand.c_str();
1146
1147 char *beginopt = ptr;
1148 while (*ptr)
1149 {
1150 if (*ptr == 0x1F)
1151 {
1152 *ptr = '\0';
1153 if (strcmp(beginopt, " ")) //the server will give a " " for empty_completion (no matches)
1154 {
1155 result.push_back(autocomplete::ACState::Completion(beginopt, false));
1156 }
1157
1158 beginopt = ptr + 1;
1159 }
1160 ptr++;
1161 }
1162 if (*beginopt && strcmp(beginopt, " "))
1163 {
1164 result.push_back(autocomplete::ACState::Completion(beginopt, false));
1165 }
1166
1167 if (result.size() == 1 && result[0].s == completionword.s)
1168 {
1169 result.clear(); // for parameters it returns the same string when there are no matches
1170 }
1171 return result;
1172 }
1173 }
1174
exec_clear(autocomplete::ACState & s)1175 void exec_clear(autocomplete::ACState& s)
1176 {
1177 console->clearScreen();
1178 }
1179
exec_history(autocomplete::ACState & s)1180 void exec_history(autocomplete::ACState& s)
1181 {
1182 console->outputHistory();
1183 }
1184
exec_dos_unix(autocomplete::ACState & s)1185 void exec_dos_unix(autocomplete::ACState& s)
1186 {
1187 if (s.words.size() < 2)
1188 {
1189 OUTSTREAM << "autocomplete style: " << (console->getAutocompleteStyle() ? "unix" : "dos") << endl;
1190 }
1191 else
1192 {
1193 console->setAutocompleteStyle(s.words[1].s == "unix");
1194 }
1195 }
1196
exec_codepage(autocomplete::ACState & s)1197 void exec_codepage(autocomplete::ACState& s)
1198 {
1199 if (s.words.size() == 1)
1200 {
1201 UINT cp1, cp2;
1202 console->getShellCodepages(cp1, cp2);
1203 cout << "Current codepage is " << cp1;
1204 if (cp2 != cp1)
1205 {
1206 cout << " with failover to codepage " << cp2 << " for any absent glyphs";
1207 }
1208 cout << endl;
1209 for (int i = 32; i < 256; ++i)
1210 {
1211 string theCharUtf8 = WinConsole::toUtf8String(WinConsole::toUtf16String(string(1, (char)i), cp1));
1212 cout << " dec/" << i << " hex/" << hex << i << dec << ": '" << theCharUtf8 << "'";
1213 if (i % 4 == 3)
1214 {
1215 cout << endl;
1216 }
1217 }
1218 }
1219 else if (s.words.size() == 2 && atoi(s.words[1].s.c_str()) != 0)
1220 {
1221 if (!console->setShellConsole(atoi(s.words[1].s.c_str()), atoi(s.words[1].s.c_str())))
1222 {
1223 cout << "Code page change failed - unicode selected" << endl;
1224 }
1225 }
1226 else if (s.words.size() == 3 && atoi(s.words[1].s.c_str()) != 0 && atoi(s.words[2].s.c_str()) != 0)
1227 {
1228 if (!console->setShellConsole(atoi(s.words[1].s.c_str()), atoi(s.words[2].s.c_str())))
1229 {
1230 cout << "Code page change failed - unicode selected" << endl;
1231 }
1232 }
1233 else
1234 {
1235 cout << " codepage [N [N]]" << endl;
1236 }
1237 }
1238
1239
1240 autocomplete::ACN autocompleteSyntax;
1241
buildAutocompleteSyntax()1242 autocomplete::ACN buildAutocompleteSyntax()
1243 {
1244 using namespace autocomplete;
1245 std::unique_ptr<Either> p(new Either(" "));
1246
1247 p->Add(exec_clear, sequence(text("clear")));
1248 p->Add(exec_codepage, sequence(text("codepage"), opt(sequence(wholenumber(65001), opt(wholenumber(65001))))));
1249 p->Add(exec_dos_unix, sequence(text("autocomplete"), opt(either(text("unix"), text("dos")))));
1250 p->Add(exec_history, sequence(text("history")));
1251
1252 return autocompleteSyntax = std::move(p);
1253 }
1254
printHistory()1255 void printHistory()
1256 {
1257 console->outputHistory();
1258 }
1259 #endif
1260
isserverloggedin()1261 bool isserverloggedin()
1262 {
1263 if (comms->executeCommand(("loggedin")) == MCMD_NOTLOGGEDIN )
1264 {
1265 return false;
1266 }
1267 return true;
1268 }
1269
1270
process_line(const char * line)1271 void process_line(const char * line)
1272 {
1273 string refactoredline;
1274
1275 switch (prompt)
1276 {
1277 case AREYOUSURE:
1278 //this is currently never used
1279 if (!strcasecmp(line,"yes") || !strcasecmp(line,"y"))
1280 {
1281 comms->setResponseConfirmation(true);
1282 setprompt(COMMAND);
1283 }
1284 else if (!strcasecmp(line,"no") || !strcasecmp(line,"n"))
1285 {
1286 comms->setResponseConfirmation(false);
1287 setprompt(COMMAND);
1288 }
1289 else
1290 {
1291 //Do nth, ask again
1292 OUTSTREAM << "Please enter: [y]es/[n]o: " << flush;
1293 }
1294 break;
1295 case LOGINPASSWORD:
1296 {
1297 if (!strlen(line))
1298 {
1299 break;
1300 }
1301 if (confirminglink)
1302 {
1303 string confirmcommand("confirm ");
1304 confirmcommand+=linktoconfirm;
1305 confirmcommand+=" " ;
1306 confirmcommand+=loginname;
1307 confirmcommand+=" \"";
1308 confirmcommand+=line;
1309 confirmcommand+="\"" ;
1310 OUTSTREAM << endl;
1311 comms->executeCommand(confirmcommand.c_str(), readresponse);
1312 }
1313 else if (confirmingcancellink)
1314 {
1315 string confirmcommand("confirmcancel ");
1316 confirmcommand+=linktoconfirm;
1317 confirmcommand+=" \"";
1318 confirmcommand+=line;
1319 confirmcommand+="\"" ;
1320 OUTSTREAM << endl;
1321 comms->executeCommand(confirmcommand.c_str(), readresponse);
1322 }
1323 else
1324 {
1325 string logincommand("login -v ");
1326 if (clientID.size())
1327 {
1328 logincommand += "--clientID=";
1329 logincommand+=clientID;
1330 logincommand+=" ";
1331 }
1332 logincommand+=loginname;
1333 logincommand+=" \"" ;
1334 logincommand+=line;
1335 logincommand+="\"" ;
1336 OUTSTREAM << endl;
1337 comms->executeCommand(logincommand.c_str(), readresponse);
1338 }
1339 confirminglink = false;
1340 confirmingcancellink = false;
1341
1342 setprompt(COMMAND);
1343 break;
1344 }
1345 case NEWPASSWORD:
1346 {
1347 if (!strlen(line))
1348 {
1349 break;
1350 }
1351 newpasswd = line;
1352 OUTSTREAM << endl;
1353 setprompt(PASSWORDCONFIRM);
1354 break;
1355 }
1356 case PASSWORDCONFIRM:
1357 {
1358 if (!strlen(line))
1359 {
1360 break;
1361 }
1362 if (line != newpasswd)
1363 {
1364 OUTSTREAM << endl << "New passwords differ, please try again" << endl;
1365 }
1366 else
1367 {
1368 OUTSTREAM << endl;
1369
1370 if (signingup)
1371 {
1372 signupline += " \"";
1373 signupline += newpasswd;
1374 signupline += "\"";
1375 comms->executeCommand(signupline.c_str(), readresponse);
1376
1377 signingup = false;
1378 }
1379 else
1380 {
1381 string changepasscommand(passwdline);
1382 passwdline = " ";
1383 changepasscommand+=" " ;
1384 if (oldpasswd.size())
1385 {
1386 changepasscommand+="\"" ;
1387 changepasscommand+=oldpasswd;
1388 changepasscommand+="\"" ;
1389 }
1390 changepasscommand+=" \"" ;
1391 changepasscommand+=newpasswd;
1392 changepasscommand+="\"" ;
1393 comms->executeCommand(changepasscommand.c_str(), readresponse);
1394 }
1395 }
1396
1397 setprompt(COMMAND);
1398 break;
1399 }
1400 case COMMAND:
1401 {
1402
1403 #ifdef NO_READLINE
1404 // if local command and syntax is satisfied, execute it
1405 string consoleOutput;
1406 if (autocomplete::autoExec(line, string::npos, autocompleteSyntax, false, consoleOutput, false))
1407 {
1408 COUT << consoleOutput << flush;
1409 return;
1410 }
1411
1412 // normalize any partially or intermediately quoted strings, eg. `put c:\Program" Fi` or get `/My" Documents/"`
1413 autocomplete::ACState acs = autocomplete::prepACState(line, strlen(line), console->getAutocompleteStyle());
1414 for (auto& s : acs.words)
1415 {
1416 refactoredline += (refactoredline.empty() ? "" : " ") + s.getQuoted();
1417 }
1418 line = refactoredline.c_str();
1419 #endif
1420
1421 vector<string> words = getlistOfWords((char *)line);
1422
1423 string clientWidth = "--client-width=";
1424 clientWidth+= SSTR(getNumberOfCols(80));
1425
1426 words.insert(words.begin()+1, clientWidth);
1427
1428 string scommandtoexec(words[0]);
1429 scommandtoexec+=" ";
1430 scommandtoexec+=clientWidth;
1431 scommandtoexec+=" ";
1432
1433 if (strlen(line)>(words[0].size()+1))
1434 {
1435 scommandtoexec+=line+words[0].size()+1;
1436 }
1437
1438 const char *commandtoexec = scommandtoexec.c_str();
1439
1440 bool helprequested = false;
1441 for (unsigned int i = 1; i< words.size(); i++)
1442 {
1443 if (words[i]== "--help") helprequested = true;
1444 }
1445 if (words.size())
1446 {
1447 if ( words[0] == "exit" || words[0] == "quit")
1448 {
1449 if (find(words.begin(), words.end(), "--only-shell") == words.end())
1450 {
1451 comms->executeCommand(commandtoexec, readresponse);
1452 }
1453
1454 if (find(words.begin(), words.end(), "--help") == words.end()
1455 && find(words.begin(), words.end(), "--only-server") == words.end() )
1456 {
1457 doExit = true;
1458 }
1459 }
1460 #if defined(_WIN32) || defined(__APPLE__)
1461 else if (words[0] == "update")
1462 {
1463 MegaCmdShellCommunications::updating = true;
1464 int ret = comms->executeCommand(commandtoexec, readresponse);
1465 if (ret == MCMD_REQRESTART)
1466 {
1467 OUTSTREAM << "MEGAcmd has been updated ... this shell will be restarted before proceding...." << endl;
1468 doExit = true;
1469 doReboot = true;
1470 }
1471 else if (ret != MCMD_INVALIDSTATE && words.size() == 1)
1472 {
1473 MegaCmdShellCommunications::updating = false;
1474 }
1475 }
1476 #endif
1477 else if (words[0] == "history")
1478 {
1479 if (helprequested)
1480 {
1481 OUTSTREAM << " Prints commands history" << endl;
1482 }
1483 else
1484 {
1485 printHistory();
1486 }
1487 }
1488 #if defined(_WIN32) && !defined(NO_READLINE)
1489 else if (!helprequested && words[0] == "unicode" && words.size() == 1)
1490 {
1491 rl_getc_function=(rl_getc_function==&getcharacterreadlineUTF16support)?rl_getc:&getcharacterreadlineUTF16support;
1492 OUTSTREAM << "Unicode shell input " << ((rl_getc_function==&getcharacterreadlineUTF16support)?"ENABLED":"DISABLED") << endl;
1493 return;
1494 }
1495 #endif
1496 else if (!helprequested && words[0] == "passwd")
1497 {
1498 if (isserverloggedin())
1499 {
1500 passwdline = commandtoexec;
1501 discardOptionsAndFlags(&words);
1502 if (words.size() == 1)
1503 {
1504 setprompt(NEWPASSWORD);
1505 }
1506 else
1507 {
1508 comms->executeCommand(commandtoexec, readresponse);
1509 }
1510 }
1511 else
1512 {
1513 cerr << "Not logged in." << endl;
1514 }
1515
1516 return;
1517 }
1518 else if (!helprequested && words[0] == "login")
1519 {
1520 if (!isserverloggedin())
1521 {
1522 discardOptionsAndFlags(&words);
1523
1524 if ( (words.size() == 2 || ( words.size() == 3 && !words[2].size() ) )
1525 && (words[1].find("@") != string::npos))
1526 {
1527 loginname = words[1];
1528 setprompt(LOGINPASSWORD);
1529 }
1530 else
1531 {
1532 string s = commandtoexec;
1533 if (clientID.size())
1534 {
1535 s = "login --clientID=";
1536 s+=clientID;
1537 s.append(string(commandtoexec).substr(5));
1538 }
1539 comms->executeCommand(s, readresponse);
1540 }
1541 }
1542 else
1543 {
1544 cerr << "Already logged in. Please log out first." << endl;
1545 }
1546 return;
1547 }
1548 else if (!helprequested && words[0] == "signup")
1549 {
1550 if (!isserverloggedin())
1551 {
1552 signupline = commandtoexec;
1553 discardOptionsAndFlags(&words);
1554
1555 if (words.size() == 2)
1556 {
1557 loginname = words[1];
1558 signingup = true;
1559 setprompt(NEWPASSWORD);
1560 }
1561 else
1562 {
1563 comms->executeCommand(commandtoexec, readresponse);
1564 }
1565 }
1566 else
1567 {
1568 cerr << "Please loggout first." << endl;
1569 }
1570 return;
1571 }
1572 else if (!helprequested && words[0] == "confirm")
1573 {
1574 discardOptionsAndFlags(&words);
1575
1576 if (words.size() == 3)
1577 {
1578 linktoconfirm = words[1];
1579 loginname = words[2];
1580 confirminglink = true;
1581 setprompt(LOGINPASSWORD);
1582 }
1583 else
1584 {
1585 comms->executeCommand(commandtoexec, readresponse);
1586 }
1587 }
1588 else if (!helprequested && words[0] == "confirmcancel")
1589 {
1590 discardOptionsAndFlags(&words);
1591
1592 if (words.size() == 2)
1593 {
1594 linktoconfirm = words[1];
1595 confirmingcancellink = true;
1596 setprompt(LOGINPASSWORD);
1597 }
1598 else
1599 {
1600 comms->executeCommand(commandtoexec, readresponse);
1601 }
1602 return;
1603 }
1604 else if (!helprequested && words[0] == "clear")
1605 {
1606 #ifdef _WIN32
1607 HANDLE hStdOut;
1608 CONSOLE_SCREEN_BUFFER_INFO csbi;
1609 DWORD count;
1610
1611 hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
1612 if (hStdOut == INVALID_HANDLE_VALUE) return;
1613
1614 /* Get the number of cells in the current buffer */
1615 if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return;
1616 /* Fill the entire buffer with spaces */
1617 if (!FillConsoleOutputCharacter( hStdOut, (TCHAR) ' ', csbi.dwSize.X *csbi.dwSize.Y, { 0, 0 }, &count ))
1618 {
1619 return;
1620 }
1621 /* Fill the entire buffer with the current colors and attributes */
1622 if (!FillConsoleOutputAttribute(hStdOut, csbi.wAttributes, csbi.dwSize.X *csbi.dwSize.Y, { 0, 0 }, &count))
1623 {
1624 return;
1625 }
1626 /* Move the cursor home */
1627 SetConsoleCursorPosition( hStdOut, { 0, 0 } );
1628 #elif __linux__
1629 printf("\033[H\033[J");
1630 #else
1631 rl_clear_screen(0,0);
1632 #endif
1633 return;
1634 }
1635 else if ( (words[0] == "transfers"))
1636 {
1637 string toexec;
1638
1639 if (!strstr (commandtoexec,"path-display-size"))
1640 {
1641 unsigned int width = getNumberOfCols(75);
1642 int pathSize = int((width-46)/2);
1643
1644 toexec+=words[0];
1645 toexec+=" --path-display-size=";
1646 toexec+=SSTR(pathSize);
1647 toexec+=" ";
1648 if (strlen(commandtoexec)>(words[0].size()+1))
1649 {
1650 toexec+=commandtoexec+words[0].size()+1;
1651 }
1652 }
1653 else
1654 {
1655 toexec+=commandtoexec;
1656 }
1657
1658 comms->executeCommand(toexec.c_str(), readresponse);
1659 }
1660 else if ( (words[0] == "du"))
1661 {
1662 string toexec;
1663
1664 if (!strstr (commandtoexec,"path-display-size"))
1665 {
1666 unsigned int width = getNumberOfCols(75);
1667 int pathSize = int(width-13);
1668 if (strstr(commandtoexec, "--versions"))
1669 {
1670 pathSize -= 11;
1671 }
1672
1673 toexec+=words[0];
1674 toexec+=" --path-display-size=";
1675 toexec+=SSTR(pathSize);
1676 toexec+=" ";
1677 if (strlen(commandtoexec)>(words[0].size()+1))
1678 {
1679 toexec+=commandtoexec+words[0].size()+1;
1680 }
1681 }
1682 else
1683 {
1684 toexec+=commandtoexec;
1685 }
1686
1687 comms->executeCommand(toexec.c_str(), readresponse);
1688 }
1689 else if (words[0] == "sync")
1690 {
1691 string toexec;
1692
1693 if (!strstr (commandtoexec,"path-display-size"))
1694 {
1695 unsigned int width = getNumberOfCols(75);
1696 int pathSize = int((width-46)/2);
1697
1698 toexec+="sync --path-display-size=";
1699 toexec+=SSTR(pathSize);
1700 toexec+=" ";
1701 if (strlen(commandtoexec)>strlen("sync "))
1702 {
1703 toexec+=commandtoexec+strlen("sync ");
1704 }
1705 }
1706 else
1707 {
1708 toexec+=commandtoexec;
1709 }
1710
1711 comms->executeCommand(toexec.c_str(), readresponse);
1712 }
1713 else if (words[0] == "mediainfo")
1714 {
1715 string toexec;
1716
1717 if (!strstr (commandtoexec,"path-display-size"))
1718 {
1719 unsigned int width = getNumberOfCols(75);
1720 int pathSize = int(width - 28);
1721
1722 toexec+=words[0];
1723 toexec+=" --path-display-size=";
1724 toexec+=SSTR(pathSize);
1725 toexec+=" ";
1726 if (strlen(commandtoexec)>(words[0].size()+1))
1727 {
1728 toexec+=commandtoexec+words[0].size()+1;
1729 }
1730 }
1731 else
1732 {
1733 toexec+=commandtoexec;
1734 }
1735
1736 comms->executeCommand(toexec.c_str(), readresponse);
1737 }
1738 else if (words[0] == "backup")
1739 {
1740 string toexec;
1741
1742 if (!strstr (commandtoexec,"path-display-size"))
1743 {
1744 unsigned int width = getNumberOfCols(75);
1745 int pathSize = int((width-21)/2);
1746
1747 toexec+="backup --path-display-size=";
1748 toexec+=SSTR(pathSize);
1749 toexec+=" ";
1750 if (strlen(commandtoexec)>strlen("backup "))
1751 {
1752 toexec+=commandtoexec+strlen("backup ");
1753 }
1754 }
1755 else
1756 {
1757 toexec+=commandtoexec;
1758 }
1759
1760 comms->executeCommand(toexec.c_str(), readresponse);
1761 }
1762 else
1763 {
1764 if ( words[0] == "get" || words[0] == "put" || words[0] == "reload")
1765 {
1766 string s = commandtoexec;
1767 if (clientID.size())
1768 {
1769 string sline = commandtoexec;
1770 size_t pspace = sline.find_first_of(" ");
1771 s="";
1772 s=sline.substr(0,pspace);
1773 s += " --clientID=";
1774 s+=clientID;
1775 if (pspace!=string::npos)
1776 {
1777 s+=sline.substr(pspace);
1778 }
1779 words.push_back(s);
1780 }
1781 comms->executeCommand(s, readresponse);
1782 #ifdef _WIN32
1783 Sleep(200); // give a brief while to print progress ended
1784 #endif
1785 }
1786 else
1787 {
1788 // execute user command
1789 comms->executeCommand(commandtoexec, readresponse);
1790 }
1791 }
1792 }
1793 else
1794 {
1795 cerr << "failed to interprete input commandtoexec: " << commandtoexec << endl;
1796 }
1797 break;
1798 }
1799 }
1800
1801 }
1802
1803 // main loop
1804 #ifndef NO_READLINE
readloop()1805 void readloop()
1806 {
1807 time_t lasttimeretrycons = 0;
1808
1809 char *saved_line = NULL;
1810 int saved_point = 0;
1811
1812 rl_save_prompt();
1813
1814 int readline_fd = -1;
1815
1816 readline_fd = fileno(rl_instream);
1817
1818 procesingline = true;
1819 comms->registerForStateChanges(true, statechangehandle);
1820
1821
1822 //now we can relay on having a prompt received if the server is running
1823 {
1824 std::unique_lock<std::mutex> lk(promptLogReceivedMutex);
1825 if (!promtpLogReceivedBool)
1826 {
1827 if (promtpLogReceivedCV.wait_for(lk, std::chrono::seconds(2*RESUME_SESSION_TIMEOUT)) == std::cv_status::timeout)
1828 {
1829 std::cerr << "Server seems irresponsive" << endl;
1830 }
1831 }
1832 }
1833
1834
1835 procesingline = false;
1836 promptreinstalledwhenprocessingline = false;
1837
1838 #if defined(_WIN32) && defined(USE_PORT_COMMS)
1839 // due to a failure in reconnecting to the socket, if the server was initiated in while registeringForStateChanges
1840 // in windows we would not be yet connected. we need to manually try to register again.
1841 if (comms->registerAgainRequired)
1842 {
1843 comms->registerForStateChanges(true, statechangehandle);
1844 }
1845 //give it a while to communicate the state
1846 sleepMilliSeconds(1);
1847 #endif
1848
1849 for (;; )
1850 {
1851 if (prompt == COMMAND)
1852 {
1853 mutexPrompt.lock();
1854 if (requirepromptinstall)
1855 {
1856 install_rl_handler(*dynamicprompt ? dynamicprompt : prompts[COMMAND], false);
1857
1858 // display prompt
1859 if (saved_line)
1860 {
1861 rl_replace_line(saved_line, 0);
1862 free(saved_line);
1863 saved_line = NULL;
1864 }
1865
1866 rl_point = saved_point;
1867 rl_redisplay();
1868 }
1869 mutexPrompt.unlock();
1870 }
1871
1872
1873
1874 // command editing loop - exits when a line is submitted
1875 for (;; )
1876 {
1877 if (prompt == COMMAND || prompt == AREYOUSURE)
1878 {
1879 procesingline = false;
1880 promptreinstalledwhenprocessingline = false;
1881
1882 wait_for_input(readline_fd);
1883
1884 std::lock_guard<std::mutex> g(mutexPrompt);
1885
1886 rl_callback_read_char(); //this calls store_line if last char was enter
1887
1888 time_t tnow = time(NULL);
1889 if ( (tnow - lasttimeretrycons) > 5 && !doExit && !MegaCmdShellCommunications::updating)
1890 {
1891 char * sl = rl_copy_text(0, rl_end);
1892 if (string("quit").find(sl) != 0 && string("exit").find(sl) != 0)
1893 {
1894 comms->executeCommand("retrycons");
1895 lasttimeretrycons = tnow;
1896 }
1897 free(sl);
1898 }
1899
1900 rl_resize_terminal(); // to always adjust to new screen sizes
1901
1902 if (doExit)
1903 {
1904 if (saved_line != NULL)
1905 free(saved_line);
1906 saved_line = NULL;
1907 return;
1908 }
1909 }
1910 else
1911 {
1912 console_readpwchar(pw_buf, sizeof pw_buf, &pw_buf_pos, &line);
1913 }
1914
1915 if (line)
1916 {
1917 break;
1918 }
1919
1920 }
1921
1922 cleanLastMessage();// clean last message that avoids broadcasts repetitions
1923
1924 mutexPrompt.lock();
1925 // save line
1926 saved_point = rl_point;
1927 if (saved_line != NULL)
1928 free(saved_line);
1929 saved_line = rl_copy_text(0, rl_end);
1930
1931 // remove prompt
1932 rl_save_prompt();
1933 rl_replace_line("", 0);
1934 rl_redisplay();
1935
1936 mutexPrompt.unlock();
1937 if (line)
1938 {
1939 if (strlen(line))
1940 {
1941 alreadyFinished = false;
1942 percentDowloaded = 0.0;
1943
1944 handlerOverridenByExternalThread = false;
1945 process_line(line);
1946
1947 {
1948 //after processing the line, we want to reinstall the handler (except if during the process, or due to it,
1949 // the handler is reinstalled by e.g: a change in prompt)
1950 std::lock_guard<std::mutex> lkrlhandler(handlerInstallerMutex);
1951 if (!handlerOverridenByExternalThread)
1952 {
1953 requirepromptinstall = true;
1954 }
1955 }
1956
1957 if (comms->registerAgainRequired)
1958 {
1959 // register again for state changes
1960 comms->registerForStateChanges(true, statechangehandle);
1961 comms->registerAgainRequired = false;
1962 }
1963
1964 // sleep, so that in case there was a changeprompt waiting, gets executed before relooping
1965 // this is not 100% guaranteed to happen
1966 sleepSeconds(0);
1967 }
1968 free(line);
1969 line = NULL;
1970 }
1971 if (doExit)
1972 {
1973 if (saved_line != NULL)
1974 free(saved_line);
1975 saved_line = NULL;
1976 return;
1977 }
1978 }
1979 }
1980 #else // NO_READLINE
readloop()1981 void readloop()
1982 {
1983 time_t lasttimeretrycons = 0;
1984
1985 comms->registerForStateChanges(true, statechangehandle);
1986
1987 //give it a while to communicate the state
1988 sleepMilliSeconds(700);
1989
1990 #if defined(_WIN32) && defined(USE_PORT_COMMS)
1991 // due to a failure in reconnecting to the socket, if the server was initiated in while registeringForStateChanges
1992 // in windows we would not be yet connected. we need to manually try to register again.
1993 if (comms->registerAgainRequired)
1994 {
1995 comms->registerForStateChanges(true, statechangehandle);
1996 }
1997 //give it a while to communicate the state
1998 sleepMilliSeconds(1);
1999 #endif
2000
2001 for (;; )
2002 {
2003 if (prompt == COMMAND)
2004 {
2005 console->updateInputPrompt(*dynamicprompt ? dynamicprompt : prompts[COMMAND]);
2006 }
2007
2008 // command editing loop - exits when a line is submitted
2009 for (;; )
2010 {
2011 line = console->checkForCompletedInputLine();
2012
2013
2014 if (line)
2015 {
2016 break;
2017 }
2018 else
2019 {
2020 time_t tnow = time(NULL);
2021 if ((tnow - lasttimeretrycons) > 5 && !doExit && !MegaCmdShellCommunications::updating)
2022 {
2023 if (wstring(L"quit").find(console->getInputLineToCursor()) != 0 &&
2024 wstring(L"exit").find(console->getInputLineToCursor()) != 0 )
2025 {
2026 comms->executeCommand("retrycons");
2027 lasttimeretrycons = tnow;
2028 }
2029 }
2030
2031 if (doExit)
2032 {
2033 return;
2034 }
2035 }
2036 }
2037
2038 cleanLastMessage();// clean last message that avoids broadcasts repetitions
2039
2040 if (line)
2041 {
2042 if (strlen(line))
2043 {
2044 alreadyFinished = false;
2045 percentDowloaded = 0.0;
2046 // mutexPrompt.lock();
2047 process_line(line);
2048 requirepromptinstall = true;
2049 // mutexPrompt.unlock();
2050
2051 if (comms->registerAgainRequired)
2052 {
2053 // register again for state changes
2054 comms->registerForStateChanges(true, statechangehandle);
2055 comms->registerAgainRequired = false;
2056 }
2057
2058 // sleep, so that in case there was a changeprompt waiting, gets executed before relooping
2059 // this is not 100% guaranteed to happen
2060 sleepSeconds(0);
2061 }
2062 free(line);
2063 line = NULL;
2064 }
2065 if (doExit)
2066 {
2067 return;
2068 }
2069 }
2070 }
2071 #endif
2072
2073 class NullBuffer : public std::streambuf
2074 {
2075 public:
overflow(int c)2076 int overflow(int c)
2077 {
2078 return c;
2079 }
2080 };
2081
printWelcomeMsg(unsigned int width)2082 void printWelcomeMsg(unsigned int width)
2083 {
2084 if (!width)
2085 {
2086 width = getNumberOfCols(75);
2087 }
2088
2089 COUT << endl;
2090 COUT << ".";
2091 for (unsigned int i = 0; i < width; i++)
2092 COUT << "=" ;
2093 COUT << ".";
2094 COUT << endl;
2095 printCenteredLine(" __ __ _____ ____ _ _ ",width);
2096 printCenteredLine("| \\/ | ___|/ ___| / \\ ___ _ __ ___ __| |",width);
2097 printCenteredLine("| |\\/| | \\ / | _ / _ \\ / __| '_ ` _ \\ / _` |",width);
2098 printCenteredLine("| | | | /__\\ |_| |/ ___ \\ (__| | | | | | (_| |",width);
2099 printCenteredLine("|_| |_|____|\\____/_/ \\_\\___|_| |_| |_|\\__,_|",width);
2100
2101 COUT << "|";
2102 for (unsigned int i = 0; i < width; i++)
2103 COUT << " " ;
2104 COUT << "|";
2105 COUT << endl;
2106 printCenteredLine("Welcome to MEGAcmd! A Command Line Interactive and Scriptable",width);
2107 printCenteredLine("Application to interact with your MEGA account.",width);
2108 printCenteredLine("Please write to support@mega.nz if you find any issue or",width);
2109 printCenteredLine("have any suggestion concerning its functionalities.",width);
2110 printCenteredLine("Enter \"help --non-interactive\" to learn how to use MEGAcmd with scripts.",width);
2111 printCenteredLine("Enter \"help\" for basic info and a list of available commands.",width);
2112
2113 #if defined(_WIN32) && defined(NO_READLINE)
2114 printCenteredLine("Unicode support in the console is improved, see \"help --unicode\"", width);
2115 #elif defined(_WIN32)
2116 printCenteredLine("Enter \"help --unicode\" for info regarding non-ASCII support.",width);
2117 #endif
2118
2119 COUT << "`";
2120 for (unsigned int i = 0; i < width; i++)
2121 COUT << "=" ;
2122 COUT << "´";
2123 COUT << endl;
2124
2125 }
2126
quote_detector(char * line,int index)2127 int quote_detector(char *line, int index)
2128 {
2129 return (
2130 index > 0 &&
2131 line[index - 1] == '\\' &&
2132 !quote_detector(line, index - 1)
2133 );
2134 }
2135
runningInBackground()2136 bool runningInBackground()
2137 {
2138 #ifndef _WIN32
2139 pid_t fg = tcgetpgrp(STDIN_FILENO);
2140 if(fg == -1) {
2141 // Piped:
2142 return false;
2143 } else if (fg == getpgrp()) {
2144 // foreground
2145 return false;
2146 } else {
2147 // background
2148 return true;
2149 }
2150 #endif
2151 return false;
2152 }
2153
2154 #ifdef _WIN32
mycompletefunct(char ** c,int num_matches,int max_length)2155 void mycompletefunct(char **c, int num_matches, int max_length)
2156 {
2157 int rows = 1, cols = 80;
2158
2159 #if defined( RL_ISSTATE ) && defined( RL_STATE_INITIALIZED )
2160
2161 if (RL_ISSTATE(RL_STATE_INITIALIZED))
2162 {
2163 rl_resize_terminal();
2164 rl_get_screen_size(&rows, &cols);
2165 }
2166 #endif
2167
2168
2169 // max_length is not trustworthy
2170 for (int i=1; i <= num_matches; i++) //contrary to what the documentation says, num_matches is not the size of c (but num_matches+1), current text is preappended in c[0]
2171 {
2172 max_length = max(max_length,(int)strlen(c[i]));
2173 }
2174
2175 OUTSTREAM << endl;
2176
2177 int nelements_per_col = max(1,(cols-1)/(max_length+1));
2178 for (int i=1; i <= num_matches; i++) //contrary to what the documentation says, num_matches is not the size of c (but num_matches+1), current text is preappended in c[0]
2179 {
2180 string option = c[i];
2181
2182 MegaCmdShellCommunications::megaCmdStdoutputing.lock();
2183 OUTSTREAM << setw(min(cols-1,max_length+1)) << left;
2184 int oldmode = _setmode(_fileno(stdout), _O_U16TEXT);
2185 OUTSTREAM << c[i];
2186 _setmode(_fileno(stdout), oldmode);
2187 MegaCmdShellCommunications::megaCmdStdoutputing.unlock();
2188
2189 if ( (i%nelements_per_col == 0) && (i != num_matches))
2190 {
2191 OUTSTREAM << endl;
2192 }
2193 }
2194 OUTSTREAM << endl;
2195 }
2196 #endif
2197
2198 #ifndef NO_READLINE
readresponse(const char * question)2199 std::string readresponse(const char* question)
2200 {
2201 string response;
2202 auto responseRaw = readline(question);
2203 if (responseRaw)
2204 {
2205 response = responseRaw;
2206 }
2207 rl_set_prompt("");
2208 rl_replace_line("", 0);
2209
2210 rl_callback_handler_remove(); //To fix broken readline (e.g: upper key wouldnt work)
2211
2212 return response;
2213 }
2214 #else
readresponse(const char * question)2215 std::string readresponse(const char* question)
2216 {
2217 COUT << question << flush;
2218 console->updateInputPrompt(question);
2219 for (;;)
2220 {
2221 if (char* line = console->checkForCompletedInputLine())
2222 {
2223 console->updateInputPrompt("");
2224 string response(line);
2225 free(line);
2226 return response;
2227 }
2228 else
2229 {
2230 sleepMilliSeconds(200);
2231 }
2232 }
2233 }
2234 #endif
2235
2236 } //end namespace
2237
2238 using namespace megacmd;
2239
main(int argc,char * argv[])2240 int main(int argc, char* argv[])
2241 {
2242
2243 #if defined(_WIN32) && !defined(NO_READLINE)
2244 // Set Environment's default locale
2245 setlocale(LC_ALL, "en-US");
2246 rl_completion_display_matches_hook = mycompletefunct;
2247 #endif
2248
2249 // intialize the comms object
2250 #if defined(_WIN32) && !defined(USE_PORT_COMMS)
2251 comms = new MegaCmdShellCommunicationsNamedPipes();
2252 #else
2253 comms = new MegaCmdShellCommunications();
2254 #endif
2255
2256 #ifndef NO_READLINE
2257 rl_attempted_completion_function = getCompletionMatches;
2258 rl_completer_quote_characters = "\"'";
2259 rl_filename_quote_characters = " ";
2260 rl_completer_word_break_characters = (char *)" ";
2261
2262 rl_char_is_quoted_p = "e_detector;
2263
2264 if (!runningInBackground())
2265 {
2266 rl_initialize(); // initializes readline,
2267 // so that we can use rl_message or rl_resize_terminal safely before ever
2268 // prompting anything.
2269 }
2270 #endif
2271
2272 #if defined(_WIN32) && defined(NO_READLINE)
2273 console = new CONSOLE_CLASS;
2274 console->setAutocompleteSyntax(buildAutocompleteSyntax());
2275 console->setAutocompleteFunction(remote_completion);
2276 console->setShellConsole(CP_UTF8, GetConsoleOutputCP());
2277 console->blockingConsolePeek = true;
2278 #endif
2279
2280 #ifdef _WIN32
2281 // in windows, rl_resize_terminal fails to resize before first prompt appears, we take the width from elsewhere
2282 CONSOLE_SCREEN_BUFFER_INFO csbi;
2283 int columns;
2284 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
2285 columns = csbi.srWindow.Right - csbi.srWindow.Left - 2;
2286 printWelcomeMsg(columns);
2287 #else
2288 sleepMilliSeconds(200); // this gives a little while so that the console is ready and rl_resize_terminal works fine
2289 printWelcomeMsg();
2290 #endif
2291
2292 readloop();
2293
2294 #ifndef NO_READLINE
2295 clear_history();
2296 if (!doReboot)
2297 {
2298 rl_callback_handler_remove(); //To avoid having the terminal messed up (requiring a "reset")
2299 }
2300 #endif
2301 delete comms;
2302
2303 if (doReboot)
2304 {
2305 #ifdef _WIN32
2306 sleepSeconds(5); // Give a while for server to restart
2307 LPWSTR szPathExecQuoted = GetCommandLineW();
2308 wstring wspathexec = wstring(szPathExecQuoted);
2309
2310 if (wspathexec.at(0) == '"')
2311 {
2312 wspathexec = wspathexec.substr(1);
2313 }
2314 while (wspathexec.size() && ( wspathexec.at(wspathexec.size()-1) == '"' || wspathexec.at(wspathexec.size()-1) == ' ' ))
2315 {
2316 wspathexec = wspathexec.substr(0,wspathexec.size()-1);
2317 }
2318 LPWSTR szPathExec = (LPWSTR) wspathexec.c_str();
2319
2320 STARTUPINFO si;
2321 PROCESS_INFORMATION pi;
2322 ZeroMemory( &si, sizeof(si) );
2323 ZeroMemory( &pi, sizeof(pi) );
2324 si.cb = sizeof(si);
2325
2326 if (!CreateProcess( szPathExec,szPathExec,NULL,NULL,TRUE,
2327 CREATE_NEW_CONSOLE,
2328 NULL,NULL,
2329 &si,&pi) )
2330 {
2331 COUT << "Unable to execute: " << szPathExec << " errno = : " << ERRNO << endl;
2332 sleepSeconds(5);
2333 }
2334 #elif defined(__linux__)
2335 system("reset -I");
2336 string executable = argv[0];
2337 if (executable.find("/") != 0)
2338 {
2339 executable.insert(0, getCurrentExecPath()+"/");
2340 }
2341 execv(executable.c_str(), argv);
2342 #else
2343 system("reset -I");
2344 execv(argv[0], argv);
2345 #endif
2346
2347 }
2348 }
2349