1 /**
2  * @file src/megacmd.cpp
3  * @brief MEGAcmd: Interactive CLI and service application
4  *
5  * (c) 2013 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGAcmd.
8  *
9  * MEGAcmd is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * @copyright Simplified (2-clause) BSD License.
14  *
15  * You should have received a copy of the license along with this
16  * program.
17  */
18 
19 #include "megacmd.h"
20 
21 #include "megacmdsandbox.h"
22 #include "megacmdexecuter.h"
23 #include "megacmdutils.h"
24 #include "configurationmanager.h"
25 #include "megacmdlogger.h"
26 #include "comunicationsmanager.h"
27 #include "listeners.h"
28 
29 #include "megacmdplatform.h"
30 #include "megacmdversion.h"
31 
32 #define USE_VARARGS
33 #define PREFER_STDARG
34 
35 #include <iomanip>
36 #include <string>
37 #include <deque>
38 #include <atomic>
39 
40 #ifdef __linux__
41 #include <condition_variable>
42 #endif
43 #ifndef _WIN32
44 #include "signal.h"
45 #include <sys/wait.h>
46 #else
47 #include <taskschd.h>
48 #include <comutil.h>
49 #include <comdef.h>
50 #include <sddl.h>
51 #include <fcntl.h>
52 #include <io.h>
53 #define strdup _strdup  // avoid warning
54 #endif
55 
56 #ifdef __APPLE__
57 #include <sys/sysctl.h>
58 #endif
59 
60 #if !defined (PARAMS)
61 #  if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus)
62 #    define PARAMS(protos) protos
63 #  else
64 #    define PARAMS(protos) ()
65 #  endif
66 #endif
67 
68 
69 #define SSTR( x ) static_cast< const std::ostringstream & >( \
70         ( std::ostringstream() << std::dec << x ) ).str()
71 
72 
73 #ifndef ERRNO
74 #ifdef _WIN32
75 #include <windows.h>
76 #define ERRNO WSAGetLastError()
77 #else
78 #define ERRNO errno
79 #endif
80 #endif
81 
82 #ifdef _WIN32
83 #ifdef USE_PORT_COMMS
84 #include "comunicationsmanagerportsockets.h"
85 #define COMUNICATIONMANAGER ComunicationsManagerPortSockets
86 #else
87 #include "comunicationsmanagernamedpipes.h"
88 #define COMUNICATIONMANAGER ComunicationsManagerNamedPipes
89 #endif
90 #else
91 #include "comunicationsmanagerfilesockets.h"
92 #define COMUNICATIONMANAGER ComunicationsManagerFileSockets
93 #include <signal.h>
94 #endif
95 
96 using namespace mega;
97 
98 namespace megacmd {
99 using namespace std;
100 typedef char *completionfunction_t PARAMS((const char *, int));
101 
102 MegaCmdExecuter *cmdexecuter;
103 MegaCmdSandbox *sandboxCMD;
104 
105 MegaSemaphore semaphoreClients; //to limit max parallel petitions
106 
107 MegaApi *api = nullptr;
108 
109 //api objects for folderlinks
110 std::queue<MegaApi *> apiFolders;
111 std::vector<MegaApi *> occupiedapiFolders;
112 MegaSemaphore semaphoreapiFolders;
113 std::mutex mutexapiFolders;
114 
115 MegaCMDLogger *loggerCMD;
116 
117 std::mutex mutexEndedPetitionThreads;
118 std::vector<MegaThread *> petitionThreads;
119 std::vector<MegaThread *> endedPetitionThreads;
120 MegaThread *threadRetryConnections;
121 
122 std::deque<std::string> greetingsFirstClientMsgs; // to be given on first client to register as state listener
123 std::deque<std::string> greetingsAllClientMsgs; // to be given on all clients when registering as state listener
124 std::mutex greetingsmsgsMutex;
125 
126 //Comunications Manager
127 ComunicationsManager * cm;
128 
129 // global listener
130 MegaCmdGlobalListener* megaCmdGlobalListener;
131 
132 MegaCmdMegaListener* megaCmdMegaListener;
133 
134 vector<string> validCommands = allValidCommands;
135 
136 // password change-related state information
137 string oldpasswd;
138 string newpasswd;
139 
140 bool doExit = false;
141 bool consoleFailed = false;
142 bool alreadyCheckingForUpdates = false;
143 bool stopcheckingforUpdaters = false;
144 
145 string dynamicprompt = "MEGA CMD> ";
146 
147 static prompttype prompt = COMMAND;
148 
149 static std::atomic_bool loginInAtStartup(false);
150 static std::atomic<int> blocked(0);
151 
152 time_t lastTimeCheckBlockStatus = 0;
153 
154 static std::atomic<::mega::m_time_t> timeOfLoginInAtStartup(0);
155 ::mega::m_time_t timeLoginStarted();
156 
157 
158 // local console
159 Console* console;
160 
161 std::mutex mutexHistory;
162 
163 map<unsigned long long, string> threadline;
164 
165 char ** mcmdMainArgv;
166 int mcmdMainArgc;
167 
168 void printWelcomeMsg();
169 
170 void delete_finished_threads();
171 
appendGreetingStatusFirstListener(const std::string & msj)172 void appendGreetingStatusFirstListener(const std::string &msj)
173 {
174     std::lock_guard<std::mutex> g(greetingsmsgsMutex);
175     greetingsFirstClientMsgs.push_front(msj);
176 }
177 
removeGreetingStatusFirstListener(const std::string & msj)178 void removeGreetingStatusFirstListener(const std::string &msj)
179 {
180     std::lock_guard<std::mutex> g(greetingsmsgsMutex);
181     for(auto it = greetingsFirstClientMsgs.begin(); it != greetingsFirstClientMsgs.end();)
182     {
183        if (*it == msj)
184        {
185            it = greetingsFirstClientMsgs.erase(it);
186        }
187        else
188        {
189            ++it;
190        }
191     }
192 }
193 
appendGreetingStatusAllListener(const std::string & msj)194 void appendGreetingStatusAllListener(const std::string &msj)
195 {
196     std::lock_guard<std::mutex> g(greetingsmsgsMutex);
197     greetingsAllClientMsgs.push_front(msj);
198 }
199 
removeGreetingStatusAllListener(const std::string & msj)200 void removeGreetingStatusAllListener(const std::string &msj)
201 {
202     std::lock_guard<std::mutex> g(greetingsmsgsMutex);
203     for(auto it = greetingsAllClientMsgs.begin(); it != greetingsAllClientMsgs.end();)
204     {
205        if (*it == msj)
206        {
207            it = greetingsAllClientMsgs.erase(it);
208        }
209        else
210        {
211            ++it;
212        }
213     }
214 }
215 
getCurrentThreadLine()216 string getCurrentThreadLine()
217 {
218     uint64_t currentThread = MegaThread::currentThreadId();
219     if (threadline.find(currentThread) == threadline.end())
220     { // not found thread
221         return string();
222     }
223     else
224     {
225         return threadline[currentThread];
226     }
227 }
228 
setCurrentThreadLine(string s)229 void setCurrentThreadLine(string s)
230 {
231     threadline[MegaThread::currentThreadId()] = s;
232 }
233 
setCurrentThreadLine(const vector<string> & vec)234 void setCurrentThreadLine(const vector<string>& vec)
235 {
236    setCurrentThreadLine(joinStrings(vec));
237 }
238 
sigint_handler(int signum)239 void sigint_handler(int signum)
240 {
241     LOG_verbose << "Received signal: " << signum;
242     LOG_debug << "Exiting due to SIGINT";
243 
244     stopcheckingforUpdaters = true;
245     doExit = true;
246 }
247 
248 #ifdef _WIN32
CtrlHandler(DWORD fdwCtrlType)249 BOOL __stdcall CtrlHandler( DWORD fdwCtrlType )
250 {
251   LOG_verbose << "Reached CtrlHandler: " << fdwCtrlType;
252 
253   switch( fdwCtrlType )
254   {
255     // Handle the CTRL-C signal.
256     case CTRL_C_EVENT:
257        sigint_handler((int)fdwCtrlType);
258       return( TRUE );
259 
260     default:
261       return FALSE;
262   }
263 }
264 #endif
265 
getprompt()266 prompttype getprompt()
267 {
268     return prompt;
269 }
270 
setprompt(prompttype p,string arg)271 void setprompt(prompttype p, string arg)
272 {
273     prompt = p;
274 
275     if (p == COMMAND)
276     {
277         console->setecho(true);
278     }
279     else
280     {
281         if (arg.size())
282         {
283             OUTSTREAM << arg << flush;
284         }
285         else
286         {
287             OUTSTREAM << prompts[p] << flush;
288         }
289 
290         console->setecho(false);
291     }
292 }
293 
changeprompt(const char * newprompt)294 void changeprompt(const char *newprompt)
295 {
296     dynamicprompt = newprompt;
297     string s = "prompt:";
298     s+=dynamicprompt;
299     cm->informStateListeners(s);
300 }
301 
informStateListeners(string s)302 void informStateListeners(string s)
303 {
304     cm->informStateListeners(s);
305 }
306 
informStateListener(string message,int clientID)307 void informStateListener(string message, int clientID)
308 {
309     string s;
310     if (message.size())
311     {
312         s += "message:";
313         s+=message;
314         cm->informStateListenerByClientId(s, clientID);
315     }
316 }
317 
broadcastMessage(string message,bool keepIfNoListeners)318 void broadcastMessage(string message, bool keepIfNoListeners)
319 {
320     string s;
321     if (message.size())
322     {
323         s += "message:";
324         s+=message;
325         string unalteredCopy(s);
326         if (!cm->informStateListeners(s) && keepIfNoListeners)
327         {
328             appendGreetingStatusFirstListener(unalteredCopy);
329         }
330     }
331 }
332 
informTransferUpdate(MegaTransfer * transfer,int clientID)333 void informTransferUpdate(MegaTransfer *transfer, int clientID)
334 {
335     informProgressUpdate(transfer->getTransferredBytes(),transfer->getTotalBytes(), clientID);
336 }
337 
informStateListenerByClientId(int clientID,string s)338 void informStateListenerByClientId(int clientID, string s)
339 {
340     cm->informStateListenerByClientId(s, clientID);
341 }
342 
informProgressUpdate(long long transferred,long long total,int clientID,string title)343 void informProgressUpdate(long long transferred, long long total, int clientID, string title)
344 {
345     string s = "progress:";
346     s+=SSTR(transferred);
347     s+=":";
348     s+=SSTR(total);
349 
350     if (title.size())
351     {
352         s+=":";
353         s+=title;
354     }
355 
356     informStateListenerByClientId(clientID, s);
357 }
358 
insertValidParamsPerCommand(set<string> * validParams,string thecommand,set<string> * validOptValues=NULL)359 void insertValidParamsPerCommand(set<string> *validParams, string thecommand, set<string> *validOptValues = NULL)
360 {
361     if (!validOptValues)
362     {
363         validOptValues = validParams;
364     }
365 
366     validOptValues->insert("client-width");
367 
368 
369     if ("ls" == thecommand)
370     {
371         validParams->insert("R");
372         validParams->insert("r");
373         validParams->insert("l");
374         validParams->insert("a");
375         validParams->insert("h");
376         validParams->insert("show-handles");
377         validParams->insert("versions");
378         validOptValues->insert("time-format");
379         validParams->insert("tree");
380 #ifdef USE_PCRE
381         validParams->insert("use-pcre");
382 #endif
383     }
384     else if ("passwd" == thecommand)
385     {
386         validParams->insert("f");
387         validOptValues->insert("auth-code");
388     }
389     else if ("du" == thecommand)
390     {
391         validParams->insert("h");
392         validParams->insert("versions");
393         validOptValues->insert("path-display-size");
394 #ifdef USE_PCRE
395         validParams->insert("use-pcre");
396 #endif
397     }
398     else if ("help" == thecommand)
399     {
400         validParams->insert("f");
401         validParams->insert("non-interactive");
402         validParams->insert("paths");
403         validParams->insert("upgrade");
404 #ifdef _WIN32
405         validParams->insert("unicode");
406 #endif
407     }
408     else if ("version" == thecommand)
409     {
410         validParams->insert("l");
411         validParams->insert("c");
412     }
413     else if ("rm" == thecommand)
414     {
415         validParams->insert("r");
416         validParams->insert("f");
417 #ifdef USE_PCRE
418         validParams->insert("use-pcre");
419 #endif
420     }
421     else if ("mv" == thecommand)
422     {
423 #ifdef USE_PCRE
424         validParams->insert("use-pcre");
425 #endif
426     }
427     else if ("cp" == thecommand)
428     {
429 #ifdef USE_PCRE
430         validParams->insert("use-pcre");
431 #endif
432     }
433     else if ("speedlimit" == thecommand)
434     {
435         validParams->insert("u");
436         validParams->insert("d");
437         validParams->insert("h");
438     }
439     else if ("whoami" == thecommand)
440     {
441         validParams->insert("l");
442     }
443     else if ("df" == thecommand)
444     {
445         validParams->insert("h");
446     }
447     else if ("mediainfo" == thecommand)
448     {
449         validOptValues->insert("path-display-size");
450     }
451     else if ("log" == thecommand)
452     {
453         validParams->insert("c");
454         validParams->insert("s");
455     }
456 #ifndef _WIN32
457     else if ("permissions" == thecommand)
458     {
459         validParams->insert("s");
460         validParams->insert("files");
461         validParams->insert("folders");
462     }
463 #endif
464     else if ("deleteversions" == thecommand)
465     {
466         validParams->insert("all");
467         validParams->insert("f");
468 #ifdef USE_PCRE
469         validParams->insert("use-pcre");
470 #endif
471     }
472     else if ("exclude" == thecommand)
473     {
474         validParams->insert("a");
475         validParams->insert("d");
476         validParams->insert("restart-syncs");
477     }
478 #ifdef HAVE_LIBUV
479     else if ("webdav" == thecommand)
480     {
481         validParams->insert("d");
482         validParams->insert("all");
483         validParams->insert("tls");
484         validParams->insert("public");
485         validOptValues->insert("port");
486         validOptValues->insert("certificate");
487         validOptValues->insert("key");
488 #ifdef USE_PCRE
489         validParams->insert("use-pcre");
490 #endif
491     }
492     else if ("ftp" == thecommand)
493     {
494         validParams->insert("d");
495         validParams->insert("all");
496         validParams->insert("tls");
497         validParams->insert("public");
498         validOptValues->insert("port");
499         validOptValues->insert("data-ports");
500         validOptValues->insert("certificate");
501         validOptValues->insert("key");
502 #ifdef USE_PCRE
503         validParams->insert("use-pcre");
504 #endif
505     }
506 #endif
507     else if ("backup" == thecommand)
508     {
509         validOptValues->insert("period");
510         validOptValues->insert("num-backups");
511         validParams->insert("d");
512 //        validParams->insert("s");
513 //        validParams->insert("r");
514         validParams->insert("a");
515 //        validParams->insert("i");
516         validParams->insert("l");
517         validParams->insert("h");
518         validOptValues->insert("path-display-size");
519         validOptValues->insert("time-format");
520     }
521     else if ("sync" == thecommand)
522     {
523         validParams->insert("d");
524         validParams->insert("s");
525         validParams->insert("r");
526         validOptValues->insert("path-display-size");
527     }
528     else if ("export" == thecommand)
529     {
530         validParams->insert("a");
531         validParams->insert("d");
532         validParams->insert("f");
533         validOptValues->insert("expire");
534         validOptValues->insert("password");
535 #ifdef USE_PCRE
536         validParams->insert("use-pcre");
537 #endif
538     }
539     else if ("share" == thecommand)
540     {
541         validParams->insert("a");
542         validParams->insert("d");
543         validParams->insert("p");
544         validOptValues->insert("with");
545         validOptValues->insert("level");
546         validOptValues->insert("personal-representation");
547 #ifdef USE_PCRE
548         validParams->insert("use-pcre");
549 #endif
550         validOptValues->insert("time-format");
551     }
552     else if ("find" == thecommand)
553     {
554         validOptValues->insert("pattern");
555         validOptValues->insert("l");
556         validParams->insert("show-handles");
557 #ifdef USE_PCRE
558         validParams->insert("use-pcre");
559 #endif
560         validOptValues->insert("mtime");
561         validOptValues->insert("size");
562         validOptValues->insert("time-format");
563     }
564     else if ("mkdir" == thecommand)
565     {
566         validParams->insert("p");
567     }
568     else if ("users" == thecommand)
569     {
570         validParams->insert("s");
571         validParams->insert("h");
572         validParams->insert("d");
573         validParams->insert("n");
574         validOptValues->insert("time-format");
575     }
576     else if ("killsession" == thecommand)
577     {
578         validParams->insert("a");
579     }
580     else if ("invite" == thecommand)
581     {
582         validParams->insert("d");
583         validParams->insert("r");
584         validOptValues->insert("message");
585     }
586     else if ("signup" == thecommand)
587     {
588         validOptValues->insert("name");
589     }
590     else if ("logout" == thecommand)
591     {
592         validParams->insert("keep-session");
593     }
594     else if ("attr" == thecommand)
595     {
596         validParams->insert("d");
597         validParams->insert("s");
598     }
599     else if ("userattr" == thecommand)
600     {
601         validOptValues->insert("user");
602         validParams->insert("s");
603         validParams->insert("list");
604     }
605     else if ("ipc" == thecommand)
606     {
607         validParams->insert("a");
608         validParams->insert("d");
609         validParams->insert("i");
610     }
611     else if ("showpcr" == thecommand)
612     {
613         validParams->insert("in");
614         validParams->insert("out");
615         validOptValues->insert("time-format");
616     }
617     else if ("thumbnail" == thecommand)
618     {
619         validParams->insert("s");
620     }
621     else if ("preview" == thecommand)
622     {
623         validParams->insert("s");
624     }
625     else if ("put" == thecommand)
626     {
627         validParams->insert("c");
628         validParams->insert("q");
629         validParams->insert("ignore-quota-warn");
630         validOptValues->insert("clientID");
631     }
632     else if ("get" == thecommand)
633     {
634         validParams->insert("m");
635         validParams->insert("q");
636         validParams->insert("ignore-quota-warn");
637         validOptValues->insert("password");
638 #ifdef USE_PCRE
639         validParams->insert("use-pcre");
640 #endif
641         validOptValues->insert("clientID");
642     }
643     else if ("import" == thecommand)
644     {
645         validOptValues->insert("password");
646     }
647     else if ("login" == thecommand)
648     {
649         validOptValues->insert("clientID");
650         validOptValues->insert("auth-code");
651     }
652     else if ("psa" == thecommand)
653     {
654         validParams->insert("discard");
655     }
656     else if ("reload" == thecommand)
657     {
658         validOptValues->insert("clientID");
659     }
660     else if ("transfers" == thecommand)
661     {
662         validParams->insert("show-completed");
663         validParams->insert("summary");
664         validParams->insert("only-uploads");
665         validParams->insert("only-completed");
666         validParams->insert("only-downloads");
667         validParams->insert("show-syncs");
668         validParams->insert("c");
669         validParams->insert("a");
670         validParams->insert("p");
671         validParams->insert("r");
672         validOptValues->insert("limit");
673         validOptValues->insert("path-display-size");
674     }
675     else if ("proxy" == thecommand)
676     {
677         validParams->insert("auto");
678         validParams->insert("none");
679         validOptValues->insert("username");
680         validOptValues->insert("password");
681     }
682     else if ("exit" == thecommand || "quit" == thecommand)
683     {
684         validParams->insert("only-shell");
685     }
686 #if defined(_WIN32) || defined(__APPLE__)
687     else if ("update" == thecommand)
688     {
689         validOptValues->insert("auto");
690     }
691 #endif
692 }
693 
escapeEspace(string & orig)694 void escapeEspace(string &orig)
695 {
696     replaceAll(orig," ", "\\ ");
697 }
698 
unescapeEspace(string & orig)699 void unescapeEspace(string &orig)
700 {
701     replaceAll(orig,"\\ ", " ");
702 }
703 
empty_completion(const char * text,int state)704 char* empty_completion(const char* text, int state)
705 {
706     // we offer 2 different options so that it doesn't complete (no space is inserted)
707     if (state == 0)
708     {
709         return strdup(" ");
710     }
711     if (state == 1)
712     {
713         return strdup(text);
714     }
715     return NULL;
716 }
717 
generic_completion(const char * text,int state,vector<string> validOptions)718 char* generic_completion(const char* text, int state, vector<string> validOptions)
719 {
720     static size_t list_index, len;
721     static bool foundone;
722     string name;
723     if (!validOptions.size()) // no matches
724     {
725         return empty_completion(text,state); //dont fall back to filenames
726     }
727     if (!state)
728     {
729         list_index = 0;
730         foundone = false;
731         len = strlen(text);
732     }
733     while (list_index < validOptions.size())
734     {
735         name = validOptions.at(list_index);
736         //Notice: do not escape options for cmdshell. Plus, we won't filter here, because we don't know if the value of rl_completion_quote_chararcter of cmdshell
737         // The filtering and escaping will be performed by the completion function in cmdshell
738         if (interactiveThread() && !getCurrentThreadIsCmdShell()) {
739             escapeEspace(name);
740         }
741 
742         list_index++;
743 
744         if (!( strcmp(text, ""))
745                 || (( name.size() >= len ) && ( strlen(text) >= len ) &&  ( name.find(text) == 0 ) )
746                 || getCurrentThreadIsCmdShell()  //do not filter if cmdshell (it will be filter there)
747                 )
748         {
749             foundone = true;
750             return dupstr((char*)name.c_str());
751         }
752     }
753 
754     if (!foundone)
755     {
756         return empty_completion(text,state); //dont fall back to filenames
757     }
758 
759     return((char*)NULL );
760 }
761 
commands_completion(const char * text,int state)762 char* commands_completion(const char* text, int state)
763 {
764     return generic_completion(text, state, validCommands);
765 }
766 
local_completion(const char * text,int state)767 char* local_completion(const char* text, int state)
768 {
769     return((char*)NULL );
770 }
771 
addGlobalFlags(set<string> * setvalidparams)772 void addGlobalFlags(set<string> *setvalidparams)
773 {
774     for (auto &v :  validGlobalParameters)
775     {
776         setvalidparams->insert(v);
777     }
778 }
779 
flags_completion(const char * text,int state)780 char * flags_completion(const char*text, int state)
781 {
782     static vector<string> validparams;
783     if (state == 0)
784     {
785         validparams.clear();
786         char *saved_line = strdup(getCurrentThreadLine().c_str());
787         vector<string> words = getlistOfWords(saved_line, !getCurrentThreadIsCmdShell());
788         free(saved_line);
789         if (words.size())
790         {
791             set<string> setvalidparams;
792             set<string> setvalidOptValues;
793             addGlobalFlags(&setvalidparams);
794 
795             string thecommand = words[0];
796             insertValidParamsPerCommand(&setvalidparams, thecommand, &setvalidOptValues);
797             set<string>::iterator it;
798             for (it = setvalidparams.begin(); it != setvalidparams.end(); it++)
799             {
800                 string param = *it;
801                 string toinsert;
802 
803                 if (param.size() > 1)
804                 {
805                     toinsert = "--" + param;
806                 }
807                 else
808                 {
809                     toinsert = "-" + param;
810                 }
811 
812                 validparams.push_back(toinsert);
813             }
814 
815             for (it = setvalidOptValues.begin(); it != setvalidOptValues.end(); it++)
816             {
817                 string param = *it;
818                 string toinsert;
819 
820                 if (param.size() > 1)
821                 {
822                     toinsert = "--" + param + '=';
823                 }
824                 else
825                 {
826                     toinsert = "-" + param + '=';
827                 }
828 
829                 validparams.push_back(toinsert);
830             }
831         }
832     }
833     char *toret = generic_completion(text, state, validparams);
834     return toret;
835 }
836 
flags_value_completion(const char * text,int state)837 char * flags_value_completion(const char*text, int state)
838 {
839     static vector<string> validValues;
840 
841     if (state == 0)
842     {
843         validValues.clear();
844 
845         char *saved_line = strdup(getCurrentThreadLine().c_str());
846         vector<string> words = getlistOfWords(saved_line, !getCurrentThreadIsCmdShell());
847         free(saved_line);
848         saved_line = NULL;
849         if (words.size() > 1)
850         {
851             string thecommand = words[0];
852             string currentFlag = words[words.size() - 1];
853 
854             map<string, string> cloptions;
855             map<string, int> clflags;
856 
857             set<string> validParams;
858 
859             insertValidParamsPerCommand(&validParams, thecommand);
860 
861             if (setOptionsAndFlags(&cloptions, &clflags, &words, validParams, true))
862             {
863                 // return invalid??
864             }
865 
866             if (currentFlag.find("--time-format=") == 0)
867             {
868                 string prefix = strncmp(text, "--time-format=", strlen("--time-format="))?"":"--time-format=";
869                 for (int i = 0; i < MCMDTIME_TOTAL; i++)
870                 {
871                     validValues.push_back(prefix+getTimeFormatNameFromId(i));
872                 }
873             }
874 
875             if (thecommand == "share")
876             {
877                 if (currentFlag.find("--level=") == 0)
878                 {
879                     string prefix = strncmp(text, "--level=", strlen("--level="))?"":"--level=";
880                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_UNKNOWN));
881                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_READ));
882                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_READWRITE));
883                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_FULL));
884                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_OWNER));
885                     validValues.push_back(prefix+getShareLevelStr(MegaShare::ACCESS_UNKNOWN));
886                 }
887                 if (currentFlag.find("--with=") == 0)
888                 {
889                     validValues = cmdexecuter->getlistusers();
890                     string prefix = strncmp(text, "--with=", strlen("--with="))?"":"--with=";
891                     for (unsigned int i=0;i<validValues.size();i++)
892                     {
893                         validValues.at(i)=prefix+validValues.at(i);
894                     }
895                 }
896             }
897             else if (( thecommand == "userattr" ) && ( currentFlag.find("--user=") == 0 ))
898             {
899                 validValues = cmdexecuter->getlistusers();
900                 string prefix = strncmp(text, "--user=", strlen("--user="))?"":"--user=";
901                 for (unsigned int i=0;i<validValues.size();i++)
902                 {
903                     validValues.at(i)=prefix+validValues.at(i);
904                 }
905             }
906             else if  ( ( thecommand == "ftp" || thecommand == "webdav" )
907                 && ( currentFlag.find("--key=") == 0 || currentFlag.find("--certificate=") == 0 ) )
908             {
909                 const char * cflag = (currentFlag.find("--key=") == 0)? "--key=" : "--certificate=";
910                 string stext = text;
911                 size_t begin = strncmp(text, cflag, strlen(cflag))?0:strlen(cflag);
912                 size_t end = stext.find_last_of('/');
913                 if (end != string::npos && (end + 1 ) < stext.size() )
914                 {
915                     end = end - begin +1;
916                 }
917                 else
918                 {
919                     end = string::npos;
920                 }
921 
922                 validValues = cmdexecuter->listlocalpathsstartingby(stext.substr(begin));
923                 string prefix = strncmp(text, cflag, strlen(cflag))?"":cflag;
924                 for (unsigned int i=0;i<validValues.size();i++)
925                 {
926                     validValues.at(i)=prefix+validValues.at(i);
927                 }
928             }
929         }
930     }
931 
932     char *toret = generic_completion(text, state, validValues);
933     return toret;
934 }
935 
unescapeifRequired(string & what)936 void unescapeifRequired(string &what)
937 {
938     if (interactiveThread() ) {
939         return unescapeEspace(what);
940     }
941 }
942 
943 
remotepaths_completion(const char * text,int state,bool onlyfolders)944 char* remotepaths_completion(const char* text, int state, bool onlyfolders)
945 {
946     static vector<string> validpaths;
947     if (state == 0)
948     {
949         string wildtext(text);
950         bool usepcre = false; //pcre makes no sense in paths completion
951         if (usepcre)
952         {
953 #ifdef USE_PCRE
954         wildtext += ".";
955 #elif __cplusplus >= 201103L
956         wildtext += ".";
957 #endif
958         }
959 
960         wildtext += "*";
961 
962         unescapeEspace(wildtext);
963 
964         validpaths = cmdexecuter->listpaths(usepcre, wildtext, onlyfolders);
965 
966         // we need to escape '\' to fit what's done when parsing words
967         if (!getCurrentThreadIsCmdShell())
968         {
969             for (int i = 0; i < (int)validpaths.size(); i++)
970             {
971                 replaceAll(validpaths[i],"\\","\\\\");
972             }
973         }
974 
975     }
976     return generic_completion(text, state, validpaths);
977 }
978 
remotepaths_completion(const char * text,int state)979 char* remotepaths_completion(const char* text, int state)
980 {
981     return remotepaths_completion(text, state, false);
982 }
983 
remotefolders_completion(const char * text,int state)984 char* remotefolders_completion(const char* text, int state)
985 {
986     return remotepaths_completion(text, state, true);
987 }
988 
loglevels_completion(const char * text,int state)989 char* loglevels_completion(const char* text, int state)
990 {
991     static vector<string> validloglevels;
992     if (state == 0)
993     {
994         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_FATAL));
995         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_ERROR));
996         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_WARNING));
997         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_INFO));
998         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_DEBUG));
999         validloglevels.push_back(getLogLevelStr(MegaApi::LOG_LEVEL_MAX));
1000     }
1001     return generic_completion(text, state, validloglevels);
1002 }
1003 
localfolders_completion(const char * text,int state)1004 char* localfolders_completion(const char* text, int state)
1005 {
1006     static vector<string> validpaths;
1007     if (state == 0)
1008     {
1009         string what(text);
1010         unescapeEspace(what);
1011         validpaths = cmdexecuter->listlocalpathsstartingby(what.c_str(), true);
1012     }
1013     return generic_completion(text, state, validpaths);
1014 }
1015 
transfertags_completion(const char * text,int state)1016 char* transfertags_completion(const char* text, int state)
1017 {
1018     static vector<string> validtransfertags;
1019     if (state == 0)
1020     {
1021         MegaTransferData * transferdata = api->getTransferData();
1022         if (transferdata)
1023         {
1024             for (int i = 0; i < transferdata->getNumUploads(); i++)
1025             {
1026                 validtransfertags.push_back(SSTR(transferdata->getUploadTag(i)));
1027             }
1028             for (int i = 0; i < transferdata->getNumDownloads(); i++)
1029             {
1030                 validtransfertags.push_back(SSTR(transferdata->getDownloadTag(i)));
1031             }
1032 
1033             // TODO: reconsider including completed transfers (sth like this:)
1034 //            globalTransferListener->completedTransfersMutex.lock();
1035 //            for (unsigned int i = 0;i < globalTransferListener->completedTransfers.size() && shownCompleted < limit; i++)
1036 //            {
1037 //                MegaTransfer *transfer = globalTransferListener->completedTransfers.at(shownCompleted);
1038 //                if (!transfer->isSyncTransfer())
1039 //                {
1040 //                    validtransfertags.push_back(SSTR(transfer->getTag()));
1041 //                    shownCompleted++;
1042 //                }
1043 //            }
1044 //            globalTransferListener->completedTransfersMutex.unlock();
1045         }
1046     }
1047     return generic_completion(text, state, validtransfertags);
1048 }
contacts_completion(const char * text,int state)1049 char* contacts_completion(const char* text, int state)
1050 {
1051     static vector<string> validcontacts;
1052     if (state == 0)
1053     {
1054         validcontacts = cmdexecuter->getlistusers();
1055     }
1056     return generic_completion(text, state, validcontacts);
1057 }
1058 
sessions_completion(const char * text,int state)1059 char* sessions_completion(const char* text, int state)
1060 {
1061     static vector<string> validSessions;
1062     if (state == 0)
1063     {
1064         validSessions = cmdexecuter->getsessions();
1065     }
1066 
1067     if (validSessions.size() == 0)
1068     {
1069         return empty_completion(text, state);
1070     }
1071 
1072     return generic_completion(text, state, validSessions);
1073 }
1074 
nodeattrs_completion(const char * text,int state)1075 char* nodeattrs_completion(const char* text, int state)
1076 {
1077     static vector<string> validAttrs;
1078     if (state == 0)
1079     {
1080         validAttrs.clear();
1081         char *saved_line = strdup(getCurrentThreadLine().c_str());
1082         vector<string> words = getlistOfWords(saved_line, !getCurrentThreadIsCmdShell());
1083         free(saved_line);
1084         saved_line = NULL;
1085         if (words.size() > 1)
1086         {
1087             validAttrs = cmdexecuter->getNodeAttrs(words[1]);
1088         }
1089     }
1090 
1091     if (validAttrs.size() == 0)
1092     {
1093         return empty_completion(text, state);
1094     }
1095 
1096     return generic_completion(text, state, validAttrs);
1097 }
1098 
userattrs_completion(const char * text,int state)1099 char* userattrs_completion(const char* text, int state)
1100 {
1101     static vector<string> validAttrs;
1102     if (state == 0)
1103     {
1104         validAttrs.clear();
1105         validAttrs = cmdexecuter->getUserAttrs();
1106     }
1107 
1108     if (validAttrs.size() == 0)
1109     {
1110         return empty_completion(text, state);
1111     }
1112 
1113     return generic_completion(text, state, validAttrs);
1114 }
1115 
getCompletionFunction(vector<string> words)1116 completionfunction_t *getCompletionFunction(vector<string> words)
1117 {
1118     // Strip words without flags
1119     string thecommand = words[0];
1120 
1121     if (words.size() > 1)
1122     {
1123         string lastword = words[words.size() - 1];
1124         if (lastword.find_first_of("-") == 0)
1125         {
1126             if (lastword.find_last_of("=") != string::npos)
1127             {
1128                 return flags_value_completion;
1129             }
1130             else
1131             {
1132                 return flags_completion;
1133             }
1134         }
1135     }
1136     discardOptionsAndFlags(&words);
1137 
1138     int currentparameter = int(words.size() - 1);
1139     if (stringcontained(thecommand.c_str(), localremotefolderpatterncommands))
1140     {
1141         if (currentparameter == 1)
1142         {
1143             return local_completion;
1144         }
1145         if (currentparameter == 2)
1146         {
1147             return remotefolders_completion;
1148         }
1149     }
1150     else if (thecommand == "put")
1151     {
1152         if (currentparameter == 1)
1153         {
1154             return local_completion;
1155         }
1156         else
1157         {
1158             return remotepaths_completion;
1159         }
1160     }
1161     else if (thecommand == "backup")
1162     {
1163         if (currentparameter == 1)
1164         {
1165             return localfolders_completion;
1166         }
1167         else
1168         {
1169             return remotefolders_completion;
1170         }
1171     }
1172     else if (stringcontained(thecommand.c_str(), remotepatterncommands))
1173     {
1174         if (currentparameter == 1)
1175         {
1176             return remotepaths_completion;
1177         }
1178     }
1179     else if (stringcontained(thecommand.c_str(), remotefolderspatterncommands))
1180     {
1181         if (currentparameter == 1)
1182         {
1183             return remotefolders_completion;
1184         }
1185     }
1186     else if (stringcontained(thecommand.c_str(), multipleremotepatterncommands))
1187     {
1188         if (currentparameter >= 1)
1189         {
1190             return remotepaths_completion;
1191         }
1192     }
1193     else if (stringcontained(thecommand.c_str(), localfolderpatterncommands))
1194     {
1195         if (currentparameter == 1)
1196         {
1197             return localfolders_completion;
1198         }
1199     }
1200     else if (stringcontained(thecommand.c_str(), remoteremotepatterncommands))
1201     {
1202         if (( currentparameter == 1 ) || ( currentparameter == 2 ))
1203         {
1204             return remotepaths_completion;
1205         }
1206     }
1207     else if (stringcontained(thecommand.c_str(), remotelocalpatterncommands))
1208     {
1209         if (currentparameter == 1)
1210         {
1211             return remotepaths_completion;
1212         }
1213         if (currentparameter == 2)
1214         {
1215             return local_completion;
1216         }
1217     }
1218     else if (stringcontained(thecommand.c_str(), emailpatterncommands))
1219     {
1220         if (currentparameter == 1)
1221         {
1222             return contacts_completion;
1223         }
1224     }
1225     else if (thecommand == "import")
1226     {
1227         if (currentparameter == 2)
1228         {
1229             return remotepaths_completion;
1230         }
1231     }
1232     else if (thecommand == "killsession")
1233     {
1234         if (currentparameter == 1)
1235         {
1236             return sessions_completion;
1237         }
1238     }
1239     else if (thecommand == "attr")
1240     {
1241         if (currentparameter == 1)
1242         {
1243             return remotepaths_completion;
1244         }
1245         if (currentparameter == 2)
1246         {
1247             return nodeattrs_completion;
1248         }
1249     }
1250     else if (thecommand == "userattr")
1251     {
1252         if (currentparameter == 1)
1253         {
1254             return userattrs_completion;
1255         }
1256     }
1257     else if (thecommand == "log")
1258     {
1259         if (currentparameter == 1)
1260         {
1261             return loglevels_completion;
1262         }
1263     }
1264     else if (thecommand == "transfers")
1265     {
1266         if (currentparameter == 1)
1267         {
1268             return transfertags_completion;
1269         }
1270     }
1271     return empty_completion;
1272 }
1273 
getListOfCompletionValues(vector<string> words,char separator=' ',const char * separators=" :;!`\\"'\\\\()[]{}<>",bool suppressflag=true)1274 string getListOfCompletionValues(vector<string> words, char separator = ' ', const char * separators = " :;!`\"'\\()[]{}<>", bool suppressflag = true)
1275 {
1276     string completionValues;
1277     completionfunction_t * compfunction = getCompletionFunction(words);
1278     if (compfunction == local_completion)
1279     {
1280         if (!interactiveThread())
1281         {
1282             return "MEGACMD_USE_LOCAL_COMPLETION";
1283         }
1284         else
1285         {
1286             string toret="MEGACMD_USE_LOCAL_COMPLETION";
1287             toret+=cmdexecuter->getLPWD();
1288             return toret;
1289         }
1290     }
1291 #ifdef _WIN32
1292 //    // let MEGAcmdShell handle the local folder completion (available via autocomplete.cpp stuff that takes into account units/unicode/etc...)
1293 //    else if (compfunction == localfolders_completion)
1294 //    {
1295 //        if (!interactiveThread())
1296 //        {
1297 //            return "MEGACMD_USE_LOCAL_COMPLETIONFOLDERS";
1298 //        }
1299 //        else
1300 //        {
1301 //            string toret="MEGACMD_USE_LOCAL_COMPLETIONFOLDERS";
1302 //            toret+=cmdexecuter->getLPWD();
1303 //            return toret;
1304 //        }
1305 //    }
1306 #endif
1307     int state=0;
1308     if (words.size()>1)
1309     while (true)
1310     {
1311         char *newval;
1312         string &lastword = words[words.size()-1];
1313         if (suppressflag && lastword.size()>3 && lastword[0]== '-' && lastword[1]== '-' && lastword.find('=')!=string::npos)
1314         {
1315             newval = compfunction(lastword.substr(lastword.find_first_of('=')+1).c_str(), state);
1316         }
1317         else
1318         {
1319             newval = compfunction(lastword.c_str(), state);
1320         }
1321 
1322         if (!newval) break;
1323         if (completionValues.size())
1324         {
1325             completionValues+=separator;
1326         }
1327 
1328         string snewval=newval;
1329         if (snewval.find_first_of(separators) != string::npos)
1330         {
1331             completionValues+="\"";
1332             replaceAll(snewval,"\"","\\\"");
1333             completionValues+=snewval;
1334             completionValues+="\"";
1335         }
1336         else
1337         {
1338             completionValues+=newval;
1339         }
1340         free(newval);
1341 
1342         state++;
1343     }
1344     return completionValues;
1345 }
1346 
getFreeApiFolder()1347 MegaApi* getFreeApiFolder()
1348 {
1349     semaphoreapiFolders.wait();
1350     mutexapiFolders.lock();
1351     MegaApi* toret = apiFolders.front();
1352     apiFolders.pop();
1353     occupiedapiFolders.push_back(toret);
1354     mutexapiFolders.unlock();
1355     return toret;
1356 }
1357 
freeApiFolder(MegaApi * apiFolder)1358 void freeApiFolder(MegaApi *apiFolder)
1359 {
1360     mutexapiFolders.lock();
1361     occupiedapiFolders.erase(std::remove(occupiedapiFolders.begin(), occupiedapiFolders.end(), apiFolder), occupiedapiFolders.end());
1362     apiFolders.push(apiFolder);
1363     semaphoreapiFolders.release();
1364     mutexapiFolders.unlock();
1365 }
1366 
getUsageStr(const char * command)1367 const char * getUsageStr(const char *command)
1368 {
1369     if (!strcmp(command, "login"))
1370     {
1371         if (interactiveThread())
1372         {
1373             return "login [--auth-code=XXXX] [email [password]] | exportedfolderurl#key | session";
1374         }
1375         else
1376         {
1377             return "login [--auth-code=XXXX] email password | exportedfolderurl#key | session";
1378         }
1379     }
1380     if (!strcmp(command, "psa"))
1381     {
1382         return "psa [--discard]";
1383     }
1384     if (!strcmp(command, "cancel"))
1385     {
1386         return "cancel";
1387     }
1388     if (!strcmp(command, "confirmcancel"))
1389     {
1390         if (interactiveThread())
1391         {
1392             return "confirmcancel link [password]";
1393         }
1394         else
1395         {
1396             return "confirmcancel link password";
1397         }
1398     }
1399     if (!strcmp(command, "begin"))
1400     {
1401         return "begin [ephemeralhandle#ephemeralpw]";
1402     }
1403     if (!strcmp(command, "signup"))
1404     {
1405         if (interactiveThread())
1406         {
1407             return "signup email [password] [--name=\"Your Name\"]";
1408         }
1409         else
1410         {
1411             return "signup email password [--name=\"Your Name\"]";
1412         }
1413     }
1414     if (!strcmp(command, "confirm"))
1415     {
1416         if (interactiveThread())
1417         {
1418             return "confirm link email [password]";
1419         }
1420         else
1421         {
1422             return "confirm link email password";
1423         }
1424     }
1425     if (!strcmp(command, "errorcode"))
1426     {
1427         return "errorcode number";
1428     }
1429     if (!strcmp(command, "graphics"))
1430     {
1431         return "graphics [on|off]";
1432     }
1433     if (!strcmp(command, "session"))
1434     {
1435         return "session";
1436     }
1437     if (!strcmp(command, "mount"))
1438     {
1439         return "mount";
1440     }
1441 #if defined(_WIN32) && !defined(NO_READLINE)
1442     if (!strcmp(command, "unicode"))
1443     {
1444         return "unicode";
1445     }
1446 #endif
1447     if (!strcmp(command, "ls"))
1448     {
1449 #ifdef USE_PCRE
1450         return "ls [-halRr] [--show-handles] [--tree] [--versions] [remotepath] [--use-pcre] [--time-format=FORMAT]";
1451 #else
1452         return "ls [-halRr] [--show-handles] [--tree] [--versions] [remotepath] [--time-format=FORMAT]";
1453 #endif
1454     }
1455     if (!strcmp(command, "tree"))
1456     {
1457         return "tree [remotepath]";
1458     }
1459     if (!strcmp(command, "cd"))
1460     {
1461         return "cd [remotepath]";
1462     }
1463     if (!strcmp(command, "log"))
1464     {
1465         return "log [-sc] level";
1466     }
1467     if (!strcmp(command, "du"))
1468     {
1469 #ifdef USE_PCRE
1470         return "du [-h] [--versions] [remotepath remotepath2 remotepath3 ... ] [--use-pcre]";
1471 #else
1472         return "du [-h] [--versions] [remotepath remotepath2 remotepath3 ... ]";
1473 #endif
1474     }
1475     if (!strcmp(command, "pwd"))
1476     {
1477         return "pwd";
1478     }
1479     if (!strcmp(command, "lcd"))
1480     {
1481         return "lcd [localpath]";
1482     }
1483     if (!strcmp(command, "lpwd"))
1484     {
1485         return "lpwd";
1486     }
1487     if (!strcmp(command, "import"))
1488     {
1489         return "import exportedlink [--password=PASSWORD] [remotepath]";
1490     }
1491     if (!strcmp(command, "put"))
1492     {
1493         return "put  [-c] [-q] [--ignore-quota-warn] localfile [localfile2 localfile3 ...] [dstremotepath]";
1494     }
1495     if (!strcmp(command, "putq"))
1496     {
1497         return "putq [cancelslot]";
1498     }
1499     if (!strcmp(command, "get"))
1500     {
1501 #ifdef USE_PCRE
1502         return "get [-m] [-q] [--ignore-quota-warn] [--use-pcre] [--password=PASSWORD] exportedlink|remotepath [localpath]";
1503 #else
1504         return "get [-m] [-q] [--ignore-quota-warn] [--password=PASSWORD] exportedlink|remotepath [localpath]";
1505 #endif
1506     }
1507     if (!strcmp(command, "getq"))
1508     {
1509         return "getq [cancelslot]";
1510     }
1511     if (!strcmp(command, "pause"))
1512     {
1513         return "pause [get|put] [hard] [status]";
1514     }
1515     if (!strcmp(command, "attr"))
1516     {
1517         return "attr remotepath [-s attribute value|-d attribute]";
1518     }
1519     if (!strcmp(command, "userattr"))
1520     {
1521         return "userattr [-s attribute value|attribute|--list] [--user=user@email]";
1522     }
1523     if (!strcmp(command, "mkdir"))
1524     {
1525         return "mkdir [-p] remotepath";
1526     }
1527     if (!strcmp(command, "rm"))
1528     {
1529 #ifdef USE_PCRE
1530         return "rm [-r] [-f] [--use-pcre] remotepath";
1531 #else
1532         return "rm [-r] [-f] remotepath";
1533 #endif
1534     }
1535     if (!strcmp(command, "mv"))
1536     {
1537 #ifdef USE_PCRE
1538         return "mv srcremotepath [--use-pcre] [srcremotepath2 srcremotepath3 ..] dstremotepath";
1539 #else
1540         return "mv srcremotepath [srcremotepath2 srcremotepath3 ..] dstremotepath";
1541 #endif
1542     }
1543     if (!strcmp(command, "cp"))
1544     {
1545 #ifdef USE_PCRE
1546         return "cp [--use-pcre] srcremotepath [srcremotepath2 srcremotepath3 ..] dstremotepath|dstemail:";
1547 #else
1548         return "cp srcremotepath [srcremotepath2 srcremotepath3 ..] dstremotepath|dstemail:";
1549 #endif
1550     }
1551     if (!strcmp(command, "deleteversions"))
1552     {
1553 #ifdef USE_PCRE
1554         return "deleteversions [-f] (--all | remotepath1 remotepath2 ...)  [--use-pcre]";
1555 #else
1556         return "deleteversions [-f] (--all | remotepath1 remotepath2 ...)";
1557 #endif
1558 
1559     }
1560     if (!strcmp(command, "exclude"))
1561     {
1562         return "exclude [(-a|-d) pattern1 pattern2 pattern3 [--restart-syncs]]";
1563     }
1564 #ifdef HAVE_LIBUV
1565     if (!strcmp(command, "webdav"))
1566     {
1567 #ifdef USE_PCRE
1568         return "webdav [-d (--all | remotepath ) ] [ remotepath [--port=PORT] [--public] [--tls --certificate=/path/to/certificate.pem --key=/path/to/certificate.key]] [--use-pcre]";
1569 #else
1570         return "webdav [-d (--all | remotepath ) ] [ remotepath [--port=PORT] [--public] [--tls --certificate=/path/to/certificate.pem --key=/path/to/certificate.key]]";
1571 #endif
1572     }
1573     if (!strcmp(command, "ftp"))
1574     {
1575 #ifdef USE_PCRE
1576         return "ftp [-d ( --all | remotepath ) ] [ remotepath [--port=PORT] [--data-ports=BEGIN-END] [--public] [--tls --certificate=/path/to/certificate.pem --key=/path/to/certificate.key]] [--use-pcre]";
1577 #else
1578         return "ftp [-d ( --all | remotepath ) ] [ remotepath [--port=PORT] [--data-ports=BEGIN-END] [--public] [--tls --certificate=/path/to/certificate.pem --key=/path/to/certificate.key]]";
1579 #endif
1580     }
1581 #endif
1582     if (!strcmp(command, "sync"))
1583     {
1584         return "sync [localpath dstremotepath| [-dsr] [ID|localpath]";
1585     }
1586     if (!strcmp(command, "backup"))
1587     {
1588         return "backup (localpath remotepath --period=\"PERIODSTRING\" --num-backups=N  | [-lhda] [TAG|localpath] [--period=\"PERIODSTRING\"] [--num-backups=N]) [--time-format=FORMAT]";
1589     }
1590     if (!strcmp(command, "https"))
1591     {
1592         return "https [on|off]";
1593     }
1594 #ifndef _WIN32
1595     if (!strcmp(command, "permissions"))
1596     {
1597         return "permissions [(--files|--folders) [-s XXX]]";
1598     }
1599 #endif
1600     if (!strcmp(command, "export"))
1601     {
1602 #ifdef USE_PCRE
1603         return "export [-d|-a [--password=PASSWORD] [--expire=TIMEDELAY] [-f]] [remotepath] [--use-pcre] [--time-format=FORMAT]";
1604 #else
1605         return "export [-d|-a [--password=PASSWORD] [--expire=TIMEDELAY] [-f]] [remotepath] [--time-format=FORMAT]";
1606 #endif
1607     }
1608     if (!strcmp(command, "share"))
1609     {
1610 #ifdef USE_PCRE
1611         return "share [-p] [-d|-a --with=user@email.com [--level=LEVEL]] [remotepath] [--use-pcre] [--time-format=FORMAT]";
1612 #else
1613         return "share [-p] [-d|-a --with=user@email.com [--level=LEVEL]] [remotepath] [--time-format=FORMAT]";
1614 #endif
1615     }
1616     if (!strcmp(command, "invite"))
1617     {
1618         return "invite [-d|-r] dstemail [--message=\"MESSAGE\"]";
1619     }
1620     if (!strcmp(command, "ipc"))
1621     {
1622         return "ipc email|handle -a|-d|-i";
1623     }
1624     if (!strcmp(command, "showpcr"))
1625     {
1626         return "showpcr [--in | --out] [--time-format=FORMAT]";
1627     }
1628     if (!strcmp(command, "masterkey"))
1629     {
1630         return "masterkey pathtosave";
1631     }
1632     if (!strcmp(command, "users"))
1633     {
1634         return "users [-s] [-h] [-n] [-d contact@email] [--time-format=FORMAT]";
1635     }
1636     if (!strcmp(command, "getua"))
1637     {
1638         return "getua attrname [email]";
1639     }
1640     if (!strcmp(command, "putua"))
1641     {
1642         return "putua attrname [del|set string|load file]";
1643     }
1644     if (!strcmp(command, "speedlimit"))
1645     {
1646         return "speedlimit [-u|-d] [-h] [NEWLIMIT]";
1647     }
1648     if (!strcmp(command, "killsession"))
1649     {
1650         return "killsession [-a | sessionid1 sessionid2 ... ]";
1651     }
1652     if (!strcmp(command, "whoami"))
1653     {
1654         return "whoami [-l]";
1655     }
1656     if (!strcmp(command, "df"))
1657     {
1658         return "df [-h]";
1659     }
1660     if (!strcmp(command, "proxy"))
1661     {
1662         return "proxy [URL|--auto|--none] [--username=USERNAME --password=PASSWORD]";
1663     }
1664     if (!strcmp(command, "cat"))
1665     {
1666         return "cat remotepath1 remotepath2 ...";
1667     }
1668     if (!strcmp(command, "mediainfo"))
1669     {
1670         return "info remotepath1 remotepath2 ...";
1671     }
1672     if (!strcmp(command, "passwd"))
1673     {
1674         if (interactiveThread())
1675         {
1676             return "passwd [-f]  [--auth-code=XXXX] [newpassword]";
1677         }
1678         else
1679         {
1680             return "passwd [-f]  [--auth-code=XXXX] newpassword";
1681         }
1682     }
1683     if (!strcmp(command, "retry"))
1684     {
1685         return "retry";
1686     }
1687     if (!strcmp(command, "recon"))
1688     {
1689         return "recon";
1690     }
1691     if (!strcmp(command, "reload"))
1692     {
1693         return "reload";
1694     }
1695     if (!strcmp(command, "logout"))
1696     {
1697         return "logout [--keep-session]";
1698     }
1699     if (!strcmp(command, "symlink"))
1700     {
1701         return "symlink";
1702     }
1703     if (!strcmp(command, "version"))
1704     {
1705         return "version [-l][-c]";
1706     }
1707     if (!strcmp(command, "debug"))
1708     {
1709         return "debug";
1710     }
1711     if (!strcmp(command, "chatf"))
1712     {
1713         return "chatf ";
1714     }
1715     if (!strcmp(command, "chatc"))
1716     {
1717         return "chatc group [email ro|rw|full|op]*";
1718     }
1719     if (!strcmp(command, "chati"))
1720     {
1721         return "chati chatid email ro|rw|full|op";
1722     }
1723     if (!strcmp(command, "chatr"))
1724     {
1725         return "chatr chatid [email]";
1726     }
1727     if (!strcmp(command, "chatu"))
1728     {
1729         return "chatu chatid";
1730     }
1731     if (!strcmp(command, "chatga"))
1732     {
1733         return "chatga chatid nodehandle uid";
1734     }
1735     if (!strcmp(command, "chatra"))
1736     {
1737         return "chatra chatid nodehandle uid";
1738     }
1739     if (!strcmp(command, "exit"))
1740     {
1741         return "exit [--only-shell]";
1742     }
1743     if (!strcmp(command, "quit"))
1744     {
1745         return "quit [--only-shell]";
1746     }
1747     if (!strcmp(command, "history"))
1748     {
1749         return "history";
1750     }
1751     if (!strcmp(command, "thumbnail"))
1752     {
1753         return "thumbnail [-s] remotepath localpath";
1754     }
1755     if (!strcmp(command, "preview"))
1756     {
1757         return "preview [-s] remotepath localpath";
1758     }
1759     if (!strcmp(command, "find"))
1760     {
1761 #ifdef USE_PCRE
1762         return "find [remotepath] [-l] [--pattern=PATTERN] [--mtime=TIMECONSTRAIN] [--size=SIZECONSTRAIN] [--use-pcre] [--time-format=FORMAT] [--show-handles]";
1763 #else
1764         return "find [remotepath] [-l] [--pattern=PATTERN] [--mtime=TIMECONSTRAIN] [--size=SIZECONSTRAIN] [--time-format=FORMAT] [--show-handles]";
1765 #endif
1766     }
1767     if (!strcmp(command, "help"))
1768     {
1769         return "help [-f]";
1770     }
1771     if (!strcmp(command, "clear"))
1772     {
1773         return "clear";
1774     }
1775     if (!strcmp(command, "transfers"))
1776     {
1777         return "transfers [-c TAG|-a] | [-r TAG|-a]  | [-p TAG|-a] [--only-downloads | --only-uploads] [SHOWOPTIONS]";
1778     }
1779 #if defined(_WIN32) && defined(NO_READLINE)
1780     if (!strcmp(command, "autocomplete"))
1781     {
1782         return "autocomplete [dos | unix]";
1783     }
1784     else if (!strcmp(command, "codepage"))
1785     {
1786         return "codepage [N [M]]";
1787     }
1788 #endif
1789 #if defined(_WIN32) || defined(__APPLE__)
1790     if (!strcmp(command, "update"))
1791     {
1792         return "update [--auto=on|off|query]";
1793     }
1794 #endif
1795     return "command not found: ";
1796 }
1797 
validCommand(string thecommand)1798 bool validCommand(string thecommand)
1799 {
1800     return stringcontained((char*)thecommand.c_str(), validCommands);
1801 }
1802 
getsupportedregexps()1803 string getsupportedregexps()
1804 {
1805 #ifdef USE_PCRE
1806         return "Perl Compatible Regular Expressions with \"--use-pcre\"\n   or wildcarded expresions with ? or * like f*00?.txt";
1807 #elif __cplusplus >= 201103L
1808         return "c++11 Regular Expressions";
1809 #else
1810         return "it accepts wildcards: ? and *. e.g.: f*00?.txt";
1811 #endif
1812 }
1813 
printTimeFormatHelp(ostringstream & os)1814 void printTimeFormatHelp(ostringstream &os)
1815 {
1816     os << " --time-format=FORMAT" << "\t" << "show time in available formats. Examples:" << endl;
1817     os << "               RFC2822: " << " Example: Fri, 06 Apr 2018 13:05:37 +0200" << endl;
1818     os << "               ISO6081: " << " Example: 2018-04-06" << endl;
1819     os << "               ISO6081_WITH_TIME: " << " Example: 2018-04-06T13:05:37" << endl;
1820     os << "               SHORT: " << " Example: 06Apr2018 13:05:37" << endl;
1821     os << "               SHORT_UTC: " << " Example: 06Apr2018 13:05:37" << endl;
1822     os << "               CUSTOM. e.g: --time-format=\"%Y %b\": "<< " Example: 2018 Apr" << endl;
1823     os << "                 You can use any strftime compliant format: http://www.cplusplus.com/reference/ctime/strftime/" << endl;
1824 }
1825 
getHelpStr(const char * command)1826 string getHelpStr(const char *command)
1827 {
1828     ostringstream os;
1829 
1830     os << "Usage: " << getUsageStr(command) << endl;
1831     if (!strcmp(command, "login"))
1832     {
1833         os << "Logs into a MEGA account" << endl;
1834         os << " You can log in either with email and password, with session ID," << endl;
1835         os << " or into a folder (an exported/public folder)" << endl;
1836         os << " If logging into a folder indicate url#key" << endl;
1837         os << endl;
1838         os << " Please, avoid using passwords containing \" or '." << endl;
1839         os << endl;
1840         os << "Options:" << endl;
1841         os << " --auth-code=XXXX" << "\t" << "Two-factor Authentication code. More info: https://mega.nz/blog_48" << endl;
1842     }
1843     else if (!strcmp(command, "cancel"))
1844     {
1845         os << "Cancels your MEGA account" << endl;
1846         os << " Caution: The account under this email address will be permanently closed" << endl;
1847         os << " and your data deleted. This can not be undone." << endl;
1848         os << endl;
1849         os << "The cancellation will not take place immediately. You will need to confirm the cancellation" << endl;
1850         os << "using a link that will be delivered to your email. See \"confirmcancel --help\"" << endl;
1851     }
1852     else if (!strcmp(command, "psa"))
1853     {
1854         os << "Shows the next available Public Service Announcement (PSA)" << endl;
1855         os << endl;
1856         os << "Options:" << endl;
1857         os << " --discard" << "\t" << "Discards last received PSA" << endl;
1858         os << endl;
1859     }
1860     else if (!strcmp(command, "confirmcancel"))
1861     {
1862         os << "Confirms the cancellation of your MEGA account" << endl;
1863         os << " Caution: The account under this email address will be permanently closed" << endl;
1864         os << " and your data deleted. This can not be undone." << endl;
1865     }
1866     else if (!strcmp(command, "errorcode"))
1867     {
1868         os << "Translate error code into string" << endl;
1869     }
1870     else if (!strcmp(command, "graphics"))
1871     {
1872         os << "Shows if special features related to images and videos are enabled. " << endl;
1873         os << "Use \"graphics on/off\" to enable/disable it." << endl;
1874         os << endl;
1875         os << "Disabling these features will avoid the upload of previews and thumbnails" << endl;
1876         os << "for images and videos." << endl;
1877         os << endl;
1878         os << "It's only recommended to disable these features before uploading files" << endl;
1879         os << "with image or video extensions that are not really images or videos," << endl;
1880         os << "or that are encrypted in the local drive so they can't be analyzed anyway." << endl;
1881         os << endl;
1882         os << "Notice that this setting will be saved for the next time you open MEGAcmd" << endl;
1883     }
1884     else if (!strcmp(command, "signup"))
1885     {
1886         os << "Register as user with a given email" << endl;
1887         os << endl;
1888         os << " Please, avoid using passwords containing \" or '" << endl;
1889         os << endl;
1890         os << "Options:" << endl;
1891         os << " --name=\"Your Name\"" << "\t" << "Name to register. e.g. \"John Smith\"" << endl;
1892         os << endl;
1893         os << " You will receive an email to confirm your account. " << endl;
1894         os << " Once you have received the email, please proceed to confirm the link " << endl;
1895         os << " included in that email with \"confirm\"." << endl;
1896         os << endl;
1897         os << "Warning: Due to our end-to-end encryption paradigm, you will not be able to access your data " << endl;
1898         os << "without either your password or a backup of your Recovery Key (master key)." << endl;
1899         os << "Exporting the master key and keeping it in a secure location enables you " << endl;
1900         os << "to set a new password without data loss. Always keep physical control of " << endl;
1901         os << "your master key (e.g. on a client device, external storage, or print)." << endl;
1902         os << " See \"masterkey --help\" for further info." << endl;
1903     }
1904     else if (!strcmp(command, "clear"))
1905     {
1906         os << "Clear screen" << endl;
1907     }
1908     else if (!strcmp(command, "help"))
1909     {
1910         os << "Prints list of commands" << endl;
1911         os << endl;
1912         os << "Options:" << endl;
1913         os << " -f" << "\t" << "Include a brief description of the commands" << endl;
1914     }
1915     else if (!strcmp(command, "history"))
1916     {
1917         os << "Prints history of used commands" << endl;
1918         os << "  Only commands used in interactive mode are registered" << endl;
1919     }
1920     else if (!strcmp(command, "confirm"))
1921     {
1922         os << "Confirm an account using the link provided after the \"signup\" process." << endl;
1923         os << " It requires the email and the password used to obtain the link." << endl;
1924         os << endl;
1925     }
1926     else if (!strcmp(command, "session"))
1927     {
1928         os << "Prints (secret) session ID" << endl;
1929     }
1930     else if (!strcmp(command, "mount"))
1931     {
1932         os << "Lists all the root nodes" << endl;
1933         os << endl;
1934         os << "This includes the root node in your cloud drive, Inbox, Rubbish Bin " << endl;
1935         os << "and all the in-shares (nodes shares to you from other users)" << endl;
1936     }
1937 #if defined(_WIN32) && !defined(NO_READLINE)
1938     else if (!strcmp(command, "unicode"))
1939     {
1940         os << "Toggle unicode input enabled/disabled in interactive shell" << endl;
1941         os << endl;
1942         os << " Unicode mode is experimental, you might experience" << endl;
1943         os << " some issues interacting with the console" << endl;
1944         os << " (e.g. history navigation fails)." << endl;
1945         os << "Type \"help --unicode\" for further info" << endl;
1946     }
1947 #endif
1948     else if (!strcmp(command, "ls"))
1949     {
1950         os << "Lists files in a remote path" << endl;
1951         os << " remotepath can be a pattern (" << getsupportedregexps() << ") " << endl;
1952         os << " Also, constructions like /PATTERN1/PATTERN2/PATTERN3 are allowed" << endl;
1953         os << endl;
1954         os << "Options:" << endl;
1955         os << " -R|-r" << "\t" << "List folders recursively" << endl;
1956         os << " --tree" << "\t" << "Prints tree-like exit (implies -r)" << endl;
1957         os << " --show-handles" << "\t" << "Prints files/folders handles (H:XXXXXXXX). You can address a file/folder by its handle" << endl;
1958         os << " -l" << "\t" << "Print summary (--tree has no effect)" << endl;
1959         os << "   " << "\t" << " SUMMARY contents:" << endl;
1960         os << "   " << "\t" << "   FLAGS: Indicate type/status of an element:" << endl;
1961         os << "   " << "\t" << "     xxxx" << endl;
1962         os << "   " << "\t" << "     |||+---- Sharing status: (s)hared, (i)n share or not shared(-)" << endl;
1963         os << "   " << "\t" << "     ||+----- if exported, whether it is (p)ermanent or (t)temporal" << endl;
1964         os << "   " << "\t" << "     |+------ e/- wheter node is (e)xported" << endl;
1965         os << "   " << "\t" << "     +-------- Type(d=folder,-=file,r=root,i=inbox,b=rubbish,x=unsupported)" << endl;
1966         os << "   " << "\t" << "   VERS: Number of versions in a file" << endl;
1967         os << "   " << "\t" << "   SIZE: Size of the file in bytes:" << endl;
1968         os << "   " << "\t" << "   DATE: Modification date for files and creation date for folders (in UTC time):" << endl;
1969         os << "   " << "\t" << "   NAME: name of the node" << endl;
1970         os << " -h" << "\t" << "Show human readable sizes in summary" << endl;
1971         os << " -a" << "\t" << "Include extra information" << endl;
1972         os << "   " << "\t" << " If this flag is repeated (e.g: -aa) more info will appear" << endl;
1973         os << "   " << "\t" << " (public links, expiration dates, ...)" << endl;
1974         os << " --versions" << "\t" << "show historical versions" << endl;
1975         os << "   " << "\t" << "You can delete all versions of a file with \"deleteversions\"" << endl;
1976         printTimeFormatHelp(os);
1977 
1978 #ifdef USE_PCRE
1979         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
1980 #endif
1981     }
1982     else if (!strcmp(command, "tree"))
1983     {
1984         os << "Lists files in a remote path in a nested tree decorated output" << endl;
1985         os << endl;
1986         os << "This is similar to \"ls --tree\"" << endl;
1987     }
1988 #if defined(_WIN32) || defined(__APPLE__)
1989     else if (!strcmp(command, "update"))
1990     {
1991         os << "Updates MEGAcmd" << endl;
1992         os << endl;
1993         os << "Looks for updates and applies if available." << endl;
1994         os << "This command can also be used to enable/disable automatic updates." << endl;
1995 
1996         os << "Options:" << endl;
1997         os << " --auto=ON|OFF|query" << "\t" << "Enables/disables/queries status of auto updates." << endl;
1998         os << endl;
1999         os << "If auto updates are enabled it will be checked while MEGAcmd server is running." << endl;
2000         os << " If there is an update available, it will be downloaded and applied. " << endl;
2001         os << " This will cause MEGAcmd to be restarted whenever the updates are applied." << endl;
2002         os << endl;
2003         os << "Further info at https://github.com/meganz/megacmd#megacmd-updates";
2004     }
2005 #endif
2006     else if (!strcmp(command, "cd"))
2007     {
2008         os << "Changes the current remote folder" << endl;
2009         os << endl;
2010         os << "If no folder is provided, it will be changed to the root folder" << endl;
2011     }
2012     else if (!strcmp(command, "log"))
2013     {
2014         os << "Prints/Modifies the current logs level" << endl;
2015         os << endl;
2016         os << "Options:" << endl;
2017         os << " -c" << "\t" << "CMD log level (higher level messages). " << endl;
2018         os << "   " << "\t" << " Messages captured by MEGAcmd server." << endl;
2019         os << " -s" << "\t" << "SDK log level (lower level messages)." << endl;
2020         os << "   " << "\t" << " Messages captured by the engine and libs" << endl;
2021 
2022         os << endl;
2023         os << "Regardless of the log level of the" << endl;
2024         os << " interactive shell, you can increase the amount of information given" <<  endl;
2025         os << "   by any command by passing \"-v\" (\"-vv\", \"-vvv\", ...)" << endl;
2026 
2027 
2028     }
2029     else if (!strcmp(command, "du"))
2030     {
2031         os << "Prints size used by files/folders" << endl;
2032         os << " remotepath can be a pattern (" << getsupportedregexps() << ") " << endl;
2033         os << endl;
2034         os << "Options:" << endl;
2035         os << " -h" << "\t" << "Human readable" << endl;
2036         os << " --versions" << "\t" << "Calculate size including all versions." << endl;
2037         os << "   " << "\t" << "You can remove all versions with \"deleteversions\" and list them with \"ls --versions\"" << endl;
2038         os << " --path-display-size=N" << "\t" << "Use a fixed size of N characters for paths" << endl;
2039 
2040 #ifdef USE_PCRE
2041         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2042 #endif
2043     }
2044     else if (!strcmp(command, "pwd"))
2045     {
2046         os << "Prints the current remote folder" << endl;
2047     }
2048     else if (!strcmp(command, "lcd"))
2049     {
2050         os << "Changes the current local folder for the interactive console" << endl;
2051         os << endl;
2052         os << "It will be used for uploads and downloads" << endl;
2053         os << endl;
2054         os << "If not using interactive console, the current local folder will be " << endl;
2055         os << " that of the shell executing mega comands" << endl;
2056     }
2057     else if (!strcmp(command, "lpwd"))
2058     {
2059         os << "Prints the current local folder for the interactive console" << endl;
2060         os << endl;
2061         os << "It will be used for uploads and downloads" << endl;
2062         os << endl;
2063         os << "If not using interactive console, the current local folder will be " << endl;
2064         os << " that of the shell executing mega comands" << endl;
2065     }
2066     else if (!strcmp(command, "logout"))
2067     {
2068         os << "Logs out" << endl;
2069         os << endl;
2070         os << "Options:" << endl;
2071         os << " --keep-session" << "\t" << "Keeps the current session." << endl;
2072     }
2073     else if (!strcmp(command, "import"))
2074     {
2075         os << "Imports the contents of a remote link into user's cloud" << endl;
2076         os << endl;
2077         os << "If no remote path is provided, the current local folder will be used" << endl;
2078         os << "Exported links: Exported links are usually formed as publiclink#key." << endl;
2079         os << " Alternativelly you can provide a password-protected link and" << endl;
2080         os << " provide the password with --password. Please, avoid using passwords containing \" or '" << endl;
2081     }
2082     else if (!strcmp(command, "put"))
2083     {
2084         os << "Uploads files/folders to a remote folder" << endl;
2085         os << endl;
2086         os << "Options:" << endl;
2087         os << " -c" << "\t" << "Creates remote folder destination in case of not existing." << endl;
2088         os << " -q" << "\t" << "queue upload: execute in the background. Don't wait for it to end' " << endl;
2089         os << " --ignore-quota-warn" << "\t" << "ignore quota surpassing warning. " << endl;
2090         os << "                    " << "\t" << "  The upload will be attempted anyway." << endl;
2091 
2092         os << endl;
2093         os << "Notice that the dstremotepath can only be omitted when only one local path is provided. " << endl;
2094         os << " In such case, the current remote working dir will be the destination for the upload." << endl;
2095         os << " Mind that using wildcards for local paths in non-interactive mode in a supportive console (e.g. bash)," << endl;
2096         os << " could result in multiple paths being passed to MEGAcmd." << endl;
2097     }
2098     else if (!strcmp(command, "get"))
2099     {
2100         os << "Downloads a remote file/folder or a public link " << endl;
2101         os << endl;
2102         os << "In case it is a file, the file will be downloaded at the specified folder " << endl;
2103         os << "                             (or at the current folder if none specified)." << endl;
2104         os << "  If the localpath (destination) already exists and is the same (same contents)" << endl;
2105         os << "  nothing will be done. If differs, it will create a new file appending \" (NUM)\" " << endl;
2106         os << endl;
2107         os << "For folders, the entire contents (and the root folder itself) will be" << endl;
2108         os << "                    by default downloaded into the destination folder" << endl;
2109         os << endl;
2110         os << "Exported links: Exported links are usually formed as publiclink#key." << endl;
2111         os << " Alternativelly you can provide a password-protected link and" << endl;
2112         os << " provide the password with --password. Please, avoid using passwords containing \" or '" << endl;
2113         os << "" << endl;
2114         os << endl;
2115         os << "Options:" << endl;
2116         os << " -q" << "\t" << "queue download: execute in the background. Don't wait for it to end' " << endl;
2117         os << " -m" << "\t" << "if the folder already exists, the contents will be merged with the " << endl;
2118         os << "                     downloaded one (preserving the existing files)" << endl;
2119         os << " --ignore-quota-warn" << "\t" << "ignore quota surpassing warning. " << endl;
2120         os << "                    " << "\t" << "  The download will be attempted anyway." << endl;
2121         os << " --password=PASSWORD" << "\t" << "Password to decrypt the password-protected link. Please, avoid using passwords containing \" or '" << endl;
2122 #ifdef USE_PCRE
2123         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2124 #endif
2125 
2126     }
2127     if (!strcmp(command, "attr"))
2128     {
2129         os << "Lists/updates node attributes" << endl;
2130         os << endl;
2131         os << "Options:" << endl;
2132         os << " -s" << "\tattribute value \t" << "sets an attribute to a value" << endl;
2133         os << " -d" << "\tattribute       \t" << "removes the attribute" << endl;
2134     }
2135     if (!strcmp(command, "userattr"))
2136     {
2137         os << "Lists/updates user attributes" << endl;
2138         os << endl;
2139         os << "Options:" << endl;
2140         os << " -s" << "\tattribute value \t" << "sets an attribute to a value" << endl;
2141         os << " --user=user@email" << "\t" << "select the user to query" << endl;
2142         os << " --list" << "\t" << "lists valid attributes" << endl;
2143     }
2144     else if (!strcmp(command, "mkdir"))
2145     {
2146         os << "Creates a directory or a directories hierarchy" << endl;
2147         os << endl;
2148         os << "Options:" << endl;
2149         os << " -p" << "\t" << "Allow recursive" << endl;
2150     }
2151     else if (!strcmp(command, "rm"))
2152     {
2153         os << "Deletes a remote file/folder" << endl;
2154         os << endl;
2155         os << "Options:" << endl;
2156         os << " -r" << "\t" << "Delete recursively (for folders)" << endl;
2157         os << " -f" << "\t" << "Force (no asking)" << endl;
2158 #ifdef USE_PCRE
2159         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2160 #endif
2161     }
2162     else if (!strcmp(command, "mv"))
2163     {
2164         os << "Moves file(s)/folder(s) into a new location (all remotes)" << endl;
2165         os << endl;
2166         os << "If the location exists and is a folder, the source will be moved there" << endl;
2167         os << "If the location doesn't exist, the source will be renamed to the destination name given" << endl;
2168 #ifdef USE_PCRE
2169         os << "Options:" << endl;
2170         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2171 #endif
2172     }
2173     else if (!strcmp(command, "cp"))
2174     {
2175         os << "Copies files/folders into a new location (all remotes)" << endl;
2176         os << endl;
2177         os << "If the location exists and is a folder, the source will be copied there" << endl;
2178         os << "If the location doesn't exist, and only one source is provided," << endl;
2179         os << " the file/folder will be copied and renamed to the destination name given." << endl;
2180         os << endl;
2181         os << "If \"dstemail:\" provided, the file/folder will be sent to that user's inbox (//in)" << endl;
2182         os << " e.g: cp /path/to/file user@doma.in:" << endl;
2183         os << " Remember the trailing \":\", otherwise a file with the name of that user (\"user@doma.in\") will be created" << endl;
2184 #ifdef USE_PCRE
2185         os << "Options:" << endl;
2186         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2187 #endif
2188     }
2189 #ifndef _WIN32
2190     else if (!strcmp(command, "permissions"))
2191     {
2192         os << "Shows/Establish default permissions for files and folders created by MEGAcmd." << endl;
2193         os << endl;
2194         os << "Permissions are unix-like permissions, with 3 numbers: one for owner, one for group and one for others" << endl;
2195         os << "Options:" << endl;
2196         os << " --files" << "\t" << "To show/set files default permissions." << endl;
2197         os << " --folders" << "\t" << "To show/set folders default permissions." << endl;
2198         os << " --s XXX" << "\t" << "To set new permissions for newly created files/folder. " << endl;
2199         os << "        " << "\t" << " Notice that for files minimum permissions is 600," << endl;
2200         os << "        " << "\t" << " for folders minimum permissions is 700." << endl;
2201         os << "        " << "\t" << " Further restrictions to owner are not allowed (to avoid missfunctioning)." << endl;
2202         os << "        " << "\t" << " Notice that permissions of already existing files/folders will not change." << endl;
2203         os << "        " << "\t" << " Notice that permissions of already existing files/folders will not change." << endl;
2204         os << endl;
2205         os << "Notice: this permissions will be saved for the next time you execute MEGAcmd server. They will be removed if you logout." << endl;
2206 
2207     }
2208 #endif
2209     else if (!strcmp(command, "https"))
2210     {
2211         os << "Shows if HTTPS is used for transfers. Use \"https on\" to enable it." << endl;
2212         os << endl;
2213         os << "HTTPS is not necesary since all data is stored and transfered encrypted." << endl;
2214         os << "Enabling it will increase CPU usage and add network overhead." << endl;
2215         os << endl;
2216         os << "Notice that this setting will be saved for the next time you open MEGAcmd" << endl;
2217     }
2218     else if (!strcmp(command, "deleteversions"))
2219     {
2220         os << "Deletes previous versions." << endl;
2221         os << endl;
2222         os << "This will permanently delete all historical versions of a file. " << endl;
2223         os << "The current version of the file will remain." << endl;
2224         os << "Note: any file version shared to you from a contact will need to be deleted by them." << endl;
2225 
2226         os << endl;
2227         os << "Options:" << endl;
2228         os << " -f   " << "\t" << "Force (no asking)" << endl;
2229         os << " --all" << "\t" << "Delete versions of all nodes. This will delete the version histories of all files (not current files)." << endl;
2230 #ifdef USE_PCRE
2231         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2232 #endif
2233         os << endl;
2234         os << "To see versions of a file use \"ls --versions\"." << endl;
2235         os << "To see space occupied by file versions use \"du --versions\"." << endl;
2236     }
2237 #ifdef HAVE_LIBUV
2238     else if (!strcmp(command, "webdav"))
2239     {
2240         os << "Configures a WEBDAV server to serve a location in MEGA" << endl;
2241         os << endl;
2242         os << "This can also be used for streaming files. The server will be running as long as MEGAcmd Server is. " << endl;
2243         os << "If no argument is given, it will list the webdav enabled locations." << endl;
2244         os << endl;
2245         os << "Options:" << endl;
2246         os << " --d        " << "\t" << "Stops serving that location" << endl;
2247         os << " --all      " << "\t" << "When used with -d, stops serving all locations (and stops the server)" << endl;
2248         os << " --public   " << "\t" << "*Allow access from outside localhost" << endl;
2249         os << " --port=PORT" << "\t" << "*Port to serve. DEFAULT= 4443" << endl;
2250         os << " --tls      " << "\t" << "*Serve with TLS (HTTPS)" << endl;
2251         os << " --certificate=/path/to/certificate.pem" << "\t" << "*Path to PEM formated certificate" << endl;
2252         os << " --key=/path/to/certificate.key" << "\t" << "*Path to PEM formated key" << endl;
2253 #ifdef USE_PCRE
2254         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2255 #endif
2256         os << endl;
2257         os << "*If you serve more than one location, these parameters will be ignored and use those of the first location served." << endl;
2258         os << " If you want to change those parameters, you need to stop serving all locations and configure them again." << endl;
2259         os << endl;
2260         os << "Caveat: This functionality is in BETA state. If you experience any issue with this, please contact: support@mega.nz" << endl;
2261         os << endl;
2262     }
2263     else if (!strcmp(command, "ftp"))
2264     {
2265         os << "Configures a FTP server to serve a location in MEGA" << endl;
2266         os << endl;
2267         os << "This can also be used for streaming files. The server will be running as long as MEGAcmd Server is. " << endl;
2268         os << "If no argument is given, it will list the ftp enabled locations." << endl;
2269         os << endl;
2270         os << "Options:" << endl;
2271         os << " --d        " << "\t" << "Stops serving that location" << endl;
2272         os << " --all      " << "\t" << "When used with -d, stops serving all locations (and stops the server)" << endl;
2273         os << " --public   " << "\t" << "*Allow access from outside localhost" << endl;
2274         os << " --port=PORT" << "\t" << "*Port to serve. DEFAULT=4990" << endl;
2275         os << " --data-ports=BEGIN-END" << "\t" << "*Ports range used for data channel (in passive mode). DEFAULT=1500-1600" << endl;
2276         os << " --tls      " << "\t" << "*Serve with TLS (FTPs)" << endl;
2277         os << " --certificate=/path/to/certificate.pem" << "\t" << "*Path to PEM formated certificate" << endl;
2278         os << " --key=/path/to/certificate.key" << "\t" << "*Path to PEM formated key" << endl;
2279 #ifdef USE_PCRE
2280         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2281 #endif
2282         os << endl;
2283         os << "*If you serve more than one location, these parameters will be ignored and used those of the first location served." << endl;
2284         os << " If you want to change those parameters, you need to stop serving all locations and configure them again." << endl;
2285         os << endl;
2286         os << "Caveat: This functionality is in BETA state. If you experience any issue with this, please contact: support@mega.nz" << endl;
2287         os << endl;
2288     }
2289 #endif
2290     else if (!strcmp(command, "exclude"))
2291     {
2292         os << "Manages exclusions in syncs." << endl;
2293         os << endl;
2294         os << "Options:" << endl;
2295         os << " -a pattern1 pattern2 ..." << "\t" << "adds pattern(s) to the exclusion list" << endl;
2296         os << "                         " << "\t" << "          (* and ? wildcards allowed)" << endl;
2297         os << " -d pattern1 pattern2 ..." << "\t" << "deletes pattern(s) from the exclusion list" << endl;
2298         os << " --restart-syncs" << "\t" << "Try to restart synchronizations." << endl;
2299 
2300         os << endl;
2301         os << "Changes will not be applied immediately to actions being performed in active syncs. " << endl;
2302         os << "After adding/deleting patterns, you might want to: " << endl;
2303         os << " a) disable/reenable synchronizations manually" << endl;
2304         os << " b) restart MEGAcmd server" << endl;
2305         os << " c) use --restart-syncs flag. Caveats:" << endl;
2306         os << "  This will cause active transfers to be restarted" << endl;
2307         os << "  In certain cases --restart-syncs might be unable to re-enable a synchronization. " << endl;
2308         os << "  In such case, you will need to manually resume it or restart MEGAcmd server." << endl;
2309     }
2310     else if (!strcmp(command, "sync"))
2311     {
2312         os << "Controls synchronizations" << endl;
2313         os << endl;
2314         os << "If no argument is provided, it lists current configured synchronizations" << endl;
2315         os << endl;
2316         os << "If provided local and remote paths, it will start synchronizing " << endl;
2317         os << " a local folder into a remote folder" << endl;
2318         os << endl;
2319         os << "If an ID/local path is provided, it will list such synchronization " << endl;
2320         os << " unless an option is specified." << endl;
2321         os << endl;
2322         os << "Options:" << endl;
2323         os << "-d" << " " << "ID|localpath" << "\t" << "deletes a synchronization" << endl;
2324         os << "-s" << " " << "ID|localpath" << "\t" << "stops(pauses) a synchronization" << endl;
2325         os << "-r" << " " << "ID|localpath" << "\t" << "resumes a synchronization" << endl;
2326         os << " --path-display-size=N" << "\t" << "Use at least N characters for displaying paths" << endl;
2327     }
2328     else if (!strcmp(command, "backup"))
2329     {
2330         os << "Controls backups" << endl;
2331         os << endl;
2332         os << "This command can be used to configure and control backups. " << endl;
2333         os << "A tutorial can be found here: https://github.com/meganz/MEGAcmd/blob/master/contrib/docs/BACKUPS.md" << endl;
2334         os << endl;
2335         os << "If no argument is given it will list the configured backups" << endl;
2336         os << " To get extra info on backups use -l or -h (see Options below)" << endl;
2337         os << endl;
2338         os << "When a backup of a folder (localfolder) is established in a remote folder (remotepath)" << endl;
2339         os << " MEGAcmd will create subfolder within the remote path with names like: \"localfoldername_bk_TIME\"" << endl;
2340         os << " which shall contain a backup of the local folder at that specific time" << endl;
2341         os << "In order to configure a backup you need to specify the local and remote paths, " << endl;
2342         os << "the period and max number of backups to store (see Configuration Options below)." << endl;
2343         os << "Once configured, you can see extended info asociated to the backup (See Display Options)" << endl;
2344         os << "Notice that MEGAcmd server need to be running for backups to be created." << endl;
2345         os << endl;
2346         os << "Display Options:" << endl;
2347         os << "-l\t" << "Show extended info: period, max number, next scheduled backup" << endl;
2348         os << "  \t" << " or the status of current/last backup" << endl;
2349         os << "-h\t" << "Show history of created backups" << endl;
2350         os << "  \t" << "Backup states:" << endl;
2351         os << "  \t"  << "While a backup is being performed, the backup will be considered and labeled as ONGOING" << endl;
2352         os << "  \t"  << "If a transfer is cancelled or fails, the backup will be considered INCOMPLETE" << endl;
2353         os << "  \t"  << "If a backup is aborted (see -a), all the transfers will be canceled and the backup be ABORTED" << endl;
2354         os << "  \t"  << "If MEGAcmd server stops during a transfer, it will be considered MISCARRIED" << endl;
2355         os << "  \t"  << "  Notice that currently when MEGAcmd server is restarted, ongoing and scheduled transfers " << endl;
2356         os << "  \t"  << "  will be carried out nevertheless." << endl;
2357         os << "  \t"  << "If MEGAcmd server is not running when a backup is scheduled and the time for the next one has already arrived," << endl;
2358         os << "  \t"  << " an empty BACKUP will be created with state SKIPPED" << endl;
2359         os << "  \t"  << "If a backup(1) is ONGOING and the time for the next backup(2) arrives, it won't start untill the previous one(1) " << endl;
2360         os << "  \t"  << " is completed, and if by the time the first one(1) ends the time for the next one(3) has already arrived," << endl;
2361         os << "  \t"  << " an empty BACKUP(2) will be created with state SKIPPED" << endl;
2362         os << " --path-display-size=N" << "\t" << "Use a fixed size of N characters for paths" << endl;
2363         printTimeFormatHelp(os);
2364         os << endl;
2365         os << "Configuration Options:" << endl;
2366         os << "--period=\"PERIODSTRING\"\t" << "Period: either time in TIMEFORMAT (see below) or a cron like expression" << endl;
2367         os << "                       \t" << " Cron like period is formatted as follows" << endl;
2368         os << "                       \t" << "  - - - - - -" << endl;
2369         os << "                       \t" << "  | | | | | |" << endl;
2370         os << "                       \t" << "  | | | | | |" << endl;
2371         os << "                       \t" << "  | | | | | +---- Day of the Week   (range: 1-7, 1 standing for Monday)" << endl;
2372         os << "                       \t" << "  | | | | +------ Month of the Year (range: 1-12)" << endl;
2373         os << "                       \t" << "  | | | +-------- Day of the Month  (range: 1-31)" << endl;
2374         os << "                       \t" << "  | | +---------- Hour              (range: 0-23)" << endl;
2375         os << "                       \t" << "  | +------------ Minute            (range: 0-59)" << endl;
2376         os << "                       \t" << "  +-------------- Second            (range: 0-59)" << endl;
2377         os << "                       \t" << " examples:" << endl;
2378         os << "                       \t" << "  - daily at 04:00:00 (UTC): \"0 0 4 * * *\"" << endl;
2379         os << "                       \t" << "  - every 15th day at 00:00:00 (UTC) \"0 0 0 15 * *\"" << endl;
2380         os << "                       \t" << "  - mondays at 04.30.00 (UTC): \"0 30 4 * * 1\"" << endl;
2381         os << "                       \t" << " TIMEFORMAT can be expressed in hours(h), days(d), " << endl;
2382         os << "                       \t"  << "   minutes(M), seconds(s), months(m) or years(y)" << endl;
2383         os << "                       \t" << "   e.g. \"1m12d3h\" indicates 1 month, 12 days and 3 hours" << endl;
2384         os << "                       \t" << "  Notice that this is an uncertain measure since not all months" << endl;
2385         os << "                       \t" << "  last the same and Daylight saving time changes are not considered" << endl;
2386         os << "                       \t" << "  If possible use a cron like expresion" << endl;
2387         os << "                       \t" << "Notice: regardless of the period expresion, the first time you establish a backup," << endl;
2388         os << "                       \t" << " it will be created immediately" << endl;
2389         os << "--num-backups=N\t" << "Maximum number of backups to store" << endl;
2390         os << "                 \t" << " After creating the backup (N+1) the oldest one will be deleted" << endl;
2391         os << "                 \t" << "  That might not be true in case there are incomplete backups:" << endl;
2392         os << "                 \t" << "   in order not to lose data, at least one COMPLETE backup will be kept" << endl;
2393         os << "Use backup TAG|localpath --option=VALUE to modify existing backups" << endl;
2394         os << endl;
2395         os << "Management Options:" << endl;
2396         os << "-d TAG|localpath\t" << "Removes a backup by its TAG or local path" << endl;
2397         os << "                \t" << " Folders created by backup won't be deleted" << endl;
2398         os << "-a TAG|localpath\t" << "Aborts ongoing backup" << endl;
2399         os << endl;
2400         os << "Caveat: This functionality is in BETA state. If you experience any issue with this, please contact: support@mega.nz" << endl;
2401     }
2402     else if (!strcmp(command, "export"))
2403     {
2404         os << "Prints/Modifies the status of current exports" << endl;
2405         os << endl;
2406         os << "Options:" << endl;
2407 #ifdef USE_PCRE
2408         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2409 #endif
2410         os << " -a" << "\t" << "Adds an export (or modifies it if existing)" << endl;
2411         os << " --password=PASSWORD" << "\t" << "Protects link with password. Please, avoid using passwords containing \" or '" << endl;
2412         os << " --expire=TIMEDELAY" << "\t" << "Determines the expiration time of a node." << endl;
2413         os << "                   " << "\t" << "   It indicates the delay in hours(h), days(d), " << endl;
2414         os << "                   " << "\t"  << "   minutes(M), seconds(s), months(m) or years(y)" << endl;
2415         os << "                   " << "\t" << "   e.g. \"1m12d3h\" establish an expiration time 1 month, " << endl;
2416         os << "                   " << "\t"  << "   12 days and 3 hours after the current moment" << endl;
2417         os << " -f" << "\t" << "Implicitly accept copyright terms (only shown the first time an export is made)" << endl;
2418         os << "   " << "\t" << "MEGA respects the copyrights of others and requires that users of the MEGA cloud service " << endl;
2419         os << "   " << "\t" << "comply with the laws of copyright." << endl;
2420         os << "   " << "\t" << "You are strictly prohibited from using the MEGA cloud service to infringe copyrights." << endl;
2421         os << "   " << "\t" << "You may not upload, download, store, share, display, stream, distribute, email, link to, " << endl;
2422         os << "   " << "\t" << "transmit or otherwise make available any files, data or content that infringes any copyright " << endl;
2423         os << "   " << "\t" << "or other proprietary rights of any person or entity." << endl;
2424         os << " -d" << "\t" << "Deletes an export" << endl;
2425         printTimeFormatHelp(os);
2426         os << endl;
2427         os << "If a remote path is given it'll be used to add/delete or in case of no option selected," << endl;
2428         os << " it will display all the exports existing in the tree of that path" << endl;
2429     }
2430     else if (!strcmp(command, "share"))
2431     {
2432         os << "Prints/Modifies the status of current shares" << endl;
2433         os << endl;
2434         os << "Options:" << endl;
2435 #ifdef USE_PCRE
2436         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2437 #endif
2438         os << " -p" << "\t" << "Show pending shares" << endl;
2439         os << " --with=email" << "\t" << "Determines the email of the user to [no longer] share with" << endl;
2440         os << " -d" << "\t" << "Stop sharing with the selected user" << endl;
2441         os << " -a" << "\t" << "Adds a share (or modifies it if existing)" << endl;
2442         os << " --level=LEVEL" << "\t" << "Level of acces given to the user" << endl;
2443         os << "              " << "\t" << "0: " << "Read access" << endl;
2444         os << "              " << "\t" << "1: " << "Read and write" << endl;
2445         os << "              " << "\t" << "2: " << "Full access" << endl;
2446         os << "              " << "\t" << "3: " << "Owner access" << endl;
2447         os << endl;
2448         os << "If a remote path is given it'll be used to add/delete or in case " << endl;
2449         os << " of no option selected, it will display all the shares existing " << endl;
2450         os << " in the tree of that path" << endl;
2451         os << endl;
2452         os << "When sharing a folder with a user that is not a contact (see \"users --help\")" << endl;
2453         os << "  the share will be in a pending state. You can list pending shares with" << endl;
2454         os << " \"share -p\". He would need to accept your invitation (see \"ipc\")" << endl;
2455         os << endl;
2456         os << "If someone has shared something with you, it will be listed as a root folder" << endl;
2457         os << " Use \"mount\" to list folders shared with you" << endl;
2458     }
2459     else if (!strcmp(command, "invite"))
2460     {
2461         os << "Invites a contact / deletes an invitation" << endl;
2462         os << endl;
2463         os << "Options:" << endl;
2464         os << " -d" << "\t" << "Deletes invitation" << endl;
2465         os << " -r" << "\t" << "Sends the invitation again" << endl;
2466         os << " --message=\"MESSAGE\"" << "\t" << "Sends inviting message" << endl;
2467         os << endl;
2468         os << "Use \"showpcr\" to browse invitations" << endl;
2469         os << "Use \"ipc\" to manage invitations received" << endl;
2470         os << "Use \"users\" to see contacts" << endl;
2471     }
2472     if (!strcmp(command, "ipc"))
2473     {
2474         os << "Manages contact incoming invitations." << endl;
2475         os << endl;
2476         os << "Options:" << endl;
2477         os << " -a" << "\t" << "Accepts invitation" << endl;
2478         os << " -d" << "\t" << "Rejects invitation" << endl;
2479         os << " -i" << "\t" << "Ignores invitation [WARNING: do not use unless you know what you are doing]" << endl;
2480         os << endl;
2481         os << "Use \"invite\" to send/remove invitations to other users" << endl;
2482         os << "Use \"showpcr\" to browse incoming/outgoing invitations" << endl;
2483         os << "Use \"users\" to see contacts" << endl;
2484     }
2485     if (!strcmp(command, "masterkey"))
2486     {
2487         os << "Shows your master key." << endl;
2488         os << endl;
2489         os << "Your data is only readable through a chain of decryption operations that begins " << endl;
2490         os << "with your master encryption key (Recovery Key), which MEGA stores encrypted with your password." << endl;
2491         os << "This means that if you lose your password, your Recovery Key can no longer be decrypted, " << endl;
2492         os << "and you can no longer decrypt your data." << endl;
2493         os << "Exporting the Recovery Key and keeping it in a secure location " << endl;
2494         os << "enables you to set a new password without data loss." << endl;
2495         os << "Always keep physical control of your master key (e.g. on a client device, external storage, or print)" << endl;
2496     }
2497     if (!strcmp(command, "showpcr"))
2498     {
2499         os << "Shows incoming and outgoing contact requests." << endl;
2500         os << endl;
2501         os << "Options:" << endl;
2502         os << " --in" << "\t" << "Shows incoming requests" << endl;
2503         os << " --out" << "\t" << "Shows outgoing invitations" << endl;
2504         printTimeFormatHelp(os);
2505         os << endl;
2506         os << "Use \"ipc\" to manage invitations received" << endl;
2507         os << "Use \"users\" to see contacts" << endl;
2508     }
2509     else if (!strcmp(command, "users"))
2510     {
2511         os << "List contacts" << endl;
2512         os << endl;
2513         os << "Options:" << endl;
2514         os << " -s" << "\t" << "Show shared folders with listed contacts" << endl;
2515         os << " -h" << "\t" << "Show all contacts (hidden, blocked, ...)" << endl;
2516         os << " -n" << "\t" << "Show users names" << endl;
2517         os << " -d" << "\tcontact@email " << "Deletes the specified contact" << endl;
2518         printTimeFormatHelp(os);
2519         os << endl;
2520         os << "Use \"invite\" to send/remove invitations to other users" << endl;
2521         os << "Use \"showpcr\" to browse incoming/outgoing invitations" << endl;
2522         os << "Use \"ipc\" to manage invitations received" << endl;
2523         os << "Use \"users\" to see contacts" << endl;
2524     }
2525     else if (!strcmp(command, "speedlimit"))
2526     {
2527         os << "Displays/modifies upload/download rate limits" << endl;
2528         os << " NEWLIMIT establish the new limit in size per second (0 = no limit)" << endl;
2529         os << " NEWLIMIT may include (B)ytes, (K)ilobytes, (M)egabytes, (G)igabytes & (T)erabytes." << endl;
2530         os << "  Examples: \"1m12k3B\" \"3M\". If no units are given, bytes are assumed" << endl;
2531         os << endl;
2532         os << "Options:" << endl;
2533         os << " -d" << "\t" << "Download speed limit" << endl;
2534         os << " -u" << "\t" << "Upload speed limit" << endl;
2535         os << " -h" << "\t" << "Human readable" << endl;
2536         os << endl;
2537         os << "Notice: this limit will be saved for the next time you execute MEGAcmd server. They will be removed if you logout." << endl;
2538     }
2539     else if (!strcmp(command, "killsession"))
2540     {
2541         os << "Kills a session of current user." << endl;
2542         os << endl;
2543         os << "Options:" << endl;
2544         os << " -a" << "\t" << "kills all sessions except the current one" << endl;
2545         os << endl;
2546         os << "To see all sessions use \"whoami -l\"" << endl;
2547     }
2548     else if (!strcmp(command, "whoami"))
2549     {
2550         os << "Prints info of the user" << endl;
2551         os << endl;
2552         os << "Options:" << endl;
2553         os << " -l" << "\t" << "Show extended info: total storage used, storage per main folder " << endl;
2554         os << "   " << "\t" << "(see mount), pro level, account balance, and also the active sessions" << endl;
2555     }
2556     else if (!strcmp(command, "df"))
2557     {
2558         os << "Shows storage info" << endl;
2559         os << endl;
2560         os << "Shows total storage used in the account, storage per main folder (see mount)" << endl;
2561         os << endl;
2562         os << "Options:" << endl;
2563         os << " -h" << "\t" << "Human readable sizes. Otherwise, size will be expressed in Bytes" << endl;
2564     }
2565     else if (!strcmp(command, "proxy"))
2566     {
2567         os << "Show or sets proxy configuration" << endl;
2568         os << endl;
2569         os << "With no parameter given, this will print proxy configuration" << endl;
2570         os << endl;
2571 
2572         os << "Options:" << endl;
2573         os << "URL" << "\t" << "Proxy URL (e.g: https://127.0.0.1:8080)" << endl;
2574         os << " --none" << "\t" << "To disable using a proxy" << endl;
2575         os << " --auto" << "\t" << "To use the proxy configured in your system" << endl;
2576         os << " --username=USERNAME" << "\t" << "The username, for authenticated proxies" << endl;
2577         os << " --password=PASSWORD" << "\t" << "The password, for authenticated proxies. Please, avoid using passwords containing \" or '" << endl;
2578 
2579     }
2580     else if (!strcmp(command, "cat"))
2581     {
2582         os << "Prints the contents of remote files" << endl;
2583         os << endl;
2584 #ifdef _WIN32
2585         os << "To avoid issues with encoding, if you want to cat the exact binary contents of a remote file into a local one, " << endl;
2586         os << "use non-interactive mode with -o /path/to/file. See help \"non-interactive\"" << endl;
2587 #endif
2588     }
2589     else if (!strcmp(command, "mediainfo"))
2590     {
2591         os << "Prints media info of remote files" << endl;
2592         os << endl;
2593         os << "Options:" << endl;
2594         os << " --path-display-size=N" << "\t" << "Use a fixed size of N characters for paths" << endl;
2595     }
2596     else if (!strcmp(command, "passwd"))
2597     {
2598         os << "Modifies user password" << endl;
2599         os << endl;
2600         os << "Notice that modifying the password will close all your active sessions" << endl;
2601         os << " in all your devices (except for the current one)" << endl;
2602         os << endl;
2603         os << " Please, avoid using passwords containing \" or '" << endl;
2604         os << endl;
2605         os << "Options:" << endl;
2606         os << " -f   " << "\t" << "Force (no asking)" << endl;
2607         os << " --auth-code=XXXX" << "\t" << "Two-factor Authentication code. More info: https://mega.nz/blog_48" << endl;
2608     }
2609     else if (!strcmp(command, "reload"))
2610     {
2611         os << "Forces a reload of the remote files of the user" << endl;
2612         os << "It will also resume synchronizations." << endl;
2613     }
2614     else if (!strcmp(command, "version"))
2615     {
2616         os << "Prints MEGAcmd versioning and extra info" << endl;
2617         os << endl;
2618         os << "Options:" << endl;
2619         os << " -c" << "\t" << "Shows changelog for the current version" << endl;
2620         os << " -l" << "\t" << "Show extended info: MEGA SDK version and features enabled" << endl;
2621     }
2622     else if (!strcmp(command, "thumbnail"))
2623     {
2624         os << "To download/upload the thumbnail of a file." << endl;
2625         os << " If no -s is inidicated, it will download the thumbnail." << endl;
2626         os << endl;
2627         os << "Options:" << endl;
2628         os << " -s" << "\t" << "Sets the thumbnail to the specified file" << endl;
2629     }
2630     else if (!strcmp(command, "preview"))
2631     {
2632         os << "To download/upload the preview of a file." << endl;
2633         os << " If no -s is inidicated, it will download the preview." << endl;
2634         os << endl;
2635         os << "Options:" << endl;
2636         os << " -s" << "\t" << "Sets the preview to the specified file" << endl;
2637     }
2638     else if (!strcmp(command, "find"))
2639     {
2640         os << "Find nodes matching a pattern" << endl;
2641         os << endl;
2642         os << "Options:" << endl;
2643         os << " --pattern=PATTERN" << "\t" << "Pattern to match";
2644         os << " (" << getsupportedregexps() << ") " << endl;
2645         os << " --mtime=TIMECONSTRAIN" << "\t" << "Determines time constrains, in the form: [+-]TIMEVALUE" << endl;
2646         os << "                      " << "\t" << "  TIMEVALUE may include hours(h), days(d), minutes(M)," << endl;
2647         os << "                      " << "\t" << "   seconds(s), months(m) or years(y)" << endl;
2648         os << "                      " << "\t" << "  Examples:" << endl;
2649         os << "                      " << "\t" << "   \"+1m12d3h\" shows files modified before 1 month, " << endl;
2650         os << "                      " << "\t" << "    12 days and 3 hours the current moment" << endl;
2651         os << "                      " << "\t" << "   \"-3h\" shows files modified within the last 3 hours" << endl;
2652         os << "                      " << "\t" << "   \"-3d+1h\" shows files modified in the last 3 days prior to the last hour" << endl;
2653         os << " --size=SIZECONSTRAIN" << "\t" << "Determines size constrains, in the form: [+-]TIMEVALUE" << endl;
2654         os << "                      " << "\t" << "  TIMEVALUE may include (B)ytes, (K)ilobytes, (M)egabytes, (G)igabytes & (T)erabytes" << endl;
2655         os << "                      " << "\t" << "  Examples:" << endl;
2656         os << "                      " << "\t" << "   \"+1m12k3B\" shows files bigger than 1 Mega, 12 Kbytes and 3Bytes" << endl;
2657         os << "                      " << "\t" << "   \"-3M\" shows files smaller than 3 Megabytes" << endl;
2658         os << "                      " << "\t" << "   \"-4M+100K\" shows files smaller than 4 Mbytes and bigger than 100 Kbytes" << endl;
2659         os << " --show-handles" << "\t" << "Prints files/folders handles (H:XXXXXXXX). You can address a file/folder by its handle" << endl;
2660 #ifdef USE_PCRE
2661         os << " --use-pcre" << "\t" << "use PCRE expressions" << endl;
2662 #endif
2663         os << " -l" << "\t" << "Prints file info" << endl;
2664         printTimeFormatHelp(os);
2665     }
2666     else if(!strcmp(command,"debug") )
2667     {
2668         os << "Enters debugging mode (HIGHLY VERBOSE)" << endl;
2669         os << endl;
2670         os << "For a finer control of log level see \"log --help\"" << endl;
2671     }
2672     else if (!strcmp(command, "quit") || !strcmp(command, "exit"))
2673     {
2674         os << "Quits MEGAcmd" << endl;
2675         os << endl;
2676         os << "Notice that the session will still be active, and local caches available" << endl;
2677         os << "The session will be resumed when the service is restarted" << endl;
2678         if (getCurrentThreadIsCmdShell())
2679         {
2680             os << endl;
2681             os << "Be aware that this will exit both the interactive shell and the server." << endl;
2682             os << "To only exit current shell and keep server running, use \"exit --only-shell\"" << endl;
2683         }
2684     }
2685     else if (!strcmp(command, "transfers"))
2686     {
2687         os << "List or operate with transfers" << endl;
2688         os << endl;
2689         os << "If executed without option it will list the first 10 tranfers" << endl;
2690         os << "Options:" << endl;
2691         os << " -c (TAG|-a)" << "\t" << "Cancel transfer with TAG (or all with -a)" << endl;
2692         os << " -p (TAG|-a)" << "\t" << "Pause transfer with TAG (or all with -a)" << endl;
2693         os << " -r (TAG|-a)" << "\t" << "Resume transfer with TAG (or all with -a)" << endl;
2694         os << " --only-uploads" << "\t" << "Show/Operate only upload transfers" << endl;
2695         os << " --only-downloads" << "\t" << "Show/Operate only download transfers" << endl;
2696         os << endl;
2697         os << "Show options:" << endl;
2698         os << " --summary" << "\t" << "Prints summary of on going transfers" << endl;
2699         os << " --show-syncs" << "\t" << "Show synchronization transfers" << endl;
2700         os << " --show-completed" << "\t" << "Show completed transfers" << endl;
2701         os << " --only-completed" << "\t" << "Show only completed download" << endl;
2702         os << " --limit=N" << "\t" << "Show only first N transfers" << endl;
2703         os << " --path-display-size=N" << "\t" << "Use at least N characters for displaying paths" << endl;
2704         os << endl;
2705         os << "TYPE legend correspondence:" << endl;
2706 #ifdef _WIN32
2707 
2708         const string cD = getutf8fromUtf16(L"\u25bc");
2709         const string cU = getutf8fromUtf16(L"\u25b2");
2710         const string cS = getutf8fromUtf16(L"\u21a8");
2711         const string cB = getutf8fromUtf16(L"\u2191");
2712 #else
2713         const string cD = "\u21d3";
2714         const string cU = "\u21d1";
2715         const string cS = "\u21f5";
2716         const string cB = "\u23eb";
2717 #endif
2718         os << "  " << cD <<" = \t" << "Download transfer" << endl;
2719         os << "  " << cU <<" = \t" << "Upload transfer" << endl;
2720         os << "  " << cS <<" = \t" << "Sync transfer. The transfer is done in the context of a synchronization" << endl;
2721         os << "  " << cB <<" = \t" << "Backup transfer. The transfer is done in the context of a backup" << endl;
2722 
2723     }
2724 #if defined(_WIN32) && defined(NO_READLINE)
2725     else if (!strcmp(command, "autocomplete"))
2726     {
2727         os << "Modifes how tab completion operates." << endl;
2728         os << endl;
2729         os << "The default is to operate like the native platform. However" << endl;
2730         os << "you can switch it between mode 'dos' and 'unix' as you prefer." << endl;
2731         os << "Options:" << endl;
2732         os << " dos" << "\t" << "Each press of tab places the next option into the command line" << endl;
2733         os << " unix" << "\t" << "Options are listed in a table, or put in-line if there is only one" << endl;
2734     }
2735     else if (!strcmp(command, "codepage"))
2736     {
2737         os << "Switches the codepage used to decide which characters show on-screen." << endl;
2738         os << endl;
2739         os << "MEGAcmd supports unicode or specific code pages.  For european countries you may need" << endl;
2740         os << "to select a suitable codepage or secondary codepage for the character set you use." << endl;
2741         os << "Of course a font containing the glyphs you need must have been selected for the terminal first." << endl;
2742         os << "Options:" << endl;
2743         os << " (no option)" << "\t" << "Outputs the selected code page and secondary codepage (if configured)." << endl;
2744         os << " N" << "\t" << "Sets the main codepage to N. 65001 is Unicode." << endl;
2745         os << " M" << "\t" << "Sets the secondary codepage to M, which is used if the primary can't translate a character." << endl;
2746     }
2747 #endif
2748     return os.str();
2749 }
2750 
2751 #define SSTR( x ) static_cast< const std::ostringstream & >( \
2752         ( std::ostringstream() << std::dec << x ) ).str()
2753 
printAvailableCommands(int extensive=0)2754 void printAvailableCommands(int extensive = 0)
2755 {
2756     vector<string> validCommandsOrdered = validCommands;
2757     sort(validCommandsOrdered.begin(), validCommandsOrdered.end());
2758     if (!extensive)
2759     {
2760         size_t i = 0;
2761         size_t j = (validCommandsOrdered.size()/3)+((validCommandsOrdered.size()%3>0)?1:0);
2762         size_t k = 2*(validCommandsOrdered.size()/3)+validCommandsOrdered.size()%3;
2763         for (i = 0; i < validCommandsOrdered.size() && j < validCommandsOrdered.size()  && k < validCommandsOrdered.size(); i++, j++, k++)
2764         {
2765             OUTSTREAM << "      " << getLeftAlignedStr(validCommandsOrdered.at(i), 20) <<  getLeftAlignedStr(validCommandsOrdered.at(j), 20)  <<  "      " << validCommandsOrdered.at(k) << endl;
2766         }
2767         if (validCommandsOrdered.size()%3)
2768         {
2769             OUTSTREAM << "      " << getLeftAlignedStr(validCommandsOrdered.at(i), 20) ;
2770             if (validCommandsOrdered.size()%3 > 1 )
2771             {
2772                 OUTSTREAM << getLeftAlignedStr(validCommandsOrdered.at(j), 20) ;
2773             }
2774             OUTSTREAM << endl;
2775         }
2776     }
2777     else
2778     {
2779         for (size_t i = 0; i < validCommandsOrdered.size(); i++)
2780         {
2781             if (validCommandsOrdered.at(i)!="completion")
2782             {
2783                 if (extensive > 1)
2784                 {
2785                     unsigned int width = getNumberOfCols();
2786 
2787                     OUTSTREAM <<  "<" << validCommandsOrdered.at(i) << ">" << endl;
2788                     OUTSTREAM <<  getHelpStr(validCommandsOrdered.at(i).c_str());
2789                     for (unsigned int j = 0; j< width; j++) OUTSTREAM << "-";
2790                     OUTSTREAM << endl;
2791                 }
2792                 else
2793                 {
2794                     OUTSTREAM << "      " << getUsageStr(validCommandsOrdered.at(i).c_str());
2795                     string helpstr = getHelpStr(validCommandsOrdered.at(i).c_str());
2796                     helpstr=string(helpstr,helpstr.find_first_of("\n")+1);
2797                     OUTSTREAM << ": " << string(helpstr,0,helpstr.find_first_of("\n"));
2798 
2799                     OUTSTREAM << endl;
2800                 }
2801             }
2802         }
2803     }
2804 }
2805 
checkBlockStatus(bool waitcompletion=true)2806 void checkBlockStatus(bool waitcompletion = true)
2807 {
2808     time_t tnow = time(NULL);
2809     if ( (tnow - lastTimeCheckBlockStatus) > 30)
2810     {
2811         std::unique_ptr<MegaCmdListener> megaCmdListener{waitcompletion?new MegaCmdListener(api, NULL):nullptr};
2812 
2813         api->whyAmIBlocked(megaCmdListener.get());//TO enforce acknowledging unblock transition
2814 
2815         if (megaCmdListener)
2816         {
2817             megaCmdListener->wait();
2818         }
2819 
2820         lastTimeCheckBlockStatus = tnow;
2821     }
2822 }
2823 
executecommand(char * ptr)2824 void executecommand(char* ptr)
2825 {
2826     vector<string> words = getlistOfWords(ptr, !getCurrentThreadIsCmdShell());
2827     if (!words.size())
2828     {
2829         return;
2830     }
2831 
2832     string thecommand = words[0];
2833 
2834     if (( thecommand == "?" ) || ( thecommand == "h" ))
2835     {
2836         printAvailableCommands();
2837         return;
2838     }
2839 
2840     if (words[0] == "completion")
2841     {
2842         if (words.size() >= 2 && words[1].find("--client-width=") == 0)
2843         {
2844             words.erase(++words.begin());
2845         }
2846         if (words.size() < 3) words.push_back("");
2847         vector<string> wordstocomplete(words.begin()+1,words.end());
2848         setCurrentThreadLine(wordstocomplete);
2849         OUTSTREAM << getListOfCompletionValues(wordstocomplete);
2850         return;
2851     }
2852 
2853     if (getBlocked())
2854     {
2855         checkBlockStatus(!validCommand(thecommand) && thecommand != "retrycons");
2856     }
2857 
2858     if (words[0] == "retrycons")
2859     {
2860         api->retryPendingConnections();
2861         return;
2862     }
2863     if (words[0] == "loggedin")
2864     {
2865         if (!api->isFilesystemAvailable())
2866         {
2867             setCurrentOutCode(MCMD_NOTLOGGEDIN);
2868         }
2869         return;
2870     }
2871     if (words[0] == "completionshell")
2872     {
2873         if (words.size() == 2)
2874         {
2875             vector<string> validCommandsOrdered = validCommands;
2876             sort(validCommandsOrdered.begin(), validCommandsOrdered.end());
2877             for (size_t i = 0; i < validCommandsOrdered.size(); i++)
2878             {
2879                 if (validCommandsOrdered.at(i)!="completion")
2880                 {
2881                     OUTSTREAM << validCommandsOrdered.at(i);
2882                     if (i != validCommandsOrdered.size() -1)
2883                     {
2884                         OUTSTREAM << (char)0x1F;
2885                     }
2886                 }
2887             }
2888         }
2889         else
2890         {
2891             if (words.size() < 3) words.push_back("");
2892             vector<string> wordstocomplete(words.begin()+1,words.end());
2893             setCurrentThreadLine(wordstocomplete);
2894             OUTSTREAM << getListOfCompletionValues(wordstocomplete,(char)0x1F, string().append(1, (char)0x1F).c_str(), false);
2895         }
2896 
2897         return;
2898     }
2899 
2900     words = getlistOfWords(ptr, !getCurrentThreadIsCmdShell(), true); //Get words again ignoring trailing spaces (only reasonable for completion)
2901 
2902     map<string, string> cloptions;
2903     map<string, int> clflags;
2904 
2905     set<string> validParams;
2906     addGlobalFlags(&validParams);
2907 
2908     if (setOptionsAndFlags(&cloptions, &clflags, &words, validParams, true))
2909     {
2910         setCurrentOutCode(MCMD_EARGS);
2911         LOG_err << "      " << getUsageStr(thecommand.c_str());
2912         return;
2913     }
2914 
2915     insertValidParamsPerCommand(&validParams, thecommand);
2916 
2917     if (!validCommand(thecommand))   //unknown command
2918     {
2919         setCurrentOutCode(MCMD_EARGS);
2920         if (loginInAtStartup)
2921         {
2922             LOG_err << "Command not valid while login in: " << thecommand;
2923         }
2924         else
2925         {
2926             LOG_err << "Command not found: " << thecommand;
2927         }
2928         return;
2929     }
2930 
2931     if (setOptionsAndFlags(&cloptions, &clflags, &words, validParams))
2932     {
2933         setCurrentOutCode(MCMD_EARGS);
2934         LOG_err << "      " << getUsageStr(thecommand.c_str());
2935         return;
2936     }
2937     setCurrentThreadLogLevel(MegaApi::LOG_LEVEL_ERROR + (getFlag(&clflags, "v")?(1+getFlag(&clflags, "v")):0));
2938 
2939     if (getFlag(&clflags, "help"))
2940     {
2941         string h = getHelpStr(thecommand.c_str());
2942         OUTSTREAM << h << endl;
2943         return;
2944     }
2945 
2946     if ( thecommand == "help" )
2947     {
2948         if (getFlag(&clflags,"upgrade"))
2949         {
2950 
2951              const char *userAgent = api->getUserAgent();
2952              char* url = new char[strlen(userAgent)+10];
2953 
2954              sprintf(url, "pro/uao=%s",userAgent);
2955 
2956              string theurl;
2957 
2958              if (api->isLoggedIn())
2959              {
2960                  MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
2961                  api->getSessionTransferURL(url, megaCmdListener);
2962                  megaCmdListener->wait();
2963                  if (megaCmdListener->getError() && megaCmdListener->getError()->getErrorCode() == MegaError::API_OK)
2964                  {
2965                      theurl = megaCmdListener->getRequest()->getLink();
2966                  }
2967                  else
2968                  {
2969                      setCurrentOutCode(MCMD_EUNEXPECTED);
2970                      LOG_warn << "Unable to get session transfer url: " << megaCmdListener->getError()->getErrorString();
2971                  }
2972                  delete megaCmdListener;
2973              }
2974 
2975              if (!theurl.size())
2976              {
2977                  theurl = "https://mega.nz/pro";
2978              }
2979 
2980              OUTSTREAM << "MEGA offers different PRO plans to increase your allowed transfer quota and user storage." << endl;
2981              OUTSTREAM << "Open the following link in your browser to obtain a PRO account: " << endl;
2982              OUTSTREAM << "  " << theurl << endl;
2983 
2984              delete [] url;
2985         }
2986         else if (getFlag(&clflags,"paths"))
2987         {
2988             OUTSTREAM << "MEGAcmd will allow you to enter local and remote paths." << endl;
2989             OUTSTREAM << " - REMOTE paths are case-sensitive, and use '/' as path separator." << endl;
2990             OUTSTREAM << "    The root folder in your cloud will be `/`. " << endl;
2991             OUTSTREAM << "    There are other possible root folders (Rubbish Bin, Inbox & in-shares). " << endl;
2992             OUTSTREAM << "       For further info on root folders, see \"mount --help\"" << endl;
2993             OUTSTREAM << " - LOCAL paths are system dependant. " << endl;
2994             OUTSTREAM << "    In Windows, you will be able to use both '\\' and '/' as separator." << endl;
2995             OUTSTREAM << endl;
2996             OUTSTREAM << "To refer to paths that include spaces, you will need to either surround the path between quotes \"\"," << endl;
2997             OUTSTREAM << "   or scape the space with '\\ '." << endl;
2998             OUTSTREAM << "     e.g: <ls /a\\ folder> or <ls \"a folder\"> will list the contents of a folder named 'a folder' " << endl;
2999             OUTSTREAM << "          located in the root folder of your cloud."  << endl;
3000             OUTSTREAM << endl;
3001             OUTSTREAM << "USE autocompletion! MEGAcmd features autocompletion. Pressing <TAB> will autocomplete paths" << endl;
3002             OUTSTREAM << " (both LOCAL & REMOTE) along with other parameters of commands. It will surely save you some typing!" << endl;
3003         }
3004         else if (getFlag(&clflags,"non-interactive"))
3005         {
3006             OUTSTREAM << "MEGAcmd features two modes of interaction:" << endl;
3007             OUTSTREAM << " - interactive: entering commands in this shell. Enter \"help\" to list available commands" << endl;
3008             OUTSTREAM << " - non-interactive: MEGAcmd is also listening to outside petitions" << endl;
3009             OUTSTREAM << "For the non-interactive mode, there are client commands you can use. " << endl;
3010 #ifdef _WIN32
3011 
3012             OUTSTREAM << "Along with the interactive shell, there should be several mega-*.bat scripts" << endl;
3013             OUTSTREAM << "installed with MEGAcmd. You can use them writting their absolute paths, " << endl;
3014             OUTSTREAM << "or including their location into your environment PATH and execute simply with mega-*" << endl;
3015             OUTSTREAM << "If you use PowerShell, you can add the the location of the scripts to the PATH with:" << endl;
3016             OUTSTREAM << "  $env:PATH += \";$env:LOCALAPPDATA\\MEGAcmd\"" << endl;
3017             OUTSTREAM << "Client commands completion requires bash, hence, it is not available for Windows. " << endl;
3018             OUTSTREAM << "You can add \" -o outputfile\" to save the output into a file instead of to standard output." << endl;
3019             OUTSTREAM << endl;
3020 
3021 #elif __MACH__
3022             OUTSTREAM << "After installing the dmg, along with the interactive shell, client commands" << endl;
3023             OUTSTREAM << "should be located at /Applications/MEGAcmd.app/Contents/MacOS" << endl;
3024             OUTSTREAM << "If you wish to use the client commands from MacOS Terminal, open the Terminal and " << endl;
3025             OUTSTREAM << "include the installation folder in the PATH. Typically:" << endl;
3026             OUTSTREAM << endl;
3027             OUTSTREAM << " export PATH=/Applications/MEGAcmd.app/Contents/MacOS:$PATH" << endl;
3028             OUTSTREAM << endl;
3029             OUTSTREAM << "And for bash completion, source megacmd_completion.sh:" << endl;
3030             OUTSTREAM << " source /Applications/MEGAcmd.app/Contents/MacOS/megacmd_completion.sh" << endl;
3031 #else
3032             OUTSTREAM << "If you have installed MEGAcmd using one of the available packages" << endl;
3033             OUTSTREAM << "both the interactive shell (mega-cmd) and the different client commands (mega-*) " << endl;
3034             OUTSTREAM << "will be in your PATH (you might need to open your shell again). " << endl;
3035             OUTSTREAM << "If you are using bash, you should also have autocompletion for client commands working. " << endl;
3036 
3037 #endif
3038         }
3039 
3040 #if defined(_WIN32) && defined(NO_READLINE)
3041         else if (getFlag(&clflags, "unicode"))
3042         {
3043             OUTSTREAM << "Unicode support has been considerably improved in the interactive console since version 1.0.0." << endl;
3044             OUTSTREAM << "If you do experience issues with it, please do not hesistate to contact us." << endl;
3045             OUTSTREAM << endl;
3046             OUTSTREAM << "Known issues: " << endl;
3047             OUTSTREAM << endl;
3048             OUTSTREAM << "If some symbols are not displaying, or displaying correctly, please first check you have a suitable font" << endl;
3049             OUTSTREAM << "selected, and a suitable codepage. See \"help codepage\" for details on that." << endl;
3050             OUTSTREAM << "When using the non-interactive mode (See \"help --non-interactive\"), piping or redirecting can be quite" << endl;
3051             OUTSTREAM << "problematic due to different encoding expectations between programs.  You can use \"-o outputfile\" with your " << endl;
3052             OUTSTREAM << "mega-*.bat commands to have the output written to a file in UTF-8, and then open it with a suitable editor." << endl;
3053         }
3054 #elif defined(_WIN32)
3055         else if (getFlag(&clflags,"unicode"))
3056         {
3057             OUTSTREAM << "A great effort has been done so as to have MEGAcmd support non-ASCII characters." << endl;
3058             OUTSTREAM << "However, it might still be consider in an experimantal state. You might experiment some issues." << endl;
3059             OUTSTREAM << "If that is the case, do not hesistate to contact us so as to improve our support." << endl;
3060             OUTSTREAM << endl;
3061             OUTSTREAM << "Known issues: " << endl;
3062             OUTSTREAM << endl;
3063             OUTSTREAM << "In Windows, when executing a client command in non-interactive mode or the interactive shell " << endl;
3064             OUTSTREAM << "Some symbols might not be printed. This is something expected, since your terminal (PowerShell/Command Prompt)" << endl;
3065             OUTSTREAM << "is not able to draw those symbols. However you can use the non-interactive mode to have the output " << endl;
3066             OUTSTREAM << "written into a file and open it with a graphic editor that supports them. The file will be UTF-8 encoded." << endl;
3067             OUTSTREAM << "To do that, use \"-o outputfile\" with your mega-*.bat commands. (See \"help --non-interactive\")." << endl;
3068             OUTSTREAM << "Please, restrain using \"> outputfile\" or piping the output into another command if you require unicode support" << endl;
3069             OUTSTREAM << "because for instance, when piping, your terminal does not treat the output as binary; " << endl;
3070             OUTSTREAM << "it will meddle with the encoding, resulting in unusable output." << endl;
3071             OUTSTREAM << endl;
3072             OUTSTREAM << "In the interactive shell, the library used for reading the inputs is not able to capture unicode inputs by default" << endl;
3073             OUTSTREAM << "There's a workaround to activate an alternative way to read input. You can activate it using \"unicode\" command. " << endl;
3074             OUTSTREAM << "However, if you do so, arrow keys and hotkeys combinations will be disabled. You can disable this input mode again. " << endl;
3075             OUTSTREAM << "See \"unicode --help\" for further info." << endl;
3076         }
3077 #endif
3078         else
3079         {
3080             OUTSTREAM << "Here is the list of available commands and their usage" << endl;
3081             OUTSTREAM << "Use \"help -f\" to get a brief description of the commands" << endl;
3082             OUTSTREAM << "You can get further help on a specific command with \"command --help\" " << endl;
3083             OUTSTREAM << "Alternatively, you can use \"help -ff\" to get a complete description of all commands" << endl;
3084             OUTSTREAM << "Use \"help --non-interactive\" to learn how to use MEGAcmd with scripts" << endl;
3085             OUTSTREAM << "Use \"help --upgrade\" to learn about the limitations and obtaining PRO accounts" << endl;
3086             OUTSTREAM << "Use \"help --paths\" to learn about paths and how to enter them" << endl;
3087 
3088             OUTSTREAM << endl << "Commands:" << endl;
3089 
3090             printAvailableCommands(getFlag(&clflags,"f"));
3091             OUTSTREAM << endl << "Verbosity: You can increase the amount of information given by any command by passing \"-v\" (\"-vv\", \"-vvv\", ...)" << endl;
3092 
3093             if (getBlocked())
3094             {
3095                 unsigned int width = getintOption(&cloptions, "client-width", getNumberOfCols(75));
3096                 if (width > 1 ) width--;
3097 
3098                 OUTSTRINGSTREAM os;
3099                 printCenteredContents(os, string("[BLOCKED]\n").append(sandboxCMD->getReasonblocked()).c_str(), width);
3100 
3101                 OUTSTREAM << os.str();
3102 
3103             }
3104         }
3105         return;
3106     }
3107 
3108     cmdexecuter->executecommand(words, &clflags, &cloptions);
3109 }
3110 
executeUpdater(bool * restartRequired,bool doNotInstall=false)3111 bool executeUpdater(bool *restartRequired, bool doNotInstall = false)
3112 {
3113     LOG_debug << "Executing updater..." ;
3114 #ifdef _WIN32
3115 
3116 #ifndef NDEBUG
3117     LPCWSTR szPath = TEXT("..\\MEGAcmdUpdater\\debug\\MEGAcmdUpdater.exe");
3118 #else
3119     TCHAR szPath[MAX_PATH];
3120 
3121     if (!SUCCEEDED(GetModuleFileName(NULL, szPath , MAX_PATH)))
3122     {
3123         LOG_err << "Couldnt get EXECUTABLE folder: " << wstring(szPath);
3124         setCurrentOutCode(MCMD_EUNEXPECTED);
3125         return false;
3126     }
3127 
3128     if (SUCCEEDED(PathRemoveFileSpec(szPath)))
3129     {
3130         if (!PathAppend(szPath,TEXT("MEGAcmdUpdater.exe")))
3131         {
3132             LOG_err << "Couldnt append MEGAcmdUpdater exec: " << wstring(szPath);
3133             setCurrentOutCode(MCMD_EUNEXPECTED);
3134             return false;
3135         }
3136     }
3137     else
3138     {
3139         LOG_err << "Couldnt remove file spec: " << wstring(szPath);
3140         setCurrentOutCode(MCMD_EUNEXPECTED);
3141         return false;
3142     }
3143 #endif
3144     STARTUPINFO si;
3145     PROCESS_INFORMATION pi;
3146     ZeroMemory( &si, sizeof(si) );
3147     ZeroMemory( &pi, sizeof(pi) );
3148 
3149     si.cb = sizeof(si);
3150     si.dwFlags = STARTF_USESHOWWINDOW;
3151     TCHAR szPathUpdaterCL[MAX_PATH+30];
3152     if (doNotInstall)
3153     {
3154         wsprintfW(szPathUpdaterCL,L"%ls --normal-update --do-not-install", szPath);
3155     }
3156     else
3157     {
3158         wsprintfW(szPathUpdaterCL,L"%ls --normal-update", szPath);
3159     }
3160     LOG_verbose << "Executing: " << wstring(szPathUpdaterCL);
3161     if (!CreateProcess( szPath,(LPWSTR) szPathUpdaterCL,NULL,NULL,TRUE,
3162                         0,
3163                         NULL,NULL,
3164                         &si,&pi) )
3165     {
3166         LOG_err << "Unable to execute: <" << wstring(szPath) << "> errno = : " << ERRNO;
3167         setCurrentOutCode(MCMD_EUNEXPECTED);
3168         return false;
3169     }
3170 
3171     WaitForSingleObject( pi.hProcess, INFINITE );
3172 
3173     DWORD exit_code;
3174     GetExitCodeProcess(pi.hProcess, &exit_code);
3175     *restartRequired = exit_code != 0;
3176 
3177     LOG_verbose << " The execution of Updater returns: " << exit_code;
3178 
3179     CloseHandle( pi.hProcess );
3180     CloseHandle( pi.hThread );
3181 
3182 #else
3183     pid_t pidupdater = fork();
3184 
3185     if ( pidupdater == 0 )
3186     {
3187         char * donotinstallstr = NULL;
3188         if (doNotInstall)
3189         {
3190             donotinstallstr = "--do-not-install";
3191         }
3192 
3193 #ifdef __MACH__
3194 #ifndef NDEBUG
3195         char * args[] = {"../../../../MEGAcmdUpdater/MEGAcmdUpdater.app/Contents/MacOS/MEGAcmdUpdater", "--normal-update", donotinstallstr, NULL};
3196 #else
3197         char * args[] = {"/Applications/MEGAcmd.app/Contents/MacOS/MEGAcmdUpdater", "--normal-update", donotinstallstr, NULL};
3198 #endif
3199 #else //linux don't use autoupdater: this is just for testing
3200 #ifndef NDEBUG
3201         char * args[] = {"../MEGAcmdUpdater/MEGAcmdUpdater", "--normal-update", donotinstallstr, NULL}; // notice: won't work after lcd
3202 #else
3203         char * args[] = {"mega-cmd-updater", "--normal-update", donotinstallstr, NULL};
3204 #endif
3205 #endif
3206 
3207         LOG_verbose << "Exec updater line: " << args[0] << " " << args[1] << " " << args[2];
3208 
3209         if (execvp(args[0], args) < 0)
3210         {
3211 
3212             LOG_err << " FAILED to initiate updater. errno = " << ERRNO;
3213         }
3214     }
3215 
3216     int status;
3217 
3218     waitpid(pidupdater, &status, 0);
3219 
3220     if ( WIFEXITED(status) )
3221     {
3222         int exit_code = WEXITSTATUS(status);
3223         LOG_debug << "Exit status of the updater was " << exit_code;
3224         *restartRequired = exit_code != 0;
3225 
3226     }
3227     else
3228     {
3229         LOG_err << " Unexpected error waiting for Updater. errno = " << ERRNO;
3230     }
3231 #endif
3232 
3233 
3234     if (*restartRequired && api)
3235     {
3236         std::unique_ptr<MegaCmdListener> megaCmdListener{new MegaCmdListener(api, NULL)};
3237         api->sendEvent(MCMD_EVENT_UPDATE_RESTART_ID,MCMD_EVENT_UPDATE_RESTART_MESSAGE, megaCmdListener.get());
3238         megaCmdListener->wait();
3239     }
3240 
3241     return true;
3242 }
3243 
restartServer()3244 bool restartServer()
3245 {
3246 #ifdef _WIN32
3247         LPWSTR szPathExecQuoted = GetCommandLineW();
3248         wstring wspathexec = wstring(szPathExecQuoted);
3249 
3250         if (wspathexec.at(0) == '"')
3251         {
3252             wspathexec = wspathexec.substr(1);
3253         }
3254 
3255         size_t pos = wspathexec.find(L"--wait-for");
3256         if (pos != string::npos)
3257         {
3258             wspathexec = wspathexec.substr(0,pos);
3259         }
3260 
3261         while (wspathexec.size() && ( wspathexec.at(wspathexec.size()-1) == '"' || wspathexec.at(wspathexec.size()-1) == ' ' ))
3262         {
3263             wspathexec = wspathexec.substr(0,wspathexec.size()-1);
3264         }
3265 
3266         LPWSTR szPathServerCommand = (LPWSTR) wspathexec.c_str();
3267         TCHAR szPathServer[MAX_PATH];
3268         if (!SUCCEEDED(GetModuleFileName(NULL, szPathServer , MAX_PATH)))
3269         {
3270             LOG_err << "Couldnt get EXECUTABLE folder: " << wstring(szPathServer);
3271             setCurrentOutCode(MCMD_EUNEXPECTED);
3272             return false;
3273         }
3274 
3275         LOG_debug << "Restarting the server : <" << wstring(szPathServerCommand) << ">";
3276 
3277         STARTUPINFO si;
3278         PROCESS_INFORMATION pi;
3279         ZeroMemory( &si, sizeof(si) );
3280         ZeroMemory( &pi, sizeof(pi) );
3281         si.cb = sizeof(si);
3282         si.dwFlags = STARTF_USESHOWWINDOW;
3283         TCHAR szPathServerCL[MAX_PATH+30];
3284         wsprintfW(szPathServerCL,L"%ls --wait-for %d", szPathServerCommand, GetCurrentProcessId());
3285         LOG_verbose  << "Executing: " << wstring(szPathServerCL);
3286         if (!CreateProcess( szPathServer,(LPWSTR) szPathServerCL,NULL,NULL,TRUE,
3287                             0,
3288                             NULL,NULL,
3289                             &si,&pi) )
3290         {
3291             LOG_debug << "Unable to execute: <" << wstring(szPathServerCL) << "> errno = : " << ERRNO;
3292             return false;
3293         }
3294 #else
3295     pid_t childid = fork();
3296     if ( childid ) //parent
3297     {
3298         char **argv = new char*[mcmdMainArgc+3];
3299         int i = 0, j = 0;
3300 
3301 #ifdef __linux__
3302         string executable = mcmdMainArgv[0];
3303         if (executable.find("/") != 0)
3304         {
3305             executable.insert(0, getCurrentExecPath()+"/");
3306         }
3307         argv[0]=(char *)executable.c_str();
3308         i++;
3309         j++;
3310 #endif
3311 
3312         for (;i < mcmdMainArgc; i++)
3313         {
3314             if ( (i+1) < mcmdMainArgc && !strcmp(mcmdMainArgv[i],"--wait-for"))
3315             {
3316                 i+=2;
3317             }
3318             else
3319             {
3320                 argv[j++]=mcmdMainArgv[i];
3321             }
3322         }
3323 
3324         argv[j++]="--wait-for";
3325         argv[j++]=(char*)SSTR(childid).c_str();
3326         argv[j++]=NULL;
3327 
3328         LOG_debug << "Restarting the server : <" << argv[0] << ">";
3329         execv(argv[0],argv);
3330     }
3331 #endif
3332 
3333     LOG_debug << "Server restarted, indicating the shell to restart also";
3334     setCurrentOutCode(MCMD_REQRESTART);
3335 
3336     string s = "restart";
3337     cm->informStateListeners(s);
3338 
3339     return true;
3340 }
3341 
isBareCommand(const char * l,const string & command)3342 bool isBareCommand(const char *l, const string &command)
3343 {
3344     string what(l);
3345     string xcommand = "X" + command;
3346     if (what == command || what == xcommand)
3347     {
3348         return true;
3349     }
3350     if (what.find(command+" ") != 0 && what.find(xcommand+" ") != 0 )
3351     {
3352         return false;
3353     }
3354 
3355    vector<string> words = getlistOfWords((char *)l, !getCurrentThreadIsCmdShell());
3356    for (int i = 1; i<words.size(); i++)
3357    {
3358        if (words[i].empty()) continue;
3359        if (words[i] == "--help") return false;
3360        if (words[i].find("--client-width") == 0) continue;
3361        if (words[i].find("--clientID") == 0) continue;
3362 
3363        return false;
3364    }
3365 
3366    return true;
3367 }
3368 
process_line(char * l)3369 static bool process_line(char* l)
3370 {
3371     switch (prompt)
3372     {
3373         case AREYOUSURETODELETE:
3374             if (!strcmp(l,"yes") || !strcmp(l,"YES") || !strcmp(l,"y") || !strcmp(l,"Y"))
3375             {
3376                 cmdexecuter->confirmDelete();
3377             }
3378             else if (!strcmp(l,"no") || !strcmp(l,"NO") || !strcmp(l,"n") || !strcmp(l,"N"))
3379             {
3380                 cmdexecuter->discardDelete();
3381             }
3382             else if (!strcmp(l,"All") || !strcmp(l,"ALL") || !strcmp(l,"a") || !strcmp(l,"A") || !strcmp(l,"all"))
3383             {
3384                 cmdexecuter->confirmDeleteAll();
3385             }
3386             else if (!strcmp(l,"None") || !strcmp(l,"NONE") || !strcmp(l,"none"))
3387             {
3388                 cmdexecuter->discardDeleteAll();
3389             }
3390             else
3391             {
3392                 //Do nth, ask again
3393                 OUTSTREAM << "Please enter [y]es/[n]o/[a]ll/none: " << flush;
3394             }
3395         break;
3396         case LOGINPASSWORD:
3397         {
3398             if (!strlen(l))
3399             {
3400                 break;
3401             }
3402             if (cmdexecuter->confirming)
3403             {
3404                 cmdexecuter->confirmWithPassword(l);
3405             }
3406             else if (cmdexecuter->confirmingcancel)
3407             {
3408                 cmdexecuter->confirmCancel(cmdexecuter->link.c_str(), l);
3409             }
3410             else
3411             {
3412                 cmdexecuter->loginWithPassword(l);
3413             }
3414 
3415             cmdexecuter->confirming = false;
3416             cmdexecuter->confirmingcancel = false;
3417 
3418             setprompt(COMMAND);
3419             break;
3420         }
3421         case NEWPASSWORD:
3422         {
3423             if (!strlen(l))
3424             {
3425                 break;
3426             }
3427             newpasswd = l;
3428             OUTSTREAM << endl;
3429             setprompt(PASSWORDCONFIRM);
3430         }
3431         break;
3432 
3433         case PASSWORDCONFIRM:
3434         {
3435             if (!strlen(l))
3436             {
3437                 break;
3438             }
3439             if (l != newpasswd)
3440             {
3441                 OUTSTREAM << endl << "New passwords differ, please try again" << endl;
3442             }
3443             else
3444             {
3445                 OUTSTREAM << endl;
3446                 if (!cmdexecuter->signingup)
3447                 {
3448                     cmdexecuter->changePassword(newpasswd.c_str());
3449                 }
3450                 else
3451                 {
3452                     cmdexecuter->signupWithPassword(l);
3453                     cmdexecuter->signingup = false;
3454                 }
3455             }
3456 
3457             setprompt(COMMAND);
3458             break;
3459         }
3460 
3461         case COMMAND:
3462         {
3463             if (!l || !strcmp(l, "q") || !strcmp(l, "quit") || !strcmp(l, "exit")
3464                 || ( (!strncmp(l, "quit ", strlen("quit ")) || !strncmp(l, "exit ", strlen("exit ")) ) && !strstr(l,"--help") )  )
3465             {
3466                 //                store_line(NULL);
3467 
3468                 if (strstr(l,"--wait-for-ongoing-petitions"))
3469                 {
3470                     int attempts=20; //give a while for ongoing petitions to end before killing the server
3471                     delete_finished_threads();
3472 
3473                     while(petitionThreads.size() > 1 && attempts--)
3474                     {
3475                         LOG_debug << "giving a little longer for ongoing petitions: " << petitionThreads.size();
3476                         sleepSeconds(20-attempts);
3477                         delete_finished_threads();
3478                     }
3479                 }
3480 
3481                 return true; // exit
3482             }
3483             else if (isBareCommand(l, "sendack"))
3484             {
3485                 string sack="ack";
3486                 cm->informStateListeners(sack);
3487                 break;
3488             }
3489 
3490 #if defined(_WIN32) || defined(__APPLE__)
3491             else if (isBareCommand(l, "update")) //if extra args are received, it'll be processed by executer
3492             {
3493                 string confirmationQuery("This might require restarting MEGAcmd. Are you sure to continue");
3494                 confirmationQuery+="? (Yes/No): ";
3495 
3496                 int confirmationResponse = askforConfirmation(confirmationQuery);
3497 
3498                 if (confirmationResponse != MCMDCONFIRM_YES && confirmationResponse != MCMDCONFIRM_ALL)
3499                 {
3500                     setCurrentOutCode(MCMD_INVALIDSTATE); // so as not to indicate already updated
3501                     return false;
3502                 }
3503                 bool restartRequired = false;
3504 
3505                 if (!executeUpdater(&restartRequired))
3506                 {
3507                     setCurrentOutCode(MCMD_INVALIDSTATE); // so as not to indicate already updated
3508                     return false;
3509                 }
3510 
3511                 if (restartRequired && restartServer())
3512                 {
3513                     OUTSTREAM << " " << endl;
3514 
3515                     int attempts=20; //give a while for ongoing petitions to end before killing the server
3516                     while(petitionThreads.size() > 1 && attempts--)
3517                     {
3518                         sleepSeconds(20-attempts);
3519                     }
3520                     return true;
3521                 }
3522                 else
3523                 {
3524                     OUTSTREAM << "Update is not required. You are in the last version. Further info: \"version --help\", \"update --help\"" << endl;
3525                     return false;
3526                 }
3527             }
3528 #endif
3529             executecommand(l);
3530             break;
3531         }
3532     }
3533     return false; //Do not exit
3534 }
3535 
doProcessLine(void * pointer)3536 void * doProcessLine(void *pointer)
3537 {
3538     CmdPetition *inf = (CmdPetition*)pointer;
3539 
3540     OUTSTRINGSTREAM s;
3541 
3542     setCurrentThreadLogLevel(MegaApi::LOG_LEVEL_ERROR);
3543     setCurrentOutCode(MCMD_OK);
3544     setCurrentPetition(inf);
3545     LoggedStreamPartialOutputs ls(cm, inf);
3546     setCurrentThreadOutStream(&ls);
3547 
3548     bool isInteractive = false;
3549 
3550     if (inf->getLine() && *(inf->getLine())=='X')
3551     {
3552         setCurrentThreadIsCmdShell(true);
3553         char * aux = inf->line;
3554         inf->line=strdup(inf->line+1);
3555         free(aux);
3556         isInteractive = true;
3557     }
3558     else
3559     {
3560         setCurrentThreadIsCmdShell(false);
3561     }
3562 
3563     LOG_verbose << " Processing " << inf->line << " in thread: " << MegaThread::currentThreadId() << " " << cm->get_petition_details(inf);
3564 
3565     doExit = process_line(inf->getLine());
3566 
3567     if (doExit)
3568     {
3569         stopcheckingforUpdaters = true;
3570         LOG_verbose << " Exit registered upon process_line: " ;
3571     }
3572 
3573     LOG_verbose << " Procesed " << inf->line << " in thread: " << MegaThread::currentThreadId() << " " << cm->get_petition_details(inf);
3574 
3575     MegaThread * petitionThread = inf->getPetitionThread();
3576 
3577     if (inf->clientID == -3) //self client: no actual client
3578     {
3579         delete inf;//simply delete the pointer
3580     }
3581     else
3582     {
3583         cm->returnAndClosePetition(inf, &s, getCurrentOutCode());
3584     }
3585 
3586     semaphoreClients.release();
3587 
3588     if (doExit && (!interactiveThread() || getCurrentThreadIsCmdShell() ))
3589     {
3590         cm->stopWaiting();
3591     }
3592 
3593     mutexEndedPetitionThreads.lock();
3594     endedPetitionThreads.push_back(petitionThread);
3595     mutexEndedPetitionThreads.unlock();
3596 
3597     return NULL;
3598 }
3599 
3600 
askforConfirmation(string message)3601 int askforConfirmation(string message)
3602 {
3603     CmdPetition *inf = getCurrentPetition();
3604     if (inf)
3605     {
3606         return cm->getConfirmation(inf,message);
3607     }
3608     else
3609     {
3610         LOG_err << "Unable to get current petition to ask for confirmation";
3611     }
3612 
3613     return MCMDCONFIRM_NO;
3614 }
3615 
askforUserResponse(string message)3616 string askforUserResponse(string message)
3617 {
3618     CmdPetition *inf = getCurrentPetition();
3619     if (inf)
3620     {
3621         return cm->getUserResponse(inf,message);
3622     }
3623     else
3624     {
3625         LOG_err << "Unable to get current petition to ask for confirmation";
3626     }
3627 
3628     return string("NOCURRENPETITION");
3629 }
3630 
3631 
3632 
delete_finished_threads()3633 void delete_finished_threads()
3634 {
3635     mutexEndedPetitionThreads.lock();
3636     for (std::vector<MegaThread *>::iterator it = endedPetitionThreads.begin(); it != endedPetitionThreads.end(); )
3637     {
3638         MegaThread *mt = (MegaThread*)*it;
3639         for (std::vector<MegaThread *>::iterator it2 = petitionThreads.begin(); it2 != petitionThreads.end(); )
3640         {
3641             if (mt == (MegaThread*)*it2)
3642             {
3643                 it2 = petitionThreads.erase(it2);
3644             }
3645             else
3646             {
3647                 ++it2;
3648             }
3649         }
3650 
3651         mt->join();
3652         delete mt;
3653         it = endedPetitionThreads.erase(it);
3654     }
3655     mutexEndedPetitionThreads.unlock();
3656 }
3657 
3658 void processCommandInPetitionQueues(CmdPetition *inf);
3659 void processCommandLinePetitionQueues(std::string what);
3660 
3661 bool waitForRestartSignal = false;
3662 #ifdef __linux__
3663 std::mutex mtxcondvar;
3664 std::condition_variable condVarRestart;
3665 bool condVarRestartBool = false;
3666 string appToWaitForSignal;
3667 
LinuxSignalHandler(int signum)3668 void LinuxSignalHandler(int signum)
3669 {
3670     if (signum == SIGUSR2)
3671     {
3672         std::unique_lock<std::mutex> lock(mtxcondvar);
3673         condVarRestart.notify_one();
3674         condVarRestartBool = true;
3675     }
3676     else if (signum == SIGUSR1)
3677     {
3678         if (!waitForRestartSignal)
3679         {
3680             waitForRestartSignal = true;
3681             LOG_debug << "Preparing MEGAcmd to restart: ";
3682             stopcheckingforUpdaters = true;
3683             doExit = true;
3684         }
3685     }
3686 }
3687 #endif
3688 
finalize(bool waitForRestartSignal)3689 void finalize(bool waitForRestartSignal)
3690 {
3691     static bool alreadyfinalized = false;
3692     if (alreadyfinalized)
3693         return;
3694     alreadyfinalized = true;
3695     LOG_info << "closing application ...";
3696     delete_finished_threads();
3697     if (!consoleFailed)
3698     {
3699         delete console;
3700     }
3701 
3702     delete megaCmdMegaListener;
3703     if (threadRetryConnections)
3704     {
3705         threadRetryConnections->join();
3706     }
3707     delete threadRetryConnections;
3708     delete api;
3709 
3710     while (!apiFolders.empty())
3711     {
3712         delete apiFolders.front();
3713         apiFolders.pop();
3714     }
3715 
3716     for (std::vector< MegaApi * >::iterator it = occupiedapiFolders.begin(); it != occupiedapiFolders.end(); ++it)
3717     {
3718         delete ( *it );
3719     }
3720 
3721     occupiedapiFolders.clear();
3722 
3723     delete megaCmdGlobalListener;
3724     delete cmdexecuter;
3725 
3726 #ifdef __linux__
3727     if (waitForRestartSignal)
3728     {
3729         LOG_debug << "Waiting for signal to restart MEGAcmd ... ";
3730         std::unique_lock<std::mutex> lock(mtxcondvar);
3731         if (condVarRestartBool || condVarRestart.wait_for(lock, std::chrono::minutes(30)) == std::cv_status::no_timeout )
3732         {
3733             restartServer();
3734         }
3735         else
3736         {
3737             LOG_err << "Former server still alive after waiting. Not restarted.";
3738         }
3739     }
3740 #endif
3741     delete cm; //this needs to go after restartServer();
3742     LOG_debug << "resources have been cleaned ...";
3743     delete loggerCMD;
3744     ConfigurationManager::unlockExecution();
3745     ConfigurationManager::unloadConfiguration();
3746 
3747 }
finalize()3748 void finalize()
3749 {
3750     finalize(false);
3751 }
3752 
3753 int currentclientID = 1;
3754 
retryConnections(void * pointer)3755 void * retryConnections(void *pointer)
3756 {
3757     while(!doExit)
3758     {
3759         LOG_verbose << "Calling recurrent retryPendingConnections";
3760         api->retryPendingConnections();
3761 
3762         int count = 100;
3763         while (!doExit && --count)
3764         {
3765             sleepMilliSeconds(300);
3766         }
3767     }
3768     return NULL;
3769 }
3770 
3771 
startcheckingForUpdates()3772 void startcheckingForUpdates()
3773 {
3774     ConfigurationManager::savePropertyValue("autoupdate", 1);
3775 
3776     if (!alreadyCheckingForUpdates)
3777     {
3778         alreadyCheckingForUpdates = true;
3779         LOG_info << "Starting autoupdate check mechanism";
3780         MegaThread *checkupdatesThread = new MegaThread();
3781         checkupdatesThread->start(checkForUpdates,checkupdatesThread);
3782     }
3783 }
3784 
stopcheckingForUpdates()3785 void stopcheckingForUpdates()
3786 {
3787     ConfigurationManager::savePropertyValue("autoupdate", 0);
3788 
3789     stopcheckingforUpdaters = true;
3790 }
3791 
checkForUpdates(void * param)3792 void* checkForUpdates(void *param)
3793 {
3794     stopcheckingforUpdaters = false;
3795     LOG_debug << "Initiating recurrent checkForUpdates";
3796 
3797     int secstosleep = 60;
3798     while (secstosleep>0 && !stopcheckingforUpdaters)
3799     {
3800         sleepSeconds(2);
3801         secstosleep-=2;
3802     }
3803 
3804     while (!doExit && !stopcheckingforUpdaters)
3805     {
3806         bool restartRequired = false;
3807         if (!executeUpdater(&restartRequired, true)) //only download & check
3808         {
3809             LOG_err << " Failed to execute updater";
3810         }
3811         else if (restartRequired)
3812         {
3813             LOG_info << " There is a pending update. Will be applied in a few seconds";
3814 
3815             broadcastMessage("A new update has been downloaded. It will be performed in 60 seconds");
3816             int secstosleep = 57;
3817             while (secstosleep>0 && !stopcheckingforUpdaters)
3818             {
3819                 sleepSeconds(2);
3820                 secstosleep-=2;
3821             }
3822             if (stopcheckingforUpdaters) break;
3823             broadcastMessage("  Executing update in 3");
3824             sleepSeconds(1);
3825             if (stopcheckingforUpdaters) break;
3826             broadcastMessage("  Executing update in 2");
3827             sleepSeconds(1);
3828             if (stopcheckingforUpdaters) break;
3829             broadcastMessage("  Executing update in 1");
3830             sleepSeconds(1);
3831             if (stopcheckingforUpdaters) break;
3832 
3833             while(petitionThreads.size() && !stopcheckingforUpdaters)
3834             {
3835                 LOG_fatal << " waiting for petitions to end to initiate upload " << petitionThreads.size() << petitionThreads.at(0);
3836                 sleepSeconds(2);
3837                 delete_finished_threads();
3838             }
3839 
3840             if (stopcheckingforUpdaters) break;
3841 
3842             std::unique_ptr<MegaCmdListener> megaCmdListener{new MegaCmdListener(api, NULL)};
3843             api->sendEvent(MCMD_EVENT_UPDATE_START_ID,MCMD_EVENT_UPDATE_START_MESSAGE, megaCmdListener.get());
3844             megaCmdListener->wait();
3845 
3846             broadcastMessage("  Executing update    !");
3847             LOG_info << " Applying update";
3848             executeUpdater(&restartRequired);
3849         }
3850         else
3851         {
3852             LOG_verbose << " There is no pending update";
3853         }
3854 
3855         if (stopcheckingforUpdaters) break;
3856         if (restartRequired && restartServer())
3857         {
3858             int attempts=20; //give a while for ingoin petitions to end before killing the server
3859             while(petitionThreads.size() && attempts--)
3860             {
3861                 sleepSeconds(20-attempts);
3862                 delete_finished_threads();
3863             }
3864 
3865             doExit = true;
3866             cm->stopWaiting();
3867             break;
3868         }
3869 
3870         int secstosleep = 7200;
3871         while (secstosleep>0 && !stopcheckingforUpdaters)
3872         {
3873             sleepSeconds(2);
3874             secstosleep-=2;
3875         }
3876     }
3877 
3878     alreadyCheckingForUpdates = false;
3879 
3880     delete (MegaThread *)param;
3881     return NULL;
3882 }
3883 
processCommandInPetitionQueues(CmdPetition * inf)3884 void processCommandInPetitionQueues(CmdPetition *inf)
3885 {
3886     semaphoreClients.wait();
3887 
3888     //append new one
3889     MegaThread * petitionThread = new MegaThread();
3890 
3891     petitionThreads.push_back(petitionThread);
3892     inf->setPetitionThread(petitionThread);
3893 
3894     LOG_verbose << "starting processing: <" << inf->line << ">";
3895 
3896     petitionThread->start(doProcessLine, (void*)inf);
3897 }
3898 
processCommandLinePetitionQueues(std::string what)3899 void processCommandLinePetitionQueues(std::string what)
3900 {
3901     CmdPetition *inf = new CmdPetition();
3902     inf->line = strdup(what.c_str());
3903     inf->clientDisconnected = true; //There's no actual client
3904     inf->clientID = -3;
3905     processCommandInPetitionQueues(inf);
3906 }
3907 
3908 // main loop
megacmd()3909 void megacmd()
3910 {
3911     threadRetryConnections = new MegaThread();
3912     threadRetryConnections->start(retryConnections, NULL);
3913 
3914     LOG_info << "Listening to petitions ... ";
3915 
3916     for (;; )
3917     {
3918         cm->waitForPetition();
3919 
3920         api->retryPendingConnections();
3921 
3922         if (doExit)
3923         {
3924             LOG_verbose << "closing after wait ..." ;
3925             return;
3926         }
3927 
3928         if (cm->receivedPetition())
3929         {
3930 
3931             LOG_verbose << "Client connected ";
3932 
3933             CmdPetition *inf = cm->getPetition();
3934 
3935             LOG_verbose << "petition registered: " << inf->line;
3936 
3937             delete_finished_threads();
3938 
3939             if (!inf || !strcmp(inf->getLine(),"ERROR"))
3940             {
3941                 LOG_warn << "Petition couldn't be registered. Dismissing it.";
3942                 delete inf;
3943             }
3944             // if state register petition
3945             else  if (!strncmp(inf->getLine(),"registerstatelistener",strlen("registerstatelistener")) ||
3946                       !strncmp(inf->getLine(),"Xregisterstatelistener",strlen("Xregisterstatelistener")))
3947             {
3948 
3949                 cm->registerStateListener(inf);
3950 
3951                 // communicate client ID
3952                 string s = "clientID:";
3953                 s+=SSTR(currentclientID);
3954                 s+=(char)0x1F;
3955                 inf->clientID = currentclientID;
3956                 currentclientID++;
3957                 cm->informStateListener(inf,s);
3958 
3959 #if defined(_WIN32) || defined(__APPLE__)
3960                 string message="";
3961                 ostringstream os;
3962                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
3963                 api->getLastAvailableVersion("BdARkQSQ",megaCmdListener);
3964                 if (!megaCmdListener->trywait(2000))
3965                 {
3966                     if (!megaCmdListener->getError())
3967                     {
3968                         LOG_fatal << "No MegaError at getLastAvailableVersion: ";
3969                     }
3970                     else if (megaCmdListener->getError()->getErrorCode() != MegaError::API_OK)
3971                     {
3972                         LOG_debug << "Couldn't get latests available version: " << megaCmdListener->getError()->getErrorString();
3973                     }
3974                     else
3975                     {
3976                         if (megaCmdListener->getRequest()->getNumber() != MEGACMD_CODE_VERSION)
3977                         {
3978                             os << "---------------------------------------------------------------------" << endl;
3979                             os << "--        There is a new version available of megacmd: " << setw(12) << left << megaCmdListener->getRequest()->getName() << "--" << endl;
3980                             os << "--        Please, update this one: See \"update --help\".          --" << endl;
3981                             os << "--        Or download the latest from https://mega.nz/cmd          --" << endl;
3982 #if defined(__APPLE__)
3983                             os << "--        Before installing enter \"exit\" to close MEGAcmd          --" << endl;
3984 #endif
3985                             os << "---------------------------------------------------------------------" << endl;
3986                         }
3987                     }
3988                     delete megaCmdListener;
3989                 }
3990                 else
3991                 {
3992                     LOG_debug << "Couldn't get latests available version (petition timed out)";
3993 
3994                     api->removeRequestListener(megaCmdListener);
3995                     delete megaCmdListener;
3996                 }
3997 
3998                 int autoupdate = ConfigurationManager::getConfigurationValue("autoupdate", -1);
3999                 if (autoupdate == -1 || autoupdate == 2)
4000                 {
4001                     os << "ENABLING AUTOUPDATE BY DEFAULT. You can disable it with \"update --auto=off\"" << endl;
4002                     autoupdate = 1;
4003                 }
4004 
4005                 if (autoupdate == 1)
4006                 {
4007                     startcheckingForUpdates();
4008                 }
4009                 message=os.str();
4010 
4011 
4012                 if (message.size())
4013                 {
4014                     s += "message:";
4015                     s+=message;
4016                     s+=(char)0x1F;
4017                 }
4018 #endif
4019 
4020                 bool isOSdeprecated = false;
4021 #ifdef MEGACMD_DEPRECATED_OS
4022                 isOSdeprecated = true;
4023 #endif
4024 
4025 
4026 #ifdef _WIN32
4027                 OSVERSIONINFOEX osvi;
4028                 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
4029                 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
4030                 if (GetVersionEx((OSVERSIONINFO*)&osvi) && osvi.dwMajorVersion < 6)
4031                 {
4032                     isOSdeprecated = true;
4033                 }
4034 #elif defined(__APPLE__)
4035                 char releaseStr[256];
4036                 size_t size = sizeof(releaseStr);
4037                 if (!sysctlbyname("kern.osrelease", releaseStr, &size, NULL, 0)  && size > 0)
4038                 {
4039                     if (strchr(releaseStr,'.'))
4040                     {
4041                         char *token = strtok(releaseStr, ".");
4042                         if (token)
4043                         {
4044                             errno = 0;
4045                             char *endPtr = NULL;
4046                             long majorVersion = strtol(token, &endPtr, 10);
4047                             if (endPtr != token && errno != ERANGE && majorVersion >= INT_MIN && majorVersion <= INT_MAX)
4048                             {
4049                                 if((int)majorVersion < 13) // Older versions from 10.9 (mavericks)
4050                                 {
4051                                     isOSdeprecated = true;
4052                                 }
4053                             }
4054                         }
4055                     }
4056                 }
4057 #endif
4058                 if (isOSdeprecated)
4059                 {
4060                     s += "message:";
4061                     s += "Your Operative System is too old.\n";
4062                     s += "You might not receive new updates for this application.\n";
4063                     s += "We strongly recommend you to update to a new version.\n";
4064                     s+=(char)0x1F;
4065                 }
4066 
4067                 if (sandboxCMD->storageStatus != MegaApi::STORAGE_STATE_GREEN)
4068                 {
4069                     s += "message:";
4070 
4071                     if (sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_PAYWALL)
4072                     {
4073                         std::unique_ptr<char[]> myEmail(api->getMyEmail());
4074                         std::unique_ptr<MegaIntegerList> warningsList(api->getOverquotaWarningsTs());
4075                         s += "We have contacted you by email to " + string(myEmail.get()) + " on ";
4076                         s += getReadableTime(warningsList->get(0),"%b %e %Y");
4077                         if (warningsList->size() > 1)
4078                         {
4079                             for (int i = 1; i < warningsList->size() - 1; i++)
4080                             {
4081                                 s += ", " + getReadableTime(warningsList->get(i),"%b %e %Y");
4082                             }
4083                             s += " and " + getReadableTime(warningsList->get(warningsList->size() - 1),"%b %e %Y");
4084                         }
4085                         std::unique_ptr<MegaNode> rootNode(api->getRootNode());
4086                         long long totalFiles = 0;
4087                         long long totalFolders = 0;
4088                         getNumFolderFiles(rootNode.get(),api,&totalFiles,&totalFolders);
4089                         s += ", but you still have " + std::to_string(totalFiles) + " files taking up " + sizeToText(sandboxCMD->receivedStorageSum);
4090                         s += " in your MEGA account, which requires you to upgrade your account.\n\n";
4091                         long long daysLeft = (api->getOverquotaDeadlineTs() - m_time(NULL)) / 86400;
4092                         if (daysLeft > 0)
4093                         {
4094                              s += "You have " + std::to_string(daysLeft) + " days left to upgrade. ";
4095                              s += "After that, your data is subject to deletion.\n";
4096                         }
4097                         else
4098                         {
4099                              s += "You must act immediately to save your data. From now on, your data is subject to deletion.\n";
4100                         }
4101                     }
4102                     else if (sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_RED)
4103                     {
4104                         s += "You have exeeded your available storage.\n";
4105                         s += "You can change your account plan to increase your quota limit.\n";
4106                     }
4107                     else
4108                     {
4109                         s += "You are running out of available storage.\n";
4110                         s += "You can change your account plan to increase your quota limit.\n";
4111                     }
4112                     s += "See \"help --upgrade\" for further details.\n";
4113                     s += (char)0x1F;
4114                 }
4115 
4116                 // if server resuming session, lets give him a very litle while before sending greeting message to the early clients
4117                 // (to aovid "Resuming session..." being printed fast resumed session)
4118                 while (getloginInAtStartup() && ((m_time(nullptr) - timeLoginStarted() < RESUME_SESSION_TIMEOUT * 0.3)))
4119                 {
4120                     sleepMilliSeconds(300);
4121                 }
4122 
4123                 {
4124                     std::lock_guard<std::mutex> g(greetingsmsgsMutex);
4125 
4126                     while(greetingsFirstClientMsgs.size())
4127                     {
4128                         cm->informStateListener(inf,greetingsFirstClientMsgs.front().append(1, (char)0x1F));
4129                         greetingsFirstClientMsgs.pop_front();
4130                     }
4131 
4132                     for (auto m: greetingsAllClientMsgs)
4133                     {
4134                         cm->informStateListener(inf,m.append(1, (char)0x1F));
4135                     }
4136                 }
4137 
4138                 // if server resuming session, lets give him a litle while before returning a prompt to the early clients
4139                 // This will block the server from responging any commands in the meantime, but that assumable, it will only happen
4140                 // the first time the server is initiated.
4141                 while (getloginInAtStartup() && ((m_time(nullptr) - timeLoginStarted() < RESUME_SESSION_TIMEOUT * 0.7)))
4142                 {
4143                     sleepMilliSeconds(300);
4144                 }
4145 
4146                 // communicate status info
4147                 s+= "prompt:";
4148                 s+=dynamicprompt;
4149                 s+=(char)0x1F;
4150 
4151                 if (!sandboxCMD->getReasonblocked().size())
4152                 {
4153                     cmdexecuter->checkAndInformPSA(inf);
4154                 }
4155 
4156                 cm->informStateListener(inf,s);
4157             }
4158             else
4159             { // normal petition
4160                 processCommandInPetitionQueues(inf);
4161             }
4162         }
4163     }
4164 }
4165 
4166 class NullBuffer : public std::streambuf
4167 {
4168 public:
overflow(int c)4169     int overflow(int c)
4170     {
4171         return c;
4172     }
4173 };
4174 
printWelcomeMsg()4175 void printWelcomeMsg()
4176 {
4177     unsigned int width = getNumberOfCols(75);
4178 
4179 #ifdef _WIN32
4180         width--;
4181 #endif
4182 
4183     COUT << endl;
4184     COUT << ".";
4185     for (unsigned int i = 0; i < width; i++)
4186         COUT << "=" ;
4187     COUT << ".";
4188     COUT << endl;
4189     printCenteredLine(" __  __ _____ ____    _                      _ ",width);
4190     printCenteredLine("|  \\/  | ___|/ ___|  / \\   ___ _ __ ___   __| |",width);
4191     printCenteredLine("| |\\/| | \\  / |  _  / _ \\ / __| '_ ` _ \\ / _` |",width);
4192     printCenteredLine("| |  | | /__\\ |_| |/ ___ \\ (__| | | | | | (_| |",width);
4193     printCenteredLine("|_|  |_|____|\\____/_/   \\_\\___|_| |_| |_|\\__,_|",width);
4194 
4195     COUT << "|";
4196     for (unsigned int i = 0; i < width; i++)
4197         COUT << " " ;
4198     COUT << "|";
4199     COUT << endl;
4200     printCenteredLine("SERVER",width);
4201 
4202     COUT << "`";
4203     for (unsigned int i = 0; i < width; i++)
4204         COUT << "=" ;
4205     COUT << "´";
4206     COUT << endl;
4207 
4208 }
4209 
4210 #ifdef __MACH__
4211 
4212 
enableSetuidBit()4213 bool enableSetuidBit()
4214 {
4215     char *response = runWithRootPrivileges("do shell script \"chown root /Applications/MEGAcmd.app/Contents/MacOS/MEGAcmdLoader && chmod 4755 /Applications/MEGAcmd.app/Contents/MacOS/MEGAcmdLoader && echo true\"");
4216     if (!response)
4217     {
4218         return NULL;
4219     }
4220     bool result = strlen(response) >= 4 && !strncmp(response, "true", 4);
4221     delete response;
4222     return result;
4223 }
4224 
4225 
initializeMacOSStuff(int argc,char * argv[])4226 void initializeMacOSStuff(int argc, char* argv[])
4227 {
4228 #ifndef NDEBUG
4229         return;
4230 #endif
4231 
4232     int fd = -1;
4233     if (argc)
4234     {
4235         long int value = strtol(argv[argc-1], NULL, 10);
4236         if (value > 0 && value < INT_MAX)
4237         {
4238             fd = value;
4239         }
4240     }
4241 
4242     if (fd < 0)
4243     {
4244         if (!enableSetuidBit())
4245         {
4246             ::exit(0);
4247         }
4248 
4249         //Reboot
4250         if (fork() )
4251         {
4252             execv("/Applications/MEGAcmd.app/Contents/MacOS/MEGAcmdLoader",argv);
4253         }
4254         sleep(10);
4255         ::exit(0);
4256     }
4257 }
4258 
4259 #endif
4260 
getLocaleCode()4261 string getLocaleCode()
4262 {
4263 #if defined(_WIN32) && defined(LOCALE_SISO639LANGNAME)
4264     LCID lcidLocaleId;
4265     LCTYPE lctyLocaleInfo;
4266     PWSTR pstr;
4267     INT iBuffSize;
4268 
4269     lcidLocaleId = LOCALE_USER_DEFAULT;
4270     lctyLocaleInfo = LOCALE_SISO639LANGNAME;
4271 
4272     // Determine the size
4273     iBuffSize = GetLocaleInfo( lcidLocaleId, lctyLocaleInfo, NULL, 0 );
4274 
4275     if(iBuffSize > 0)
4276     {
4277         pstr = (WCHAR *) malloc( iBuffSize * sizeof(WCHAR) );
4278         if(pstr != NULL)
4279         {
4280             if(GetLocaleInfoW( lcidLocaleId, lctyLocaleInfo, pstr, iBuffSize ))
4281             {
4282                 string toret;
4283                 std::wstring ws(pstr);
4284                 localwtostring(&ws,&toret);
4285                 free(pstr); //free locale info string
4286                 return toret;
4287             }
4288             free(pstr); //free locale info string
4289         }
4290     }
4291 
4292 #else
4293 
4294     try
4295      {
4296         locale l("");
4297 
4298         string ls = l.name();
4299         size_t posequal = ls.find("=");
4300         size_t possemicolon = ls.find_first_of(";.");
4301 
4302         if (posequal != string::npos && possemicolon != string::npos && posequal < possemicolon)
4303         {
4304             return ls.substr(posequal+1,possemicolon-posequal-1);
4305         }
4306      }
4307      catch (const std::exception& e)
4308      {
4309 #ifndef __MACH__
4310         std::cerr << "Warning: unable to get locale " << std::endl;
4311 #endif
4312      }
4313 
4314 #endif
4315     return string();
4316 
4317 }
4318 
runningInBackground()4319 bool runningInBackground()
4320 {
4321 #ifndef _WIN32
4322     pid_t fg = tcgetpgrp(STDIN_FILENO);
4323     if(fg == -1) {
4324         // Piped:
4325         return false;
4326     }  else if (fg == getpgrp()) {
4327         // foreground
4328         return false;
4329     } else {
4330         // background
4331         return true;
4332     }
4333 #endif
4334     return false;
4335 }
4336 
4337 #ifndef MEGACMD_USERAGENT_SUFFIX
4338 #define MEGACMD_USERAGENT_SUFFIX
4339 #define MEGACMD_STRINGIZE(x)
4340 #else
4341 #define MEGACMD_STRINGIZE2(x) "-" #x
4342 #define MEGACMD_STRINGIZE(x) MEGACMD_STRINGIZE2(x)
4343 #endif
4344 
extractarg(vector<const char * > & args,const char * what)4345 bool extractarg(vector<const char*>& args, const char *what)
4346 {
4347     for (int i = int(args.size()); i--; )
4348     {
4349         if (!strcmp(args[i], what))
4350         {
4351             args.erase(args.begin() + i);
4352             return true;
4353         }
4354     }
4355     return false;
4356 }
4357 
extractargparam(vector<const char * > & args,const char * what,std::string & param)4358 bool extractargparam(vector<const char*>& args, const char *what, std::string& param)
4359 {
4360     for (int i = int(args.size()) - 1; --i >= 0; )
4361     {
4362         if (!strcmp(args[i], what) && args.size() > i)
4363         {
4364             param = args[i + 1];
4365             args.erase(args.begin() + i, args.begin() + i + 2);
4366             return true;
4367         }
4368     }
4369     return false;
4370 }
4371 
4372 
4373 #ifndef _WIN32
4374 #include <sys/wait.h>
is_pid_running(pid_t pid)4375 bool is_pid_running(pid_t pid) {
4376 
4377     while(waitpid(-1, 0, WNOHANG) > 0) {
4378         // Wait for defunct....
4379     }
4380 
4381     if (0 == kill(pid, 0))
4382         return 1; // Process exists
4383 
4384     return 0;
4385 }
4386 #endif
4387 
4388 #ifdef _WIN32
getCurrentSid()4389 LPTSTR getCurrentSid()
4390 {
4391     HANDLE hTok = NULL;
4392     LPBYTE buf = NULL;
4393     DWORD  dwSize = 0;
4394     LPTSTR stringSID = NULL;
4395     if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hTok))
4396     {
4397         GetTokenInformation(hTok, TokenUser, NULL, 0, &dwSize);
4398         if (dwSize)
4399         {
4400             buf = (LPBYTE)LocalAlloc(LPTR, dwSize);
4401             if (GetTokenInformation(hTok, TokenUser, buf, dwSize, &dwSize))
4402             {
4403                 ConvertSidToStringSid(((PTOKEN_USER)buf)->User.Sid, &stringSID);
4404             }
4405             LocalFree(buf);
4406         }
4407         CloseHandle(hTok);
4408     }
4409     return stringSID;
4410 }
4411 #endif
4412 
registerUpdater()4413 bool registerUpdater()
4414 {
4415 #ifdef _WIN32
4416     ITaskService *pService = NULL;
4417     ITaskFolder *pRootFolder = NULL;
4418     ITaskFolder *pMEGAFolder = NULL;
4419     ITaskDefinition *pTask = NULL;
4420     IRegistrationInfo *pRegInfo = NULL;
4421     IPrincipal *pPrincipal = NULL;
4422     ITaskSettings *pSettings = NULL;
4423     IIdleSettings *pIdleSettings = NULL;
4424     ITriggerCollection *pTriggerCollection = NULL;
4425     ITrigger *pTrigger = NULL;
4426     IDailyTrigger *pCalendarTrigger = NULL;
4427     IRepetitionPattern *pRepetitionPattern = NULL;
4428     IActionCollection *pActionCollection = NULL;
4429     IAction *pAction = NULL;
4430     IExecAction *pExecAction = NULL;
4431     IRegisteredTask *pRegisteredTask = NULL;
4432     time_t currentTime;
4433     struct tm* currentTimeInfo;
4434     WCHAR currentTimeString[128];
4435     _bstr_t taskBaseName = L"MEGAcmd Update Task ";
4436     LPTSTR stringSID = NULL;
4437     bool success = false;
4438 
4439     stringSID = getCurrentSid();
4440     if (!stringSID)
4441     {
4442         MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Unable to get the current SID");
4443         return false;
4444     }
4445 
4446     time(&currentTime);
4447     currentTimeInfo = localtime(&currentTime);
4448     wcsftime(currentTimeString, 128,  L"%Y-%m-%dT%H:%M:%S", currentTimeInfo);
4449     _bstr_t taskName = taskBaseName + stringSID;
4450     _bstr_t userId = stringSID;
4451     LocalFree(stringSID);
4452 
4453     TCHAR MEGAcmdUpdaterPath[MAX_PATH];
4454 
4455     if (!SUCCEEDED(GetModuleFileName(NULL, MEGAcmdUpdaterPath , MAX_PATH)))
4456     {
4457         LOG_err << "Couldnt get EXECUTABLE folder: " << wstring(MEGAcmdUpdaterPath);
4458         return false;
4459     }
4460 
4461     if (SUCCEEDED(PathRemoveFileSpec(MEGAcmdUpdaterPath)))
4462     {
4463         if (!PathAppend(MEGAcmdUpdaterPath,TEXT("MEGAcmdUpdater.exe")))
4464         {
4465             LOG_err << "Couldnt append MEGAcmdUpdater exec: " << wstring(MEGAcmdUpdaterPath);
4466             return false;
4467         }
4468     }
4469     else
4470     {
4471         LOG_err << "Couldnt remove file spec: " << wstring(MEGAcmdUpdaterPath);
4472         return false;
4473     }
4474 
4475     CoInitializeEx(NULL, COINIT_MULTITHREADED);
4476 
4477     if (SUCCEEDED(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL))
4478             && SUCCEEDED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService))
4479             && SUCCEEDED(pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))
4480             && SUCCEEDED(pService->GetFolder(_bstr_t( L"\\"), &pRootFolder)))
4481     {
4482         if (pRootFolder->CreateFolder(_bstr_t(L"MEGA"), _variant_t(L""), &pMEGAFolder) == 0x800700b7)
4483         {
4484             pRootFolder->GetFolder(_bstr_t(L"MEGA"), &pMEGAFolder);
4485         }
4486 
4487         if (pMEGAFolder
4488                 && SUCCEEDED(pService->NewTask(0, &pTask))
4489                 && SUCCEEDED(pTask->get_RegistrationInfo(&pRegInfo))
4490                 && SUCCEEDED(pRegInfo->put_Author(_bstr_t(L"MEGA Limited")))
4491                 && SUCCEEDED(pTask->get_Principal(&pPrincipal))
4492                 && SUCCEEDED(pPrincipal->put_Id(_bstr_t(L"Principal1")))
4493                 && SUCCEEDED(pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN))
4494                 && SUCCEEDED(pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA))
4495                 && SUCCEEDED(pPrincipal->put_UserId(userId))
4496                 && SUCCEEDED(pTask->get_Settings(&pSettings))
4497                 && SUCCEEDED(pSettings->put_StartWhenAvailable(VARIANT_TRUE))
4498                 && SUCCEEDED(pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE))
4499                 && SUCCEEDED(pSettings->get_IdleSettings(&pIdleSettings))
4500                 && SUCCEEDED(pIdleSettings->put_StopOnIdleEnd(VARIANT_FALSE))
4501                 && SUCCEEDED(pIdleSettings->put_RestartOnIdle(VARIANT_FALSE))
4502                 && SUCCEEDED(pIdleSettings->put_WaitTimeout(_bstr_t()))
4503                 && SUCCEEDED(pIdleSettings->put_IdleDuration(_bstr_t()))
4504                 && SUCCEEDED(pTask->get_Triggers(&pTriggerCollection))
4505                 && SUCCEEDED(pTriggerCollection->Create(TASK_TRIGGER_DAILY, &pTrigger))
4506                 && SUCCEEDED(pTrigger->QueryInterface(IID_IDailyTrigger, (void**) &pCalendarTrigger))
4507                 && SUCCEEDED(pCalendarTrigger->put_Id(_bstr_t(L"Trigger1")))
4508                 && SUCCEEDED(pCalendarTrigger->put_DaysInterval(1))
4509                 && SUCCEEDED(pCalendarTrigger->put_StartBoundary(_bstr_t(currentTimeString)))
4510                 && SUCCEEDED(pCalendarTrigger->get_Repetition(&pRepetitionPattern))
4511                 && SUCCEEDED(pRepetitionPattern->put_Duration(_bstr_t(L"P1D")))
4512                 && SUCCEEDED(pRepetitionPattern->put_Interval(_bstr_t(L"PT2H")))
4513                 && SUCCEEDED(pRepetitionPattern->put_StopAtDurationEnd(VARIANT_FALSE))
4514                 && SUCCEEDED(pTask->get_Actions(&pActionCollection))
4515                 && SUCCEEDED(pActionCollection->Create(TASK_ACTION_EXEC, &pAction))
4516                 && SUCCEEDED(pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction))
4517                 && SUCCEEDED(pExecAction->put_Path(_bstr_t(MEGAcmdUpdaterPath)))
4518                 && SUCCEEDED(pExecAction->put_Arguments(_bstr_t(L"--emergency-update"))))
4519         {
4520             if (SUCCEEDED(pMEGAFolder->RegisterTaskDefinition(taskName, pTask,
4521                     TASK_CREATE_OR_UPDATE, _variant_t(), _variant_t(),
4522                     TASK_LOGON_INTERACTIVE_TOKEN, _variant_t(L""),
4523                     &pRegisteredTask)))
4524             {
4525                 success = true;
4526                 MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Update task registered OK");
4527             }
4528             else
4529             {
4530                 MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Error registering update task");
4531             }
4532         }
4533         else
4534         {
4535             MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Error creating update task");
4536         }
4537     }
4538     else
4539     {
4540         MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Error getting root task folder");
4541     }
4542 
4543     if (pRegisteredTask)
4544     {
4545         pRegisteredTask->Release();
4546     }
4547     if (pTrigger)
4548     {
4549         pTrigger->Release();
4550     }
4551     if (pTriggerCollection)
4552     {
4553         pTriggerCollection->Release();
4554     }
4555     if (pIdleSettings)
4556     {
4557         pIdleSettings->Release();
4558     }
4559     if (pSettings)
4560     {
4561         pSettings->Release();
4562     }
4563     if (pPrincipal)
4564     {
4565         pPrincipal->Release();
4566     }
4567     if (pRegInfo)
4568     {
4569         pRegInfo->Release();
4570     }
4571     if (pCalendarTrigger)
4572     {
4573         pCalendarTrigger->Release();
4574     }
4575     if (pAction)
4576     {
4577         pAction->Release();
4578     }
4579     if (pActionCollection)
4580     {
4581         pActionCollection->Release();
4582     }
4583     if (pRepetitionPattern)
4584     {
4585         pRepetitionPattern->Release();
4586     }
4587     if (pExecAction)
4588     {
4589         pExecAction->Release();
4590     }
4591     if (pTask)
4592     {
4593         pTask->Release();
4594     }
4595     if (pMEGAFolder)
4596     {
4597         pMEGAFolder->Release();
4598     }
4599     if (pRootFolder)
4600     {
4601         pRootFolder->Release();
4602     }
4603     if (pService)
4604     {
4605         pService->Release();
4606     }
4607 
4608     return success;
4609 #elif defined(__MACH__)
4610     return registerUpdateDaemon();
4611 #else
4612     return true;
4613 #endif
4614 }
4615 
getloginInAtStartup()4616 bool getloginInAtStartup()
4617 {
4618     return loginInAtStartup;
4619 }
4620 
timeLoginStarted()4621 ::mega::m_time_t timeLoginStarted()
4622 {
4623     return timeOfLoginInAtStartup;
4624 }
4625 
setloginInAtStartup(bool value)4626 void setloginInAtStartup(bool value)
4627 {
4628     loginInAtStartup = value;
4629     if (value)
4630     {
4631         timeOfLoginInAtStartup = m_time(NULL);
4632     }
4633     updatevalidCommands();
4634 }
4635 
unblock()4636 void unblock()
4637 {
4638     setBlocked(0);
4639     sandboxCMD->setReasonblocked("");
4640     broadcastMessage("Your account is not longer blocked");
4641     if (!api->isFilesystemAvailable())
4642     {
4643         auto theSandbox = sandboxCMD;
4644         std::thread t([theSandbox](){
4645             theSandbox->cmdexecuter->fetchNodes();
4646         });
4647     }
4648 }
4649 
setBlocked(int value)4650 void setBlocked(int value)
4651 {
4652     if (blocked != value)
4653     {
4654         blocked = value;
4655         updatevalidCommands();
4656         cmdexecuter->updateprompt();
4657     }
4658 }
4659 
getBlocked()4660 int getBlocked()
4661 {
4662     return blocked;
4663 }
4664 
updatevalidCommands()4665 void updatevalidCommands()
4666 {
4667     if (loginInAtStartup || blocked)
4668     {
4669         validCommands = loginInValidCommands;
4670     }
4671     else
4672     {
4673         validCommands = allValidCommands;
4674     }
4675 }
4676 
reset()4677 void reset()
4678 {
4679     setBlocked(false);
4680 }
4681 #ifdef _WIN32
uninstall()4682 void uninstall()
4683 {
4684     ITaskService *pService = NULL;
4685     ITaskFolder *pRootFolder = NULL;
4686     ITaskFolder *pMEGAFolder = NULL;
4687     _bstr_t taskBaseName = L"MEGAcmd Update Task ";
4688     LPTSTR stringSID = NULL;
4689 
4690     stringSID = getCurrentSid();
4691     if (!stringSID)
4692     {
4693         MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Unable to get the current SID");
4694         return;
4695     }
4696     _bstr_t taskName = taskBaseName + stringSID;
4697 
4698     CoInitializeEx(NULL, COINIT_MULTITHREADED);
4699 
4700     if (SUCCEEDED(CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL))
4701             && SUCCEEDED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService))
4702             && SUCCEEDED(pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))
4703             && SUCCEEDED(pService->GetFolder(_bstr_t( L"\\"), &pRootFolder)))
4704     {
4705         if (pRootFolder->CreateFolder(_bstr_t(L"MEGA"), _variant_t(L""), &pMEGAFolder) == 0x800700b7)
4706         {
4707             pRootFolder->GetFolder(_bstr_t(L"MEGA"), &pMEGAFolder);
4708         }
4709 
4710         if (pMEGAFolder)
4711         {
4712             pMEGAFolder->DeleteTask(taskName, 0);
4713             pMEGAFolder->Release();
4714         }
4715         pRootFolder->Release();
4716     }
4717 
4718     if (pService)
4719     {
4720         pService->Release();
4721     }
4722 }
4723 
4724 #endif
4725 
4726 } //end namespace
4727 
4728 using namespace megacmd;
4729 
main(int argc,char * argv[])4730 int main(int argc, char* argv[])
4731 {
4732 #ifdef __linux__
4733     // Ensure interesting signals are unblocked.
4734     sigset_t signalstounblock;
4735     sigemptyset (&signalstounblock);
4736     sigaddset(&signalstounblock, SIGUSR1);
4737     sigaddset(&signalstounblock, SIGUSR2);
4738     sigprocmask(SIG_UNBLOCK, &signalstounblock, NULL);
4739 
4740     if (signal(SIGUSR1, LinuxSignalHandler)) //TODO: do this after startup?
4741     {
4742         cerr << " Failed to register signal SIGUSR1 " << endl;
4743     }
4744     if (signal(SIGUSR2, LinuxSignalHandler))
4745     {
4746         LOG_debug << " Failed to register signal SIGUSR2 ";
4747     }
4748 #endif
4749     string localecode = getLocaleCode();
4750 #ifdef _WIN32
4751     // Set Environment's default locale
4752     setlocale(LC_ALL, "en-US");
4753 #endif
4754     mcmdMainArgv = argv;
4755     mcmdMainArgc = argc;
4756 
4757 #ifdef __MACH__
4758     initializeMacOSStuff(argc,argv);
4759 #endif
4760 
4761     NullBuffer null_buffer;
4762     std::ostream null_stream(&null_buffer);
4763 #ifndef ENABLE_LOG_PERFORMANCE
4764     SimpleLogger::setAllOutputs(&null_stream);
4765 #endif
4766     SimpleLogger::setLogLevel(logMax); // do not filter anything here, log level checking is done by loggerCMD
4767 
4768     loggerCMD = new MegaCMDLogger();
4769 
4770     loggerCMD->setApiLoggerLevel(MegaApi::LOG_LEVEL_ERROR);
4771     loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_INFO);
4772 #if DEBUG and !defined(_WIN32)
4773     loggerCMD->setApiLoggerLevel(MegaApi::LOG_LEVEL_DEBUG);
4774     loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_DEBUG);
4775 #endif
4776     string loglevelenv;
4777 #ifndef _WIN32
4778     loglevelenv = (getenv ("MEGACMD_LOGLEVEL") == NULL)?"":getenv ("MEGACMD_LOGLEVEL");
4779 #endif
4780 
4781     vector<const char*> args;
4782     if (argc > 1)
4783     {
4784         args = vector<const char*>(argv + 1, argv + argc);
4785     }
4786 
4787     string debug_api_url;
4788     bool debug = extractarg(args, "--debug");
4789     bool debugfull = extractarg(args, "--debug-full");
4790     bool verbose = extractarg(args, "--verbose");
4791     bool verbosefull = extractarg(args, "--verbose-full");
4792     bool skiplockcheck = extractarg(args, "--skip-lock-check");
4793     bool setapiurl = extractargparam(args, "--apiurl", debug_api_url);  // only for debugging
4794     bool disablepkp = extractarg(args, "--disablepkp");  // only for debugging
4795 
4796 
4797 #ifdef _WIN32
4798     bool buninstall = extractarg(args, "--uninstall") || extractarg(args, "/uninstall");
4799     if (buninstall)
4800     {
4801         MegaApi::removeRecursively(ConfigurationManager::getConfigFolder().c_str());
4802         uninstall();
4803         exit(0);
4804     }
4805 #endif
4806 
4807     string shandletowait;
4808     bool dowaitforhandle = extractargparam(args, "--wait-for", shandletowait);
4809     if (dowaitforhandle)
4810     {
4811 #ifdef _WIN32
4812         DWORD processId = atoi(shandletowait.c_str());
4813 
4814         HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
4815 
4816         cout << "Waiting for former server to end" << endl;
4817         WaitForSingleObject( processHandle, INFINITE );
4818         CloseHandle(processHandle);
4819 #else
4820 
4821         pid_t processId = atoi(shandletowait.c_str());
4822 
4823         while (is_pid_running(processId))
4824         {
4825             cerr << "Waiting for former server to end:  " << processId << endl;
4826             sleep(1);
4827         }
4828 #endif
4829     }
4830 
4831     if (!loglevelenv.compare("DEBUG") || debug )
4832     {
4833         loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_DEBUG);
4834     }
4835     if (!loglevelenv.compare("FULLDEBUG") || debugfull )
4836     {
4837         loggerCMD->setApiLoggerLevel(MegaApi::LOG_LEVEL_DEBUG);
4838         loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_DEBUG);
4839     }
4840     if (!loglevelenv.compare("VERBOSE") || verbose )
4841     {
4842         loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_MAX);
4843     }
4844     if (!loglevelenv.compare("FULLVERBOSE") || verbosefull )
4845     {
4846         loggerCMD->setApiLoggerLevel(MegaApi::LOG_LEVEL_MAX);
4847         loggerCMD->setCmdLoggerLevel(MegaApi::LOG_LEVEL_MAX);
4848     }
4849 
4850     ConfigurationManager::loadConfiguration(( argc > 1 ) && debug);
4851     if (!ConfigurationManager::lockExecution() && !skiplockcheck)
4852     {
4853         cerr << "Another instance of MEGAcmd Server is running. Execute with --skip-lock-check to force running (NOT RECOMMENDED)" << endl;
4854         sleepSeconds(5);
4855         exit(-2);
4856     }
4857 
4858     char userAgent[40];
4859     sprintf(userAgent, "MEGAcmd" MEGACMD_STRINGIZE(MEGACMD_USERAGENT_SUFFIX) "/%d.%d.%d.%d", MEGACMD_MAJOR_VERSION,MEGACMD_MINOR_VERSION,MEGACMD_MICRO_VERSION,MEGACMD_BUILD_ID);
4860 
4861     MegaApi::addLoggerObject(loggerCMD);
4862     MegaApi::setLogLevel(MegaApi::LOG_LEVEL_MAX);
4863 
4864     LOG_debug << "MEGAcmd version: " << MEGACMD_MAJOR_VERSION << "." << MEGACMD_MINOR_VERSION << "." << MEGACMD_MICRO_VERSION << "." << MEGACMD_BUILD_ID << ": code " << MEGACMD_CODE_VERSION;
4865 
4866 #if defined(__MACH__) && defined(ENABLE_SYNC)
4867     int fd = -1;
4868     if (argc)
4869     {
4870         long int value = strtol(argv[argc-1], NULL, 10);
4871         if (value > 0 && value < INT_MAX)
4872         {
4873             fd = value;
4874         }
4875     }
4876 
4877     if (fd >= 0)
4878     {
4879         api = new MegaApi("BdARkQSQ", ConfigurationManager::getConfigFolder().c_str(), userAgent, fd);
4880     }
4881     else
4882     {
4883         api = new MegaApi("BdARkQSQ", (MegaGfxProcessor*)NULL, ConfigurationManager::getConfigFolder().c_str(), userAgent);
4884     }
4885 #else
4886     api = new MegaApi("BdARkQSQ", (MegaGfxProcessor*)NULL, ConfigurationManager::getConfigFolder().c_str(), userAgent);
4887 #endif
4888 
4889     if (setapiurl)
4890     {
4891         api->changeApiUrl(debug_api_url.c_str(), disablepkp);
4892     }
4893 
4894 
4895     api->setLanguage(localecode.c_str());
4896 
4897     for (int i = 0; i < 5; i++)
4898     {
4899         MegaApi *apiFolder = new MegaApi("BdARkQSQ", (MegaGfxProcessor*)NULL, (const char*)NULL, userAgent);
4900         apiFolder->setLanguage(localecode.c_str());
4901         apiFolders.push(apiFolder);
4902         apiFolder->setLogLevel(MegaApi::LOG_LEVEL_MAX);
4903         semaphoreapiFolders.release();
4904     }
4905 
4906     for (int i = 0; i < 100; i++)
4907     {
4908         semaphoreClients.release();
4909     }
4910 
4911     LOG_debug << "Language set to: " << localecode;
4912 
4913     sandboxCMD = new MegaCmdSandbox();
4914     cmdexecuter = new MegaCmdExecuter(api, loggerCMD, sandboxCMD);
4915     sandboxCMD->cmdexecuter = cmdexecuter;
4916 
4917     megaCmdGlobalListener = new MegaCmdGlobalListener(loggerCMD, sandboxCMD);
4918     megaCmdMegaListener = new MegaCmdMegaListener(api, NULL, sandboxCMD);
4919     api->addGlobalListener(megaCmdGlobalListener);
4920     api->addListener(megaCmdMegaListener);
4921 
4922     // set up the console
4923 #ifdef _WIN32
4924     console = new CONSOLE_CLASS;
4925 #else
4926     struct termios term;
4927     if ( ( tcgetattr(STDIN_FILENO, &term) < 0 ) || runningInBackground() ) //try console
4928     {
4929         consoleFailed = true;
4930         console = NULL;
4931     }
4932     else
4933     {
4934         console = new CONSOLE_CLASS;
4935     }
4936 #endif
4937     cm = new COMUNICATIONMANAGER();
4938 
4939 #if _WIN32
4940     if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) )
4941      {
4942         LOG_debug << "Control handler set";
4943      }
4944      else
4945      {
4946         LOG_warn << "Control handler set";
4947      }
4948 #else
4949     // prevent CTRL+C exit
4950     if (!consoleFailed){
4951         signal(SIGINT, sigint_handler);
4952     }
4953 #endif
4954 
4955     atexit(finalize);
4956 
4957 
4958 #if defined(_WIN32) || defined(__APPLE__)
4959     if (!ConfigurationManager::getConfigurationValue("updaterregistered", false))
4960     {
4961         LOG_debug << "Registering automatic updater";
4962         if (registerUpdater())
4963         {
4964             ConfigurationManager::savePropertyValue("updaterregistered", true);
4965             LOG_verbose << "Registered automatic updater";
4966         }
4967         else
4968         {
4969             LOG_err << "Failed to register automatic updater";
4970         }
4971     }
4972 #endif
4973 
4974     printWelcomeMsg();
4975 
4976 
4977     int configuredProxyType = ConfigurationManager::getConfigurationValue("proxy_type", -1);
4978     auto configuredProxyUrl = ConfigurationManager::getConfigurationSValue("proxy_url");
4979 
4980     auto configuredProxyUsername = ConfigurationManager::getConfigurationSValue("proxy_username");
4981     auto configuredProxyPassword = ConfigurationManager::getConfigurationSValue("proxy_password");
4982     if (configuredProxyType != -1 && configuredProxyType != MegaProxy::PROXY_AUTO) //AUTO is default, no need to set
4983     {
4984         std::string command("proxy ");
4985         command.append(configuredProxyUrl);
4986         if (configuredProxyUsername.size())
4987         {
4988             command.append(" --username=").append(configuredProxyUsername);
4989             if (configuredProxyPassword.size())
4990             {
4991                 command.append(" --password=").append(configuredProxyPassword);
4992             }
4993         }
4994         processCommandLinePetitionQueues(command);
4995     }
4996 
4997     if (ConfigurationManager::getHasBeenUpdated())
4998     {
4999         api->sendEvent(MCMD_EVENT_UPDATE_ID,MCMD_EVENT_UPDATE_MESSAGE);
5000         stringstream ss;
5001         ss << "MEGAcmd has been updated to version " << MEGACMD_MAJOR_VERSION << "." << MEGACMD_MINOR_VERSION << "." << MEGACMD_MICRO_VERSION << "." << MEGACMD_BUILD_ID << " - code " << MEGACMD_CODE_VERSION << endl;
5002         broadcastMessage(ss.str() ,true);
5003     }
5004 
5005     if (!ConfigurationManager::session.empty())
5006     {
5007         loginInAtStartup = true;
5008         stringstream logLine;
5009         logLine << "login " << ConfigurationManager::session;
5010         LOG_debug << "Executing ... " << logLine.str().substr(0,9) << "...";
5011         processCommandLinePetitionQueues(logLine.str());
5012     }
5013 
5014     megacmd::megacmd();
5015     finalize(waitForRestartSignal);
5016 }
5017