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(¤tTime);
4447 currentTimeInfo = localtime(¤tTime);
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