1 /**
2  * @file examples/megaclient.cpp
3  * @brief Sample application, interactive GNU Readline CLI
4  *
5  * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGA SDK - Client Access Engine.
8  *
9  * Applications using the MEGA API must present a valid application key
10  * and comply with the the rules set forth in the Terms of Service.
11  *
12  * The MEGA SDK is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  *
16  * @copyright Simplified (2-clause) BSD License.
17  *
18  * You should have received a copy of the license along with this
19  * program.
20  */
21 
22 #include "mega.h"
23 #include "megacli.h"
24 #include <fstream>
25 
26 #define USE_VARARGS
27 #define PREFER_STDARG
28 
29 #ifndef NO_READLINE
30 #include <readline/readline.h>
31 #include <readline/history.h>
32 #endif
33 
34 #if (__cplusplus >= 201700L)
35     #include <filesystem>
36     namespace fs = std::filesystem;
37     #define USE_FILESYSTEM
38 #elif !defined(__MINGW32__) && !defined(__ANDROID__) && (!defined(__GNUC__) || (__GNUC__*100+__GNUC_MINOR__) >= 503)
39 #define USE_FILESYSTEM
40 #ifdef WIN32
41     #include <filesystem>
42     namespace fs = std::experimental::filesystem;
43 #else
44     #include <experimental/filesystem>
45     namespace fs = std::experimental::filesystem;
46 #endif
47 #endif
48 
49 #include <regex>
50 
51 #ifdef USE_FREEIMAGE
52 #include "mega/gfx/freeimage.h"
53 #endif
54 
55 namespace ac = ::mega::autocomplete;
56 
57 #include <iomanip>
58 
59 using namespace mega;
60 using std::cout;
61 using std::cerr;
62 using std::endl;
63 using std::flush;
64 using std::ifstream;
65 using std::ofstream;
66 using std::setw;
67 using std::hex;
68 using std::dec;
69 
70 MegaClient* client;
71 MegaClient* clientFolder;
72 
73 int gNextClientTag = 1;
74 std::map<int, std::function<void(Node*)>> gOnPutNodeTag;
75 
76 bool gVerboseMode = false;
77 
78 
79 // new account signup e-mail address and name
80 static string signupemail, signupname;
81 
82 // signup code being confirmed
83 static string signupcode;
84 
85 // signup password challenge and encrypted master key
86 static byte signuppwchallenge[SymmCipher::KEYLENGTH], signupencryptedmasterkey[SymmCipher::KEYLENGTH];
87 
88 // password recovery e-mail address and code being confirmed
89 static string recoveryemail, recoverycode;
90 
91 // password recovery code requires MK or not
92 static bool hasMasterKey;
93 
94 // master key for password recovery
95 static byte masterkey[SymmCipher::KEYLENGTH];
96 
97 // change email link to be confirmed
98 static string changeemail, changecode;
99 
100 // chained folder link creation
101 static handle hlink = UNDEF;
102 static int del = 0;
103 static int ets = 0;
104 
105 // import welcome pdf at account creation
106 static bool pdf_to_import = false;
107 
108 // public link information
109 static string publiclink;
110 
111 // local console
112 Console* console;
113 
114 // loading progress of lengthy API responses
115 int responseprogress = -1;
116 
117 //2FA pin attempts
118 int attempts = 0;
119 
120 #ifdef ENABLE_SYNC
121 
122 struct NewSyncConfig
123 {
124     SyncConfig::Type type;
125     bool syncDeletions;
126     bool forceOverwrite;
127 
fromNewSyncConfig128     static NewSyncConfig from(const SyncConfig& config)
129     {
130         return NewSyncConfig{config.getType(), config.syncDeletions(), config.forceOverwrite()};
131     }
132 };
133 
134 // sync configuration used when creating a new sync
135 static NewSyncConfig newSyncConfig;
136 
137 // converts the given sync configuration to a string
syncConfigToString(const NewSyncConfig & config)138 static std::string syncConfigToString(const NewSyncConfig& config)
139 {
140     auto getOptions = [](const NewSyncConfig& config)
141     {
142         std::string desc;
143         desc += ", syncDeletions ";
144         desc += config.syncDeletions ? "ON" : "OFF";
145         desc += ", forceOverwrite ";
146         desc += config.forceOverwrite ? "ON" : "OFF";
147         return desc;
148     };
149 
150     std::string description;
151     if (config.type == SyncConfig::TYPE_TWOWAY)
152     {
153         description = "TWOWAY";
154     }
155     else if (config.type & SyncConfig::TYPE_UP)
156     {
157         description = "UP";
158         description += getOptions(config);
159     }
160     else
161     {
162         description = "DOWN";
163         description += getOptions(config);
164     }
165     return description;
166 }
167 
168 // creates a NewSyncConfig object from config options as strings.
169 // returns a pair where `first` is success and `second` is the sync config.
syncConfigFromStrings(std::string type,std::string syncDel={},std::string overwrite={})170 static std::pair<bool, NewSyncConfig> syncConfigFromStrings(std::string type, std::string syncDel = {}, std::string overwrite = {})
171 {
172     auto toLower = [](std::string& s)
__anon840bd8880202(std::string& s) 173     {
174         for (char& c : s) { c = static_cast<char>(tolower(c)); };
175     };
176 
177     toLower(type);
178     toLower(syncDel);
179     toLower(overwrite);
180 
181     SyncConfig::Type syncType;
182     if (type == "up")
183     {
184         syncType = SyncConfig::TYPE_UP;
185     }
186     else if (type == "down")
187     {
188         syncType = SyncConfig::TYPE_DOWN;
189     }
190     else if (type == "twoway")
191     {
192         syncType = SyncConfig::TYPE_TWOWAY;
193     }
194     else
195     {
196         return std::make_pair(false, NewSyncConfig{});
197     }
198 
199     bool syncDeletions = false;
200     bool forceOverwrite = false;
201 
202     if (syncType != SyncConfig::TYPE_TWOWAY)
203     {
204         if (syncDel == "on")
205         {
206             syncDeletions = true;
207         }
208         else if (syncDel == "off")
209         {
210             syncDeletions = false;
211         }
212         else
213         {
214             return std::make_pair(false, NewSyncConfig{});
215         }
216 
217         if (overwrite == "on")
218         {
219             forceOverwrite = true;
220         }
221         else if (overwrite == "off")
222         {
223             forceOverwrite = false;
224         }
225         else
226         {
227             return std::make_pair(false, NewSyncConfig{});
228         }
229     }
230 
231     return std::make_pair(true, NewSyncConfig{syncType, syncDeletions, forceOverwrite});
232 }
233 #endif
234 
getAccessLevelStr(int access)235 static const char* getAccessLevelStr(int access)
236 {
237     switch(access)
238     {
239     case ACCESS_UNKNOWN:
240         return "unkown";
241     case RDONLY:
242         return "read-only";
243     case RDWR:
244         return "read/write";
245     case FULL:
246         return "full access";
247     case OWNER:
248         return "owner access";
249     case OWNERPRELOGIN:
250         return "owner (prelogin) access";
251     default:
252         return "UNDEFINED";
253     }
254 }
255 
errorstring(error e)256 const char* errorstring(error e)
257 {
258     switch (e)
259     {
260         case API_OK:
261             return "No error";
262         case API_EINTERNAL:
263             return "Internal error";
264         case API_EARGS:
265             return "Invalid argument";
266         case API_EAGAIN:
267             return "Request failed, retrying";
268         case API_ERATELIMIT:
269             return "Rate limit exceeded";
270         case API_EFAILED:
271             return "Transfer failed";
272         case API_ETOOMANY:
273             return "Too many concurrent connections or transfers";
274         case API_ERANGE:
275             return "Out of range";
276         case API_EEXPIRED:
277             return "Expired";
278         case API_ENOENT:
279             return "Not found";
280         case API_ECIRCULAR:
281             return "Circular linkage detected";
282         case API_EACCESS:
283             return "Access denied";
284         case API_EEXIST:
285             return "Already exists";
286         case API_EINCOMPLETE:
287             return "Incomplete";
288         case API_EKEY:
289             return "Invalid key/integrity check failed";
290         case API_ESID:
291             return "Bad session ID";
292         case API_EBLOCKED:
293             return "Blocked";
294         case API_EOVERQUOTA:
295             return "Over quota";
296         case API_ETEMPUNAVAIL:
297             return "Temporarily not available";
298         case API_ETOOMANYCONNECTIONS:
299             return "Connection overflow";
300         case API_EWRITE:
301             return "Write error";
302         case API_EREAD:
303             return "Read error";
304         case API_EAPPKEY:
305             return "Invalid application key";
306         case API_EGOINGOVERQUOTA:
307             return "Not enough quota";
308         case API_EMFAREQUIRED:
309             return "Multi-factor authentication required";
310         case API_EMASTERONLY:
311             return "Access denied for users";
312         case API_EBUSINESSPASTDUE:
313             return "Business account has expired";
314         case API_EPAYWALL:
315             return "Over Disk Quota Paywall";
316         default:
317             return "Unknown error";
318     }
319 }
320 
AppFile()321 AppFile::AppFile()
322 {
323     static int nextseqno;
324 
325     seqno = ++nextseqno;
326 }
327 
328 // transfer start
start()329 void AppFilePut::start()
330 {
331 }
332 
start()333 void AppFileGet::start()
334 {
335 }
336 
337 // transfer completion
completed(Transfer *,LocalNode *)338 void AppFileGet::completed(Transfer*, LocalNode*)
339 {
340     // (at this time, the file has already been placed in the final location)
341     delete this;
342 }
343 
344 // transfer terminated - too many failures, or unrecoverable failure, or cancelled
terminated()345 void AppFileGet::terminated()
346 {
347     delete this;
348 }
349 
completed(Transfer * t,LocalNode *)350 void AppFilePut::completed(Transfer* t, LocalNode*)
351 {
352     // perform standard completion (place node in user filesystem etc.)
353     File::completed(t, NULL);
354 
355     delete this;
356 }
357 
358 // transfer terminated - too many failures, or unrecoverable failure, or cancelled
terminated()359 void AppFilePut::terminated()
360 {
361     delete this;
362 }
363 
~AppFileGet()364 AppFileGet::~AppFileGet()
365 {
366     appxferq[GET].erase(appxfer_it);
367 }
368 
~AppFilePut()369 AppFilePut::~AppFilePut()
370 {
371     appxferq[PUT].erase(appxfer_it);
372 }
373 
displayname(string * dname)374 void AppFilePut::displayname(string* dname)
375 {
376     *dname = localname.toName(*transfer->client->fsaccess, client->fsaccess->getFilesystemType(localname));
377 }
378 
379 // transfer progress callback
progress()380 void AppFile::progress()
381 {
382 }
383 
displaytransferdetails(Transfer * t,const char * action)384 static void displaytransferdetails(Transfer* t, const char* action)
385 {
386     string name;
387 
388     for (file_list::iterator it = t->files.begin(); it != t->files.end(); it++)
389     {
390         if (it != t->files.begin())
391         {
392             cout << "/";
393         }
394 
395         (*it)->displayname(&name);
396         cout << name;
397     }
398 
399     cout << ": " << (t->type == GET ? "Incoming" : "Outgoing") << " file transfer " << action;
400 }
401 
402 // a new transfer was added
transfer_added(Transfer *)403 void DemoApp::transfer_added(Transfer* /*t*/)
404 {
405 }
406 
407 // a queued transfer was removed
transfer_removed(Transfer * t)408 void DemoApp::transfer_removed(Transfer* t)
409 {
410     displaytransferdetails(t, "removed\n");
411 }
412 
transfer_update(Transfer *)413 void DemoApp::transfer_update(Transfer* /*t*/)
414 {
415     // (this is handled in the prompt logic)
416 }
417 
transfer_failed(Transfer * t,const Error & e,dstime)418 void DemoApp::transfer_failed(Transfer* t, const Error& e, dstime)
419 {
420     displaytransferdetails(t, "failed (");
421     if (e == API_ETOOMANY && e.hasExtraInfo())
422     {
423          cout << getExtraInfoErrorString(e) << ")" << endl;
424     }
425     else
426     {
427         cout << errorstring(e) << ")" << endl;
428     }
429 }
430 
transfer_complete(Transfer * t)431 void DemoApp::transfer_complete(Transfer* t)
432 {
433     if (gVerboseMode)
434     {
435         displaytransferdetails(t, "completed, ");
436 
437         if (t->slot)
438         {
439             cout << t->slot->progressreported * 10 / (1024 * (Waiter::ds - t->slot->starttime + 1)) << " KB/s" << endl;
440         }
441         else
442         {
443             cout << "delayed" << endl;
444         }
445     }
446 }
447 
448 // transfer about to start - make final preparations (determine localfilename, create thumbnail for image upload)
transfer_prepare(Transfer * t)449 void DemoApp::transfer_prepare(Transfer* t)
450 {
451     if (gVerboseMode)
452     {
453         displaytransferdetails(t, "starting\n");
454     }
455 
456     if (t->type == GET)
457     {
458         // only set localfilename if the engine has not already done so
459         if (t->localfilename.empty())
460         {
461             client->fsaccess->tmpnamelocal(t->localfilename);
462         }
463     }
464 }
465 
466 #ifdef ENABLE_SYNC
syncstat(Sync * sync)467 static void syncstat(Sync* sync)
468 {
469     cout << ", local data in this sync: " << sync->localbytes << " byte(s) in " << sync->localnodes[FILENODE]
470          << " file(s) and " << sync->localnodes[FOLDERNODE] << " folder(s)" << endl;
471 }
472 
syncupdate_state(Sync *,syncstate_t newstate)473 void DemoApp::syncupdate_state(Sync*, syncstate_t newstate)
474 {
475     switch (newstate)
476     {
477         case SYNC_ACTIVE:
478             cout << "Sync is now active" << endl;
479             break;
480 
481         case SYNC_FAILED:
482             cout << "Sync failed." << endl;
483 
484         default:
485             ;
486     }
487 }
488 
syncupdate_scanning(bool active)489 void DemoApp::syncupdate_scanning(bool active)
490 {
491     if (active)
492     {
493         cout << "Sync - scanning files and folders" << endl;
494     }
495     else
496     {
497         cout << "Sync - scan completed" << endl;
498     }
499 }
500 
501 // sync update callbacks are for informational purposes only and must not change or delete the sync itself
syncupdate_local_folder_addition(Sync * sync,LocalNode *,const char * path)502 void DemoApp::syncupdate_local_folder_addition(Sync* sync, LocalNode *, const char* path)
503 {
504     cout << "Sync - local folder addition detected: " << path;
505     syncstat(sync);
506 }
507 
syncupdate_local_folder_deletion(Sync * sync,LocalNode * localNode)508 void DemoApp::syncupdate_local_folder_deletion(Sync* sync, LocalNode *localNode)
509 {
510     cout << "Sync - local folder deletion detected: " << localNode->name;
511     syncstat(sync);
512 }
513 
syncupdate_local_file_addition(Sync * sync,LocalNode *,const char * path)514 void DemoApp::syncupdate_local_file_addition(Sync* sync, LocalNode *, const char* path)
515 {
516     cout << "Sync - local file addition detected: " << path;
517     syncstat(sync);
518 }
519 
syncupdate_local_file_deletion(Sync * sync,LocalNode * localNode)520 void DemoApp::syncupdate_local_file_deletion(Sync* sync, LocalNode *localNode)
521 {
522     cout << "Sync - local file deletion detected: " << localNode->name;
523     syncstat(sync);
524 }
525 
syncupdate_local_file_change(Sync * sync,LocalNode *,const char * path)526 void DemoApp::syncupdate_local_file_change(Sync* sync, LocalNode *, const char* path)
527 {
528     cout << "Sync - local file change detected: " << path;
529     syncstat(sync);
530 }
531 
syncupdate_local_move(Sync *,LocalNode * localNode,const char * path)532 void DemoApp::syncupdate_local_move(Sync*, LocalNode *localNode, const char* path)
533 {
534     cout << "Sync - local rename/move " << localNode->name << " -> " << path << endl;
535 }
536 
syncupdate_local_lockretry(bool locked)537 void DemoApp::syncupdate_local_lockretry(bool locked)
538 {
539     if (locked)
540     {
541         cout << "Sync - waiting for local filesystem lock" << endl;
542     }
543     else
544     {
545         cout << "Sync - local filesystem lock issue resolved, continuing..." << endl;
546     }
547 }
548 
syncupdate_remote_move(Sync *,Node * n,Node * prevparent)549 void DemoApp::syncupdate_remote_move(Sync *, Node *n, Node *prevparent)
550 {
551     cout << "Sync - remote move " << n->displayname() << ": " << (prevparent ? prevparent->displayname() : "?") <<
552             " -> " << (n->parent ? n->parent->displayname() : "?") << endl;
553 }
554 
syncupdate_remote_rename(Sync *,Node * n,const char * prevname)555 void DemoApp::syncupdate_remote_rename(Sync *, Node *n, const char *prevname)
556 {
557     cout << "Sync - remote rename " << prevname << " -> " <<  n->displayname() << endl;
558 }
559 
syncupdate_remote_folder_addition(Sync *,Node * n)560 void DemoApp::syncupdate_remote_folder_addition(Sync *, Node* n)
561 {
562     cout << "Sync - remote folder addition detected " << n->displayname() << endl;
563 }
564 
syncupdate_remote_file_addition(Sync *,Node * n)565 void DemoApp::syncupdate_remote_file_addition(Sync *, Node* n)
566 {
567     cout << "Sync - remote file addition detected " << n->displayname() << endl;
568 }
569 
syncupdate_remote_folder_deletion(Sync *,Node * n)570 void DemoApp::syncupdate_remote_folder_deletion(Sync *, Node* n)
571 {
572     cout << "Sync - remote folder deletion detected " << n->displayname() << endl;
573 }
574 
syncupdate_remote_file_deletion(Sync *,Node * n)575 void DemoApp::syncupdate_remote_file_deletion(Sync *, Node* n)
576 {
577     cout << "Sync - remote file deletion detected " << n->displayname() << endl;
578 }
579 
syncupdate_get(Sync *,Node *,const char * path)580 void DemoApp::syncupdate_get(Sync*, Node *, const char* path)
581 {
582     cout << "Sync - requesting file " << path << endl;
583 }
584 
syncupdate_put(Sync *,LocalNode *,const char * path)585 void DemoApp::syncupdate_put(Sync*, LocalNode *, const char* path)
586 {
587     cout << "Sync - sending file " << path << endl;
588 }
589 
syncupdate_remote_copy(Sync *,const char * name)590 void DemoApp::syncupdate_remote_copy(Sync*, const char* name)
591 {
592     cout << "Sync - creating remote file " << name << " by copying existing remote file" << endl;
593 }
594 
treestatename(treestate_t ts)595 static const char* treestatename(treestate_t ts)
596 {
597     switch (ts)
598     {
599         case TREESTATE_NONE:
600             return "None/Undefined";
601         case TREESTATE_SYNCED:
602             return "Synced";
603         case TREESTATE_PENDING:
604             return "Pending";
605         case TREESTATE_SYNCING:
606             return "Syncing";
607     }
608 
609     return "UNKNOWN";
610 }
611 
syncupdate_treestate(LocalNode * l)612 void DemoApp::syncupdate_treestate(LocalNode* l)
613 {
614     cout << "Sync - state change of node " << l->name << " to " << treestatename(l->ts) << endl;
615 }
616 
617 // generic name filter
618 // FIXME: configurable regexps
is_syncable(const char * name)619 static bool is_syncable(const char* name)
620 {
621     return *name != '.' && *name != '~' && strcmp(name, "Thumbs.db") && strcmp(name, "desktop.ini");
622 }
623 
624 // determines whether remote node should be synced
sync_syncable(Sync *,const char *,LocalPath &,Node * n)625 bool DemoApp::sync_syncable(Sync *, const char *, LocalPath&, Node *n)
626 {
627     return is_syncable(n->displayname());
628 }
629 
630 // determines whether local file should be synced
sync_syncable(Sync *,const char * name,LocalPath &)631 bool DemoApp::sync_syncable(Sync *, const char *name, LocalPath&)
632 {
633     return is_syncable(name);
634 }
635 #endif
636 
AppFileGet(Node * n,handle ch,byte * cfilekey,m_off_t csize,m_time_t cmtime,string * cfilename,string * cfingerprint,const string & targetfolder)637 AppFileGet::AppFileGet(Node* n, handle ch, byte* cfilekey, m_off_t csize, m_time_t cmtime, string* cfilename,
638                        string* cfingerprint, const string& targetfolder)
639 {
640     if (n)
641     {
642         h = n->nodehandle;
643         hprivate = true;
644 
645         *(FileFingerprint*) this = *n;
646         name = n->displayname();
647     }
648     else
649     {
650         h = ch;
651         memcpy(filekey, cfilekey, sizeof filekey);
652         hprivate = false;
653 
654         size = csize;
655         mtime = cmtime;
656 
657         if (!cfingerprint->size() || !unserializefingerprint(cfingerprint))
658         {
659             memcpy(crc.data(), filekey, sizeof crc);
660         }
661 
662         name = *cfilename;
663     }
664 
665     localname = LocalPath::fromName(name, *client->fsaccess, client->fsaccess->getFilesystemType(LocalPath::fromPath(name, *client->fsaccess)));
666     if (!targetfolder.empty())
667     {
668         string s = targetfolder;
669         localname.prependWithSeparator(LocalPath::fromPath(s, *client->fsaccess), client->fsaccess->localseparator);
670     }
671 }
672 
AppFilePut(const LocalPath & clocalname,handle ch,const char * ctargetuser)673 AppFilePut::AppFilePut(const LocalPath& clocalname, handle ch, const char* ctargetuser)
674 {
675     // full local path
676     localname = clocalname;
677 
678     // target parent node
679     h = ch;
680 
681     // target user
682     targetuser = ctargetuser;
683 
684     // erase path component
685     auto fileSystemType = client->fsaccess->getFilesystemType(clocalname);
686 
687     LocalPath p = clocalname;
688     p.erase(0, p.lastpartlocal(*client->fsaccess));
689     name = p.toName(*client->fsaccess, fileSystemType);
690 }
691 
692 // user addition/update (users never get deleted)
users_updated(User ** u,int count)693 void DemoApp::users_updated(User** u, int count)
694 {
695     if (count == 1)
696     {
697         cout << "1 user received or updated" << endl;
698     }
699     else
700     {
701         cout << count << " users received or updated" << endl;
702     }
703 
704     if (u)
705     {
706         User* user;
707         for (int i = 0; i < count; i++)
708         {
709             user = u[i];
710             cout << "User " << user->email;
711             if (user->getTag()) // false if external change
712             {
713                 cout << " has been changed by your own client" << endl;
714             }
715             else
716             {
717                 cout << " has been changed externally" << endl;
718             }
719         }
720     }
721 }
722 
723 bool notifyAlerts = true;
724 
displayUser(handle user,MegaClient * mc)725 string displayUser(handle user, MegaClient* mc)
726 {
727     User* u = mc->finduser(user);
728     return u ? u->email : "<user not found>";
729 }
730 
displayTime(m_time_t t)731 string displayTime(m_time_t t)
732 {
733     char timebuf[32];
734     struct tm tmptr;
735     m_localtime(t, &tmptr);
736     strftime(timebuf, sizeof timebuf, "%c", &tmptr);
737     return timebuf;
738 }
739 
printAlert(UserAlert::Base & b)740 void printAlert(UserAlert::Base& b)
741 {
742     string header, title;
743     b.text(header, title, client);
744     cout << "**alert " << b.id << ": " << header << " - " << title << " [at " << displayTime(b.timestamp) << "]" << " seen: " << b.seen << endl;
745 }
746 
useralerts_updated(UserAlert::Base ** b,int count)747 void DemoApp::useralerts_updated(UserAlert::Base** b, int count)
748 {
749     if (b && notifyAlerts)
750     {
751         for (int i = 0; i < count; ++i)
752         {
753             if (!b[i]->seen)
754             {
755                 printAlert(*b[i]);
756             }
757         }
758     }
759 }
760 
761 
762 #ifdef ENABLE_CHAT
763 
chatcreate_result(TextChat * chat,error e)764 void DemoApp::chatcreate_result(TextChat *chat, error e)
765 {
766     if (e)
767     {
768         cout << "Chat creation failed (" << errorstring(e) << ")" << endl;
769     }
770     else
771     {
772         cout << "Chat created successfully" << endl;
773         printChatInformation(chat);
774         cout << endl;
775     }
776 }
777 
chatinvite_result(error e)778 void DemoApp::chatinvite_result(error e)
779 {
780     if (e)
781     {
782         cout << "Chat invitation failed (" << errorstring(e) << ")" << endl;
783     }
784     else
785     {
786         cout << "Chat invitation successful" << endl;
787     }
788 }
789 
chatremove_result(error e)790 void DemoApp::chatremove_result(error e)
791 {
792     if (e)
793     {
794         cout << "Peer removal failed (" << errorstring(e) << ")" << endl;
795     }
796     else
797     {
798         cout << "Peer removal successful" << endl;
799     }
800 }
801 
chaturl_result(string * url,error e)802 void DemoApp::chaturl_result(string *url, error e)
803 {
804     if (e)
805     {
806         cout << "Chat URL retrieval failed (" << errorstring(e) << ")" << endl;
807     }
808     else
809     {
810         cout << "Chat URL: " << *url << endl;
811     }
812 }
813 
chatgrantaccess_result(error e)814 void DemoApp::chatgrantaccess_result(error e)
815 {
816     if (e)
817     {
818         cout << "Grant access to node failed (" << errorstring(e) << ")" << endl;
819     }
820     else
821     {
822         cout << "Access to node granted successfully" << endl;
823     }
824 }
825 
chatremoveaccess_result(error e)826 void DemoApp::chatremoveaccess_result(error e)
827 {
828     if (e)
829     {
830         cout << "Revoke access to node failed (" << errorstring(e) << ")" << endl;
831     }
832     else
833     {
834         cout << "Access to node removed successfully" << endl;
835     }
836 }
837 
chatupdatepermissions_result(error e)838 void DemoApp::chatupdatepermissions_result(error e)
839 {
840     if (e)
841     {
842         cout << "Permissions update failed (" << errorstring(e) << ")" << endl;
843     }
844     else
845     {
846         cout << "Permissions updated successfully" << endl;
847     }
848 }
849 
chattruncate_result(error e)850 void DemoApp::chattruncate_result(error e)
851 {
852     if (e)
853     {
854         cout << "Truncate message/s failed (" << errorstring(e) << ")" << endl;
855     }
856     else
857     {
858         cout << "Message/s truncated successfully" << endl;
859     }
860 }
861 
chatsettitle_result(error e)862 void DemoApp::chatsettitle_result(error e)
863 {
864     if (e)
865     {
866         cout << "Set title failed (" << errorstring(e) << ")" << endl;
867     }
868     else
869     {
870         cout << "Title updated successfully" << endl;
871     }
872 }
873 
chatpresenceurl_result(string * url,error e)874 void DemoApp::chatpresenceurl_result(string *url, error e)
875 {
876     if (e)
877     {
878         cout << "Presence URL retrieval failed (" << errorstring(e) << ")" << endl;
879     }
880     else
881     {
882         cout << "Presence URL: " << *url << endl;
883     }
884 }
885 
chatlink_result(handle h,error e)886 void DemoApp::chatlink_result(handle h, error e)
887 {
888     if (e)
889     {
890         cout << "Chat link failed (" << errorstring(e) << ")" << endl;
891     }
892     else
893     {
894         if (ISUNDEF(h))
895         {
896             cout << "Chat link deleted successfully" << endl;
897         }
898         else
899         {
900             char hstr[sizeof(handle) * 4 / 3 + 4];
901             Base64::btoa((const byte *)&h, MegaClient::CHATLINKHANDLE, hstr);
902             cout << "Chat link: " << hstr << endl;
903         }
904     }
905 }
906 
chatlinkclose_result(error e)907 void DemoApp::chatlinkclose_result(error e)
908 {
909     if (e)
910     {
911         cout << "Set private mode for chat failed  (" << errorstring(e) << ")" << endl;
912     }
913     else
914     {
915         cout << "Private mode successfully set" << endl;
916     }
917 }
918 
chatlinkurl_result(handle chatid,int shard,string * url,string * ct,int,m_time_t ts,error e)919 void DemoApp::chatlinkurl_result(handle chatid, int shard, string *url, string *ct, int, m_time_t ts, error e)
920 {
921     if (e)
922     {
923         cout << "URL request for chat-link failed (" << errorstring(e) << ")" << endl;
924     }
925     else
926     {
927         char idstr[sizeof(handle) * 4 / 3 + 4];
928         Base64::btoa((const byte *)&chatid, MegaClient::CHATHANDLE, idstr);
929         cout << "Chatid: " << idstr << " (shard " << shard << ")" << endl;
930         cout << "URL for chat-link: " << url->c_str() << endl;
931         cout << "Encrypted chat-topic: " << ct->c_str() << endl;
932         cout << "Creation timestamp: " << ts << endl;
933     }
934 }
935 
chatlinkjoin_result(error e)936 void DemoApp::chatlinkjoin_result(error e)
937 {
938     if (e)
939     {
940         cout << "Join to openchat failed (" << errorstring(e) << ")" << endl;
941     }
942     else
943     {
944         cout << "Joined to openchat successfully." << endl;
945     }
946 }
947 
chats_updated(textchat_map * chats,int count)948 void DemoApp::chats_updated(textchat_map *chats, int count)
949 {
950     if (count == 1)
951     {
952         cout << "1 chat received or updated" << endl;
953     }
954     else
955     {
956         cout << count << " chats received or updated" << endl;
957     }
958 
959     if (chats)
960     {
961         textchat_map::iterator it;
962         for (it = chats->begin(); it != chats->end(); it++)
963         {
964             printChatInformation(it->second);
965         }
966     }
967 }
968 
printChatInformation(TextChat * chat)969 void DemoApp::printChatInformation(TextChat *chat)
970 {
971     if (!chat)
972     {
973         return;
974     }
975 
976     cout << "Chat ID: " << Base64Str<sizeof(handle)>(chat->id) << endl;
977     cout << "\tOwn privilege level: " << DemoApp::getPrivilegeString(chat->priv) << endl;
978     cout << "\tCreation ts: " << chat->ts << endl;
979     cout << "\tChat shard: " << chat->shard << endl;
980     if (chat->group)
981     {
982         cout << "\tGroup chat: yes" << endl;
983     }
984     else
985     {
986         cout << "\tGroup chat: no" << endl;
987     }
988     if (chat->isFlagSet(TextChat::FLAG_OFFSET_ARCHIVE))
989     {
990         cout << "\tArchived chat: yes" << endl;
991     }
992     else
993     {
994         cout << "\tArchived chat: no" << endl;
995     }
996     if (chat->publicchat)
997     {
998         cout << "\tPublic chat: yes" << endl;
999         cout << "\tUnified key: " << chat->unifiedKey.c_str() << endl;
1000     }
1001     else
1002     {
1003         cout << "\tPublic chat: no" << endl;
1004     }
1005     cout << "\tPeers:";
1006 
1007     if (chat->userpriv)
1008     {
1009         cout << "\t\t(userhandle)\t(privilege level)" << endl;
1010         for (unsigned i = 0; i < chat->userpriv->size(); i++)
1011         {
1012             Base64Str<sizeof(handle)> hstr(chat->userpriv->at(i).first);
1013             cout << "\t\t\t" << hstr;
1014             cout << "\t" << DemoApp::getPrivilegeString(chat->userpriv->at(i).second) << endl;
1015         }
1016     }
1017     else
1018     {
1019         cout << " no peers (only you as participant)" << endl;
1020     }
1021     if (chat->tag)
1022     {
1023         cout << "\tIs own change: yes" << endl;
1024     }
1025     else
1026     {
1027         cout << "\tIs own change: no" << endl;
1028     }
1029     if (!chat->title.empty())
1030     {
1031         cout << "\tTitle: " << chat->title.c_str() << endl;
1032     }
1033 }
1034 
getPrivilegeString(privilege_t priv)1035 string DemoApp::getPrivilegeString(privilege_t priv)
1036 {
1037     switch (priv)
1038     {
1039     case PRIV_STANDARD:
1040         return "PRIV_STANDARD (standard access)";
1041     case PRIV_MODERATOR:
1042         return "PRIV_MODERATOR (moderator)";
1043     case PRIV_RO:
1044         return "PRIV_RO (read-only)";
1045     case PRIV_RM:
1046         return "PRIV_RM (removed)";
1047     case PRIV_UNKNOWN:
1048     default:
1049         return "PRIV_UNKNOWN";
1050     }
1051 }
1052 
1053 #endif
1054 
1055 
pcrs_updated(PendingContactRequest ** list,int count)1056 void DemoApp::pcrs_updated(PendingContactRequest** list, int count)
1057 {
1058     int deletecount = 0;
1059     int updatecount = 0;
1060     if (list != NULL)
1061     {
1062         for (int i = 0; i < count; i++)
1063         {
1064             if (list[i]->changed.deleted)
1065             {
1066                 deletecount++;
1067             }
1068             else
1069             {
1070                 updatecount++;
1071             }
1072         }
1073     }
1074     else
1075     {
1076         // All pcrs are updated
1077         for (handlepcr_map::iterator it = client->pcrindex.begin(); it != client->pcrindex.end(); it++)
1078         {
1079             if (it->second->changed.deleted)
1080             {
1081                 deletecount++;
1082             }
1083             else
1084             {
1085                 updatecount++;
1086             }
1087         }
1088     }
1089 
1090     if (deletecount != 0)
1091     {
1092         cout << deletecount << " pending contact request" << (deletecount != 1 ? "s" : "") << " deleted" << endl;
1093     }
1094     if (updatecount != 0)
1095     {
1096         cout << updatecount << " pending contact request" << (updatecount != 1 ? "s" : "") << " received or updated" << endl;
1097     }
1098 }
1099 
setattr_result(handle,error e)1100 void DemoApp::setattr_result(handle, error e)
1101 {
1102     if (e)
1103     {
1104         cout << "Node attribute update failed (" << errorstring(e) << ")" << endl;
1105     }
1106 }
1107 
rename_result(handle,error e)1108 void DemoApp::rename_result(handle, error e)
1109 {
1110     if (e)
1111     {
1112         cout << "Node move failed (" << errorstring(e) << ")" << endl;
1113     }
1114 }
1115 
unlink_result(handle,error e)1116 void DemoApp::unlink_result(handle, error e)
1117 {
1118     if (e)
1119     {
1120         cout << "Node deletion failed (" << errorstring(e) << ")" << endl;
1121     }
1122 }
1123 
fetchnodes_result(const Error & e)1124 void DemoApp::fetchnodes_result(const Error& e)
1125 {
1126     if (e)
1127     {
1128         if (e == API_ENOENT && e.hasExtraInfo())
1129         {
1130             cout << "File/folder retrieval failed: " << getExtraInfoErrorString(e) << endl;
1131         }
1132         else
1133         {
1134             cout << "File/folder retrieval failed (" << errorstring(e) << ")" << endl;
1135         }
1136         pdf_to_import = false;
1137     }
1138     else
1139     {
1140         // check if we fetched a folder link and the key is invalid
1141         handle h = client->getrootpublicfolder();
1142         if (h != UNDEF)
1143         {
1144             Node *n = client->nodebyhandle(h);
1145             if (n && (n->attrs.map.find('n') == n->attrs.map.end()))
1146             {
1147                 cout << "File/folder retrieval succeed, but encryption key is wrong." << endl;
1148             }
1149             else
1150             {
1151                 cout << "Folder link loaded correctly." << endl;
1152             }
1153         }
1154 
1155         if (pdf_to_import)
1156         {
1157             client->getwelcomepdf();
1158         }
1159     }
1160 }
1161 
putnodes_result(error e,targettype_t t,NewNode * nn)1162 void DemoApp::putnodes_result(error e, targettype_t t, NewNode* nn)
1163 {
1164     if (t == USER_HANDLE)
1165     {
1166         delete[] nn;
1167 
1168         if (!e)
1169         {
1170             cout << "Success." << endl;
1171         }
1172     }
1173 
1174     if (pdf_to_import)   // putnodes from openfilelink_result()
1175     {
1176         if (!e)
1177         {
1178             cout << "Welcome PDF file has been imported successfully." << endl;
1179         }
1180         else
1181         {
1182             cout << "Failed to import Welcome PDF file" << endl;
1183         }
1184 
1185         pdf_to_import = false;
1186         return;
1187     }
1188 
1189     if (e)
1190     {
1191         cout << "Node addition failed (" << errorstring(e) << ")" << endl;
1192     }
1193 
1194     auto i = gOnPutNodeTag.find(client->restag);
1195     if (i != gOnPutNodeTag.end())
1196     {
1197         if (client->nodenotify.size())
1198         {
1199             Node* n = client->nodenotify.back();  // same trick as the intermediate layer - only works when puts are one node at a time.
1200             i->second(n);
1201             gOnPutNodeTag.erase(i);
1202         }
1203     }
1204 }
1205 
share_result(error e)1206 void DemoApp::share_result(error e)
1207 {
1208     if (e)
1209     {
1210         cout << "Share creation/modification request failed (" << errorstring(e) << ")" << endl;
1211     }
1212     else
1213     {
1214         if (hlink != UNDEF)
1215         {
1216             if (!del)
1217             {
1218                 Node *n = client->nodebyhandle(hlink);
1219                 if (!n)
1220                 {
1221                     cout << "Node was not found. (" << Base64Str<sizeof hlink>(hlink) << ")" << endl;
1222 
1223                     hlink = UNDEF;
1224                     del = ets = 0;
1225                     return;
1226                 }
1227 
1228                 client->getpubliclink(n, del, ets);
1229             }
1230             else
1231             {
1232                 hlink = UNDEF;
1233                 del = ets = 0;
1234             }
1235         }
1236     }
1237 }
1238 
share_result(int,error e)1239 void DemoApp::share_result(int, error e)
1240 {
1241     if (e)
1242     {
1243         cout << "Share creation/modification failed (" << errorstring(e) << ")" << endl;
1244     }
1245     else
1246     {
1247         cout << "Share creation/modification succeeded" << endl;
1248     }
1249 }
1250 
setpcr_result(handle h,error e,opcactions_t action)1251 void DemoApp::setpcr_result(handle h, error e, opcactions_t action)
1252 {
1253     if (e)
1254     {
1255         cout << "Outgoing pending contact request failed (" << errorstring(e) << ")" << endl;
1256     }
1257     else
1258     {
1259         if (h == UNDEF)
1260         {
1261             // must have been deleted
1262             cout << "Outgoing pending contact request " << (action == OPCA_DELETE ? "deleted" : "reminded") << " successfully" << endl;
1263         }
1264         else
1265         {
1266             cout << "Outgoing pending contact request succeeded, id: " << Base64Str<MegaClient::PCRHANDLE>(h) << endl;
1267         }
1268     }
1269 }
1270 
updatepcr_result(error e,ipcactions_t action)1271 void DemoApp::updatepcr_result(error e, ipcactions_t action)
1272 {
1273     if (e)
1274     {
1275         cout << "Incoming pending contact request update failed (" << errorstring(e) << ")" << endl;
1276     }
1277     else
1278     {
1279         string labels[3] = {"accepted", "denied", "ignored"};
1280         cout << "Incoming pending contact request successfully " << labels[(int)action] << endl;
1281     }
1282 }
1283 
fa_complete(handle h,fatype type,const char *,uint32_t len)1284 void DemoApp::fa_complete(handle h, fatype type, const char* /*data*/, uint32_t len)
1285 {
1286     cout << "Got attribute of type " << type << " (" << len << " byte(s))";
1287     Node *n = client->nodebyhandle(h);
1288     if (n)
1289     {
1290         cout << " for " << n->displayname() << endl;
1291     }
1292 }
1293 
fa_failed(handle,fatype type,int retries,error e)1294 int DemoApp::fa_failed(handle, fatype type, int retries, error e)
1295 {
1296     cout << "File attribute retrieval of type " << type << " failed (retries: " << retries << ") error: " << e << endl;
1297 
1298     return retries > 2;
1299 }
1300 
putfa_result(handle,fatype,error e)1301 void DemoApp::putfa_result(handle, fatype, error e)
1302 {
1303     if (e)
1304     {
1305         cout << "File attribute attachment failed (" << errorstring(e) << ")" << endl;
1306     }
1307 }
1308 
putfa_result(handle,fatype,const char *)1309 void DemoApp::putfa_result(handle, fatype, const char*)
1310 {
1311 }
1312 
removecontact_result(error e)1313 void DemoApp::removecontact_result(error e)
1314 {
1315     if (e)
1316     {
1317         cout << "Contact removal failed (" << errorstring(e) << ")" << endl;
1318     }
1319     else
1320     {
1321         cout << "Success." << endl;
1322     }
1323 }
1324 
putua_result(error e)1325 void DemoApp::putua_result(error e)
1326 {
1327     if (e)
1328     {
1329         cout << "User attribute update failed (" << errorstring(e) << ")" << endl;
1330     }
1331     else
1332     {
1333         cout << "Success." << endl;
1334     }
1335 }
1336 
getua_result(error e)1337 void DemoApp::getua_result(error e)
1338 {
1339     if (client->fetchingkeys)
1340     {
1341         return;
1342     }
1343 
1344     cout << "User attribute retrieval failed (" << errorstring(e) << ")" << endl;
1345 }
1346 
getua_result(byte * data,unsigned l,attr_t type)1347 void DemoApp::getua_result(byte* data, unsigned l, attr_t type)
1348 {
1349     if (client->fetchingkeys)
1350     {
1351         return;
1352     }
1353 
1354     if (gVerboseMode)
1355     {
1356         cout << "Received " << l << " byte(s) of user attribute: ";
1357         fwrite(data, 1, l, stdout);
1358         cout << endl;
1359 
1360         if (type == ATTR_ED25519_PUBK)
1361         {
1362             cout << "Credentials: " << AuthRing::fingerprint(string((const char*)data, l), true) << endl;
1363         }
1364     }
1365 }
1366 
getua_result(TLVstore * tlv,attr_t type)1367 void DemoApp::getua_result(TLVstore *tlv, attr_t type)
1368 {
1369     if (client->fetchingkeys)
1370     {
1371         return;
1372     }
1373 
1374     if (!tlv)
1375     {
1376         cout << "Error getting private user attribute" << endl;
1377     }
1378     else if (!gVerboseMode)
1379     {
1380         cout << "Received a TLV with " << tlv->size() << " item(s) of user attribute: " << endl;
1381 
1382         vector<string> *keys = tlv->getKeys();
1383         vector<string>::const_iterator it;
1384         unsigned valuelen;
1385         string value, key;
1386         char *buf;
1387         for (it=keys->begin(); it != keys->end(); it++)
1388         {
1389             key = (*it).empty() ? "(no key)" : *it;
1390             value = tlv->get(*it);
1391             valuelen = unsigned(value.length());
1392 
1393             buf = new char[valuelen * 4 / 3 + 4];
1394             Base64::btoa((const byte *) value.data(), valuelen, buf);
1395 
1396             cout << "\t" << key << "\t" << buf << endl;
1397             delete [] buf;
1398         }
1399         delete keys;
1400     }
1401 }
1402 
1403 #ifdef DEBUG
delua_result(error e)1404 void DemoApp::delua_result(error e)
1405 {
1406     if (e)
1407     {
1408         cout << "User attribute removal failed (" << errorstring(e) << ")" << endl;
1409     }
1410     else
1411     {
1412         cout << "Success." << endl;
1413     }
1414 }
1415 
senddevcommand_result(int value)1416 void DemoApp::senddevcommand_result(int value)
1417 {
1418     cout << "Dev subcommand finished with code: " << value << endl;
1419 }
1420 
exec_devcommand(autocomplete::ACState & s)1421 void exec_devcommand(autocomplete::ACState& s)
1422 {
1423     const char *email = nullptr;
1424     if (s.words.size() == 3)
1425     {
1426         email = s.words[2].s.c_str();
1427     }
1428     const char *subcommand = s.words[1].s.c_str();
1429     client->senddevcommand(subcommand, email);
1430 }
1431 #endif
1432 
1433 
notify_retry(dstime dsdelta,retryreason_t)1434 void DemoApp::notify_retry(dstime dsdelta, retryreason_t)
1435 {
1436     if (dsdelta)
1437     {
1438         cout << "API request failed, retrying in " << dsdelta * 100 << " ms - Use 'retry' to retry immediately..."
1439              << endl;
1440     }
1441     else
1442     {
1443         cout << "Retried API request completed" << endl;
1444     }
1445 }
1446 
getExtraInfoErrorString(const Error & e)1447 string DemoApp::getExtraInfoErrorString(const Error& e)
1448 {
1449     string textError;
1450 
1451     if (e.getUserStatus() == 7)
1452     {
1453         textError.append("User status is suspended due to ETD. ");
1454     }
1455 
1456     textError.append("Link status is: ");
1457     switch (e.getLinkStatus())
1458     {
1459         case 0:
1460             textError.append("Undeleted");
1461             break;
1462         case 1:
1463             textError.append("Deleted/down");
1464             break;
1465         case 2:
1466             textError.append("Down due to an ETD specifically");
1467             break;
1468         default:
1469             textError.append("Unknown link status");
1470             break;
1471     }
1472 
1473     return textError;
1474 }
1475 
1476 static void store_line(char*);
1477 static void process_line(char *);
1478 static char* line;
1479 
1480 static AccountDetails account;
1481 
1482 static handle cwd = UNDEF;
1483 
1484 static const char* rootnodenames[] =
1485 { "ROOT", "INBOX", "RUBBISH" };
1486 static const char* rootnodepaths[] =
1487 { "/", "//in", "//bin" };
1488 
nodestats(int * c,const char * action)1489 static void nodestats(int* c, const char* action)
1490 {
1491     if (c[FILENODE])
1492     {
1493         cout << c[FILENODE] << ((c[FILENODE] == 1) ? " file" : " files");
1494     }
1495     if (c[FILENODE] && c[FOLDERNODE])
1496     {
1497         cout << " and ";
1498     }
1499     if (c[FOLDERNODE])
1500     {
1501         cout << c[FOLDERNODE] << ((c[FOLDERNODE] == 1) ? " folder" : " folders");
1502     }
1503 
1504     if (c[FILENODE] || c[FOLDERNODE])
1505     {
1506         cout << " " << action << endl;
1507     }
1508 }
1509 
1510 // list available top-level nodes and contacts/incoming shares
listtrees()1511 static void listtrees()
1512 {
1513     for (int i = 0; i < (int) (sizeof client->rootnodes / sizeof *client->rootnodes); i++)
1514     {
1515         if (client->rootnodes[i] != UNDEF)
1516         {
1517             cout << rootnodenames[i] << " on " << rootnodepaths[i] << endl;
1518         }
1519     }
1520 
1521     for (user_map::iterator uit = client->users.begin(); uit != client->users.end(); uit++)
1522     {
1523         User* u = &uit->second;
1524         Node* n;
1525 
1526         if (u->show == VISIBLE || u->sharing.size())
1527         {
1528             for (handle_set::iterator sit = u->sharing.begin(); sit != u->sharing.end(); sit++)
1529             {
1530                 if ((n = client->nodebyhandle(*sit)) && n->inshare)
1531                 {
1532                     cout << "INSHARE on " << u->email << ":" << n->displayname() << " ("
1533                          << getAccessLevelStr(n->inshare->access) << ")" << endl;
1534                 }
1535             }
1536         }
1537     }
1538 
1539     if (clientFolder && !ISUNDEF(clientFolder->rootnodes[0]))
1540     {
1541         Node *n = clientFolder->nodebyhandle(clientFolder->rootnodes[0]);
1542         if (n)
1543         {
1544             cout << "FOLDERLINK on " << n->displayname() << ":" << endl;
1545         }
1546     }
1547 }
1548 
1549 // returns node pointer determined by path relative to cwd
1550 // path naming conventions:
1551 // * path is relative to cwd
1552 // * /path is relative to ROOT
1553 // * //in is in INBOX
1554 // * //bin is in RUBBISH
1555 // * X: is user X's INBOX
1556 // * X:SHARE is share SHARE from user X
1557 // * Y:name is folder in FOLDERLINK, Y is the public handle
1558 // * : and / filename components, as well as the \, must be escaped by \.
1559 // (correct UTF-8 encoding is assumed)
1560 // returns NULL if path malformed or not found
nodebypath(const char * ptr,string * user=NULL,string * namepart=NULL)1561 static Node* nodebypath(const char* ptr, string* user = NULL, string* namepart = NULL)
1562 {
1563     vector<string> c;
1564     string s;
1565     int l = 0;
1566     const char* bptr = ptr;
1567     int remote = 0;
1568     int folderlink = 0;
1569     Node* n = nullptr;
1570     Node* nn;
1571 
1572     // split path by / or :
1573     do {
1574         if (!l)
1575         {
1576             if (*(const signed char*)ptr >= 0)
1577             {
1578                 if (*ptr == '\\')
1579                 {
1580                     if (ptr > bptr)
1581                     {
1582                         s.append(bptr, ptr - bptr);
1583                     }
1584 
1585                     bptr = ++ptr;
1586 
1587                     if (*bptr == 0)
1588                     {
1589                         c.push_back(s);
1590                         break;
1591                     }
1592 
1593                     ptr++;
1594                     continue;
1595                 }
1596 
1597                 if (*ptr == '/' || *ptr == ':' || !*ptr)
1598                 {
1599                     if (*ptr == ':')
1600                     {
1601                         if (c.size())
1602                         {
1603                             return NULL;
1604                         }
1605 
1606                         remote = 1;
1607                     }
1608 
1609                     if (ptr > bptr)
1610                     {
1611                         s.append(bptr, ptr - bptr);
1612                     }
1613 
1614                     bptr = ptr + 1;
1615 
1616                     c.push_back(s);
1617 
1618                     s.erase();
1619                 }
1620             }
1621             else if ((*ptr & 0xf0) == 0xe0)
1622             {
1623                 l = 1;
1624             }
1625             else if ((*ptr & 0xf8) == 0xf0)
1626             {
1627                 l = 2;
1628             }
1629             else if ((*ptr & 0xfc) == 0xf8)
1630             {
1631                 l = 3;
1632             }
1633             else if ((*ptr & 0xfe) == 0xfc)
1634             {
1635                 l = 4;
1636             }
1637         }
1638         else
1639         {
1640             l--;
1641         }
1642     } while (*ptr++);
1643 
1644     if (l)
1645     {
1646         return NULL;
1647     }
1648 
1649     if (remote)
1650     {
1651         // target: user inbox - record username/email and return NULL
1652         if (c.size() == 2 && c[0].find("@") != string::npos && !c[1].size())
1653         {
1654             if (user)
1655             {
1656                 *user = c[0];
1657             }
1658 
1659             return NULL;
1660         }
1661 
1662         // target is not a user, but a public folder link
1663         if (c.size() >= 2 && c[0].find("@") == string::npos)
1664         {
1665             if (!clientFolder)
1666             {
1667                 return NULL;
1668             }
1669 
1670             n = clientFolder->nodebyhandle(clientFolder->rootnodes[0]);
1671             if (c.size() == 2 && c[1].empty())
1672             {
1673                 return n;
1674             }
1675             l = 1;   // <folder_name>:[/<subfolder>][/<file>]
1676             folderlink = 1;
1677         }
1678 
1679         User* u;
1680 
1681         if ((u = client->finduser(c[0].c_str())))
1682         {
1683             // locate matching share from this user
1684             handle_set::iterator sit;
1685             string name;
1686             for (sit = u->sharing.begin(); sit != u->sharing.end(); sit++)
1687             {
1688                 if ((n = client->nodebyhandle(*sit)))
1689                 {
1690                     if(!name.size())
1691                     {
1692                         name =  c[1];
1693                         n->client->fsaccess->normalize(&name);
1694                     }
1695 
1696                     if (!strcmp(name.c_str(), n->displayname()))
1697                     {
1698                         l = 2;
1699                         break;
1700                     }
1701                 }
1702             }
1703         }
1704 
1705         if (!l)
1706         {
1707             return NULL;
1708         }
1709     }
1710     else
1711     {
1712         // path starting with /
1713         if (c.size() > 1 && !c[0].size())
1714         {
1715             // path starting with //
1716             if (c.size() > 2 && !c[1].size())
1717             {
1718                 if (c[2] == "in")
1719                 {
1720                     n = client->nodebyhandle(client->rootnodes[1]);
1721                 }
1722                 else if (c[2] == "bin")
1723                 {
1724                     n = client->nodebyhandle(client->rootnodes[2]);
1725                 }
1726                 else
1727                 {
1728                     return NULL;
1729                 }
1730 
1731                 l = 3;
1732             }
1733             else
1734             {
1735                 n = client->nodebyhandle(client->rootnodes[0]);
1736 
1737                 l = 1;
1738             }
1739         }
1740         else
1741         {
1742             n = client->nodebyhandle(cwd);
1743         }
1744     }
1745 
1746     // parse relative path
1747     while (n && l < (int)c.size())
1748     {
1749         if (c[l] != ".")
1750         {
1751             if (c[l] == "..")
1752             {
1753                 if (n->parent)
1754                 {
1755                     n = n->parent;
1756                 }
1757             }
1758             else
1759             {
1760                 // locate child node (explicit ambiguity resolution: not implemented)
1761                 if (c[l].size())
1762                 {
1763                     if (folderlink)
1764                     {
1765                         nn = clientFolder->childnodebyname(n, c[l].c_str());
1766                     }
1767                     else
1768                     {
1769                         nn = client->childnodebyname(n, c[l].c_str());
1770                     }
1771 
1772                     if (!nn)
1773                     {
1774                         // mv command target? return name part of not found
1775                         if (namepart && l == (int) c.size() - 1)
1776                         {
1777                             *namepart = c[l];
1778                             return n;
1779                         }
1780 
1781                         return NULL;
1782                     }
1783 
1784                     n = nn;
1785                 }
1786             }
1787         }
1788 
1789         l++;
1790     }
1791 
1792     return n;
1793 }
1794 
listnodeshares(Node * n)1795 static void listnodeshares(Node* n)
1796 {
1797     if(n->outshares)
1798     {
1799         for (share_map::iterator it = n->outshares->begin(); it != n->outshares->end(); it++)
1800         {
1801             cout << "\t" << n->displayname();
1802 
1803             if (it->first)
1804             {
1805                 cout << ", shared with " << it->second->user->email << " (" << getAccessLevelStr(it->second->access) << ")"
1806                      << endl;
1807             }
1808             else
1809             {
1810                 cout << ", shared as exported folder link" << endl;
1811             }
1812         }
1813     }
1814 }
1815 
proc(MegaClient *,Node * n)1816 void TreeProcListOutShares::proc(MegaClient*, Node* n)
1817 {
1818     listnodeshares(n);
1819 }
1820 
1821 bool handles_on = false;
1822 
dumptree(Node * n,bool recurse,int depth,const char * title,ofstream * toFile)1823 static void dumptree(Node* n, bool recurse, int depth, const char* title, ofstream* toFile)
1824 {
1825     std::ostream& stream = toFile ? *toFile : cout;
1826     string titleTmp;
1827 
1828     if (depth)
1829     {
1830         if (!toFile)
1831         {
1832             if (!title && !(title = n->displayname()))
1833             {
1834                 title = "CRYPTO_ERROR";
1835             }
1836 
1837             for (int i = depth; i--; )
1838             {
1839                 stream << "\t";
1840             }
1841         }
1842         else
1843         {
1844             titleTmp = n->displaypath();
1845             title = titleTmp.c_str();
1846         }
1847 
1848         stream << title << " (";
1849 
1850         switch (n->type)
1851         {
1852             case FILENODE:
1853                 stream << n->size;
1854 
1855                 if (handles_on)
1856                 {
1857                     Base64Str<MegaClient::NODEHANDLE> handlestr(n->nodehandle);
1858                     stream << " " << handlestr.chars;
1859                 }
1860 
1861                 const char* p;
1862                 if ((p = strchr(n->fileattrstring.c_str(), ':')))
1863                 {
1864                     stream << ", has attributes " << p + 1;
1865                 }
1866 
1867                 if (n->plink)
1868                 {
1869                     stream << ", shared as exported";
1870                     if (n->plink->ets)
1871                     {
1872                         stream << " temporal";
1873                     }
1874                     else
1875                     {
1876                         stream << " permanent";
1877                     }
1878                     stream << " file link";
1879                 }
1880 
1881                 break;
1882 
1883             case FOLDERNODE:
1884                 stream << "folder";
1885 
1886                 if (handles_on)
1887                 {
1888                     Base64Str<MegaClient::NODEHANDLE> handlestr(n->nodehandle);
1889                     stream << " " << handlestr.chars;
1890                 }
1891 
1892                 if(n->outshares)
1893                 {
1894                     for (share_map::iterator it = n->outshares->begin(); it != n->outshares->end(); it++)
1895                     {
1896                         if (it->first)
1897                         {
1898                             stream << ", shared with " << it->second->user->email << ", access "
1899                                  << getAccessLevelStr(it->second->access);
1900                         }
1901                     }
1902 
1903                     if (n->plink)
1904                     {
1905                         stream << ", shared as exported";
1906                         if (n->plink->ets)
1907                         {
1908                             stream << " temporal";
1909                         }
1910                         else
1911                         {
1912                             stream << " permanent";
1913                         }
1914                         stream << " folder link";
1915                     }
1916                 }
1917 
1918                 if (n->pendingshares)
1919                 {
1920                     for (share_map::iterator it = n->pendingshares->begin(); it != n->pendingshares->end(); it++)
1921                     {
1922                         if (it->first)
1923                         {
1924                             stream << ", shared (still pending) with " << it->second->pcr->targetemail << ", access "
1925                                  << getAccessLevelStr(it->second->access);
1926                         }
1927                     }
1928                 }
1929 
1930                 if (n->inshare)
1931                 {
1932                     stream << ", inbound " << getAccessLevelStr(n->inshare->access) << " share";
1933                 }
1934                 break;
1935 
1936             default:
1937                 stream << "unsupported type, please upgrade";
1938         }
1939 
1940         stream << ")" << (n->changed.removed ? " (DELETED)" : "") << endl;
1941 
1942         if (!recurse)
1943         {
1944             return;
1945         }
1946     }
1947 
1948     if (n->type != FILENODE)
1949     {
1950         for (node_list::iterator it = n->children.begin(); it != n->children.end(); it++)
1951         {
1952             dumptree(*it, recurse, depth + 1, NULL, toFile);
1953         }
1954     }
1955 }
1956 
1957 #ifdef USE_FILESYSTEM
local_dumptree(const fs::path & de,int recurse,int depth=0)1958 static void local_dumptree(const fs::path& de, int recurse, int depth = 0)
1959 {
1960     if (depth)
1961     {
1962         for (int i = depth; i--; )
1963         {
1964             cout << "\t";
1965         }
1966 
1967         cout << de.filename().u8string() << " (";
1968 
1969         if (fs::is_directory(de))
1970         {
1971             cout << "folder";
1972         }
1973 
1974         cout << ")" << endl;
1975 
1976         if (!recurse)
1977         {
1978             return;
1979         }
1980     }
1981 
1982     if (fs::is_directory(de))
1983     {
1984         for (auto i = fs::directory_iterator(de); i != fs::directory_iterator(); ++i)
1985         {
1986             local_dumptree(*i, recurse, depth + 1);
1987         }
1988     }
1989 }
1990 #endif
1991 
nodepath(handle h,string * path)1992 static void nodepath(handle h, string* path)
1993 {
1994     Node* n = client->nodebyhandle(h);
1995     *path = n ? n->displaypath() : "";
1996 }
1997 
1998 appfile_list appxferq[2];
1999 
2000 static char dynamicprompt[128];
2001 
2002 static const char* prompts[] =
2003 {
2004     "MEGAcli> ", "Password:", "Old Password:", "New Password:", "Retype New Password:", "Master Key (base64):", "Type 2FA pin:", "Type pin to enable 2FA:"
2005 };
2006 
2007 enum prompttype
2008 {
2009     COMMAND, LOGINPASSWORD, OLDPASSWORD, NEWPASSWORD, PASSWORDCONFIRM, MASTERKEY, LOGINTFA, SETTFA
2010 };
2011 
2012 static prompttype prompt = COMMAND;
2013 
2014 #if defined(WIN32) && defined(NO_READLINE)
2015 static char pw_buf[512];  // double space for unicode
2016 #else
2017 static char pw_buf[256];
2018 #endif
2019 
2020 static int pw_buf_pos;
2021 
setprompt(prompttype p)2022 static void setprompt(prompttype p)
2023 {
2024     prompt = p;
2025 
2026     if (p == COMMAND)
2027     {
2028         console->setecho(true);
2029     }
2030     else
2031     {
2032         pw_buf_pos = 0;
2033 #if defined(WIN32) && defined(NO_READLINE)
2034         static_cast<WinConsole*>(console)->updateInputPrompt(prompts[p]);
2035 #else
2036         cout << prompts[p] << flush;
2037 #endif
2038         console->setecho(false);
2039     }
2040 }
2041 
2042 class TreeProcCopy_mcli : public TreeProc
2043 {
2044     // This is a duplicate of the TreeProcCopy declared in treeproc.h and defined in megaapi_impl.cpp.
2045     // However some products are built with the megaapi_impl intermediate layer and some without so
2046     // we can avoid duplicated symbols in some products this way
2047 public:
2048     NewNode * nn;
2049     unsigned nc;
2050 
2051 
TreeProcCopy_mcli()2052     TreeProcCopy_mcli()
2053     {
2054         nn = NULL;
2055         nc = 0;
2056     }
2057 
allocnodes()2058     void allocnodes()
2059     {
2060         nn = new NewNode[nc];
2061     }
2062 
~TreeProcCopy_mcli()2063     ~TreeProcCopy_mcli()
2064     {
2065         delete[] nn;
2066     }
2067 
2068     // determine node tree size (nn = NULL) or write node tree to new nodes array
proc(MegaClient * mc,Node * n)2069     void proc(MegaClient* mc, Node* n)
2070     {
2071         if (nn)
2072         {
2073             string attrstring;
2074             SymmCipher key;
2075             NewNode* t = nn + --nc;
2076 
2077             // copy node
2078             t->source = NEW_NODE;
2079             t->type = n->type;
2080             t->nodehandle = n->nodehandle;
2081             t->parenthandle = n->parent ? n->parent->nodehandle : UNDEF;
2082 
2083             // copy key (if file) or generate new key (if folder)
2084             if (n->type == FILENODE)
2085             {
2086                 t->nodekey = n->nodekey();
2087             }
2088             else
2089             {
2090                 byte buf[FOLDERNODEKEYLENGTH];
2091                 mc->rng.genblock(buf, sizeof buf);
2092                 t->nodekey.assign((char*) buf, FOLDERNODEKEYLENGTH);
2093             }
2094 
2095             key.setkey((const byte*) t->nodekey.data(), n->type);
2096 
2097             AttrMap tattrs;
2098             tattrs.map = n->attrs.map;
2099             nameid rrname = AttrMap::string2nameid("rr");
2100             attr_map::iterator it = tattrs.map.find(rrname);
2101             if (it != tattrs.map.end())
2102             {
2103                 LOG_debug << "Removing rr attribute";
2104                 tattrs.map.erase(it);
2105             }
2106 
2107             t->attrstring.reset(new string);
2108             tattrs.getjson(&attrstring);
2109             mc->makeattr(&key, t->attrstring, attrstring.c_str());
2110         }
2111         else
2112         {
2113             nc++;
2114         }
2115     }
2116 };
2117 
loadfile(LocalPath & localPath,string * data)2118 int loadfile(LocalPath& localPath, string* data)
2119 {
2120     auto fa = client->fsaccess->newfileaccess();
2121 
2122     if (fa->fopen(localPath, 1, 0))
2123     {
2124         data->resize(size_t(fa->size));
2125         fa->fread(data, unsigned(data->size()), 0, 0);
2126         return 1;
2127     }
2128     return 0;
2129 }
2130 
xferq(direction_t d,int cancel,bool showActive,bool showAll,bool showCount)2131 void xferq(direction_t d, int cancel, bool showActive, bool showAll, bool showCount)
2132 {
2133     string name;
2134     int count = 0, activeCount = 0;
2135 
2136     DBTableTransactionCommitter committer(client->tctable);
2137     for (appfile_list::iterator it = appxferq[d].begin(); it != appxferq[d].end(); )
2138     {
2139         if (cancel < 0 || cancel == (*it)->seqno)
2140         {
2141             bool active = (*it)->transfer && (*it)->transfer->slot;
2142             (*it)->displayname(&name);
2143 
2144             if (active && showActive || showAll)
2145             {
2146                 cout << (*it)->seqno << ": " << name;
2147 
2148                 if (d == PUT)
2149                 {
2150                     AppFilePut* f = (AppFilePut*)*it;
2151 
2152                     cout << " -> ";
2153 
2154                     if (f->targetuser.size())
2155                     {
2156                         cout << f->targetuser << ":";
2157                     }
2158                     else
2159                     {
2160                         string path;
2161                         nodepath(f->h, &path);
2162                         cout << path;
2163                     }
2164                 }
2165 
2166                 if (active)
2167                 {
2168                     cout << " [ACTIVE] " << ((*it)->transfer->slot->progressreported * 100 / ((*it)->transfer->size ? (*it)->transfer->size : 1)) << "% of " << (*it)->transfer->size;
2169                 }
2170                 cout << endl;
2171             }
2172 
2173             if (cancel >= 0)
2174             {
2175                 cout << "Cancelling..." << endl;
2176 
2177 
2178                 if ((*it)->transfer)
2179                 {
2180                     client->stopxfer(*it++, &committer);  // stopping calls us back, we delete it, destructor removes it from the map
2181                 }
2182                 continue;
2183             }
2184 
2185             ++count;
2186             activeCount += active ? 1 : 0;
2187         }
2188         ++it;
2189     }
2190     if (showCount)
2191     {
2192         cout << "Transfer count: " << count << " active: " << activeCount << endl;
2193     }
2194 }
2195 
2196 #ifdef USE_MEDIAINFO
2197 
showMediaInfo(const MediaProperties & mp,MediaFileInfo & mediaInfo,bool oneline)2198 string showMediaInfo(const MediaProperties& mp, MediaFileInfo& mediaInfo, bool oneline)
2199 {
2200     ostringstream out;
2201     string sep(oneline ? " " : "\n");
2202 
2203     MediaFileInfo::MediaCodecs::shortformatrec sf;
2204     sf.containerid = 0;
2205     sf.videocodecid = 0;
2206     sf.audiocodecid = 0;
2207     if (mp.shortformat == 255)
2208     {
2209         return "MediaInfo could not identify this file";
2210     }
2211     else if (mp.shortformat == 0)
2212     {
2213         // from attribute 9
2214         sf.containerid = mp.containerid;
2215         sf.videocodecid = mp.videocodecid;
2216         sf.audiocodecid = mp.audiocodecid;
2217     }
2218     else if (mp.shortformat < mediaInfo.mediaCodecs.shortformats.size())
2219     {
2220         sf = mediaInfo.mediaCodecs.shortformats[mp.shortformat];
2221     }
2222 
2223     for (std::map<std::string, unsigned>::const_iterator i = mediaInfo.mediaCodecs.containers.begin(); i != mediaInfo.mediaCodecs.containers.end(); ++i)
2224     {
2225         if (i->second == sf.containerid)
2226         {
2227             out << "Format: " << i->first << sep;
2228         }
2229     }
2230     for (std::map<std::string, unsigned>::const_iterator i = mediaInfo.mediaCodecs.videocodecs.begin(); i != mediaInfo.mediaCodecs.videocodecs.end(); ++i)
2231     {
2232         if (i->second == sf.videocodecid)
2233         {
2234             out << "Video: " << i->first << sep;
2235         }
2236     }
2237 
2238     for (std::map<std::string, unsigned>::const_iterator i = mediaInfo.mediaCodecs.audiocodecs.begin(); i != mediaInfo.mediaCodecs.audiocodecs.end(); ++i)
2239     {
2240         if (i->second == sf.audiocodecid)
2241         {
2242             out << "Audio: " << i->first << sep;
2243         }
2244     }
2245 
2246     if (mp.width > 0)
2247     {
2248         out << "Width: " << mp.width << sep;
2249     }
2250     if (mp.height > 0)
2251     {
2252         out << "Height: " << mp.height << sep;
2253     }
2254     if (mp.fps > 0)
2255     {
2256         out << "Fps: " << mp.fps << sep;
2257     }
2258     if (mp.playtime > 0)
2259     {
2260         out << "Playtime: " << mp.playtime << sep;
2261     }
2262 
2263     string result = out.str();
2264     result.erase(result.size() - (result.empty() ? 0 : 1));
2265     return result;
2266 }
2267 
showMediaInfo(const std::string & fileattributes,uint32_t fakey[4],MediaFileInfo & mediaInfo,bool oneline)2268 string showMediaInfo(const std::string& fileattributes, uint32_t fakey[4], MediaFileInfo& mediaInfo, bool oneline)
2269 {
2270     MediaProperties mp = MediaProperties::decodeMediaPropertiesAttributes(fileattributes, fakey);
2271     return showMediaInfo(mp, mediaInfo, oneline);
2272 }
2273 
showMediaInfo(Node * n,MediaFileInfo &,bool oneline)2274 string showMediaInfo(Node* n, MediaFileInfo& /*mediaInfo*/, bool oneline)
2275 {
2276     if (n->hasfileattribute(fa_media))
2277     {
2278         MediaProperties mp = MediaProperties::decodeMediaPropertiesAttributes(n->fileattrstring, (uint32_t*)(n->nodekey().data() + FILENODEKEYLENGTH / 2));
2279         return showMediaInfo(mp, client->mediaFileInfo, oneline);
2280     }
2281     return "The node has no mediainfo attribute";
2282 }
2283 
2284 #endif
2285 
2286 // password change-related state information
2287 static byte pwkey[SymmCipher::KEYLENGTH];
2288 static byte pwkeybuf[SymmCipher::KEYLENGTH];
2289 static byte newpwkey[SymmCipher::KEYLENGTH];
2290 static string newpassword;
2291 
2292 // readline callback - exit if EOF, add to history unless password
2293 #if !defined(WIN32) || !defined(NO_READLINE)
store_line(char * l)2294 static void store_line(char* l)
2295 {
2296     if (!l)
2297     {
2298         delete console;
2299         exit(0);
2300     }
2301 
2302 #ifndef NO_READLINE
2303     if (*l && prompt == COMMAND)
2304     {
2305         add_history(l);
2306     }
2307 #endif
2308 
2309     line = l;
2310 }
2311 #endif
2312 
2313 class FileFindCommand : public Command
2314 {
2315 public:
2316     struct Stack : public std::deque<handle>
2317     {
2318         size_t filesLeft = 0;
2319         set<string> servers;
2320     };
2321 
FileFindCommand(std::shared_ptr<Stack> & s,MegaClient * mc)2322     FileFindCommand(std::shared_ptr<Stack>& s, MegaClient* mc) : stack(s)
2323     {
2324         h = stack->front();
2325         stack->pop_front();
2326 
2327         client = mc;
2328 
2329         cmd("g");
2330         arg("n", (byte*)&h, MegaClient::NODEHANDLE);
2331         arg("g", 1);
2332         arg("v", 2);  // version 2: server can supply details for cloudraid files
2333 
2334         if (mc->usehttps)
2335         {
2336             arg("ssl", 2);
2337         }
2338     }
2339 
server(const string & url)2340     static string server(const string& url)
2341     {
2342         const string pattern("://");
2343         size_t start_index = url.find(pattern);
2344         if (start_index != string::npos)
2345         {
2346             start_index += pattern.size();
2347             const size_t end_index = url.find("/", start_index);
2348             if (end_index != string::npos)
2349             {
2350                 return url.substr(start_index, end_index - start_index);
2351             }
2352         }
2353         return "";
2354     }
2355 
2356     // process file credentials
procresult()2357     void procresult() override
2358     {
2359         if (client->json.isnumeric())
2360         {
2361             client->json.getint();
2362         }
2363         else
2364         {
2365             std::vector<string> tempurls;
2366             bool done = false;
2367             while (!done)
2368             {
2369                 switch (client->json.getnameid())
2370                 {
2371                 case EOO:
2372                     done = true;
2373                     break;
2374 
2375                 case 'g':
2376                     if (client->json.enterarray())   // now that we are requesting v2, the reply will be an array of 6 URLs for a raid download, or a single URL for the original direct download
2377                     {
2378                         for (;;)
2379                         {
2380                             std::string tu;
2381                             if (!client->json.storeobject(&tu))
2382                             {
2383                                 break;
2384                             }
2385                             tempurls.push_back(tu);
2386                         }
2387                         client->json.leavearray();
2388                         if (tempurls.size() == 6)
2389                         {
2390                             if (Node* n = client->nodebyhandle(h))
2391                             {
2392                                 cout << n->displaypath() << endl;
2393 
2394                                 for (const auto& url : tempurls)
2395                                 {
2396                                     stack->servers.insert(server(url));
2397                                 }
2398                             }
2399                         }
2400                         break;
2401                     }
2402                     // otherwise fall through
2403 
2404                 default:
2405                     client->json.storeobject();
2406                 }
2407             }
2408         }
2409 
2410         // now query for the next one - we don't send them all at once as there may be a lot!
2411         --stack->filesLeft;
2412         if (!stack->empty())
2413         {
2414             client->reqs.add(new FileFindCommand(stack, client));
2415         }
2416         else if (!stack->filesLeft)
2417         {
2418             cout << "<find complete>" << endl;
2419             for (auto s : stack->servers)
2420             {
2421                 cout << s << endl;
2422             }
2423         }
2424     }
2425 
2426 private:
2427     handle h;
2428     std::shared_ptr<Stack> stack;
2429 };
2430 
2431 
getDepthFirstFileHandles(Node * n,deque<handle> & q)2432 void getDepthFirstFileHandles(Node* n, deque<handle>& q)
2433 {
2434     for (auto c : n->children)
2435     {
2436         if (c->type == FILENODE)
2437         {
2438             q.push_back(c->nodehandle);
2439         }
2440     }
2441     for (auto& c : n->children)
2442     {
2443         if (c->type > FILENODE)
2444         {
2445             getDepthFirstFileHandles(c, q);
2446         }
2447     }
2448 }
2449 
exec_find(autocomplete::ACState & s)2450 void exec_find(autocomplete::ACState& s)
2451 {
2452     if (s.words[1].s == "raided")
2453     {
2454         if (Node* n = client->nodebyhandle(cwd))
2455         {
2456             auto q = std::make_shared<FileFindCommand::Stack>();
2457             getDepthFirstFileHandles(n, *q);
2458             q->filesLeft = q->size();
2459             cout << "<find checking " << q->size() << " files>" << endl;
2460             if (q->empty())
2461             {
2462                 cout << "<find complete>" << endl;
2463             }
2464             else
2465             {
2466                 for (int i = 0; i < 25 && !q->empty(); ++i)
2467                 {
2468                     client->reqs.add(new FileFindCommand(q, client));
2469                 }
2470             }
2471         }
2472     }
2473 }
2474 
recurse_findemptysubfoldertrees(Node * n,bool moveToTrash)2475 bool recurse_findemptysubfoldertrees(Node* n, bool moveToTrash)
2476 {
2477     if (n->type == FILENODE)
2478     {
2479         return false;
2480     }
2481 
2482     std::vector<Node*> emptyFolders;
2483     bool empty = true;
2484     Node* trash = client->nodebyhandle(client->rootnodes[2]);
2485     for (auto c : n->children)
2486     {
2487         bool subfolderEmpty = recurse_findemptysubfoldertrees(c, moveToTrash);
2488         if (subfolderEmpty)
2489         {
2490             emptyFolders.push_back(c);
2491         }
2492         empty = empty && subfolderEmpty;
2493     }
2494     if (!empty)
2495     {
2496         for (auto c : emptyFolders)
2497         {
2498             if (moveToTrash)
2499             {
2500                 cout << "moving to trash: " << c->displaypath() << endl;
2501                 client->rename(c, trash);
2502             }
2503             else
2504             {
2505                 cout << "empty folder tree at: " << c->displaypath() << endl;
2506             }
2507         }
2508     }
2509     return empty;
2510 }
2511 
exec_findemptysubfoldertrees(autocomplete::ACState & s)2512 void exec_findemptysubfoldertrees(autocomplete::ACState& s)
2513 {
2514     bool moveToTrash = s.extractflag("-movetotrash");
2515     if (Node* n = client->nodebyhandle(cwd))
2516     {
2517         if (recurse_findemptysubfoldertrees(n, moveToTrash))
2518         {
2519             cout << "the search root path only contains empty folders: " << n->displaypath() << endl;
2520         }
2521     }
2522 }
2523 
typematchesnodetype(nodetype_t pathtype,nodetype_t nodetype)2524 bool typematchesnodetype(nodetype_t pathtype, nodetype_t nodetype)
2525 {
2526     switch (pathtype)
2527     {
2528     case FILENODE:
2529     case FOLDERNODE: return nodetype == pathtype;
2530     default: return false;
2531     }
2532 }
2533 
2534 #ifdef USE_FILESYSTEM
recursiveCompare(Node * mn,fs::path p)2535 bool recursiveCompare(Node* mn, fs::path p)
2536 {
2537     nodetype_t pathtype = fs::is_directory(p) ? FOLDERNODE : fs::is_regular_file(p) ? FILENODE : TYPE_UNKNOWN;
2538     if (!typematchesnodetype(pathtype, mn->type))
2539     {
2540         cout << "Path type mismatch: " << mn->displaypath() << ":" << mn->type << " " << p.u8string() << ":" << pathtype << endl;
2541         return false;
2542     }
2543 
2544     if (pathtype == FILENODE)
2545     {
2546         uint64_t size = (uint64_t) fs::file_size(p);
2547         if (size != (uint64_t) mn->size)
2548         {
2549             cout << "File size mismatch: " << mn->displaypath() << ":" << mn->size << " " << p.u8string() << ":" << size << endl;
2550         }
2551     }
2552 
2553     if (pathtype != FOLDERNODE)
2554     {
2555         return true;
2556     }
2557 
2558     std::string path = p.u8string();
2559     auto fileSystemType = client->fsaccess->getFilesystemType(LocalPath::fromPath(path, *client->fsaccess));
2560     multimap<string, Node*> ms;
2561     multimap<string, fs::path> ps;
2562     for (auto& m : mn->children)
2563     {
2564         string leafname = m->displayname();
2565         client->fsaccess->escapefsincompatible(&leafname, fileSystemType);
2566         ms.emplace(leafname, m);
2567     }
2568     for (fs::directory_iterator pi(p); pi != fs::directory_iterator(); ++pi)
2569     {
2570         auto leafname = pi->path().filename().u8string();
2571         client->fsaccess->escapefsincompatible(&leafname, fileSystemType);
2572         ps.emplace(leafname, pi->path());
2573     }
2574 
2575     for (auto p_iter = ps.begin(); p_iter != ps.end(); )
2576     {
2577         auto er = ms.equal_range(p_iter->first);
2578         auto next_p = p_iter;
2579         ++next_p;
2580         for (auto i = er.first; i != er.second; ++i)
2581         {
2582             if (recursiveCompare(i->second, p_iter->second))
2583             {
2584                 ms.erase(i);
2585                 ps.erase(p_iter);
2586                 break;
2587             }
2588         }
2589         p_iter = next_p;
2590     }
2591     if (ps.empty() && ms.empty())
2592     {
2593         return true;
2594     }
2595     else
2596     {
2597         cout << "Extra content detected between " << mn->displaypath() << " and " << p.u8string() << endl;
2598         for (auto& mi : ms) cout << "Extra remote: " << mi.first << endl;
2599         for (auto& pi : ps) cout << "Extra local: " << pi.second << endl;
2600         return false;
2601     };
2602 }
2603 #endif
nodeFromRemotePath(const string & s)2604 Node* nodeFromRemotePath(const string& s)
2605 {
2606     Node* n;
2607     if (s.empty())
2608     {
2609         n = client->nodebyhandle(cwd);
2610     }
2611     else
2612     {
2613         n = nodebypath(s.c_str());
2614     }
2615     if (!n)
2616     {
2617         cout << "remote path not found: '" << s << "'" << endl;
2618     }
2619     return n;
2620 }
2621 
2622 #ifdef MEGA_MEASURE_CODE
2623 
exec_deferRequests(autocomplete::ACState & s)2624 void exec_deferRequests(autocomplete::ACState& s)
2625 {
2626     // cause all the API requests of this type to be gathered up so they will be sent in a single batch, for timing purposes
2627     bool putnodes = s.extractflag("-putnodes");
2628     bool movenode = s.extractflag("-movenode");
2629     bool delnode = s.extractflag("-delnode");
2630 
2631     client->reqs.deferRequests =    [=](Command* c)
2632                                     {
2633                                         return  (putnodes && dynamic_cast<CommandPutNodes*>(c)) ||
2634                                                 (movenode && dynamic_cast<CommandMoveNode*>(c)) ||
2635                                                 (delnode && dynamic_cast<CommandDelNode*>(c));
2636                                     };
2637 }
2638 
exec_sendDeferred(autocomplete::ACState & s)2639 void exec_sendDeferred(autocomplete::ACState& s)
2640 {
2641     // send those gathered up commands, and optionally reset the gathering
2642     client->reqs.sendDeferred();
2643 
2644     if (s.extractflag("-reset"))
2645     {
2646         client->reqs.deferRequests = nullptr;
2647     }
2648 }
2649 
exec_codeTimings(autocomplete::ACState & s)2650 void exec_codeTimings(autocomplete::ACState& s)
2651 {
2652     bool reset = s.extractflag("-reset");
2653     cout << client->performanceStats.report(reset, client->httpio, client->waiter, client->reqs) << flush;
2654 }
2655 
2656 #endif
2657 
2658 #ifdef USE_FILESYSTEM
pathFromLocalPath(const string & s,bool mustexist)2659 fs::path pathFromLocalPath(const string& s, bool mustexist)
2660 {
2661     fs::path p = s.empty() ? fs::current_path() : fs::u8path(s);
2662 #ifdef WIN32
2663     p = fs::u8path("\\\\?\\" + p.u8string());
2664 #endif
2665     if (mustexist && !fs::exists(p))
2666     {
2667         cout << "local path not found: '" << s << "'";
2668         return fs::path();
2669     }
2670     return p;
2671 }
2672 
exec_treecompare(autocomplete::ACState & s)2673 void exec_treecompare(autocomplete::ACState& s)
2674 {
2675     fs::path p = pathFromLocalPath(s.words[1].s, true);
2676     Node* n = nodeFromRemotePath(s.words[2].s);
2677     if (n && !p.empty())
2678     {
2679         recursiveCompare(n, p);
2680     }
2681 }
2682 
2683 
buildLocalFolders(fs::path targetfolder,const string & prefix,int foldersperfolder,int recurselevel,int filesperfolder,int filesize,int & totalfilecount,int & totalfoldercount)2684 bool buildLocalFolders(fs::path targetfolder, const string& prefix, int foldersperfolder, int recurselevel, int filesperfolder, int filesize, int& totalfilecount, int& totalfoldercount)
2685 {
2686     fs::path p = targetfolder / fs::u8path(prefix);
2687     if (!fs::create_directory(p))
2688         return false;
2689     ++totalfoldercount;
2690 
2691     for (int i = 0; i < filesperfolder; ++i)
2692     {
2693         string filename = prefix + "_file_" + std::to_string(++totalfilecount);
2694         fs::path fp = p / fs::u8path(filename);
2695         ofstream fs(fp.u8string(), std::ios::binary);
2696 
2697         for (unsigned j = filesize / sizeof(int); j--; )
2698         {
2699             fs.write((char*)&totalfilecount, sizeof(int));
2700         }
2701         fs.write((char*)&totalfilecount, filesize % sizeof(int));
2702     }
2703 
2704     if (recurselevel > 1)
2705     {
2706         for (int i = 0; i < foldersperfolder; ++i)
2707         {
2708             if (!buildLocalFolders(p, prefix + "_" + std::to_string(i), foldersperfolder, recurselevel - 1, filesperfolder, filesize, totalfilecount, totalfoldercount))
2709                 return false;
2710         }
2711     }
2712     return true;
2713 }
2714 
exec_generatetestfilesfolders(autocomplete::ACState & s)2715 void exec_generatetestfilesfolders(autocomplete::ACState& s)
2716 {
2717     string param, nameprefix = "test";
2718     int folderdepth = 1, folderwidth = 1, filecount = 100, filesize = 1024;
2719     if (s.extractflagparam("-folderdepth", param)) folderdepth = atoi(param.c_str());
2720     if (s.extractflagparam("-folderwidth", param)) folderwidth = atoi(param.c_str());
2721     if (s.extractflagparam("-filecount", param)) filecount = atoi(param.c_str());
2722     if (s.extractflagparam("-filesize", param)) filesize = atoi(param.c_str());
2723     if (s.extractflagparam("-nameprefix", param)) nameprefix = param;
2724 
2725     fs::path p = pathFromLocalPath(s.words[1].s, true);
2726     if (!p.empty())
2727     {
2728         int totalfilecount = 0, totalfoldercount = 0;
2729         buildLocalFolders(p, nameprefix, folderwidth, folderdepth, filecount, filesize, totalfilecount, totalfoldercount);
2730         cout << "created " << totalfilecount << " files and " << totalfoldercount << " folders";
2731     }
2732 }
2733 
2734 #endif
2735 
exec_getcloudstorageused(autocomplete::ACState & s)2736 void exec_getcloudstorageused(autocomplete::ACState& s)
2737 {
2738     cout << client->mFingerprints.getSumSizes() << endl;
2739 }
2740 
exec_getuserquota(autocomplete::ACState & s)2741 void exec_getuserquota(autocomplete::ACState& s)
2742 {
2743     bool storage = s.extractflag("-storage");
2744     bool transfer = s.extractflag("-transfer");
2745     bool pro = s.extractflag("-pro");
2746 
2747     if (!storage && !transfer && !pro)
2748     {
2749         storage = transfer = pro = true;
2750     }
2751 
2752     client->getaccountdetails(new AccountDetails, storage, transfer, pro, false, false, false, -1);
2753 }
2754 
exec_getuserdata(autocomplete::ACState & s)2755 void exec_getuserdata(autocomplete::ACState& s)
2756 {
2757     client->getuserdata();
2758 }
2759 
exec_querytransferquota(autocomplete::ACState & ac)2760 void exec_querytransferquota(autocomplete::ACState& ac)
2761 {
2762     client->querytransferquota(atoll(ac.words[1].s.c_str()));
2763 }
2764 
querytransferquota_result(int n)2765 void DemoApp::querytransferquota_result(int n)
2766 {
2767     cout << "querytransferquota_result: " << n << endl;
2768 }
2769 
2770 autocomplete::ACN autocompleteTemplate;
2771 
exec_help(ac::ACState &)2772 void exec_help(ac::ACState&)
2773 {
2774     cout << *autocompleteTemplate << flush;
2775 }
2776 
2777 bool quit_flag = false;
2778 
exec_quit(ac::ACState &)2779 void exec_quit(ac::ACState&)
2780 {
2781     quit_flag = true;
2782 }
2783 
exec_showattributes(autocomplete::ACState & s)2784 void exec_showattributes(autocomplete::ACState& s)
2785 {
2786     if (const Node* n = nodeFromRemotePath(s.words[1].s))
2787     {
2788         for (auto pair : n->attrs.map)
2789         {
2790             char namebuf[10]{};
2791             AttrMap::nameid2string(pair.first, namebuf);
2792             if (pair.first == 'c')
2793             {
2794                 FileFingerprint f;
2795                 f.unserializefingerprint(&pair.second);
2796                 cout << namebuf << ": " << pair.second << " (fingerprint: size " << f.size << " mtime " << f.mtime
2797                     << " crc " << std::hex << f.crc[0] << " " << f.crc[1] << " " << f.crc[2] << " " << f.crc[3] << std::dec << ")"
2798                     << " (node fingerprint: size " << n->size << " mtime " << n->mtime
2799                     << " crc " << std::hex << n->crc[0] << " " << n->crc[1] << " " << n->crc[2] << " " << n->crc[3] << std::dec << ")" << endl;
2800             }
2801             else
2802             {
2803                 cout << namebuf << ": " << pair.second << endl;
2804             }
2805         }
2806     }
2807 }
2808 
printAuthringInformation(handle userhandle)2809 void printAuthringInformation(handle userhandle)
2810 {
2811     for (auto &it : client->mAuthRings)
2812     {
2813         AuthRing &authring = it.second;
2814         attr_t at = it.first;
2815         cout << User::attr2string(at) << ": " << endl;
2816         for (auto &uh : authring.getTrackedUsers())
2817         {
2818             if (uh == userhandle || ISUNDEF(userhandle))    // no user was typed --> show authring for all users
2819             {
2820                 User *user = client->finduser(uh);
2821                 string email = user ? user->email : "not a contact";
2822 
2823                 cout << "\tUserhandle: \t" << Base64Str<MegaClient::USERHANDLE>(uh) << endl;
2824                 cout << "\tEmail:      \t" << email << endl;
2825                 cout << "\tFingerprint:\t" << Utils::stringToHex(authring.getFingerprint(uh)) << endl;
2826                 cout << "\tAuth. level: \t" << AuthRing::authMethodToStr(authring.getAuthMethod(uh)) << endl;
2827             }
2828         }
2829     }
2830 }
2831 
exec_setmaxconnections(autocomplete::ACState & s)2832 void exec_setmaxconnections(autocomplete::ACState& s)
2833 {
2834     auto direction = s.words[1].s == "put" ? PUT : GET;
2835     if (s.words.size() == 3)
2836     {
2837         client->setmaxconnections(direction, atoi(s.words[2].s.c_str()));
2838     }
2839     cout << "connections: " << (int)client->connections[direction] << endl;
2840 }
2841 
2842 
2843 class MegaCLILogger : public ::mega::Logger {
2844 public:
2845     ofstream mLogFile;
2846 
log(const char *,int loglevel,const char *,const char * message,const char ** directMessages,size_t * directMessagesSizes,unsigned numberMessages)2847     void log(const char*, int loglevel, const char*, const char *message
2848 #ifdef ENABLE_LOG_PERFORMANCE
2849                  , const char **directMessages, size_t *directMessagesSizes, unsigned numberMessages
2850 #endif
2851     ) override
2852     {
2853         if (mLogFile.is_open())
2854         {
2855             mLogFile << Waiter::ds << " " << SimpleLogger::toStr(static_cast<LogLevel>(loglevel)) << ": ";
2856             if (message) mLogFile << message;
2857 #ifdef ENABLE_LOG_PERFORMANCE
2858             for (unsigned i = 0; i < numberMessages; ++i) mLogFile.write(directMessages[i], directMessagesSizes[i]);
2859 #endif
2860             mLogFile << std::endl;
2861         }
2862         else
2863         {
2864 #ifdef _WIN32
2865             auto t = std::time(NULL);
2866             char ts[50];
2867             if (!std::strftime(ts, sizeof(ts), "%H:%M:%S", std::localtime(&t)))
2868             {
2869                 ts[0] = '\0';
2870             }
2871 
2872             string s;
2873             s.reserve(1024);
2874             s += ts;
2875             s += " ";
2876             if (message) s += message;
2877 #ifdef ENABLE_LOG_PERFORMANCE
2878             for (unsigned i = 0; i < numberMessages; ++i) s.append(directMessages[i], directMessagesSizes[i]);
2879 #endif
2880             s += "\r\n";
2881             OutputDebugStringA(s.c_str());
2882 #else
2883             if (loglevel >= SimpleLogger::logCurrentLevel)
2884             {
2885                 auto t = std::time(NULL);
2886                 char ts[50];
2887                 if (!std::strftime(ts, sizeof(ts), "%H:%M:%S", std::localtime(&t)))
2888                 {
2889                     ts[0] = '\0';
2890                 }
2891                 std::cout << "[" << ts << "] " << SimpleLogger::toStr(static_cast<LogLevel>(loglevel)) << ": " << message << std::endl;
2892         }
2893 #endif
2894         }
2895     }
2896 };
2897 
exec_fingerprint(autocomplete::ACState & s)2898 void exec_fingerprint(autocomplete::ACState& s)
2899 {
2900     auto localfilepath = LocalPath::fromPath(s.words[1].s, *client->fsaccess);
2901     auto fa = client->fsaccess->newfileaccess();
2902 
2903     if (fa->fopen(localfilepath, true, false, nullptr))
2904     {
2905         FileFingerprint fp;
2906         fp.genfingerprint(fa.get());
2907         cout << Utils::stringToHex(std::string((const char*)&fp.size, sizeof(fp.size))) << "/" <<
2908                 Utils::stringToHex(std::string((const char*)&fp.mtime, sizeof(fp.mtime))) << "/" <<
2909                 Utils::stringToHex(std::string((const char*)&fp.crc, sizeof(fp.crc))) << endl;
2910     }
2911     else
2912     {
2913         cout << "Failed to open: " << s.words[1].s << endl;
2914     }
2915 }
2916 
2917 MegaCLILogger gLogger;
2918 
autocompleteSyntax()2919 autocomplete::ACN autocompleteSyntax()
2920 {
2921     using namespace autocomplete;
2922     std::unique_ptr<Either> p(new Either("      "));
2923 
2924     p->Add(exec_apiurl, sequence(text("apiurl"), opt(sequence(param("url"), opt(param("disablepkp"))))));
2925     p->Add(exec_login, sequence(text("login"), either(sequence(param("email"), opt(param("password"))), exportedLink(false, true), param("session"), sequence(text("autoresume"), opt(param("id"))))));
2926     p->Add(exec_begin, sequence(text("begin"), opt(param("ephemeralhandle#ephemeralpw"))));
2927     p->Add(exec_signup, sequence(text("signup"), either(sequence(param("email"), param("name")), param("confirmationlink"))));
2928     p->Add(exec_cancelsignup, sequence(text("cancelsignup")));
2929     p->Add(exec_confirm, sequence(text("confirm")));
2930     p->Add(exec_session, sequence(text("session"), opt(sequence(text("autoresume"), opt(param("id"))))));
2931     p->Add(exec_mount, sequence(text("mount")));
2932     p->Add(exec_ls, sequence(text("ls"), opt(flag("-R")), opt(sequence(flag("-tofile"), param("filename"))), opt(remoteFSFolder(client, &cwd))));
2933     p->Add(exec_cd, sequence(text("cd"), opt(remoteFSFolder(client, &cwd))));
2934     p->Add(exec_pwd, sequence(text("pwd")));
2935     p->Add(exec_lcd, sequence(text("lcd"), opt(localFSFolder())));
2936 #ifdef USE_FILESYSTEM
2937     p->Add(exec_lls, sequence(text("lls"), opt(flag("-R")), opt(localFSFolder())));
2938     p->Add(exec_lpwd, sequence(text("lpwd")));
2939     p->Add(exec_lmkdir, sequence(text("lmkdir"), localFSFolder()));
2940 #endif
2941     p->Add(exec_import, sequence(text("import"), exportedLink(true, false)));
2942     p->Add(exec_folderlinkinfo, sequence(text("folderlink"), opt(param("link"))));
2943     p->Add(exec_open, sequence(text("open"), exportedLink(false, true)));
2944     p->Add(exec_put, sequence(text("put"), opt(flag("-r")), localFSPath("localpattern"), opt(either(remoteFSPath(client, &cwd, "dst"),param("dstemail")))));
2945     p->Add(exec_putq, sequence(text("putq"), repeat(either(flag("-active"), flag("-all"), flag("-count"))), opt(param("cancelslot"))));
2946 #ifdef USE_FILESYSTEM
2947     p->Add(exec_get, sequence(text("get"), opt(sequence(flag("-r"), opt(flag("-foldersonly")))), remoteFSPath(client, &cwd), opt(sequence(param("offset"), opt(param("length"))))));
2948 #else
2949     p->Add(exec_get, sequence(text("get"), remoteFSPath(client, &cwd), opt(sequence(param("offset"), opt(param("length"))))));
2950 #endif
2951     p->Add(exec_get, sequence(text("get"), flag("-re"), param("regularexpression")));
2952     p->Add(exec_get, sequence(text("get"), exportedLink(true, false), opt(sequence(param("offset"), opt(param("length"))))));
2953     p->Add(exec_getq, sequence(text("getq"), repeat(either(flag("-active"), flag("-all"), flag("-count"))), opt(param("cancelslot"))));
2954     p->Add(exec_pause, sequence(text("pause"), either(text("status"), sequence(opt(either(text("get"), text("put"))), opt(text("hard"))))));
2955     p->Add(exec_getfa, sequence(text("getfa"), wholenumber(1), opt(remoteFSPath(client, &cwd)), opt(text("cancel"))));
2956 #ifdef USE_MEDIAINFO
2957     p->Add(exec_mediainfo, sequence(text("mediainfo"), either(sequence(text("calc"), localFSFile()), sequence(text("show"), remoteFSFile(client, &cwd)))));
2958 #endif
2959     p->Add(exec_smsverify, sequence(text("smsverify"), either(sequence(text("send"), param("phonenumber"), opt(param("reverifywhitelisted"))), sequence(text("code"), param("verificationcode")))));
2960     p->Add(exec_verifiedphonenumber, sequence(text("verifiedphone")));
2961     p->Add(exec_resetverifiedphonenumber, sequence(text("resetverifiedphone")));
2962     p->Add(exec_mkdir, sequence(text("mkdir"), opt(flag("-allowduplicate")), opt(flag("-exactleafname")), remoteFSFolder(client, &cwd)));
2963     p->Add(exec_rm, sequence(text("rm"), remoteFSPath(client, &cwd), opt(sequence(flag("-regexchild"), param("regex")))));
2964     p->Add(exec_mv, sequence(text("mv"), remoteFSPath(client, &cwd, "src"), remoteFSPath(client, &cwd, "dst")));
2965     p->Add(exec_cp, sequence(text("cp"), remoteFSPath(client, &cwd, "src"), either(remoteFSPath(client, &cwd, "dst"), param("dstemail"))));
2966     p->Add(exec_du, sequence(text("du"), remoteFSPath(client, &cwd)));
2967 #ifdef ENABLE_SYNC
2968     p->Add(exec_sync, sequence(text("sync"), opt(either(sequence(localFSPath(), remoteFSPath(client, &cwd, "dst")), param("cancelslot")))));
2969     p->Add(exec_syncconfig, sequence(text("syncconfig"), opt(sequence(param("type (TWOWAY/UP/DOWN)"), opt(sequence(param("syncDeletions (ON/OFF)"), param("forceOverwrite (ON/OFF)")))))));
2970 #endif
2971     p->Add(exec_export, sequence(text("export"), remoteFSPath(client, &cwd), opt(either(param("expiretime"), text("del")))));
2972     p->Add(exec_share, sequence(text("share"), opt(sequence(remoteFSPath(client, &cwd), opt(sequence(contactEmail(client), opt(either(text("r"), text("rw"), text("full"))), opt(param("origemail"))))))));
2973     p->Add(exec_invite, sequence(text("invite"), param("dstemail"), opt(either(param("origemail"), text("del"), text("rmd")))));
2974 
2975     p->Add(exec_clink, sequence(text("clink"), either(text("renew"), sequence(text("query"), param("handle")), sequence(text("del"), opt(param("handle"))))));
2976 
2977     p->Add(exec_ipc, sequence(text("ipc"), param("handle"), either(text("a"), text("d"), text("i"))));
2978     p->Add(exec_showpcr, sequence(text("showpcr")));
2979     p->Add(exec_users, sequence(text("users"), opt(sequence(contactEmail(client), text("del")))));
2980     p->Add(exec_getua, sequence(text("getua"), param("attrname"), opt(contactEmail(client))));
2981     p->Add(exec_putua, sequence(text("putua"), param("attrname"), opt(either(text("del"), sequence(text("set"), param("string")), sequence(text("load"), localFSFile())))));
2982 #ifdef DEBUG
2983     p->Add(exec_delua, sequence(text("delua"), param("attrname")));
2984     p->Add(exec_devcommand, sequence(text("devcommand"), param("subcommand"), opt(param("email"))));
2985 #endif
2986     p->Add(exec_alerts, sequence(text("alerts"), opt(either(text("new"), text("old"), wholenumber(10), text("notify"), text("seen")))));
2987     p->Add(exec_recentactions, sequence(text("recentactions"), param("hours"), param("maxcount")));
2988     p->Add(exec_recentnodes, sequence(text("recentnodes"), param("hours"), param("maxcount")));
2989 
2990     p->Add(exec_putbps, sequence(text("putbps"), opt(either(wholenumber(100000), text("auto"), text("none")))));
2991     p->Add(exec_killsession, sequence(text("killsession"), opt(either(text("all"), param("sessionid")))));
2992     p->Add(exec_whoami, sequence(text("whoami"), repeat(either(flag("-storage"), flag("-transfer"), flag("-pro"), flag("-transactions"), flag("-purchases"), flag("-sessions")))));
2993     p->Add(exec_verifycredentials, sequence(text("credentials"), either(text("show"), text("status"), text("verify"), text("reset")), opt(contactEmail(client))));
2994     p->Add(exec_passwd, sequence(text("passwd")));
2995     p->Add(exec_reset, sequence(text("reset"), contactEmail(client), opt(text("mk"))));
2996     p->Add(exec_recover, sequence(text("recover"), param("recoverylink")));
2997     p->Add(exec_cancel, sequence(text("cancel"), opt(param("cancellink"))));
2998     p->Add(exec_email, sequence(text("email"), opt(either(param("newemail"), param("emaillink")))));
2999     p->Add(exec_retry, sequence(text("retry")));
3000     p->Add(exec_recon, sequence(text("recon")));
3001     p->Add(exec_reload, sequence(text("reload"), opt(text("nocache"))));
3002     p->Add(exec_logout, sequence(text("logout")));
3003     p->Add(exec_locallogout, sequence(text("locallogout")));
3004     p->Add(exec_symlink, sequence(text("symlink")));
3005     p->Add(exec_version, sequence(text("version")));
3006     p->Add(exec_debug, sequence(text("debug"), opt(either(flag("-on"), flag("-off"))), opt(localFSFile())));
3007     p->Add(exec_verbose, sequence(text("verbose"), opt(either(flag("-on"), flag("-off")))));
3008 #if defined(WIN32) && defined(NO_READLINE)
3009     p->Add(exec_clear, sequence(text("clear")));
3010     p->Add(exec_codepage, sequence(text("codepage"), opt(sequence(wholenumber(65001), opt(wholenumber(65001))))));
3011     p->Add(exec_log, sequence(text("log"), either(text("utf8"), text("utf16"), text("codepage")), localFSFile()));
3012 #endif
3013     p->Add(exec_test, sequence(text("test"), opt(param("data"))));
3014     p->Add(exec_fingerprint, sequence(text("fingerprint"), localFSFile("localfile")));
3015 #ifdef ENABLE_CHAT
3016     p->Add(exec_chats, sequence(text("chats")));
3017     p->Add(exec_chatc, sequence(text("chatc"), param("group"), repeat(opt(sequence(contactEmail(client), either(text("ro"), text("sta"), text("mod")))))));
3018     p->Add(exec_chati, sequence(text("chati"), param("chatid"), contactEmail(client), either(text("ro"), text("sta"), text("mod"))));
3019     p->Add(exec_chatcp, sequence(text("chatcp"), param("mownkey"), opt(sequence(text("t"), param("title64"))), repeat(sequence(contactEmail(client), either(text("ro"), text("sta"), text("mod"))))));
3020     p->Add(exec_chatr, sequence(text("chatr"), param("chatid"), opt(contactEmail(client))));
3021     p->Add(exec_chatu, sequence(text("chatu"), param("chatid")));
3022     p->Add(exec_chatup, sequence(text("chatup"), param("chatid"), param("userhandle"), either(text("ro"), text("sta"), text("mod"))));
3023     p->Add(exec_chatpu, sequence(text("chatpu")));
3024     p->Add(exec_chatga, sequence(text("chatga"), param("chatid"), param("nodehandle"), param("uid")));
3025     p->Add(exec_chatra, sequence(text("chatra"), param("chatid"), param("nodehandle"), param("uid")));
3026     p->Add(exec_chatst, sequence(text("chatst"), param("chatid"), param("title64")));
3027     p->Add(exec_chata, sequence(text("chata"), param("chatid"), param("archive")));
3028     p->Add(exec_chatl, sequence(text("chatl"), param("chatid"), either(text("del"), text("query"))));
3029     p->Add(exec_chatsm, sequence(text("chatsm"), param("chatid"), opt(param("title64"))));
3030     p->Add(exec_chatlu, sequence(text("chatlu"), param("publichandle")));
3031     p->Add(exec_chatlj, sequence(text("chatlj"), param("publichandle"), param("unifiedkey")));
3032 #endif
3033     p->Add(exec_enabletransferresumption, sequence(text("enabletransferresumption"), opt(either(text("on"), text("off")))));
3034     p->Add(exec_setmaxdownloadspeed, sequence(text("setmaxdownloadspeed"), opt(wholenumber(10000))));
3035     p->Add(exec_setmaxuploadspeed, sequence(text("setmaxuploadspeed"), opt(wholenumber(10000))));
3036     p->Add(exec_handles, sequence(text("handles"), opt(either(text("on"), text("off")))));
3037     p->Add(exec_httpsonly, sequence(text("httpsonly"), opt(either(text("on"), text("off")))));
3038 
3039     p->Add(exec_mfac, sequence(text("mfac"), param("email")));
3040     p->Add(exec_mfae, sequence(text("mfae")));
3041     p->Add(exec_mfad, sequence(text("mfad"), param("pin")));
3042 
3043 #if defined(WIN32) && defined(NO_READLINE)
3044     p->Add(exec_autocomplete, sequence(text("autocomplete"), opt(either(text("unix"), text("dos")))));
3045     p->Add(exec_history, sequence(text("history")));
3046 #endif
3047     p->Add(exec_help, either(text("help"), text("h"), text("?")));
3048     p->Add(exec_quit, either(text("quit"), text("q"), text("exit")));
3049 
3050     p->Add(exec_find, sequence(text("find"), text("raided")));
3051     p->Add(exec_findemptysubfoldertrees, sequence(text("findemptysubfoldertrees"), opt(flag("-movetotrash"))));
3052 
3053 #ifdef MEGA_MEASURE_CODE
3054     p->Add(exec_deferRequests, sequence(text("deferrequests"), repeat(either(flag("-putnodes")))));
3055     p->Add(exec_sendDeferred, sequence(text("senddeferred"), opt(flag("-reset"))));
3056     p->Add(exec_codeTimings, sequence(text("codetimings"), opt(flag("-reset"))));
3057 #endif
3058 
3059 #ifdef USE_FILESYSTEM
3060     p->Add(exec_treecompare, sequence(text("treecompare"), localFSPath(), remoteFSPath(client, &cwd)));
3061     p->Add(exec_generatetestfilesfolders, sequence(text("generatetestfilesfolders"), repeat(either(sequence(flag("-folderdepth"), param("depth")),
3062                                                                                                    sequence(flag("-folderwidth"), param("width")),
3063                                                                                                    sequence(flag("-filecount"), param("count")),
3064                                                                                                    sequence(flag("-filesize"), param("size")),
3065                                                                                                    sequence(flag("-nameprefix"), param("prefix")))), localFSFolder("parent")));
3066 #endif
3067     p->Add(exec_querytransferquota, sequence(text("querytransferquota"), param("filesize")));
3068     p->Add(exec_getcloudstorageused, sequence(text("getcloudstorageused")));
3069     p->Add(exec_getuserquota, sequence(text("getuserquota"), repeat(either(flag("-storage"), flag("-transfer"), flag("-pro")))));
3070     p->Add(exec_getuserdata, text("getuserdata"));
3071 
3072     p->Add(exec_showattributes, sequence(text("showattributes"), remoteFSPath(client, &cwd)));
3073 
3074     p->Add(exec_setmaxconnections, sequence(text("setmaxconnections"), either(text("put"), text("get")), opt(wholenumber(4))));
3075     p->Add(exec_metamac, sequence(text("metamac"), localFSPath(), remoteFSPath(client, &cwd)));
3076 
3077     return autocompleteTemplate = std::move(p);
3078 }
3079 
3080 
3081 #ifdef USE_FILESYSTEM
recursiveget(fs::path && localpath,Node * n,bool folders,unsigned & queued)3082 bool recursiveget(fs::path&& localpath, Node* n, bool folders, unsigned& queued)
3083 {
3084     if (n->type == FILENODE)
3085     {
3086         if (!folders)
3087         {
3088             auto f = new AppFileGet(n, UNDEF, NULL, -1, 0, NULL, NULL, localpath.u8string());
3089             f->appxfer_it = appxferq[GET].insert(appxferq[GET].end(), f);
3090             DBTableTransactionCommitter committer(client->tctable);
3091             client->startxfer(GET, f, committer);
3092             queued += 1;
3093         }
3094     }
3095     else if (n->type == FOLDERNODE || n->type == ROOTNODE)
3096     {
3097         fs::path newpath = localpath / fs::u8path(n->type == ROOTNODE ? "ROOTNODE" : n->displayname());
3098         if (folders)
3099         {
3100             std::error_code ec;
3101             if (fs::create_directory(newpath, ec) || !ec)
3102             {
3103                 cout << newpath << endl;
3104             }
3105             else
3106             {
3107                 cout << "Failed trying to create " << newpath << ": " << ec.message() << endl;
3108                 return false;
3109             }
3110         }
3111         for (node_list::iterator it = n->children.begin(); it != n->children.end(); it++)
3112         {
3113             if (!recursiveget(std::move(newpath), *it, folders, queued))
3114             {
3115                 return false;
3116             }
3117         }
3118     }
3119     return true;
3120 }
3121 #endif
3122 
regexget(const string & expression,Node * n,unsigned & queued)3123 bool regexget(const string& expression, Node* n, unsigned& queued)
3124 {
3125     try
3126     {
3127         std::regex re(expression);
3128 
3129         if (n->type == FOLDERNODE || n->type == ROOTNODE)
3130         {
3131             DBTableTransactionCommitter committer(client->tctable);
3132             for (node_list::iterator it = n->children.begin(); it != n->children.end(); it++)
3133             {
3134                 if ((*it)->type == FILENODE)
3135                 {
3136                     if (regex_search(string((*it)->displayname()), re))
3137                     {
3138                         auto f = new AppFileGet(*it);
3139                         f->appxfer_it = appxferq[GET].insert(appxferq[GET].end(), f);
3140                         client->startxfer(GET, f, committer);
3141                         queued += 1;
3142                     }
3143                 }
3144             }
3145         }
3146     }
3147     catch (std::exception& e)
3148     {
3149         cout << "ERROR: " << e.what() << endl;
3150         return false;
3151     }
3152     return true;
3153 }
3154 
3155 struct Login
3156 {
3157     string email, password, salt, pin;
3158     int version;
3159 
LoginLogin3160     Login() : version(0)
3161     {
3162     }
3163 
resetLogin3164     void reset()
3165     {
3166         *this = Login();
3167     }
3168 
loginLogin3169     void login(MegaClient* mc)
3170     {
3171         byte keybuf[SymmCipher::KEYLENGTH];
3172 
3173         if (version == 1)
3174         {
3175             if (error e = mc->pw_key(password.c_str(), keybuf))
3176             {
3177                 cout << "Login error: " << e << endl;
3178             }
3179             else
3180             {
3181                 mc->login(email.c_str(), keybuf, (!pin.empty()) ? pin.c_str() : NULL);
3182             }
3183         }
3184         else if (version == 2 && !salt.empty())
3185         {
3186             mc->login2(email.c_str(), password.c_str(), &salt, (!pin.empty()) ? pin.c_str() : NULL);
3187         }
3188         else
3189         {
3190             cout << "Login unexpected error" << endl;
3191         }
3192     }
3193 };
3194 static Login login;
3195 
3196 ofstream* pread_file = NULL;
3197 m_off_t pread_file_end = 0;
3198 
3199 
3200 // execute command
process_line(char * l)3201 static void process_line(char* l)
3202 {
3203     switch (prompt)
3204     {
3205     case LOGINTFA:
3206         if (strlen(l) > 1)
3207         {
3208             login.pin = l;
3209             login.login(client);
3210         }
3211         else
3212         {
3213             cout << endl << "The pin length is invalid, please try to login again." << endl;
3214         }
3215 
3216         setprompt(COMMAND);
3217         return;
3218 
3219     case SETTFA:
3220         client->multifactorauthsetup(l);
3221         setprompt(COMMAND);
3222         return;
3223 
3224     case LOGINPASSWORD:
3225 
3226         if (signupcode.size())
3227         {
3228             // verify correctness of supplied signup password
3229             client->pw_key(l, pwkey);
3230             SymmCipher pwcipher(pwkey);
3231             pwcipher.ecb_decrypt(signuppwchallenge);
3232 
3233             if (MemAccess::get<int64_t>((const char*)signuppwchallenge + 4))
3234             {
3235                 cout << endl << "Incorrect password, please try again." << endl;
3236             }
3237             else
3238             {
3239                 client->confirmsignuplink((const byte*)signupcode.data(), unsigned(signupcode.size()),
3240                     MegaClient::stringhash64(&signupemail, &pwcipher));
3241             }
3242 
3243             signupcode.clear();
3244         }
3245         else if (recoverycode.size())   // cancelling account --> check password
3246         {
3247             client->pw_key(l, pwkey);
3248             client->validatepwd(pwkey);
3249         }
3250         else if (changecode.size())     // changing email --> check password to avoid creating an invalid hash
3251         {
3252             client->pw_key(l, pwkey);
3253             client->validatepwd(pwkey);
3254         }
3255         else
3256         {
3257             login.password = l;
3258             login.login(client);
3259             cout << endl << "Logging in..." << endl;
3260         }
3261 
3262         setprompt(COMMAND);
3263         return;
3264 
3265     case OLDPASSWORD:
3266         client->pw_key(l, pwkeybuf);
3267 
3268         if (!memcmp(pwkeybuf, pwkey, sizeof pwkey))
3269         {
3270             cout << endl;
3271             setprompt(NEWPASSWORD);
3272         }
3273         else
3274         {
3275             cout << endl << "Bad password, please try again" << endl;
3276             setprompt(COMMAND);
3277         }
3278         return;
3279 
3280     case NEWPASSWORD:
3281         newpassword = l;
3282         client->pw_key(l, newpwkey);
3283 
3284         cout << endl;
3285         setprompt(PASSWORDCONFIRM);
3286         return;
3287 
3288     case PASSWORDCONFIRM:
3289         client->pw_key(l, pwkeybuf);
3290 
3291         if (memcmp(pwkeybuf, newpwkey, sizeof pwkeybuf))
3292         {
3293             cout << endl << "Mismatch, please try again" << endl;
3294         }
3295         else
3296         {
3297             error e;
3298 
3299             if (signupemail.size())
3300             {
3301                 client->sendsignuplink(signupemail.c_str(), signupname.c_str(), newpwkey);
3302             }
3303             else if (recoveryemail.size() && recoverycode.size())
3304             {
3305                 cout << endl << "Resetting password..." << endl;
3306 
3307                 if (hasMasterKey)
3308                 {
3309                     client->confirmrecoverylink(recoverycode.c_str(), recoveryemail.c_str(), newpassword.c_str(), masterkey);
3310                 }
3311                 else
3312                 {
3313                     client->confirmrecoverylink(recoverycode.c_str(), recoveryemail.c_str(), newpassword.c_str(), NULL);
3314                 }
3315 
3316                 recoverycode.clear();
3317                 recoveryemail.clear();
3318                 hasMasterKey = false;
3319                 memset(masterkey, 0, sizeof masterkey);
3320             }
3321             else
3322             {
3323                 if ((e = client->changepw(newpassword.c_str())) == API_OK)
3324                 {
3325                     memcpy(pwkey, newpwkey, sizeof pwkey);
3326                     cout << endl << "Changing password..." << endl;
3327                 }
3328                 else
3329                 {
3330                     cout << "You must be logged in to change your password." << endl;
3331                 }
3332             }
3333         }
3334 
3335         setprompt(COMMAND);
3336         signupemail.clear();
3337         return;
3338 
3339     case MASTERKEY:
3340         cout << endl << "Retrieving private RSA key for checking integrity of the Master Key..." << endl;
3341 
3342         Base64::atob(l, masterkey, sizeof masterkey);
3343         client->getprivatekey(recoverycode.c_str());
3344         return;
3345 
3346     case COMMAND:
3347         try
3348         {
3349             std::string consoleOutput;
3350             ac::autoExec(string(l), string::npos, autocompleteTemplate, false, consoleOutput, true); // todo: pass correct unixCompletions flag
3351             if (!consoleOutput.empty())
3352             {
3353                 cout << consoleOutput << flush;
3354             }
3355         }
3356         catch (std::exception& e)
3357         {
3358             cout << "Command failed: " << e.what() << endl;
3359         }
3360         return;
3361     }
3362 }
3363 
exec_ls(autocomplete::ACState & s)3364 void exec_ls(autocomplete::ACState& s)
3365 {
3366     Node* n;
3367     bool recursive = s.extractflag("-R");
3368     string toFilename;
3369     bool toFileFlag = s.extractflagparam("-tofile", toFilename);
3370 
3371     ofstream toFile;
3372     if (toFileFlag)
3373     {
3374         toFile.open(toFilename);
3375     }
3376 
3377     if (s.words.size() > 1)
3378     {
3379         n = nodebypath(s.words[1].s.c_str());
3380     }
3381     else
3382     {
3383         n = client->nodebyhandle(cwd);
3384     }
3385 
3386     if (n)
3387     {
3388         dumptree(n, recursive, 0, NULL, toFileFlag ? &toFile : nullptr);
3389     }
3390 }
3391 
exec_cd(autocomplete::ACState & s)3392 void exec_cd(autocomplete::ACState& s)
3393 {
3394     if (s.words.size() > 1)
3395     {
3396         if (Node* n = nodebypath(s.words[1].s.c_str()))
3397         {
3398             if (n->type == FILENODE)
3399             {
3400                 cout << s.words[1].s << ": Not a directory" << endl;
3401             }
3402             else
3403             {
3404                 cwd = n->nodehandle;
3405             }
3406         }
3407         else
3408         {
3409             cout << s.words[1].s << ": No such file or directory" << endl;
3410         }
3411     }
3412     else
3413     {
3414         cwd = client->rootnodes[0];
3415     }
3416 }
3417 
exec_rm(autocomplete::ACState & s)3418 void exec_rm(autocomplete::ACState& s)
3419 {
3420     string childregexstring;
3421     bool useregex = s.extractflagparam("-regexchild", childregexstring);
3422 
3423     if (Node* n = nodebypath(s.words[1].s.c_str()))
3424     {
3425         vector<Node*> v;
3426         if (useregex)
3427         {
3428             std::regex re(childregexstring);
3429             for (Node* c : n->children)
3430             {
3431                 if (std::regex_match(c->displayname(), re))
3432                 {
3433                     v.push_back(c);
3434                 }
3435             }
3436         }
3437         else
3438         {
3439             v.push_back(n);
3440         }
3441 
3442         for (auto d : v)
3443         {
3444             if (client->checkaccess(d, FULL))
3445             {
3446                 error e = client->unlink(d);
3447 
3448                 if (e)
3449                 {
3450                     cout << d->displaypath() << ": Deletion failed (" << errorstring(e) << ")" << endl;
3451                 }
3452             }
3453             else
3454             {
3455                 cout << d->displaypath() << ": Access denied" << endl;
3456             }
3457         }
3458     }
3459     else
3460     {
3461         cout << s.words[1].s << ": No such file or directory" << endl;
3462     }
3463 }
3464 
exec_mv(autocomplete::ACState & s)3465 void exec_mv(autocomplete::ACState& s)
3466 {
3467     Node *n, *tn;
3468     string newname;
3469 
3470     if (s.words.size() > 2)
3471     {
3472         // source node must exist
3473         if ((n = nodebypath(s.words[1].s.c_str())))
3474         {
3475             // we have four situations:
3476             // 1. target path does not exist - fail
3477             // 2. target node exists and is folder - move
3478             // 3. target node exists and is file - delete and rename (unless same)
3479             // 4. target path exists, but filename does not - rename
3480             if ((tn = nodebypath(s.words[2].s.c_str(), NULL, &newname)))
3481             {
3482                 error e;
3483 
3484                 if (newname.size())
3485                 {
3486                     if (tn->type == FILENODE)
3487                     {
3488                         cout << s.words[2].s << ": Not a directory" << endl;
3489 
3490                         return;
3491                     }
3492                     else
3493                     {
3494                         if ((e = client->checkmove(n, tn)) == API_OK)
3495                         {
3496                             if (!client->checkaccess(n, RDWR))
3497                             {
3498                                 cout << "Write access denied" << endl;
3499 
3500                                 return;
3501                             }
3502 
3503                             // rename
3504                             client->fsaccess->normalize(&newname);
3505                             n->attrs.map['n'] = newname;
3506 
3507                             if ((e = client->setattr(n)))
3508                             {
3509                                 cout << "Cannot rename file (" << errorstring(e) << ")" << endl;
3510                             }
3511                         }
3512                     }
3513                 }
3514                 else
3515                 {
3516                     if (tn->type == FILENODE)
3517                     {
3518                         // (there should never be any orphaned filenodes)
3519                         if (!tn->parent)
3520                         {
3521                             return;
3522                         }
3523 
3524                         if ((e = client->checkmove(n, tn->parent)) == API_OK)
3525                         {
3526                             if (!client->checkaccess(n, RDWR))
3527                             {
3528                                 cout << "Write access denied" << endl;
3529 
3530                                 return;
3531                             }
3532 
3533                             // overwrite existing target file: rename source...
3534                             n->attrs.map['n'] = tn->attrs.map['n'];
3535                             e = client->setattr(n);
3536 
3537                             if (e)
3538                             {
3539                                 cout << "Rename failed (" << errorstring(e) << ")" << endl;
3540                             }
3541 
3542                             if (n != tn)
3543                             {
3544                                 // ...delete target...
3545                                 e = client->unlink(tn);
3546 
3547                                 if (e)
3548                                 {
3549                                     cout << "Remove failed (" << errorstring(e) << ")" << endl;
3550                                 }
3551                             }
3552                         }
3553 
3554                         // ...and set target to original target's parent
3555                         tn = tn->parent;
3556                     }
3557                     else
3558                     {
3559                         e = client->checkmove(n, tn);
3560                     }
3561                 }
3562 
3563                 if (n->parent != tn)
3564                 {
3565                     if (e == API_OK)
3566                     {
3567                         e = client->rename(n, tn);
3568 
3569                         if (e)
3570                         {
3571                             cout << "Move failed (" << errorstring(e) << ")" << endl;
3572                         }
3573                     }
3574                     else
3575                     {
3576                         cout << "Move not permitted - try copy" << endl;
3577                     }
3578                 }
3579             }
3580             else
3581             {
3582                 cout << s.words[2].s << ": No such directory" << endl;
3583             }
3584         }
3585         else
3586         {
3587             cout << s.words[1].s << ": No such file or directory" << endl;
3588         }
3589     }
3590 }
3591 
3592 
exec_cp(autocomplete::ACState & s)3593 void exec_cp(autocomplete::ACState& s)
3594 {
3595     Node *n, *tn;
3596     string targetuser;
3597     string newname;
3598     error e;
3599 
3600     if (s.words.size() > 2)
3601     {
3602         if ((n = nodebypath(s.words[1].s.c_str())))
3603         {
3604             if ((tn = nodebypath(s.words[2].s.c_str(), &targetuser, &newname)))
3605             {
3606                 if (!client->checkaccess(tn, RDWR))
3607                 {
3608                     cout << "Write access denied" << endl;
3609 
3610                     return;
3611                 }
3612 
3613                 if (tn->type == FILENODE)
3614                 {
3615                     if (n->type == FILENODE)
3616                     {
3617                         // overwrite target if source and taret are files
3618 
3619                         // (there should never be any orphaned filenodes)
3620                         if (!tn->parent)
3621                         {
3622                             return;
3623                         }
3624 
3625                         // ...delete target...
3626                         e = client->unlink(tn);
3627 
3628                         if (e)
3629                         {
3630                             cout << "Cannot delete existing file (" << errorstring(e) << ")"
3631                                 << endl;
3632                         }
3633 
3634                         // ...and set target to original target's parent
3635                         tn = tn->parent;
3636                     }
3637                     else
3638                     {
3639                         cout << "Cannot overwrite file with folder" << endl;
3640                         return;
3641                     }
3642                 }
3643             }
3644 
3645             TreeProcCopy_mcli tc;
3646             unsigned nc;
3647             handle ovhandle = UNDEF;
3648 
3649             if (!n->keyApplied())
3650             {
3651                 cout << "Cannot copy a node without key" << endl;
3652                 return;
3653             }
3654 
3655             if (n->attrstring)
3656             {
3657                 n->applykey();
3658                 n->setattr();
3659                 if (n->attrstring)
3660                 {
3661                     cout << "Cannot copy undecryptable node" << endl;
3662                     return;
3663                 }
3664             }
3665 
3666             string sname;
3667             if (newname.size())
3668             {
3669                 sname = newname;
3670                 client->fsaccess->normalize(&sname);
3671             }
3672             else
3673             {
3674                 attr_map::iterator it = n->attrs.map.find('n');
3675                 if (it != n->attrs.map.end())
3676                 {
3677                     sname = it->second;
3678                 }
3679             }
3680 
3681             if (!client->versions_disabled && tn && n->type == FILENODE)
3682             {
3683                 Node *ovn = client->childnodebyname(tn, sname.c_str(), true);
3684                 if (ovn)
3685                 {
3686                     if (n->isvalid && ovn->isvalid && *(FileFingerprint*)n == *(FileFingerprint*)ovn)
3687                     {
3688                         cout << "Skipping identical node" << endl;
3689                         return;
3690                     }
3691 
3692                     ovhandle = ovn->nodehandle;
3693                 }
3694             }
3695 
3696             // determine number of nodes to be copied
3697             client->proctree(n, &tc, false, ovhandle != UNDEF);
3698 
3699             tc.allocnodes();
3700             nc = tc.nc;
3701 
3702             // build new nodes array
3703             client->proctree(n, &tc, false, ovhandle != UNDEF);
3704 
3705             // if specified target is a filename, use it
3706             if (newname.size())
3707             {
3708                 SymmCipher key;
3709                 string attrstring;
3710 
3711                 // copy source attributes and rename
3712                 AttrMap attrs;
3713 
3714                 attrs.map = n->attrs.map;
3715                 attrs.map['n'] = sname;
3716 
3717                 key.setkey((const byte*)tc.nn->nodekey.data(), tc.nn->type);
3718 
3719                 // JSON-encode object and encrypt attribute string
3720                 attrs.getjson(&attrstring);
3721                 tc.nn->attrstring.reset(new string);
3722                 client->makeattr(&key, tc.nn->attrstring, attrstring.c_str());
3723             }
3724 
3725             // tree root: no parent
3726             tc.nn->parenthandle = UNDEF;
3727             tc.nn->ovhandle = ovhandle;
3728 
3729             if (tn)
3730             {
3731                 // add the new nodes
3732                 client->putnodes(tn->nodehandle, tc.nn, nc);
3733 
3734                 // free in putnodes_result()
3735                 tc.nn = NULL;
3736             }
3737             else
3738             {
3739                 if (targetuser.size())
3740                 {
3741                     cout << "Attempting to drop into user " << targetuser << "'s inbox..." << endl;
3742 
3743                     client->putnodes(targetuser.c_str(), tc.nn, nc);
3744 
3745                     // free in putnodes_result()
3746                     tc.nn = NULL;
3747                 }
3748                 else
3749                 {
3750                     cout << s.words[2].s << ": No such file or directory" << endl;
3751                 }
3752             }
3753         }
3754         else
3755         {
3756             cout << s.words[1].s << ": No such file or directory" << endl;
3757         }
3758     }
3759 }
3760 
exec_du(autocomplete::ACState & s)3761 void exec_du(autocomplete::ACState& s)
3762 {
3763     Node *n;
3764     TreeProcDU du;
3765 
3766     if (s.words.size() > 1)
3767     {
3768         if (!(n = nodebypath(s.words[1].s.c_str())))
3769         {
3770             cout << s.words[1].s << ": No such file or directory" << endl;
3771 
3772             return;
3773         }
3774     }
3775     else
3776     {
3777         n = client->nodebyhandle(cwd);
3778     }
3779 
3780     if (n)
3781     {
3782         client->proctree(n, &du);
3783 
3784         cout << "Total storage used: " << (du.numbytes / 1048576) << " MB" << endl;
3785         cout << "Total # of files: " << du.numfiles << endl;
3786         cout << "Total # of folders: " << du.numfolders << endl;
3787     }
3788 }
3789 
exec_get(autocomplete::ACState & s)3790 void exec_get(autocomplete::ACState& s)
3791 {
3792     Node *n;
3793     string regularexpression;
3794     if (s.extractflag("-r"))
3795     {
3796 #ifdef USE_FILESYSTEM
3797         // recursive get.  create local folder structure first, then queue transfer of all files
3798         bool foldersonly = s.extractflag("-foldersonly");
3799 
3800         if (!(n = nodebypath(s.words[1].s.c_str())))
3801         {
3802             cout << s.words[1].s << ": No such folder (or file)" << endl;
3803         }
3804         else if (n->type != FOLDERNODE && n->type != ROOTNODE)
3805         {
3806             cout << s.words[1].s << ": not a folder" << endl;
3807         }
3808         else
3809         {
3810             unsigned queued = 0;
3811             cout << "creating folders: " << endl;
3812             if (recursiveget(fs::current_path(), n, true, queued))
3813             {
3814                 if (!foldersonly)
3815                 {
3816                     cout << "queueing files..." << endl;
3817                     bool alldone = recursiveget(fs::current_path(), n, false, queued);
3818                     cout << "queued " << queued << " files for download" << (!alldone ? " before failure" : "") << endl;
3819                 }
3820             }
3821         }
3822 #else
3823         cout << "Sorry, -r not supported yet" << endl;
3824 #endif
3825     }
3826     else if (s.extractflagparam("-re", regularexpression))
3827     {
3828         if (!(n = nodebypath(".")))
3829         {
3830             cout << ": No current folder" << endl;
3831         }
3832         else if (n->type != FOLDERNODE && n->type != ROOTNODE)
3833         {
3834             cout << ": not in a folder" << endl;
3835         }
3836         else
3837         {
3838             unsigned queued = 0;
3839             if (regexget(regularexpression, n, queued))
3840             {
3841                 cout << "queued " << queued << " files for download" << endl;
3842             }
3843         }
3844     }
3845     else
3846     {
3847         handle ph = UNDEF;
3848         byte key[FILENODEKEYLENGTH];
3849         if (client->parsepubliclink(s.words[1].s.c_str(), ph, key, false) == API_OK)
3850         {
3851             cout << "Checking link..." << endl;
3852             client->openfilelink(ph, key, 0);
3853             return;
3854         }
3855 
3856         n = nodebypath(s.words[1].s.c_str());
3857 
3858         if (n)
3859         {
3860             if (s.words.size() > 2)
3861             {
3862                 // read file slice
3863                 if (s.words.size() == 5)
3864                 {
3865                     pread_file = new ofstream(s.words[4].s.c_str(), std::ios_base::binary);
3866                     pread_file_end = atol(s.words[2].s.c_str()) + atol(s.words[3].s.c_str());
3867                 }
3868 
3869                 client->pread(n, atol(s.words[2].s.c_str()), (s.words.size() > 3) ? atol(s.words[3].s.c_str()) : 0, NULL);
3870             }
3871             else
3872             {
3873                 DBTableTransactionCommitter committer(client->tctable);
3874 
3875                 // queue specified file...
3876                 if (n->type == FILENODE)
3877                 {
3878                     auto f = new AppFileGet(n);
3879 
3880                     string::size_type index = s.words[1].s.find(":");
3881                     // node from public folder link
3882                     if (index != string::npos && s.words[1].s.substr(0, index).find("@") == string::npos)
3883                     {
3884                         handle h = clientFolder->getrootpublicfolder();
3885                         char *pubauth = new char[12];
3886                         Base64::btoa((byte*)&h, MegaClient::NODEHANDLE, pubauth);
3887                         f->pubauth = pubauth;
3888                         f->hprivate = true;
3889                         f->hforeign = true;
3890                         memcpy(f->filekey, n->nodekey().data(), FILENODEKEYLENGTH);
3891                     }
3892 
3893                     f->appxfer_it = appxferq[GET].insert(appxferq[GET].end(), f);
3894                     client->startxfer(GET, f, committer);
3895                 }
3896                 else
3897                 {
3898                     // ...or all files in the specified folder (non-recursive)
3899                     for (node_list::iterator it = n->children.begin(); it != n->children.end(); it++)
3900                     {
3901                         if ((*it)->type == FILENODE)
3902                         {
3903                             auto f = new AppFileGet(*it);
3904                             f->appxfer_it = appxferq[GET].insert(appxferq[GET].end(), f);
3905                             client->startxfer(GET, f, committer);
3906                         }
3907                     }
3908                 }
3909             }
3910         }
3911         else
3912         {
3913             cout << s.words[1].s << ": No such file or folder" << endl;
3914         }
3915     }
3916 }
3917 
3918 void uploadLocalFolderContent(LocalPath& localname, Node* cloudFolder);
3919 
uploadLocalPath(nodetype_t type,std::string name,LocalPath & localname,Node * parent,const std::string targetuser,DBTableTransactionCommitter & committer,int & total,bool recursive)3920 void uploadLocalPath(nodetype_t type, std::string name, LocalPath& localname, Node* parent, const std::string targetuser, DBTableTransactionCommitter& committer, int& total, bool recursive)
3921 {
3922 
3923     Node *previousNode = client->childnodebyname(parent, name.c_str(), false);
3924 
3925     if (type == FILENODE)
3926     {
3927         auto fa = client->fsaccess->newfileaccess();
3928         if (fa->fopen(localname, true, false))
3929         {
3930             FileFingerprint fp;
3931             fp.genfingerprint(fa.get());
3932 
3933             if (previousNode)
3934             {
3935                 if (previousNode->type == FILENODE)
3936                 {
3937                     if (fp.isvalid && previousNode->isvalid && fp == *((FileFingerprint *)previousNode))
3938                     {
3939                         cout << "Identical file already exist. Skipping transfer of " << name << endl;
3940                         return;
3941                     }
3942                 }
3943                 else
3944                 {
3945                     cout << "Can't upload file over the top of a folder with the same name: " << name << endl;
3946                     return;
3947                 }
3948             }
3949             fa.reset();
3950 
3951             AppFile* f = new AppFilePut(localname, parent ? parent->nodehandle : UNDEF, targetuser.c_str());
3952             *static_cast<FileFingerprint*>(f) = fp;
3953             f->appxfer_it = appxferq[PUT].insert(appxferq[PUT].end(), f);
3954             client->startxfer(PUT, f, committer);
3955             total++;
3956         }
3957         else
3958         {
3959             cout << "Can't open file: " << name << endl;
3960         }
3961     }
3962     else if (type == FOLDERNODE && recursive)
3963     {
3964 
3965         if (previousNode)
3966         {
3967             if (previousNode->type == FILENODE)
3968             {
3969                 cout << "Can't upload a folder over the top of a file with the same name: " << name << endl;
3970                 return;
3971             }
3972             else
3973             {
3974                 // upload into existing folder with the same name
3975                 uploadLocalFolderContent(localname, previousNode);
3976             }
3977         }
3978         else
3979         {
3980             auto nn = new NewNode[1];
3981             client->putnodes_prepareOneFolder(nn, name);
3982 
3983             gOnPutNodeTag[gNextClientTag] = [localname](Node* parent) {
3984                 auto tmp = localname;
3985                 uploadLocalFolderContent(tmp, parent);
3986             };
3987 
3988             client->reqtag = gNextClientTag++;
3989             client->putnodes(parent->nodehandle, nn, 1);
3990             client->reqtag = 0;
3991         }
3992     }
3993 }
3994 
3995 
localpathToUtf8Leaf(const LocalPath & itemlocalname)3996 string localpathToUtf8Leaf(const LocalPath& itemlocalname)
3997 {
3998 
3999     size_t n = itemlocalname.lastpartlocal(*client->fsaccess);
4000     LocalPath leaf = itemlocalname.subpathFrom(n);
4001     return leaf.toPath(*client->fsaccess);
4002 }
4003 
uploadLocalFolderContent(LocalPath & localname,Node * cloudFolder)4004 void uploadLocalFolderContent(LocalPath& localname, Node* cloudFolder)
4005 {
4006     DirAccess* da = client->fsaccess->newdiraccess();
4007 
4008     if (da->dopen(&localname, NULL, false))
4009     {
4010         DBTableTransactionCommitter committer(client->tctable);
4011 
4012         int total = 0;
4013         nodetype_t type;
4014         LocalPath itemlocalleafname;
4015         while (da->dnext(localname, itemlocalleafname, true, &type))
4016         {
4017             string leafNameUtf8 = localpathToUtf8Leaf(itemlocalleafname);
4018 
4019             if (gVerboseMode)
4020             {
4021                 cout << "Queueing " << leafNameUtf8 << "..." << endl;
4022             }
4023             auto newpath = localname;
4024             newpath.appendWithSeparator(itemlocalleafname, true, client->fsaccess->localseparator);
4025             uploadLocalPath(type, leafNameUtf8, newpath, cloudFolder, "", committer, total, true);
4026         }
4027         if (gVerboseMode)
4028         {
4029             cout << "Queued " << total << " more uploads from folder " << localpathToUtf8Leaf(localname) << endl;
4030         }
4031     }
4032 }
4033 
exec_put(autocomplete::ACState & s)4034 void exec_put(autocomplete::ACState& s)
4035 {
4036     handle target = cwd;
4037     string targetuser;
4038     string newname;
4039     int total = 0;
4040     Node* n = NULL;
4041 
4042     bool recursive = s.extractflag("-r");
4043 
4044     if (s.words.size() > 2)
4045     {
4046         if ((n = nodebypath(s.words[2].s.c_str(), &targetuser, &newname)))
4047         {
4048             target = n->nodehandle;
4049         }
4050     }
4051     else    // target is current path
4052     {
4053         n = client->nodebyhandle(target);
4054     }
4055 
4056     if (client->loggedin() == NOTLOGGEDIN && !targetuser.size())
4057     {
4058         cout << "Not logged in." << endl;
4059 
4060         return;
4061     }
4062 
4063     if (recursive && !targetuser.empty())
4064     {
4065         cout << "Sorry, can't send recursively to a user" << endl;
4066     }
4067 
4068     auto localname = LocalPath::fromPath(s.words[1].s, *client->fsaccess);
4069 
4070     DirAccess* da = client->fsaccess->newdiraccess();
4071 
4072     if (da->dopen(&localname, NULL, true))
4073     {
4074         DBTableTransactionCommitter committer(client->tctable);
4075 
4076         nodetype_t type;
4077         LocalPath itemlocalname;
4078         while (da->dnext(localname, itemlocalname, true, &type))
4079         {
4080             string leafNameUtf8 = localpathToUtf8Leaf(itemlocalname);
4081 
4082             if (gVerboseMode)
4083             {
4084                 cout << "Queueing " << leafNameUtf8 << "..." << endl;
4085             }
4086             uploadLocalPath(type, leafNameUtf8, itemlocalname, n, targetuser, committer, total, recursive);
4087         }
4088     }
4089 
4090     delete da;
4091 
4092     cout << "Queued " << total << " file(s) for upload, " << appxferq[PUT].size()
4093         << " file(s) in queue" << endl;
4094 }
4095 
exec_pwd(autocomplete::ACState & s)4096 void exec_pwd(autocomplete::ACState& s)
4097 {
4098     string path;
4099 
4100     nodepath(cwd, &path);
4101 
4102     cout << path << endl;
4103 }
4104 
exec_lcd(autocomplete::ACState & s)4105 void exec_lcd(autocomplete::ACState& s)
4106 {
4107     LocalPath localpath = LocalPath::fromPath(s.words[1].s, *client->fsaccess);
4108 
4109     if (!client->fsaccess->chdirlocal(localpath))
4110     {
4111         cout << s.words[1].s << ": Failed" << endl;
4112     }
4113 }
4114 
4115 #ifdef USE_FILESYSTEM
exec_lls(autocomplete::ACState & s)4116 void exec_lls(autocomplete::ACState& s)
4117 {
4118     bool recursive = s.extractflag("-R");
4119     fs::path ls_folder = s.words.size() > 1 ? fs::u8path(s.words[1].s) : fs::current_path();
4120     std::error_code ec;
4121     auto status = fs::status(ls_folder, ec);
4122     (void)status;
4123     if (ec)
4124     {
4125         cerr << ec.message() << endl;
4126     }
4127     else if (!fs::exists(ls_folder))
4128     {
4129         cerr << "not found" << endl;
4130     }
4131     else
4132     {
4133         local_dumptree(ls_folder, recursive);
4134     }
4135 }
4136 #endif
4137 
exec_ipc(autocomplete::ACState & s)4138 void exec_ipc(autocomplete::ACState& s)
4139 {
4140     // incoming pending contact action
4141     handle phandle;
4142     if (s.words.size() == 3 && Base64::atob(s.words[1].s.c_str(), (byte*) &phandle, sizeof phandle) == sizeof phandle)
4143     {
4144         ipcactions_t action;
4145         if (s.words[2].s == "a")
4146         {
4147             action = IPCA_ACCEPT;
4148         }
4149         else if (s.words[2].s == "d")
4150         {
4151             action = IPCA_DENY;
4152         }
4153         else if (s.words[2].s == "i")
4154         {
4155             action = IPCA_IGNORE;
4156         }
4157         else
4158         {
4159             return;
4160         }
4161         client->updatepcr(phandle, action);
4162     }
4163 }
4164 
4165 #if defined(WIN32) && defined(NO_READLINE)
exec_log(autocomplete::ACState & s)4166 void exec_log(autocomplete::ACState& s)
4167 {
4168     if (s.words.size() == 1)
4169     {
4170         // close log
4171         static_cast<WinConsole*>(console)->log("", WinConsole::no_log);
4172         cout << "log closed" << endl;
4173     }
4174     else if (s.words.size() == 3)
4175     {
4176         // open log
4177         WinConsole::logstyle style = WinConsole::no_log;
4178         if (s.words[1].s == "utf8")
4179         {
4180             style = WinConsole::utf8_log;
4181         }
4182         else if (s.words[1].s == "utf16")
4183         {
4184             style = WinConsole::utf16_log;
4185         }
4186         else if (s.words[1].s == "codepage")
4187         {
4188             style = WinConsole::codepage_log;
4189         }
4190         else
4191         {
4192             cout << "unknown log style" << endl;
4193         }
4194         if (!static_cast<WinConsole*>(console)->log(s.words[2].s, style))
4195         {
4196             cout << "failed to open log file" << endl;
4197         }
4198     }
4199 }
4200 #endif
4201 
exec_putq(autocomplete::ACState & s)4202 void exec_putq(autocomplete::ACState& s)
4203 {
4204     bool showActive = s.extractflag("-active");
4205     bool showAll = s.extractflag("-all");
4206     bool showCount = s.extractflag("-count");
4207 
4208     if (!showActive && !showAll && !showCount)
4209     {
4210         showCount = true;
4211     }
4212 
4213     xferq(PUT, s.words.size() > 1 ? atoi(s.words[1].s.c_str()) : -1, showActive, showAll, showCount);
4214 }
4215 
exec_getq(autocomplete::ACState & s)4216 void exec_getq(autocomplete::ACState& s)
4217 {
4218     bool showActive = s.extractflag("-active");
4219     bool showAll = s.extractflag("-all");
4220     bool showCount = s.extractflag("-count");
4221 
4222     if (!showActive && !showAll && !showCount)
4223     {
4224         showCount = true;
4225     }
4226 
4227     xferq(GET, s.words.size() > 1 ? atoi(s.words[1].s.c_str()) : -1, showActive, showAll, showCount);
4228 }
4229 
exec_open(autocomplete::ACState & s)4230 void exec_open(autocomplete::ACState& s)
4231 {
4232     if (strstr(s.words[1].s.c_str(), "#F!") || strstr(s.words[1].s.c_str(), "folder/"))  // folder link indicator
4233     {
4234         if (!clientFolder)
4235         {
4236             using namespace mega;
4237             // create a new MegaClient with a different MegaApp to process callbacks
4238             // from the client logged into a folder. Reuse the waiter and httpio
4239             clientFolder = new MegaClient(new DemoAppFolder, client->waiter,
4240                                             client->httpio, new FSACCESS_CLASS,
4241                 #ifdef DBACCESS_CLASS
4242                                             new DBACCESS_CLASS,
4243                 #else
4244                                             NULL,
4245                 #endif
4246                 #ifdef GFX_CLASS
4247                                             new GFX_CLASS,
4248                 #else
4249                                             NULL,
4250                 #endif
4251                                             "Gk8DyQBS",
4252                                             "megacli_folder/" TOSTRING(MEGA_MAJOR_VERSION)
4253                                             "." TOSTRING(MEGA_MINOR_VERSION)
4254                                             "." TOSTRING(MEGA_MICRO_VERSION),
4255                                             2);
4256         }
4257         else
4258         {
4259             clientFolder->logout();
4260         }
4261 
4262         return clientFolder->app->login_result(clientFolder->folderaccess(s.words[1].s.c_str()));
4263     }
4264     else
4265     {
4266         cout << "Invalid folder link." << endl;
4267     }
4268 }
4269 
4270 #ifdef ENABLE_SYNC
exec_sync(autocomplete::ACState & s)4271 void exec_sync(autocomplete::ACState& s)
4272 {
4273     if (s.words.size() == 3)
4274     {
4275         Node* n = nodebypath(s.words[2].s.c_str());
4276 
4277         if (client->checkaccess(n, FULL))
4278         {
4279 
4280             if (!n)
4281             {
4282                 cout << s.words[2].s << ": Not found." << endl;
4283             }
4284             else if (n->type == FILENODE)
4285             {
4286                 cout << s.words[2].s << ": Remote sync root must be folder." << endl;
4287             }
4288             else
4289             {
4290                 SyncConfig syncConfig{s.words[1].s, n->nodehandle, 0, {}, newSyncConfig.type,
4291                             newSyncConfig.syncDeletions, newSyncConfig.forceOverwrite};
4292                 error e = client->addsync(std::move(syncConfig), DEBRISFOLDER, NULL);
4293 
4294                 if (e)
4295                 {
4296                     cout << "Sync could not be added: " << errorstring(e) << endl;
4297                 }
4298             }
4299         }
4300         else
4301         {
4302             cout << s.words[2].s << ": Syncing requires full access to path." << endl;
4303         }
4304     }
4305     else if (s.words.size() == 2)
4306     {
4307         int i = 0, cancel = atoi(s.words[1].s.c_str());
4308 
4309         for (sync_list::iterator it = client->syncs.begin(); it != client->syncs.end(); it++)
4310         {
4311             if ((*it)->state > SYNC_CANCELED && i++ == cancel)
4312             {
4313                 client->delsync(*it);
4314 
4315                 cout << "Sync " << cancel << " deactivated and removed." << endl;
4316                 break;
4317             }
4318         }
4319     }
4320     else if (s.words.size() == 1)
4321     {
4322         if (client->syncs.size())
4323         {
4324             int i = 0;
4325             string remotepath, localpath;
4326 
4327             for (sync_list::iterator it = client->syncs.begin(); it != client->syncs.end(); it++)
4328             {
4329                 if ((*it)->state > SYNC_CANCELED)
4330                 {
4331                     static const char* syncstatenames[] =
4332                     { "Initial scan, please wait", "Active", "Failed" };
4333 
4334                     if ((*it)->localroot->node)
4335                     {
4336                         nodepath((*it)->localroot->node->nodehandle, &remotepath);
4337                         localpath = (*it)->localroot->localname.toPath(*client->fsaccess);
4338 
4339                         cout << i++ << " (" << syncConfigToString(NewSyncConfig::from((*it)->getConfig())) << "): " << localpath << " to " << remotepath << " - "
4340                                 << syncstatenames[(*it)->state] << ", " << (*it)->localbytes
4341                                 << " byte(s) in " << (*it)->localnodes[FILENODE] << " file(s) and "
4342                                 << (*it)->localnodes[FOLDERNODE] << " folder(s)" << endl;
4343                     }
4344                 }
4345             }
4346         }
4347         else
4348         {
4349             cout << "No syncs active at this time." << endl;
4350         }
4351     }
4352 }
4353 
exec_syncconfig(autocomplete::ACState & s)4354 void exec_syncconfig(autocomplete::ACState& s)
4355 {
4356     if (s.words.size() == 1)
4357     {
4358         cout << "Current sync config: " << syncConfigToString(newSyncConfig) << endl;
4359     }
4360     else if (s.words.size() == 2 || s.words.size() == 4)
4361     {
4362         std::pair<bool, NewSyncConfig> pair;
4363         if (s.words.size() == 2)
4364         {
4365             pair = syncConfigFromStrings(s.words[1].s);
4366         }
4367         else
4368         {
4369             pair = syncConfigFromStrings(s.words[1].s, s.words[2].s, s.words[3].s);
4370         }
4371 
4372         if (pair.first)
4373         {
4374             newSyncConfig = pair.second;
4375             cout << "Successfully applied new sync config!" << endl;
4376         }
4377         else
4378         {
4379             cout << "Invalid parameters for syncconfig command." << endl;
4380         }
4381     }
4382     else
4383     {
4384         assert(false);
4385     }
4386 }
4387 #endif
4388 
4389 #ifdef USE_FILESYSTEM
exec_lpwd(autocomplete::ACState & s)4390 void exec_lpwd(autocomplete::ACState& s)
4391 {
4392     cout << fs::current_path().u8string() << endl;
4393 }
4394 #endif
4395 
4396 
exec_test(autocomplete::ACState & s)4397 void exec_test(autocomplete::ACState& s)
4398 {
4399 }
4400 
exec_mfad(autocomplete::ACState & s)4401 void exec_mfad(autocomplete::ACState& s)
4402 {
4403     client->multifactorauthdisable(s.words[1].s.c_str());
4404 }
4405 
exec_mfac(autocomplete::ACState & s)4406 void exec_mfac(autocomplete::ACState& s)
4407 {
4408     string email;
4409     if (s.words.size() == 2)
4410     {
4411         email = s.words[1].s;
4412     }
4413     else
4414     {
4415         email = login.email;
4416     }
4417 
4418     client->multifactorauthcheck(email.c_str());
4419 }
4420 
exec_mfae(autocomplete::ACState & s)4421 void exec_mfae(autocomplete::ACState& s)
4422 {
4423     client->multifactorauthsetup();
4424 }
4425 
exec_login(autocomplete::ACState & s)4426 void exec_login(autocomplete::ACState& s)
4427 {
4428     if (client->loggedin() == NOTLOGGEDIN)
4429     {
4430         if (s.words.size() > 1)
4431         {
4432             if ((s.words.size() == 2 || s.words.size() == 3) && s.words[1].s == "autoresume")
4433             {
4434                 string filename = "megacli_autoresume_session" + (s.words.size() == 3 ? "_" + s.words[2].s : "");
4435                 ifstream file(filename.c_str());
4436                 string session;
4437                 file >> session;
4438                 if (file.is_open() && session.size())
4439                 {
4440                     byte sessionraw[64];
4441                     if (session.size() < sizeof sessionraw * 4 / 3)
4442                     {
4443                         int size = Base64::atob(session.c_str(), sessionraw, sizeof sessionraw);
4444 
4445                         cout << "Resuming session..." << endl;
4446                         return client->login(sessionraw, size);
4447                     }
4448                 }
4449                 cout << "Failed to get a valid session id from file " << filename << endl;
4450             }
4451             else if (strchr(s.words[1].s.c_str(), '@'))
4452             {
4453                 login.reset();
4454                 login.email = s.words[1].s;
4455 
4456                 // full account login
4457                 if (s.words.size() > 2)
4458                 {
4459                     login.password = s.words[2].s;
4460                     cout << "Initiated login attempt..." << endl;
4461                 }
4462                 client->prelogin(login.email.c_str());
4463             }
4464             else
4465             {
4466                 const char* ptr;
4467                 if ((ptr = strchr(s.words[1].s.c_str(), '#')))  // folder link indicator
4468                 {
4469                     return client->app->login_result(client->folderaccess(s.words[1].s.c_str()));
4470                 }
4471                 else
4472                 {
4473                     byte session[64];
4474                     int size;
4475 
4476                     if (s.words[1].s.size() < sizeof session * 4 / 3)
4477                     {
4478                         size = Base64::atob(s.words[1].s.c_str(), session, sizeof session);
4479 
4480                         cout << "Resuming session..." << endl;
4481 
4482                         return client->login(session, size);
4483                     }
4484                 }
4485 
4486                 cout << "Invalid argument. Please specify a valid e-mail address, "
4487                     << "a folder link containing the folder key "
4488                     << "or a valid session." << endl;
4489             }
4490         }
4491         else
4492         {
4493             cout << "      login email [password]" << endl
4494                 << "      login exportedfolderurl#key" << endl
4495                 << "      login session" << endl;
4496         }
4497     }
4498     else
4499     {
4500         cout << "Already logged in. Please log out first." << endl;
4501     }
4502 }
4503 
exec_begin(autocomplete::ACState & s)4504 void exec_begin(autocomplete::ACState& s)
4505 {
4506     if (s.words.size() == 1)
4507     {
4508         cout << "Creating ephemeral session..." << endl;
4509         pdf_to_import = true;
4510         client->createephemeral();
4511     }
4512     else if (s.words.size() == 2)
4513     {
4514         handle uh;
4515         byte pw[SymmCipher::KEYLENGTH];
4516 
4517         if (Base64::atob(s.words[1].s.c_str(), (byte*) &uh, MegaClient::USERHANDLE) == sizeof uh && Base64::atob(
4518             s.words[1].s.c_str() + 12, pw, sizeof pw) == sizeof pw)
4519         {
4520             client->resumeephemeral(uh, pw);
4521         }
4522         else
4523         {
4524             cout << "Malformed ephemeral session identifier." << endl;
4525         }
4526     }
4527 }
4528 
exec_mount(autocomplete::ACState & s)4529 void exec_mount(autocomplete::ACState& s)
4530 {
4531     listtrees();
4532 }
4533 
exec_share(autocomplete::ACState & s)4534 void exec_share(autocomplete::ACState& s)
4535 {
4536     switch (s.words.size())
4537     {
4538     case 1:		// list all shares (incoming and outgoing)
4539     {
4540         TreeProcListOutShares listoutshares;
4541         Node* n;
4542 
4543         cout << "Shared folders:" << endl;
4544 
4545         for (unsigned i = 0; i < sizeof client->rootnodes / sizeof *client->rootnodes; i++)
4546         {
4547             if ((n = client->nodebyhandle(client->rootnodes[i])))
4548             {
4549                 client->proctree(n, &listoutshares);
4550             }
4551         }
4552 
4553         for (user_map::iterator uit = client->users.begin();
4554             uit != client->users.end(); uit++)
4555         {
4556             User* u = &uit->second;
4557             Node* n;
4558 
4559             if (u->show == VISIBLE && u->sharing.size())
4560             {
4561                 cout << "From " << u->email << ":" << endl;
4562 
4563                 for (handle_set::iterator sit = u->sharing.begin();
4564                     sit != u->sharing.end(); sit++)
4565                 {
4566                     if ((n = client->nodebyhandle(*sit)))
4567                     {
4568                         cout << "\t" << n->displayname() << " ("
4569                             << getAccessLevelStr(n->inshare->access) << ")" << endl;
4570                     }
4571                 }
4572             }
4573         }
4574     }
4575     break;
4576 
4577     case 2:	    // list all outgoing shares on this path
4578     case 3:	    // remove outgoing share to specified e-mail address
4579     case 4:	    // add outgoing share to specified e-mail address
4580     case 5:     // user specified a personal representation to appear as for the invitation
4581         if (Node* n = nodebypath(s.words[1].s.c_str()))
4582         {
4583             if (s.words.size() == 2)
4584             {
4585                 listnodeshares(n);
4586             }
4587             else
4588             {
4589                 accesslevel_t a = ACCESS_UNKNOWN;
4590                 const char* personal_representation = NULL;
4591                 if (s.words.size() > 3)
4592                 {
4593                     if (s.words[3].s == "r" || s.words[3].s == "ro")
4594                     {
4595                         a = RDONLY;
4596                     }
4597                     else if (s.words[3].s == "rw")
4598                     {
4599                         a = RDWR;
4600                     }
4601                     else if (s.words[3].s == "full")
4602                     {
4603                         a = FULL;
4604                     }
4605                     else
4606                     {
4607                         cout << "Access level must be one of r, rw or full" << endl;
4608 
4609                         return;
4610                     }
4611 
4612                     if (s.words.size() > 4)
4613                     {
4614                         personal_representation = s.words[4].s.c_str();
4615                     }
4616                 }
4617 
4618                 client->setshare(n, s.words[2].s.c_str(), a, personal_representation);
4619             }
4620         }
4621         else
4622         {
4623             cout << s.words[1].s << ": No such directory" << endl;
4624         }
4625         break;
4626     }
4627 }
4628 
exec_users(autocomplete::ACState & s)4629 void exec_users(autocomplete::ACState& s)
4630 {
4631     if (s.words.size() == 1)
4632     {
4633         for (user_map::iterator it = client->users.begin(); it != client->users.end(); it++)
4634         {
4635             if (it->second.email.size())
4636             {
4637                 cout << "\t" << it->second.email;
4638 
4639                 if (it->second.userhandle == client->me)
4640                 {
4641                     cout << ", session user";
4642                 }
4643                 else if (it->second.show == VISIBLE)
4644                 {
4645                     cout << ", visible";
4646                 }
4647                 else if (it->second.show == HIDDEN)
4648                 {
4649                     cout << ", hidden";
4650                 }
4651                 else if (it->second.show == INACTIVE)
4652                 {
4653                     cout << ", inactive";
4654                 }
4655                 else if (it->second.show == BLOCKED)
4656                 {
4657                     cout << ", blocked";
4658                 }
4659                 else
4660                 {
4661                     cout << ", unknown visibility (" << it->second.show << ")";
4662                 }
4663 
4664                 if (it->second.sharing.size())
4665                 {
4666                     cout << ", sharing " << it->second.sharing.size() << " folder(s)";
4667                 }
4668 
4669                 if (it->second.pubk.isvalid())
4670                 {
4671                     cout << ", public key cached";
4672                 }
4673 
4674                 if (it->second.mBizMode == BIZ_MODE_MASTER)
4675                 {
4676                     cout << ", business master user";
4677                 }
4678                 else if (it->second.mBizMode == BIZ_MODE_SUBUSER)
4679                 {
4680                     cout << ", business sub-user";
4681                 }
4682 
4683                 cout << endl;
4684             }
4685         }
4686     }
4687     else if (s.words.size() == 3 && s.words[2].s == "del")
4688     {
4689         client->removecontact(s.words[1].s.c_str(), HIDDEN);
4690     }
4691 }
4692 
exec_mkdir(autocomplete::ACState & s)4693 void exec_mkdir(autocomplete::ACState& s)
4694 {
4695     bool allowDuplicate = s.extractflag("-allowduplicate");
4696     bool exactLeafName = s.extractflag("-exactleafname");
4697 
4698     if (s.words.size() > 1)
4699     {
4700         string newname;
4701 
4702         Node* n;
4703         if (exactLeafName)
4704         {
4705             n = client->nodebyhandle(cwd);
4706             newname = s.words[1].s;
4707         }
4708         else
4709         {
4710             n = nodebypath(s.words[1].s.c_str(), NULL, &newname);
4711         }
4712 
4713         if (n)
4714         {
4715             if (!client->checkaccess(n, RDWR))
4716             {
4717                 cout << "Write access denied" << endl;
4718 
4719                 return;
4720             }
4721 
4722             if (newname.size())
4723             {
4724                 auto nn = new NewNode[1];
4725                 client->putnodes_prepareOneFolder(nn, newname);
4726                 client->putnodes(n->nodehandle, nn, 1);
4727             }
4728             else if (allowDuplicate && n->parent && n->parent->nodehandle != UNDEF)
4729             {
4730                 // the leaf name already exists and was returned in n
4731                 auto leafname = s.words[1].s;
4732                 auto pos = leafname.find_last_of("/");
4733                 if (pos != string::npos) leafname.erase(0, pos + 1);
4734                 auto nn = new NewNode[1];
4735                 client->putnodes_prepareOneFolder(nn, leafname);
4736                 client->putnodes(n->parent->nodehandle, nn, 1);
4737             }
4738             else
4739             {
4740                 cout << s.words[1].s << ": Path already exists" << endl;
4741             }
4742         }
4743         else
4744         {
4745             cout << s.words[1].s << ": Target path not found" << endl;
4746         }
4747     }
4748 }
4749 
exec_getfa(autocomplete::ACState & s)4750 void exec_getfa(autocomplete::ACState& s)
4751 {
4752     Node* n;
4753     int cancel = s.words.size() > 2 && s.words.back().s == "cancel";
4754 
4755     if (s.words.size() < 3)
4756     {
4757         n = client->nodebyhandle(cwd);
4758     }
4759     else if (!(n = nodebypath(s.words[2].s.c_str())))
4760     {
4761         cout << s.words[2].s << ": Path not found" << endl;
4762     }
4763 
4764     if (n)
4765     {
4766         int c = 0;
4767         fatype type;
4768 
4769         type = fatype(atoi(s.words[1].s.c_str()));
4770 
4771         if (n->type == FILENODE)
4772         {
4773             if (n->hasfileattribute(type))
4774             {
4775                 client->getfa(n->nodehandle, &n->fileattrstring, n->nodekey(), type, cancel);
4776                 c++;
4777             }
4778         }
4779         else
4780         {
4781             for (node_list::iterator it = n->children.begin(); it != n->children.end(); it++)
4782             {
4783                 if ((*it)->type == FILENODE && (*it)->hasfileattribute(type))
4784                 {
4785                     client->getfa((*it)->nodehandle, &(*it)->fileattrstring, (*it)->nodekey(), type, cancel);
4786                     c++;
4787                 }
4788             }
4789         }
4790 
4791         cout << (cancel ? "Canceling " : "Fetching ") << c << " file attribute(s) of type " << type << "..." << endl;
4792     }
4793 }
4794 
exec_getua(autocomplete::ACState & s)4795 void exec_getua(autocomplete::ACState& s)
4796 {
4797     User* u = NULL;
4798 
4799     if (s.words.size() == 3)
4800     {
4801         // get other user's attribute
4802         if (!(u = client->finduser(s.words[2].s.c_str())))
4803         {
4804             cout << "Retrieving user attribute for unknown user: " << s.words[2].s << endl;
4805             client->getua(s.words[2].s.c_str(), User::string2attr(s.words[1].s.c_str()));
4806             return;
4807         }
4808     }
4809     else if (s.words.size() != 2)
4810     {
4811         cout << "      getua attrname [email]" << endl;
4812         return;
4813     }
4814 
4815     if (!u)
4816     {
4817         // get logged in user's attribute
4818         if (!(u = client->ownuser()))
4819         {
4820             cout << "Must be logged in to query own attributes." << endl;
4821             return;
4822         }
4823     }
4824 
4825     if (s.words[1].s == "pubk")
4826     {
4827         client->getpubkey(u->uid.c_str());
4828         return;
4829     }
4830 
4831     client->getua(u, User::string2attr(s.words[1].s.c_str()));
4832 }
4833 
exec_putua(autocomplete::ACState & s)4834 void exec_putua(autocomplete::ACState& s)
4835 {
4836     attr_t attrtype = User::string2attr(s.words[1].s.c_str());
4837     if (attrtype == ATTR_UNKNOWN)
4838     {
4839         cout << "Attribute not recognized" << endl;
4840         return;
4841     }
4842 
4843     if (s.words.size() == 2)
4844     {
4845         // delete attribute
4846         client->putua(attrtype);
4847 
4848         return;
4849     }
4850     else if (s.words.size() == 3)
4851     {
4852         if (s.words[2].s == "del")
4853         {
4854             client->putua(attrtype);
4855 
4856             return;
4857         }
4858     }
4859     else if (s.words.size() == 4)
4860     {
4861         if (s.words[2].s == "set")
4862         {
4863             client->putua(attrtype, (const byte*)s.words[3].s.c_str(), unsigned(s.words[3].s.size()));
4864 
4865             return;
4866         }
4867         else if (s.words[2].s == "set64")
4868         {
4869             int len = int(s.words[3].s.size() * 3 / 4 + 3);
4870             byte *value = new byte[len];
4871             int valuelen = Base64::atob(s.words[3].s.data(), value, len);
4872             client->putua(attrtype, value, valuelen);
4873             delete [] value;
4874             return;
4875         }
4876         else if (s.words[2].s == "load")
4877         {
4878             string data;
4879             auto localpath = LocalPath::fromPath(s.words[3].s, *client->fsaccess);
4880 
4881             if (loadfile(localpath, &data))
4882             {
4883                 client->putua(attrtype, (const byte*) data.data(), unsigned(data.size()));
4884             }
4885             else
4886             {
4887                 cout << "Cannot read " << s.words[3].s << endl;
4888             }
4889 
4890             return;
4891         }
4892     }
4893 }
4894 
4895 #ifdef DEBUG
exec_delua(autocomplete::ACState & s)4896 void exec_delua(autocomplete::ACState& s)
4897 {
4898     client->delua(s.words[1].s.c_str());
4899 }
4900 #endif
4901 
exec_pause(autocomplete::ACState & s)4902 void exec_pause(autocomplete::ACState& s)
4903 {
4904     bool getarg = false, putarg = false, hardarg = false, statusarg = false;
4905 
4906     for (size_t i = s.words.size(); --i; )
4907     {
4908         if (s.words[i].s == "get")
4909         {
4910             getarg = true;
4911         }
4912         if (s.words[i].s == "put")
4913         {
4914             putarg = true;
4915         }
4916         if (s.words[i].s == "hard")
4917         {
4918             hardarg = true;
4919         }
4920         if (s.words[i].s == "status")
4921         {
4922             statusarg = true;
4923         }
4924     }
4925 
4926     if (statusarg)
4927     {
4928         if (!hardarg && !getarg && !putarg)
4929         {
4930             if (!client->xferpaused[GET] && !client->xferpaused[PUT])
4931             {
4932                 cout << "Transfers not paused at the moment." << endl;
4933             }
4934             else
4935             {
4936                 if (client->xferpaused[GET])
4937                 {
4938                     cout << "GETs currently paused." << endl;
4939                 }
4940                 if (client->xferpaused[PUT])
4941                 {
4942                     cout << "PUTs currently paused." << endl;
4943                 }
4944             }
4945         }
4946         return;
4947     }
4948 
4949     if (!getarg && !putarg)
4950     {
4951         getarg = true;
4952         putarg = true;
4953     }
4954 
4955     DBTableTransactionCommitter committer(client->tctable);
4956 
4957     if (getarg)
4958     {
4959         client->pausexfers(GET, client->xferpaused[GET] ^= true, hardarg, committer);
4960         if (client->xferpaused[GET])
4961         {
4962             cout << "GET transfers paused. Resume using the same command." << endl;
4963         }
4964         else
4965         {
4966             cout << "GET transfers unpaused." << endl;
4967         }
4968     }
4969 
4970     if (putarg)
4971     {
4972         client->pausexfers(PUT, client->xferpaused[PUT] ^= true, hardarg, committer);
4973         if (client->xferpaused[PUT])
4974         {
4975             cout << "PUT transfers paused. Resume using the same command." << endl;
4976         }
4977         else
4978         {
4979             cout << "PUT transfers unpaused." << endl;
4980         }
4981     }
4982 }
4983 
exec_debug(autocomplete::ACState & s)4984 void exec_debug(autocomplete::ACState& s)
4985 {
4986     bool turnon = s.extractflag("-on");
4987     bool turnoff = s.extractflag("-off");
4988 
4989     if (s.words.size() > 1)
4990     {
4991         gLogger.mLogFile.close();
4992         if (!s.words[1].s.empty())
4993         {
4994             gLogger.mLogFile.open(s.words[1].s.c_str());
4995             if (!gLogger.mLogFile.is_open())
4996             {
4997                 cout << "Log file open failed: '" << s.words[1].s << "'" << endl;
4998             }
4999         }
5000     }
5001 
5002     bool state = client->debugstate();
5003     if ((turnon && !state) || (turnoff && state) || (!turnon && !turnoff))
5004     {
5005         client->toggledebug();
5006     }
5007 
5008     cout << "Debug mode " << (client->debugstate() ? "on" : "off") << endl;
5009 }
5010 
exec_verbose(autocomplete::ACState & s)5011 void exec_verbose(autocomplete::ACState& s)
5012 {
5013     bool turnon = s.extractflag("-on");
5014     bool turnoff = s.extractflag("-off");
5015 
5016     if (turnon)
5017     {
5018         gVerboseMode = true;
5019     }
5020     else if (turnoff)
5021     {
5022         gVerboseMode = false;
5023     }
5024     else
5025     {
5026         gVerboseMode = !gVerboseMode;
5027     }
5028     cout << "Verbose mode " << (gVerboseMode ? "on" : "off") << endl;
5029 }
5030 
5031 #if defined(WIN32) && defined(NO_READLINE)
exec_clear(autocomplete::ACState & s)5032 void exec_clear(autocomplete::ACState& s)
5033 {
5034     static_cast<WinConsole*>(console)->clearScreen();
5035 }
5036 #endif
5037 
exec_retry(autocomplete::ACState & s)5038 void exec_retry(autocomplete::ACState& s)
5039 {
5040     if (client->abortbackoff())
5041     {
5042         cout << "Retrying..." << endl;
5043     }
5044     else
5045     {
5046         cout << "No failed request pending." << endl;
5047     }
5048 }
5049 
exec_recon(autocomplete::ACState & s)5050 void exec_recon(autocomplete::ACState& s)
5051 {
5052     cout << "Closing all open network connections..." << endl;
5053 
5054     client->disconnect();
5055 }
5056 
exec_email(autocomplete::ACState & s)5057 void exec_email(autocomplete::ACState& s)
5058 {
5059     if (s.words.size() == 1)
5060     {
5061         User *u = client->finduser(client->me);
5062         if (u)
5063         {
5064             cout << "Your current email address is " << u->email << endl;
5065         }
5066         else
5067         {
5068             cout << "Please, login first" << endl;
5069         }
5070     }
5071     else if (s.words.size() == 2)
5072     {
5073         if (s.words[1].s.find("@") != string::npos)    // get change email link
5074         {
5075             client->getemaillink(s.words[1].s.c_str());
5076         }
5077         else    // confirm change email link
5078         {
5079             string link = s.words[1].s;
5080 
5081             size_t pos = link.find("#verify");
5082             if (pos == link.npos)
5083             {
5084                 cout << "Invalid email change link." << endl;
5085                 return;
5086             }
5087 
5088             changecode.assign(link.substr(pos + strlen("#verify")));
5089             client->queryrecoverylink(changecode.c_str());
5090         }
5091     }
5092 }
5093 
5094 #ifdef ENABLE_CHAT
exec_chatc(autocomplete::ACState & s)5095 void exec_chatc(autocomplete::ACState& s)
5096 {
5097     size_t wordscount = s.words.size();
5098     if (wordscount < 2 || wordscount == 3)
5099     {
5100         cout << "Invalid syntax to create chatroom" << endl;
5101         cout << "      chatc group [email ro|sta|mod]* " << endl;
5102         return;
5103     }
5104 
5105     int group = atoi(s.words[1].s.c_str());
5106     if (group != 0 && group != 1)
5107     {
5108         cout << "Invalid syntax to create chatroom" << endl;
5109         cout << "      chatc group [email ro|sta|mod]* " << endl;
5110         return;
5111     }
5112 
5113     unsigned parseoffset = 2;
5114     if (((wordscount - parseoffset) % 2) == 0)
5115     {
5116         if (!group && (wordscount - parseoffset) != 2)
5117         {
5118             cout << "Peer to peer chats must have only one peer" << endl;
5119             return;
5120         }
5121 
5122         userpriv_vector *userpriv = new userpriv_vector;
5123 
5124         unsigned numUsers = 0;
5125         while ((numUsers + 1) * 2 + parseoffset <= wordscount)
5126         {
5127             string email = s.words[numUsers * 2 + parseoffset].s;
5128             User *u = client->finduser(email.c_str(), 0);
5129             if (!u)
5130             {
5131                 cout << "User not found: " << email << endl;
5132                 delete userpriv;
5133                 return;
5134             }
5135 
5136             string privstr = s.words[numUsers * 2 + parseoffset + 1].s;
5137             privilege_t priv;
5138             if (!group) // 1:1 chats enforce peer to be moderator
5139             {
5140                 priv = PRIV_MODERATOR;
5141             }
5142             else
5143             {
5144                 if (privstr == "ro")
5145                 {
5146                     priv = PRIV_RO;
5147                 }
5148                 else if (privstr == "sta")
5149                 {
5150                     priv = PRIV_STANDARD;
5151                 }
5152                 else if (privstr == "mod")
5153                 {
5154                     priv = PRIV_MODERATOR;
5155                 }
5156                 else
5157                 {
5158                     cout << "Unknown privilege for " << email << endl;
5159                     delete userpriv;
5160                     return;
5161                 }
5162             }
5163 
5164             userpriv->push_back(userpriv_pair(u->userhandle, priv));
5165             numUsers++;
5166         }
5167 
5168         client->createChat(group, false, userpriv);
5169         delete userpriv;
5170     }
5171 }
5172 
exec_chati(autocomplete::ACState & s)5173 void exec_chati(autocomplete::ACState& s)
5174 {
5175     if (s.words.size() >= 4 && s.words.size() <= 7)
5176     {
5177         handle chatid;
5178         Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5179 
5180         string email = s.words[2].s;
5181         User *u = client->finduser(email.c_str(), 0);
5182         if (!u)
5183         {
5184             cout << "User not found: " << email << endl;
5185             return;
5186         }
5187 
5188         string privstr = s.words[3].s;
5189         privilege_t priv;
5190         if (privstr == "ro")
5191         {
5192             priv = PRIV_RO;
5193         }
5194         else if (privstr == "sta")
5195         {
5196             priv = PRIV_STANDARD;
5197         }
5198         else if (privstr == "mod")
5199         {
5200             priv = PRIV_MODERATOR;
5201         }
5202         else
5203         {
5204             cout << "Unknown privilege for " << email << endl;
5205             return;
5206         }
5207 
5208         string title;
5209         string unifiedKey;
5210         if (s.words.size() == 5)
5211         {
5212             unifiedKey = s.words[4].s;
5213         }
5214         else if (s.words.size() >= 6 && s.words[4].s == "t")
5215         {
5216             title = s.words[5].s;
5217             if (s.words.size() == 7)
5218             {
5219                 unifiedKey = s.words[6].s;
5220             }
5221         }
5222         const char *t = !title.empty() ? title.c_str() : NULL;
5223         const char *uk = !unifiedKey.empty() ? unifiedKey.c_str() : NULL;
5224 
5225         client->inviteToChat(chatid, u->userhandle, priv, uk, t);
5226         return;
5227     }
5228 }
5229 
exec_chatr(autocomplete::ACState & s)5230 void exec_chatr(autocomplete::ACState& s)
5231 {
5232     if (s.words.size() > 1 && s.words.size() < 4)
5233     {
5234         handle chatid;
5235         Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5236 
5237         if (s.words.size() == 2)
5238         {
5239             client->removeFromChat(chatid, client->me);
5240             return;
5241         }
5242         else if (s.words.size() == 3)
5243         {
5244             string email = s.words[2].s;
5245             User *u = client->finduser(email.c_str(), 0);
5246             if (!u)
5247             {
5248                 cout << "User not found: " << email << endl;
5249                 return;
5250             }
5251 
5252             client->removeFromChat(chatid, u->userhandle);
5253             return;
5254         }
5255     }
5256 }
5257 
exec_chatu(autocomplete::ACState & s)5258 void exec_chatu(autocomplete::ACState& s)
5259 {
5260     handle chatid;
5261     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5262 
5263     client->getUrlChat(chatid);
5264 }
5265 
exec_chata(autocomplete::ACState & s)5266 void exec_chata(autocomplete::ACState& s)
5267 {
5268     handle chatid;
5269     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5270     bool archive = (s.words[2].s == "1");
5271     if (!archive && (s.words[2].s != "0"))
5272     {
5273         cout << "Use 1 or 0 to archive/unarchive chats" << endl;
5274         return;
5275     }
5276 
5277     client->archiveChat(chatid, archive);
5278 }
5279 
exec_chats(autocomplete::ACState & s)5280 void exec_chats(autocomplete::ACState& s)
5281 {
5282     if (s.words.size() == 1)
5283     {
5284         textchat_map::iterator it;
5285         for (it = client->chats.begin(); it != client->chats.end(); it++)
5286         {
5287             DemoApp::printChatInformation(it->second);
5288         }
5289         return;
5290     }
5291     if (s.words.size() == 2)
5292     {
5293         handle chatid;
5294         Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5295 
5296         textchat_map::iterator it = client->chats.find(chatid);
5297         if (it == client->chats.end())
5298         {
5299             cout << "Chatid " << s.words[1].s.c_str() << " not found" << endl;
5300             return;
5301         }
5302 
5303         DemoApp::printChatInformation(it->second);
5304         return;
5305     }
5306 }
5307 
exec_chatl(autocomplete::ACState & s)5308 void exec_chatl(autocomplete::ACState& s)
5309 {
5310     handle chatid;
5311     Base64::atob(s.words[1].s.c_str(), (byte*) &chatid, MegaClient::CHATHANDLE);
5312     bool delflag = (s.words.size() == 3 && s.words[2].s == "del");
5313     bool createifmissing = s.words.size() == 2 || (s.words.size() == 3 && s.words[2].s != "query");
5314 
5315     client->chatlink(chatid, delflag, createifmissing);
5316 }
5317 #endif
5318 
exec_reset(autocomplete::ACState & s)5319 void exec_reset(autocomplete::ACState& s)
5320 {
5321     if (client->loggedin() != NOTLOGGEDIN)
5322     {
5323         cout << "You're logged in. Please, logout first." << endl;
5324     }
5325     else if (s.words.size() == 2 ||
5326         (s.words.size() == 3 && (hasMasterKey = (s.words[2].s == "mk"))))
5327     {
5328         recoveryemail = s.words[1].s;
5329         client->getrecoverylink(recoveryemail.c_str(), hasMasterKey);
5330     }
5331     else
5332     {
5333         cout << "      reset email [mk]" << endl;
5334     }
5335 }
5336 
exec_clink(autocomplete::ACState & s)5337 void exec_clink(autocomplete::ACState& s)
5338 {
5339     bool renew = false;
5340     if (s.words.size() == 1 || (s.words.size() == 2 && (renew = s.words[1].s == "renew")))
5341     {
5342         client->contactlinkcreate(renew);
5343     }
5344     else if ((s.words.size() == 3) && (s.words[1].s == "query"))
5345     {
5346         handle clink = UNDEF;
5347         Base64::atob(s.words[2].s.c_str(), (byte*)&clink, MegaClient::CONTACTLINKHANDLE);
5348 
5349         client->contactlinkquery(clink);
5350 
5351     }
5352     else if (((s.words.size() == 3) || (s.words.size() == 2)) && (s.words[1].s == "del"))
5353     {
5354         handle clink = UNDEF;
5355 
5356         if (s.words.size() == 3)
5357         {
5358             Base64::atob(s.words[2].s.c_str(), (byte*)&clink, MegaClient::CONTACTLINKHANDLE);
5359         }
5360 
5361         client->contactlinkdelete(clink);
5362     }
5363 }
5364 
exec_apiurl(autocomplete::ACState & s)5365 void exec_apiurl(autocomplete::ACState& s)
5366 {
5367     if (s.words.size() == 1)
5368     {
5369         cout << "Current APIURL = " << MegaClient::APIURL << endl;
5370         cout << "Current disablepkp = " << (MegaClient::disablepkp ? "true" : "false") << endl;
5371     }
5372     else if (client->loggedin() != NOTLOGGEDIN)
5373     {
5374         cout << "You must not be logged in, to change APIURL" << endl;
5375     }
5376     else if (s.words.size() == 3 || s.words.size() == 2)
5377     {
5378         if (s.words[1].s.size() < 8 || s.words[1].s.substr(0, 8) != "https://")
5379         {
5380             s.words[1].s = "https://" + s.words[1].s;
5381         }
5382         if (s.words[1].s.empty() || s.words[1].s[s.words[1].s.size() - 1] != '/')
5383         {
5384             s.words[1].s += '/';
5385         }
5386         MegaClient::APIURL = s.words[1].s;
5387         if (s.words.size() == 3)
5388         {
5389             MegaClient::disablepkp = s.words[2].s == "true";
5390         }
5391     }
5392 }
5393 
exec_passwd(autocomplete::ACState & s)5394 void exec_passwd(autocomplete::ACState& s)
5395 {
5396     if (client->loggedin() != NOTLOGGEDIN)
5397     {
5398         setprompt(NEWPASSWORD);
5399     }
5400     else
5401     {
5402         cout << "Not logged in." << endl;
5403     }
5404 }
5405 
exec_putbps(autocomplete::ACState & s)5406 void exec_putbps(autocomplete::ACState& s)
5407 {
5408     if (s.words.size() > 1)
5409     {
5410         if (s.words[1].s == "auto")
5411         {
5412             client->putmbpscap = -1;
5413         }
5414         else if (s.words[1].s == "none")
5415         {
5416             client->putmbpscap = 0;
5417         }
5418         else
5419         {
5420             int t = atoi(s.words[1].s.c_str());
5421 
5422             if (t > 0)
5423             {
5424                 client->putmbpscap = t;
5425             }
5426             else
5427             {
5428                 cout << "      putbps [limit|auto|none]" << endl;
5429                 return;
5430             }
5431         }
5432     }
5433 
5434     cout << "Upload speed limit set to ";
5435 
5436     if (client->putmbpscap < 0)
5437     {
5438         cout << "AUTO (approx. 90% of your available bandwidth)" << endl;
5439     }
5440     else if (!client->putmbpscap)
5441     {
5442         cout << "NONE" << endl;
5443     }
5444     else
5445     {
5446         cout << client->putmbpscap << " byte(s)/second" << endl;
5447     }
5448 }
5449 
exec_invite(autocomplete::ACState & s)5450 void exec_invite(autocomplete::ACState& s)
5451 {
5452     if (client->loggedin() != FULLACCOUNT)
5453     {
5454         cout << "Not logged in." << endl;
5455     }
5456     else
5457     {
5458         if (client->ownuser()->email.compare(s.words[1].s))
5459         {
5460             int delflag = s.words.size() == 3 && s.words[2].s == "del";
5461             int rmd = s.words.size() == 3 && s.words[2].s == "rmd";
5462             int clink = s.words.size() == 4 && s.words[2].s == "clink";
5463             if (s.words.size() == 2 || s.words.size() == 3 || s.words.size() == 4)
5464             {
5465                 if (delflag || rmd)
5466                 {
5467                     client->setpcr(s.words[1].s.c_str(), delflag ? OPCA_DELETE : OPCA_REMIND);
5468                 }
5469                 else
5470                 {
5471                     handle contactLink = UNDEF;
5472                     if (clink)
5473                     {
5474                         Base64::atob(s.words[3].s.c_str(), (byte*)&contactLink, MegaClient::CONTACTLINKHANDLE);
5475                     }
5476 
5477                     // Original email is not required, but can be used if this account has multiple email addresses associated,
5478                     // to have the invite come from a specific email
5479                     client->setpcr(s.words[1].s.c_str(), OPCA_ADD, "Invite from MEGAcli", s.words.size() == 3 ? s.words[2].s.c_str() : NULL, contactLink);
5480                 }
5481             }
5482             else
5483             {
5484                 cout << "      invite dstemail [origemail|del|rmd|clink <link>]" << endl;
5485             }
5486         }
5487         else
5488         {
5489             cout << "Cannot send invitation to your own user" << endl;
5490         }
5491     }
5492 }
5493 
exec_signup(autocomplete::ACState & s)5494 void exec_signup(autocomplete::ACState& s)
5495 {
5496     if (s.words.size() == 2)
5497     {
5498         const char* ptr = s.words[1].s.c_str();
5499         const char* tptr;
5500 
5501         if ((tptr = strstr(ptr, "#confirm")))
5502         {
5503             ptr = tptr + 8;
5504         }
5505 
5506         unsigned len = unsigned((s.words[1].s.size() - (ptr - s.words[1].s.c_str())) * 3 / 4 + 4);
5507 
5508         byte* c = new byte[len];
5509         len = Base64::atob(ptr, c, len);
5510         // we first just query the supplied signup link,
5511         // then collect and verify the password,
5512         // then confirm the account
5513         client->querysignuplink(c, len);
5514         delete[] c;
5515     }
5516     else if (s.words.size() == 3)
5517     {
5518         switch (client->loggedin())
5519         {
5520         case FULLACCOUNT:
5521             cout << "Already logged in." << endl;
5522             break;
5523 
5524         case CONFIRMEDACCOUNT:
5525             cout << "Current account already confirmed." << endl;
5526             break;
5527 
5528         case EPHEMERALACCOUNT:
5529             if (s.words[1].s.find('@') + 1 && s.words[1].s.find('.') + 1)
5530             {
5531                 signupemail = s.words[1].s;
5532                 signupname = s.words[2].s;
5533 
5534                 cout << endl;
5535                 setprompt(NEWPASSWORD);
5536             }
5537             else
5538             {
5539                 cout << "Please enter a valid e-mail address." << endl;
5540             }
5541             break;
5542 
5543         case NOTLOGGEDIN:
5544             cout << "Please use the begin command to commence or resume the ephemeral session to be upgraded." << endl;
5545         }
5546     }
5547 }
5548 
exec_cancelsignup(autocomplete::ACState & s)5549 void exec_cancelsignup(autocomplete::ACState& s)
5550 {
5551     client->cancelsignup();
5552 }
5553 
exec_whoami(autocomplete::ACState & s)5554 void exec_whoami(autocomplete::ACState& s)
5555 {
5556     if (client->loggedin() == NOTLOGGEDIN)
5557     {
5558         cout << "Not logged in." << endl;
5559     }
5560     else
5561     {
5562         User* u;
5563 
5564         if ((u = client->finduser(client->me)))
5565         {
5566             cout << "Account e-mail: " << u->email << " handle: " << Base64Str<MegaClient::USERHANDLE>(client->me) << endl;
5567             if (client->signkey)
5568             {
5569                 string pubKey((const char *)client->signkey->pubKey, EdDSA::PUBLIC_KEY_LENGTH);
5570                 cout << "Credentials: " << AuthRing::fingerprint(pubKey, true) << endl;
5571             }
5572         }
5573 
5574         bool storage = s.extractflag("-storage");
5575         bool transfer = s.extractflag("-transfer");
5576         bool pro = s.extractflag("-pro");
5577         bool transactions = s.extractflag("-transactions");
5578         bool purchases = s.extractflag("-purchases");
5579         bool sessions = s.extractflag("-sessions");
5580 
5581         bool all = !storage && !transfer && !pro && !transactions && !purchases && !sessions;
5582 
5583         cout << "Retrieving account status..." << endl;
5584 
5585         client->getaccountdetails(&account, all || storage, all || transfer, all || pro, all || transactions, all || purchases, all || sessions);
5586     }
5587 }
5588 
exec_verifycredentials(autocomplete::ACState & s)5589 void exec_verifycredentials(autocomplete::ACState& s)
5590 {
5591     User* u = nullptr;
5592     if (s.words.size() == 2 && (s.words[1].s == "show" || s.words[1].s == "status"))
5593     {
5594         u = client->finduser(client->me);
5595     }
5596     else if (s.words.size() == 3)
5597     {
5598         u = client->finduser(s.words[2].s.c_str());
5599     }
5600     else
5601     {
5602         cout << "      credentials show|status|verify|reset [email]" << endl;
5603         return;
5604     }
5605 
5606     if (!u)
5607     {
5608         cout << "Invalid user" << endl;
5609         return;
5610     }
5611 
5612     if (s.words[1].s == "show")
5613     {
5614         if (u->isattrvalid(ATTR_ED25519_PUBK))
5615         {
5616             cout << "Credentials: " << AuthRing::fingerprint(*u->getattr(ATTR_ED25519_PUBK), true) << endl;
5617         }
5618         else
5619         {
5620             cout << "Fetching singing key... " << endl;
5621             client->getua(u->uid.c_str(), ATTR_ED25519_PUBK);
5622         }
5623     }
5624     else if (s.words[1].s == "status")
5625     {
5626         handle uh = s.words.size() == 3 ? u->userhandle : UNDEF;
5627         printAuthringInformation(uh);
5628     }
5629     else if (s.words[1].s == "verify")
5630     {
5631         error e;
5632         if ((e = client->verifyCredentials(u->userhandle)))
5633         {
5634             cout << "Verification failed. Error: " << errorstring(e) << endl;
5635             return;
5636         }
5637     }
5638     else if (s.words[1].s == "reset")
5639     {
5640         error e;
5641         if ((e = client->resetCredentials(u->userhandle)))
5642         {
5643             cout << "Reset verification failed. Error: " << errorstring(e) << endl;
5644             return;
5645         }
5646     }
5647 }
5648 
exec_export(autocomplete::ACState & s)5649 void exec_export(autocomplete::ACState& s)
5650 {
5651     hlink = UNDEF;
5652     del = ets = 0;
5653 
5654     Node* n;
5655     int deltmp = 0;
5656     int etstmp = 0;
5657 
5658     if ((n = nodebypath(s.words[1].s.c_str())))
5659     {
5660         if (s.words.size() > 2)
5661         {
5662             deltmp = (s.words[2].s == "del");
5663             if (!deltmp)
5664             {
5665                 etstmp = atoi(s.words[2].s.c_str());
5666             }
5667         }
5668 
5669 
5670         cout << "Exporting..." << endl;
5671 
5672         error e;
5673         if ((e = client->exportnode(n, deltmp, etstmp)))
5674         {
5675             cout << s.words[1].s << ": Export rejected (" << errorstring(e) << ")" << endl;
5676         }
5677         else
5678         {
5679             hlink = n->nodehandle;
5680             ets = etstmp;
5681             del = deltmp;
5682         }
5683     }
5684     else
5685     {
5686         cout << s.words[1].s << ": Not found" << endl;
5687     }
5688 }
5689 
exec_import(autocomplete::ACState & s)5690 void exec_import(autocomplete::ACState& s)
5691 {
5692     handle ph = UNDEF;
5693     byte key[FILENODEKEYLENGTH];
5694     error e = client->parsepubliclink(s.words[1].s.c_str(), ph, key, false);
5695     if (e == API_OK)
5696     {
5697         cout << "Opening link..." << endl;
5698         client->openfilelink(ph, key, 1);
5699     }
5700     else
5701     {
5702         cout << "Malformed link. Format: Exported URL or fileid#filekey" << endl;
5703     }
5704 }
5705 
exec_folderlinkinfo(autocomplete::ACState & s)5706 void exec_folderlinkinfo(autocomplete::ACState& s)
5707 {
5708     publiclink = s.words[1].s;
5709 
5710     handle ph = UNDEF;
5711     byte folderkey[SymmCipher::KEYLENGTH];
5712     if (client->parsepubliclink(publiclink.c_str(), ph, folderkey, true) == API_OK)
5713     {
5714         cout << "Loading public folder link info..." << endl;
5715         client->getpubliclinkinfo(ph);
5716     }
5717     else
5718     {
5719         cout << "Malformed link: " << publiclink << endl;
5720     }
5721 }
5722 
exec_reload(autocomplete::ACState & s)5723 void exec_reload(autocomplete::ACState& s)
5724 {
5725     cout << "Reloading account..." << endl;
5726 
5727     bool nocache = false;
5728     if (s.words.size() == 2 && s.words[1].s == "nocache")
5729     {
5730         nocache = true;
5731     }
5732 
5733     cwd = UNDEF;
5734     client->cachedscsn = UNDEF;
5735     client->fetchnodes(nocache);
5736 }
5737 
exec_logout(autocomplete::ACState & s)5738 void exec_logout(autocomplete::ACState& s)
5739 {
5740     cout << "Logging off..." << endl;
5741 
5742     cwd = UNDEF;
5743     client->logout();
5744 
5745     if (clientFolder)
5746     {
5747         clientFolder->logout();
5748         delete clientFolder;
5749         clientFolder = NULL;
5750     }
5751 }
5752 
5753 #ifdef ENABLE_CHAT
exec_chatga(autocomplete::ACState & s)5754 void exec_chatga(autocomplete::ACState& s)
5755 {
5756     handle chatid;
5757     Base64::atob(s.words[1].s.c_str(), (byte*) &chatid, MegaClient::CHATHANDLE);
5758 
5759     handle nodehandle = 0; // make sure top two bytes are 0
5760     Base64::atob(s.words[2].s.c_str(), (byte*) &nodehandle, MegaClient::NODEHANDLE);
5761 
5762     const char *uid = s.words[3].s.c_str();
5763 
5764     client->grantAccessInChat(chatid, nodehandle, uid);
5765 }
5766 
exec_chatra(autocomplete::ACState & s)5767 void exec_chatra(autocomplete::ACState& s)
5768 {
5769     handle chatid;
5770     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5771 
5772     handle nodehandle = 0; // make sure top two bytes are 0
5773     Base64::atob(s.words[2].s.c_str(), (byte*)&nodehandle, MegaClient::NODEHANDLE);
5774 
5775     const char *uid = s.words[3].s.c_str();
5776 
5777     client->removeAccessInChat(chatid, nodehandle, uid);
5778 }
5779 
exec_chatst(autocomplete::ACState & s)5780 void exec_chatst(autocomplete::ACState& s)
5781 {
5782     handle chatid;
5783     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5784 
5785     if (s.words.size() == 2)  // empty title / remove title
5786     {
5787         client->setChatTitle(chatid, "");
5788     }
5789     else if (s.words.size() == 3)
5790     {
5791         client->setChatTitle(chatid, s.words[2].s.c_str());
5792     }
5793 }
5794 
exec_chatpu(autocomplete::ACState & s)5795 void exec_chatpu(autocomplete::ACState& s)
5796 {
5797     client->getChatPresenceUrl();
5798 }
5799 
exec_chatup(autocomplete::ACState & s)5800 void exec_chatup(autocomplete::ACState& s)
5801 {
5802     handle chatid;
5803     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5804 
5805     handle uh;
5806     Base64::atob(s.words[2].s.c_str(), (byte*)&uh, MegaClient::USERHANDLE);
5807 
5808     string privstr = s.words[3].s;
5809     privilege_t priv;
5810     if (privstr == "ro")
5811     {
5812         priv = PRIV_RO;
5813     }
5814     else if (privstr == "sta")
5815     {
5816         priv = PRIV_STANDARD;
5817     }
5818     else if (privstr == "mod")
5819     {
5820         priv = PRIV_MODERATOR;
5821     }
5822     else
5823     {
5824         cout << "Unknown privilege for " << s.words[2].s << endl;
5825         return;
5826     }
5827 
5828     client->updateChatPermissions(chatid, uh, priv);
5829 }
5830 
exec_chatlu(autocomplete::ACState & s)5831 void exec_chatlu(autocomplete::ACState& s)
5832 {
5833     handle publichandle = 0;
5834     Base64::atob(s.words[1].s.c_str(), (byte*)&publichandle, MegaClient::CHATLINKHANDLE);
5835 
5836     client->chatlinkurl(publichandle);
5837 }
5838 
exec_chatsm(autocomplete::ACState & s)5839 void exec_chatsm(autocomplete::ACState& s)
5840 {
5841     handle chatid;
5842     Base64::atob(s.words[1].s.c_str(), (byte*)&chatid, MegaClient::CHATHANDLE);
5843 
5844     const char *title = (s.words.size() == 3) ? s.words[2].s.c_str() : NULL;
5845     client->chatlinkclose(chatid, title);
5846 }
5847 
exec_chatlj(autocomplete::ACState & s)5848 void exec_chatlj(autocomplete::ACState& s)
5849 {
5850     handle publichandle = 0;
5851     Base64::atob(s.words[1].s.c_str(), (byte*)&publichandle, MegaClient::CHATLINKHANDLE);
5852 
5853     client->chatlinkjoin(publichandle, s.words[2].s.c_str());
5854 }
5855 
exec_chatcp(autocomplete::ACState & s)5856 void exec_chatcp(autocomplete::ACState& s)
5857 {
5858     size_t wordscount = s.words.size();
5859     userpriv_vector *userpriv = new userpriv_vector;
5860     string_map *userkeymap = new string_map;
5861     string mownkey = s.words[1].s;
5862     unsigned parseoffset = 2;
5863     const char *title = NULL;
5864 
5865     if (wordscount >= 4)
5866     {
5867         if (s.words[2].s == "t")
5868         {
5869             if (s.words[3].s.empty())
5870             {
5871                 cout << "Title cannot be set to empty string" << endl;
5872                 delete userpriv;
5873                 delete userkeymap;
5874                 return;
5875             }
5876             title = s.words[3].s.c_str();
5877             parseoffset = 4;
5878         }
5879 
5880         if (((wordscount - parseoffset) % 3) != 0)
5881         {
5882             cout << "Invalid syntax to create chatroom" << endl;
5883             cout << "      chatcp mownkey [t title64] [email ro|sta|mod unifiedkey]* " << endl;
5884             delete userpriv;
5885             delete userkeymap;
5886             return;
5887         }
5888 
5889         unsigned numUsers = 0;
5890         while ((numUsers + 1) * 3 + parseoffset <= wordscount)
5891         {
5892             string email = s.words[numUsers * 3 + parseoffset].s;
5893             User *u = client->finduser(email.c_str(), 0);
5894             if (!u)
5895             {
5896                 cout << "User not found: " << email << endl;
5897                 delete userpriv;
5898                 delete userkeymap;
5899                 return;
5900             }
5901 
5902             string privstr = s.words[numUsers * 3 + parseoffset + 1].s;
5903             privilege_t priv;
5904             if (privstr == "ro")
5905             {
5906                 priv = PRIV_RO;
5907             }
5908             else if (privstr == "sta")
5909             {
5910                 priv = PRIV_STANDARD;
5911             }
5912             else if (privstr == "mod")
5913             {
5914                 priv = PRIV_MODERATOR;
5915             }
5916             else
5917             {
5918                 cout << "Unknown privilege for " << email << endl;
5919                 delete userpriv;
5920                 delete userkeymap;
5921                 return;
5922             }
5923             userpriv->push_back(userpriv_pair(u->userhandle, priv));
5924             string unifiedkey = s.words[numUsers * 3 + parseoffset + 2].s;
5925             char uhB64[12];
5926             Base64::btoa((byte *)&u->userhandle, MegaClient::USERHANDLE, uhB64);
5927             uhB64[11] = '\0';
5928             userkeymap->insert(std::pair<string, string>(uhB64, unifiedkey));
5929             numUsers++;
5930         }
5931     }
5932     char ownHandleB64[12];
5933     Base64::btoa((byte *)&client->me, MegaClient::USERHANDLE, ownHandleB64);
5934     ownHandleB64[11] = '\0';
5935     userkeymap->insert(std::pair<string, string>(ownHandleB64, mownkey));
5936     client->createChat(true, true, userpriv, userkeymap, title);
5937     delete userpriv;
5938     delete userkeymap;
5939 }
5940 #endif
5941 
exec_cancel(autocomplete::ACState & s)5942 void exec_cancel(autocomplete::ACState& s)
5943 {
5944     if (client->loggedin() != FULLACCOUNT)
5945     {
5946         cout << "Please, login into your account first." << endl;
5947         return;
5948     }
5949 
5950     if (s.words.size() == 1)  // get link
5951     {
5952         User *u = client->finduser(client->me);
5953         if (!u)
5954         {
5955             cout << "Error retrieving logged user." << endl;
5956             return;
5957         }
5958         client->getcancellink(u->email.c_str());
5959     }
5960     else if (s.words.size() == 2) // link confirmation
5961     {
5962         string link = s.words[1].s;
5963 
5964         size_t pos = link.find("#cancel");
5965         if (pos == link.npos)
5966         {
5967             cout << "Invalid cancellation link." << endl;
5968             return;
5969         }
5970 
5971         recoverycode.assign(link.substr(pos + strlen("#cancel")));
5972         setprompt(LOGINPASSWORD);
5973     }
5974 }
5975 
exec_alerts(autocomplete::ACState & s)5976 void exec_alerts(autocomplete::ACState& s)
5977 {
5978     bool shownew = false, showold = false;
5979     size_t showN = 0;
5980     if (s.words.size() == 1)
5981     {
5982         shownew = showold = true;
5983     }
5984     else if (s.words.size() == 2)
5985     {
5986         if (s.words[1].s == "seen")
5987         {
5988             client->useralerts.acknowledgeAll();
5989             return;
5990         }
5991         else if (s.words[1].s == "notify")
5992         {
5993             notifyAlerts = !notifyAlerts;
5994             cout << "notification of alerts is now " << (notifyAlerts ? "on" : "off") << endl;
5995             return;
5996         }
5997         else if (s.words[1].s == "old")
5998         {
5999             showold = true;
6000         }
6001         else if (s.words[1].s == "new")
6002         {
6003             shownew = true;
6004         }
6005         else if (s.words[1].s == "test_reminder")
6006         {
6007             client->useralerts.add(new UserAlert::PaymentReminder(time(NULL) - 86000*3 /2, client->useralerts.nextId()));
6008         }
6009         else if (s.words[1].s == "test_payment")
6010         {
6011             client->useralerts.add(new UserAlert::Payment(true, 1, time(NULL) + 86000 * 1, client->useralerts.nextId()));
6012         }
6013         else if (atoi(s.words[1].s.c_str()) > 0)
6014         {
6015             showN = atoi(s.words[1].s.c_str());
6016         }
6017     }
6018     if (showold || shownew || showN > 0)
6019     {
6020         UserAlerts::Alerts::const_iterator i = client->useralerts.alerts.begin();
6021         if (showN)
6022         {
6023             size_t n = 0;
6024             for (UserAlerts::Alerts::const_reverse_iterator i = client->useralerts.alerts.rbegin(); i != client->useralerts.alerts.rend(); ++i, ++n)
6025             {
6026                 showN += ((*i)->relevant || n >= showN) ? 0 : 1;
6027             }
6028         }
6029 
6030         size_t n = client->useralerts.alerts.size();
6031         for (; i != client->useralerts.alerts.end(); ++i)
6032         {
6033             if ((*i)->relevant)
6034             {
6035                 if (--n < showN || (shownew && !(*i)->seen) || (showold && (*i)->seen))
6036                 {
6037                     printAlert(**i);
6038                 }
6039             }
6040         }
6041     }
6042 }
6043 
6044 #ifdef USE_FILESYSTEM
exec_lmkdir(autocomplete::ACState & s)6045 void exec_lmkdir(autocomplete::ACState& s)
6046 {
6047     std::error_code ec;
6048     if (!fs::create_directory(s.words[1].s.c_str(), ec))
6049     {
6050         cerr << "Create directory failed: " << ec.message() << endl;
6051     }
6052 }
6053 #endif
6054 
6055 
exec_confirm(autocomplete::ACState & s)6056 void exec_confirm(autocomplete::ACState& s)
6057 {
6058     if (signupemail.size() && signupcode.size())
6059     {
6060         cout << "Please type " << signupemail << "'s password to confirm the signup." << endl;
6061         setprompt(LOGINPASSWORD);
6062     }
6063 }
6064 
exec_recover(autocomplete::ACState & s)6065 void exec_recover(autocomplete::ACState& s)
6066 {
6067     if (client->loggedin() != NOTLOGGEDIN)
6068     {
6069         cout << "You're logged in. Please, logout first." << endl;
6070     }
6071     else if (s.words.size() == 2)
6072     {
6073         string link = s.words[1].s;
6074 
6075         size_t pos = link.find("#recover");
6076         if (pos == link.npos)
6077         {
6078             cout << "Invalid recovery link." << endl;
6079         }
6080 
6081         recoverycode.assign(link.substr(pos + strlen("#recover")));
6082         client->queryrecoverylink(recoverycode.c_str());
6083     }
6084 }
6085 
exec_session(autocomplete::ACState & s)6086 void exec_session(autocomplete::ACState& s)
6087 {
6088     byte session[64];
6089     int size;
6090 
6091     size = client->dumpsession(session, sizeof session);
6092 
6093     if (size > 0)
6094     {
6095         Base64Str<sizeof session> buf(session, size);
6096 
6097         if ((s.words.size() == 2 || s.words.size() == 3) && s.words[1].s == "autoresume")
6098         {
6099             string filename = "megacli_autoresume_session" + (s.words.size() == 3 ? "_" + s.words[2].s : "");
6100             ofstream file(filename.c_str());
6101             if (file.fail() || !file.is_open())
6102             {
6103                 cout << "could not open file: " << filename << endl;
6104             }
6105             else
6106             {
6107                 file << buf;
6108                 cout << "Your (secret) session is saved in file '" << filename << "'" << endl;
6109             }
6110         }
6111         else
6112         {
6113             cout << "Your (secret) session is: " << buf << endl;
6114         }
6115     }
6116     else if (!size)
6117     {
6118         cout << "Not logged in." << endl;
6119     }
6120     else
6121     {
6122         cout << "Internal error." << endl;
6123     }
6124 }
6125 
exec_symlink(autocomplete::ACState & s)6126 void exec_symlink(autocomplete::ACState& s)
6127 {
6128     if (client->followsymlinks ^= true)
6129     {
6130         cout << "Now following symlinks. Please ensure that sync does not see any filesystem item twice!" << endl;
6131     }
6132     else
6133     {
6134         cout << "No longer following symlinks." << endl;
6135     }
6136 }
6137 
exec_version(autocomplete::ACState & s)6138 void exec_version(autocomplete::ACState& s)
6139 {
6140     cout << "MEGA SDK version: " << MEGA_MAJOR_VERSION << "." << MEGA_MINOR_VERSION << "." << MEGA_MICRO_VERSION << endl;
6141 
6142     cout << "Features enabled:" << endl;
6143 
6144 #ifdef USE_CRYPTOPP
6145     cout << "* CryptoPP" << endl;
6146 #endif
6147 
6148 #ifdef USE_SQLITE
6149     cout << "* SQLite" << endl;
6150 #endif
6151 
6152 #ifdef USE_BDB
6153     cout << "* Berkeley DB" << endl;
6154 #endif
6155 
6156 #ifdef USE_INOTIFY
6157     cout << "* inotify" << endl;
6158 #endif
6159 
6160 #ifdef HAVE_FDOPENDIR
6161     cout << "* fdopendir" << endl;
6162 #endif
6163 
6164 #ifdef HAVE_SENDFILE
6165     cout << "* sendfile" << endl;
6166 #endif
6167 
6168 #ifdef _LARGE_FILES
6169     cout << "* _LARGE_FILES" << endl;
6170 #endif
6171 
6172 #ifdef USE_FREEIMAGE
6173     cout << "* FreeImage" << endl;
6174 #endif
6175 
6176 #ifdef ENABLE_SYNC
6177     cout << "* sync subsystem" << endl;
6178 #endif
6179 
6180 #ifdef USE_MEDIAINFO
6181     cout << "* MediaInfo" << endl;
6182 #endif
6183 
6184     cwd = UNDEF;
6185 }
6186 
exec_showpcr(autocomplete::ACState & s)6187 void exec_showpcr(autocomplete::ACState& s)
6188 {
6189     string outgoing = "";
6190     string incoming = "";
6191     for (handlepcr_map::iterator it = client->pcrindex.begin(); it != client->pcrindex.end(); it++)
6192     {
6193         if (it->second->isoutgoing)
6194         {
6195             ostringstream os;
6196             os << setw(34) << it->second->targetemail;
6197 
6198             os << "\t(id: ";
6199             os << Base64Str<MegaClient::PCRHANDLE>(it->second->id);
6200 
6201             os << ", ts: ";
6202 
6203             os << it->second->ts;
6204 
6205             outgoing.append(os.str());
6206             outgoing.append(")\n");
6207         }
6208         else
6209         {
6210             ostringstream os;
6211             os << setw(34) << it->second->originatoremail;
6212 
6213             os << "\t(id: ";
6214             os << Base64Str<MegaClient::PCRHANDLE>(it->second->id);
6215 
6216             os << ", ts: ";
6217 
6218             os << it->second->ts;
6219 
6220             incoming.append(os.str());
6221             incoming.append(")\n");
6222         }
6223     }
6224     cout << "Incoming PCRs:" << endl << incoming << endl;
6225     cout << "Outgoing PCRs:" << endl << outgoing << endl;
6226 }
6227 
6228 #if defined(WIN32) && defined(NO_READLINE)
exec_history(autocomplete::ACState & s)6229 void exec_history(autocomplete::ACState& s)
6230 {
6231     static_cast<WinConsole*>(console)->outputHistory();
6232 }
6233 #endif
6234 
exec_handles(autocomplete::ACState & s)6235 void exec_handles(autocomplete::ACState& s)
6236 {
6237     if (s.words.size() == 2)
6238     {
6239         if (s.words[1].s == "on")
6240         {
6241             handles_on = true;
6242         }
6243         else if (s.words[1].s == "off")
6244         {
6245             handles_on = false;
6246         }
6247         else
6248         {
6249             cout << "invalid handles setting" << endl;
6250         }
6251     }
6252     else
6253     {
6254         cout << "      handles on|off " << endl;
6255     }
6256 }
6257 
6258 
6259 #if defined(WIN32) && defined(NO_READLINE)
exec_codepage(autocomplete::ACState & s)6260 void exec_codepage(autocomplete::ACState& s)
6261 {
6262     WinConsole* wc = static_cast<WinConsole*>(console);
6263     if (s.words.size() == 1)
6264     {
6265         UINT cp1, cp2;
6266         wc->getShellCodepages(cp1, cp2);
6267         cout << "Current codepage is " << cp1;
6268         if (cp2 != cp1)
6269         {
6270             cout << " with failover to codepage " << cp2 << " for any absent glyphs";
6271         }
6272         cout << endl;
6273         for (int i = 32; i < 256; ++i)
6274         {
6275             string theCharUtf8 = WinConsole::toUtf8String(WinConsole::toUtf16String(string(1, (char)i), cp1));
6276             cout << "  dec/" << i << " hex/" << hex << i << dec << ": '" << theCharUtf8 << "'";
6277             if (i % 4 == 3)
6278             {
6279                 cout << endl;
6280             }
6281         }
6282     }
6283     else if (s.words.size() == 2 && atoi(s.words[1].s.c_str()) != 0)
6284     {
6285         if (!wc->setShellConsole(atoi(s.words[1].s.c_str()), atoi(s.words[1].s.c_str())))
6286         {
6287             cout << "Code page change failed - unicode selected" << endl;
6288         }
6289     }
6290     else if (s.words.size() == 3 && atoi(s.words[1].s.c_str()) != 0 && atoi(s.words[2].s.c_str()) != 0)
6291     {
6292         if (!wc->setShellConsole(atoi(s.words[1].s.c_str()), atoi(s.words[2].s.c_str())))
6293         {
6294             cout << "Code page change failed - unicode selected" << endl;
6295         }
6296     }
6297 }
6298 #endif
6299 
exec_httpsonly(autocomplete::ACState & s)6300 void exec_httpsonly(autocomplete::ACState& s)
6301 {
6302     if (s.words.size() == 1)
6303     {
6304         cout << "httpsonly: " << (client->usehttps ? "on" : "off") << endl;
6305     }
6306     else if (s.words.size() == 2)
6307     {
6308         if (s.words[1].s == "on")
6309         {
6310             client->usehttps = true;
6311         }
6312         else if (s.words[1].s == "off")
6313         {
6314             client->usehttps = false;
6315         }
6316         else
6317         {
6318             cout << "invalid setting" << endl;
6319         }
6320     }
6321 }
6322 
6323 #ifdef USE_MEDIAINFO
exec_mediainfo(autocomplete::ACState & s)6324 void exec_mediainfo(autocomplete::ACState& s)
6325 {
6326     if (client->mediaFileInfo.mediaCodecsFailed)
6327     {
6328         cout << "Sorry, mediainfo lookups could not be retrieved." << endl;
6329         return;
6330     }
6331     else if (!client->mediaFileInfo.mediaCodecsReceived)
6332     {
6333         client->mediaFileInfo.requestCodecMappingsOneTime(client, NULL);
6334         cout << "Mediainfo lookups requested" << endl;
6335     }
6336 
6337     if (s.words.size() == 3 && s.words[1].s == "calc")
6338     {
6339         MediaProperties mp;
6340         auto localFilename = LocalPath::fromPath(s.words[2].s, *client->fsaccess);
6341 
6342         char ext[8];
6343         if (client->fsaccess->getextension(localFilename, ext, sizeof(ext)) && MediaProperties::isMediaFilenameExt(ext))
6344         {
6345             mp.extractMediaPropertyFileAttributes(localFilename, client->fsaccess);
6346                                 uint32_t dummykey[4] = { 1, 2, 3, 4 };  // check encode/decode
6347                                 string attrs = mp.convertMediaPropertyFileAttributes(dummykey, client->mediaFileInfo);
6348                                 MediaProperties dmp = MediaProperties::decodeMediaPropertiesAttributes(":" + attrs, dummykey);
6349                                 cout << showMediaInfo(dmp, client->mediaFileInfo, false) << endl;
6350         }
6351         else
6352         {
6353             cout << "Filename extension is not suitable for mediainfo analysis." << endl;
6354         }
6355     }
6356     else if (s.words.size() == 3 && s.words[1].s == "show")
6357     {
6358         if (Node *n = nodebypath(s.words[2].s.c_str()))
6359         {
6360             switch (n->type)
6361             {
6362             case FILENODE:
6363                 cout << showMediaInfo(n, client->mediaFileInfo, false) << endl;
6364                 break;
6365 
6366             case FOLDERNODE:
6367             case ROOTNODE:
6368             case INCOMINGNODE:
6369             case RUBBISHNODE:
6370                 for (node_list::iterator m = n->children.begin(); m != n->children.end(); ++m)
6371                 {
6372                     if ((*m)->type == FILENODE && (*m)->hasfileattribute(fa_media))
6373                     {
6374                         cout << (*m)->displayname() << "   " << showMediaInfo(*m, client->mediaFileInfo, true) << endl;
6375                     }
6376                 }
6377                 break;
6378             case TYPE_UNKNOWN: break;
6379             }
6380         }
6381         else
6382         {
6383             cout << "remote file not found: " << s.words[2].s << endl;
6384         }
6385     }
6386 }
6387 #endif
6388 
exec_smsverify(autocomplete::ACState & s)6389 void exec_smsverify(autocomplete::ACState& s)
6390 {
6391     if (s.words[1].s == "send")
6392     {
6393         bool reverifywhitelisted = (s.words.size() == 4 && s.words[3].s == "reverifywhitelisted");
6394         if (client->smsverificationsend(s.words[2].s, reverifywhitelisted) != API_OK)
6395         {
6396             cout << "phonenumber is invalid" << endl;
6397         }
6398     }
6399     else if (s.words[1].s == "code")
6400     {
6401         if (client->smsverificationcheck(s.words[2].s) != API_OK)
6402         {
6403             cout << "verificationcode is invalid" << endl;
6404         }
6405     }
6406 }
6407 
exec_verifiedphonenumber(autocomplete::ACState & s)6408 void exec_verifiedphonenumber(autocomplete::ACState& s)
6409 {
6410     cout << "Verified phone number: " << client->mSmsVerifiedPhone << endl;
6411 }
6412 
exec_killsession(autocomplete::ACState & s)6413 void exec_killsession(autocomplete::ACState& s)
6414 {
6415     if (s.words[1].s == "all")
6416     {
6417         // Kill all sessions (except current)
6418         client->killallsessions();
6419     }
6420     else
6421     {
6422         handle sessionid;
6423         if (Base64::atob(s.words[1].s.c_str(), (byte*)&sessionid, sizeof sessionid) == sizeof sessionid)
6424         {
6425             client->killsession(sessionid);
6426         }
6427         else
6428         {
6429             cout << "invalid session id provided" << endl;
6430         }
6431     }
6432 }
6433 
exec_locallogout(autocomplete::ACState & s)6434 void exec_locallogout(autocomplete::ACState& s)
6435 {
6436     cout << "Logging off locally..." << endl;
6437 
6438     cwd = UNDEF;
6439     client->locallogout(false);
6440 }
6441 
exec_recentnodes(autocomplete::ACState & s)6442 void exec_recentnodes(autocomplete::ACState& s)
6443 {
6444     if (s.words.size() == 3)
6445     {
6446         node_vector nv = client->getRecentNodes(atoi(s.words[2].s.c_str()), m_time() - 60 * 60 * atoi(s.words[1].s.c_str()), false);
6447         for (unsigned i = 0; i < nv.size(); ++i)
6448         {
6449             cout << nv[i]->displaypath() << endl;
6450         }
6451     }
6452 }
6453 
6454 #if defined(WIN32) && defined(NO_READLINE)
exec_autocomplete(autocomplete::ACState & s)6455 void exec_autocomplete(autocomplete::ACState& s)
6456 {
6457     if (s.words[1].s == "unix")
6458     {
6459         static_cast<WinConsole*>(console)->setAutocompleteStyle(true);
6460     }
6461     else if (s.words[1].s == "dos")
6462     {
6463         static_cast<WinConsole*>(console)->setAutocompleteStyle(false);
6464     }
6465     else
6466     {
6467         cout << "invalid autocomplete style" << endl;
6468     }
6469 }
6470 #endif
6471 
exec_recentactions(autocomplete::ACState & s)6472 void exec_recentactions(autocomplete::ACState& s)
6473 {
6474     recentactions_vector nvv = client->getRecentActions(atoi(s.words[2].s.c_str()), m_time() - 60 * 60 * atoi(s.words[1].s.c_str()));
6475     for (unsigned i = 0; i < nvv.size(); ++i)
6476     {
6477         if (i != 0)
6478         {
6479             cout << "---" << endl;
6480         }
6481         cout << displayTime(nvv[i].time) << " " << displayUser(nvv[i].user, client) << " " << (nvv[i].updated ? "updated" : "uploaded") << " " << (nvv[i].media ? "media" : "files") << endl;
6482         for (unsigned j = 0; j < nvv[i].nodes.size(); ++j)
6483         {
6484             cout << nvv[i].nodes[j]->displaypath() << "  (" << displayTime(nvv[i].nodes[j]->ctime) << ")" << endl;
6485         }
6486     }
6487 }
6488 
exec_setmaxuploadspeed(autocomplete::ACState & s)6489 void exec_setmaxuploadspeed(autocomplete::ACState& s)
6490 {
6491     if (s.words.size() > 1)
6492     {
6493         bool done = client->setmaxuploadspeed(atoi(s.words[1].s.c_str()));
6494         cout << (done ? "Success. " : "Failed. ");
6495     }
6496     cout << "Max Upload Speed: " << client->getmaxuploadspeed() << endl;
6497 }
6498 
exec_setmaxdownloadspeed(autocomplete::ACState & s)6499 void exec_setmaxdownloadspeed(autocomplete::ACState& s)
6500 {
6501     if (s.words.size() > 1)
6502     {
6503         bool done = client->setmaxdownloadspeed(atoi(s.words[1].s.c_str()));
6504         cout << (done ? "Success. " : "Failed. ");
6505     }
6506     cout << "Max Download Speed: " << client->getmaxdownloadspeed() << endl;
6507 }
6508 
exec_enabletransferresumption(autocomplete::ACState & s)6509 void exec_enabletransferresumption(autocomplete::ACState& s)
6510 {
6511     if (s.words.size() > 1 && s.words[1].s == "off")
6512     {
6513         client->disabletransferresumption(NULL);
6514         cout << "transfer resumption disabled" << endl;
6515     }
6516     else
6517     {
6518         client->enabletransferresumption(NULL);
6519         cout << "transfer resumption enabled" << endl;
6520     }
6521 }
6522 
6523 // callback for non-EAGAIN request-level errors
6524 // in most cases, retrying is futile, so the application exits
6525 // this can occur e.g. with syntactically malformed requests (due to a bug), an invalid application key
request_error(error e)6526 void DemoApp::request_error(error e)
6527 {
6528     if ((e == API_ESID) || (e == API_ENOENT))   // Invalid session or Invalid folder handle
6529     {
6530         cout << "Invalid or expired session, logging out..." << endl;
6531         client->locallogout(false);
6532         return;
6533     }
6534     else if (e == API_EBLOCKED)
6535     {
6536         if (client->sid.size())
6537         {
6538             cout << "Your account is blocked." << endl;
6539             client->whyamiblocked();
6540         }
6541         else
6542         {
6543             cout << "The link has been blocked." << endl;
6544         }
6545         return;
6546     }
6547 
6548     cout << "FATAL: Request failed (" << errorstring(e) << "), exiting" << endl;
6549 
6550     delete console;
6551     exit(0);
6552 }
6553 
request_response_progress(m_off_t current,m_off_t total)6554 void DemoApp::request_response_progress(m_off_t current, m_off_t total)
6555 {
6556     if (total > 0)
6557     {
6558         responseprogress = int(current * 100 / total);
6559     }
6560     else
6561     {
6562         responseprogress = -1;
6563     }
6564 }
6565 
6566 //2FA disable result
multifactorauthdisable_result(error e)6567 void DemoApp::multifactorauthdisable_result(error e)
6568 {
6569     if (!e)
6570     {
6571         cout << "2FA, disabled succesfully..." << endl;
6572     }
6573     else
6574     {
6575         cout << "Error enabling 2FA : " << errorstring(e) << endl;
6576     }
6577     setprompt(COMMAND);
6578 }
6579 
6580 //2FA check result
multifactorauthcheck_result(int enabled)6581 void DemoApp::multifactorauthcheck_result(int enabled)
6582 {
6583     if (enabled)
6584     {
6585         cout << "2FA is enabled for this account" << endl;
6586     }
6587     else
6588     {
6589         cout << "2FA is disabled for this account" << endl;
6590     }
6591     setprompt(COMMAND);
6592 }
6593 
6594 //2FA enable result
multifactorauthsetup_result(string * code,error e)6595 void DemoApp::multifactorauthsetup_result(string *code, error e)
6596 {
6597     if (!e)
6598     {
6599         if (!code)
6600         {
6601             cout << "2FA enabled successfully" << endl;
6602             setprompt(COMMAND);
6603             attempts = 0;
6604         }
6605         else
6606         {
6607             cout << "2FA code: " << *code << endl;
6608             setprompt(SETTFA);
6609         }
6610     }
6611     else
6612     {
6613         cout << "Error enabling 2FA : " << errorstring(e) << endl;
6614         if (e == API_EFAILED)
6615         {
6616             if (++attempts >= 3)
6617             {
6618                 attempts = 0;
6619                 cout << "Too many attempts"<< endl;
6620                 setprompt(COMMAND);
6621             }
6622             else
6623             {
6624                 setprompt(SETTFA);
6625             }
6626         }
6627     }
6628 }
6629 
6630 
prelogin_result(int version,string *,string * salt,error e)6631 void DemoApp::prelogin_result(int version, string* /*email*/, string *salt, error e)
6632 {
6633     if (e)
6634     {
6635         cout << "Login error: " << e << endl;
6636         setprompt(COMMAND);
6637         return;
6638     }
6639 
6640     login.version = version;
6641     login.salt = (version == 2 && salt ? *salt : string());
6642 
6643     if (login.password.empty())
6644     {
6645         setprompt(LOGINPASSWORD);
6646     }
6647     else
6648     {
6649         login.login(client);
6650     }
6651 }
6652 
6653 
6654 // login result
login_result(error e)6655 void DemoApp::login_result(error e)
6656 {
6657     if (!e)
6658     {
6659         login.reset();
6660         cout << "Login successful, retrieving account..." << endl;
6661         client->fetchnodes();
6662     }
6663     else if (e == API_EMFAREQUIRED)
6664     {
6665         setprompt(LOGINTFA);
6666     }
6667     else
6668     {
6669         login.reset();
6670         cout << "Login failed: " << errorstring(e) << endl;
6671     }
6672 }
6673 
6674 // ephemeral session result
ephemeral_result(error e)6675 void DemoApp::ephemeral_result(error e)
6676 {
6677     if (e)
6678     {
6679         cout << "Ephemeral session error (" << errorstring(e) << ")" << endl;
6680     }
6681     pdf_to_import = false;
6682 }
6683 
6684 // signup link send request result
sendsignuplink_result(error e)6685 void DemoApp::sendsignuplink_result(error e)
6686 {
6687     if (e)
6688     {
6689         cout << "Unable to send signup link (" << errorstring(e) << ")" << endl;
6690     }
6691     else
6692     {
6693         cout << "Thank you. Please check your e-mail and enter the command signup followed by the confirmation link." << endl;
6694     }
6695 }
6696 
6697 // signup link query result
querysignuplink_result(handle,const char * email,const char * name,const byte * pwc,const byte *,const byte * c,size_t len)6698 void DemoApp::querysignuplink_result(handle /*uh*/, const char* email, const char* name, const byte* pwc, const byte* /*kc*/,
6699                                      const byte* c, size_t len)
6700 {
6701     cout << "Ready to confirm user account " << email << " (" << name << ") - enter confirm to execute." << endl;
6702 
6703     signupemail = email;
6704     signupcode.assign((char*) c, len);
6705     memcpy(signuppwchallenge, pwc, sizeof signuppwchallenge);
6706     memcpy(signupencryptedmasterkey, pwc, sizeof signupencryptedmasterkey);
6707 }
6708 
6709 // signup link query failed
querysignuplink_result(error e)6710 void DemoApp::querysignuplink_result(error e)
6711 {
6712     cout << "Signuplink confirmation failed (" << errorstring(e) << ")" << endl;
6713 }
6714 
6715 // signup link (account e-mail) confirmation result
confirmsignuplink_result(error e)6716 void DemoApp::confirmsignuplink_result(error e)
6717 {
6718     if (e)
6719     {
6720         cout << "Signuplink confirmation failed (" << errorstring(e) << ")" << endl;
6721     }
6722     else
6723     {
6724         cout << "Signup confirmed, logging in..." << endl;
6725         client->login(signupemail.c_str(), pwkey);
6726     }
6727 }
6728 
6729 // asymmetric keypair configuration result
setkeypair_result(error e)6730 void DemoApp::setkeypair_result(error e)
6731 {
6732     if (e)
6733     {
6734         cout << "RSA keypair setup failed (" << errorstring(e) << ")" << endl;
6735     }
6736     else
6737     {
6738         cout << "RSA keypair added. Account setup complete." << endl;
6739     }
6740 }
6741 
getrecoverylink_result(error e)6742 void DemoApp::getrecoverylink_result(error e)
6743 {
6744     if (e)
6745     {
6746         cout << "Unable to send the link (" << errorstring(e) << ")" << endl;
6747     }
6748     else
6749     {
6750         cout << "Please check your e-mail and enter the command \"recover\" / \"cancel\" followed by the link." << endl;
6751     }
6752 }
6753 
queryrecoverylink_result(error e)6754 void DemoApp::queryrecoverylink_result(error e)
6755 {
6756         cout << "The link is invalid (" << errorstring(e) << ")." << endl;
6757 }
6758 
queryrecoverylink_result(int type,const char * email,const char *,time_t,handle,const vector<string> *)6759 void DemoApp::queryrecoverylink_result(int type, const char *email, const char* /*ip*/, time_t /*ts*/, handle /*uh*/, const vector<string>* /*emails*/)
6760 {
6761     recoveryemail = email ? email : "";
6762     hasMasterKey = (type == RECOVER_WITH_MASTERKEY);
6763 
6764     cout << "The link is valid";
6765 
6766     if (type == RECOVER_WITH_MASTERKEY)
6767     {
6768         cout <<  " to reset the password for " << email << " with masterkey." << endl;
6769 
6770         setprompt(MASTERKEY);
6771     }
6772     else if (type == RECOVER_WITHOUT_MASTERKEY)
6773     {
6774         cout <<  " to reset the password for " << email << " without masterkey." << endl;
6775 
6776         setprompt(NEWPASSWORD);
6777     }
6778     else if (type == CANCEL_ACCOUNT)
6779     {
6780         cout << " to cancel the account for " << email << "." << endl;
6781     }
6782     else if (type == CHANGE_EMAIL)
6783     {
6784         cout << " to change the email from " << client->finduser(client->me)->email << " to " << email << "." << endl;
6785 
6786         changeemail = email ? email : "";
6787         setprompt(LOGINPASSWORD);
6788     }
6789 }
6790 
getprivatekey_result(error e,const byte * privk,const size_t len_privk)6791 void DemoApp::getprivatekey_result(error e,  const byte *privk, const size_t len_privk)
6792 {
6793     if (e)
6794     {
6795         cout << "Unable to get private key (" << errorstring(e) << ")" << endl;
6796         setprompt(COMMAND);
6797     }
6798     else
6799     {
6800         // check the private RSA is valid after decryption with master key
6801         SymmCipher key;
6802         key.setkey(masterkey);
6803 
6804         byte privkbuf[AsymmCipher::MAXKEYLENGTH * 2];
6805         memcpy(privkbuf, privk, len_privk);
6806         key.ecb_decrypt(privkbuf, len_privk);
6807 
6808         AsymmCipher uk;
6809         if (!uk.setkey(AsymmCipher::PRIVKEY, privkbuf, unsigned(len_privk)))
6810         {
6811             cout << "The master key doesn't seem to be correct." << endl;
6812 
6813             recoverycode.clear();
6814             recoveryemail.clear();
6815             hasMasterKey = false;
6816             memset(masterkey, 0, sizeof masterkey);
6817 
6818             setprompt(COMMAND);
6819         }
6820         else
6821         {
6822             cout << "Private key successfully retrieved for integrity check masterkey." << endl;
6823             setprompt(NEWPASSWORD);
6824         }
6825     }
6826 }
6827 
confirmrecoverylink_result(error e)6828 void DemoApp::confirmrecoverylink_result(error e)
6829 {
6830     if (e)
6831     {
6832         cout << "Unable to reset the password (" << errorstring(e) << ")" << endl;
6833     }
6834     else
6835     {
6836         cout << "Password changed successfully." << endl;
6837     }
6838 }
6839 
confirmcancellink_result(error e)6840 void DemoApp::confirmcancellink_result(error e)
6841 {
6842     if (e)
6843     {
6844         cout << "Unable to cancel the account (" << errorstring(e) << ")" << endl;
6845     }
6846     else
6847     {
6848         cout << "Account cancelled successfully." << endl;
6849     }
6850 }
6851 
validatepassword_result(error e)6852 void DemoApp::validatepassword_result(error e)
6853 {
6854     if (e)
6855     {
6856         cout << "Wrong password (" << errorstring(e) << ")" << endl;
6857         setprompt(LOGINPASSWORD);
6858     }
6859     else
6860     {
6861         if (recoverycode.size())
6862         {
6863             cout << "Password is correct, cancelling account..." << endl;
6864 
6865             client->confirmcancellink(recoverycode.c_str());
6866             recoverycode.clear();
6867         }
6868         else if (changecode.size())
6869         {
6870             cout << "Password is correct, changing email..." << endl;
6871 
6872             client->confirmemaillink(changecode.c_str(), changeemail.c_str(), pwkey);
6873             changecode.clear();
6874             changeemail.clear();
6875         }
6876     }
6877 }
6878 
getemaillink_result(error e)6879 void DemoApp::getemaillink_result(error e)
6880 {
6881     if (e)
6882     {
6883         cout << "Unable to send the link (" << errorstring(e) << ")" << endl;
6884     }
6885     else
6886     {
6887         cout << "Please check your e-mail and enter the command \"email\" followed by the link." << endl;
6888     }
6889 }
6890 
confirmemaillink_result(error e)6891 void DemoApp::confirmemaillink_result(error e)
6892 {
6893     if (e)
6894     {
6895         cout << "Unable to change the email address (" << errorstring(e) << ")" << endl;
6896     }
6897     else
6898     {
6899         cout << "Email address changed successfully to " << changeemail << "." << endl;
6900     }
6901 }
6902 
ephemeral_result(handle uh,const byte * pw)6903 void DemoApp::ephemeral_result(handle uh, const byte* pw)
6904 {
6905     cout << "Ephemeral session established, session ID: ";
6906     cout << Base64Str<MegaClient::USERHANDLE>(uh) << "#";
6907     cout << Base64Str<SymmCipher::KEYLENGTH>(pw) << endl;
6908 
6909     client->fetchnodes();
6910 }
6911 
cancelsignup_result(error)6912 void DemoApp::cancelsignup_result(error)
6913 {
6914     cout << "Singup link canceled. Start again!" << endl;
6915     signupcode.clear();
6916     signupemail.clear();
6917     signupname.clear();
6918 }
6919 
whyamiblocked_result(int code)6920 void DemoApp::whyamiblocked_result(int code)
6921 {
6922     if (code < 0)
6923     {
6924         error e = (error) code;
6925         cout << "Why am I blocked failed: " << errorstring(e) << endl;
6926     }
6927     else if (code == 0)
6928     {
6929         cout << "You're not blocked" << endl;
6930     }
6931     else    // code > 0
6932     {
6933         string reason = "Your account was terminated due to breach of Mega's Terms of Service, such as abuse of rights of others; sharing and/or importing illegal data; or system abuse.";
6934 
6935         if (code == 100)    // deprecated
6936         {
6937             reason = "You have been suspended due to excess data usage.";
6938         }
6939         else if (code == 200)
6940         {
6941             reason = "Your account has been suspended due to multiple breaches of Mega's Terms of Service. Please check your email inbox.";
6942         }
6943         else if (code == 300)
6944         {
6945             reason = "Your account has been suspended due to copyright violations. Please check your email inbox.";
6946         }
6947         else if (code == 400)
6948         {
6949             reason = "Your account has been disabled by your administrator. You may contact your business account administrator for further details.";
6950         }
6951         else if (code == 401)
6952         {
6953             reason = "Your account has been removed by your administrator. You may contact your business account administrator for further details.";
6954         }
6955         else if (code == 500)
6956         {
6957             reason = "Your account has been blocked pending verification via SMS.";
6958         }
6959         else if (code == 700)
6960         {
6961             reason = "Your account has been temporarily suspended for your safety. Please verify your email and follow its steps to unlock your account.";
6962         }
6963         //else if (code == ACCOUNT_BLOCKED_DEFAULT) --> default reason
6964 
6965         cout << "Reason: " << reason << endl;
6966 
6967         if (code != 500 && code != 700)
6968         {
6969             cout << "Logging out..." << endl;
6970             client->locallogout(true);
6971         }
6972     }
6973 }
6974 
6975 // password change result
changepw_result(error e)6976 void DemoApp::changepw_result(error e)
6977 {
6978     if (e)
6979     {
6980         cout << "Password update failed: " << errorstring(e) << endl;
6981     }
6982     else
6983     {
6984         cout << "Password updated." << endl;
6985     }
6986 }
6987 
6988 // node export failed
exportnode_result(error e)6989 void DemoApp::exportnode_result(error e)
6990 {
6991     if (e)
6992     {
6993         cout << "Export failed: " << errorstring(e) << endl;
6994     }
6995 
6996     del = ets = 0;
6997     hlink = UNDEF;
6998 }
6999 
exportnode_result(handle h,handle ph)7000 void DemoApp::exportnode_result(handle h, handle ph)
7001 {
7002     Node* n;
7003 
7004     if ((n = client->nodebyhandle(h)))
7005     {
7006         string path;
7007         nodepath(h, &path);
7008         cout << "Exported " << path << ": ";
7009 
7010         if (n->type != FILENODE && !n->sharekey)
7011         {
7012             cout << "No key available for exported folder" << endl;
7013 
7014             del = ets = 0;
7015             hlink = UNDEF;
7016             return;
7017         }
7018 
7019         if (n->type == FILENODE)
7020         {
7021             cout << MegaClient::getPublicLink(client->mNewLinkFormat, n->type, ph, Base64Str<FILENODEKEYLENGTH>((const byte*)n->nodekey().data())) << endl;
7022         }
7023         else
7024         {
7025             cout << MegaClient::getPublicLink(client->mNewLinkFormat, n->type, ph, Base64Str<FOLDERNODEKEYLENGTH>(n->sharekey->key)) << endl;
7026         }
7027     }
7028     else
7029     {
7030         cout << "Exported node no longer available" << endl;
7031     }
7032 
7033     del = ets = 0;
7034     hlink = UNDEF;
7035 }
7036 
7037 // the requested link could not be opened
openfilelink_result(const Error & e)7038 void DemoApp::openfilelink_result(const Error& e)
7039 {
7040     if (e)
7041     {
7042         if (pdf_to_import) // import welcome pdf has failed
7043         {
7044             cout << "Failed to import Welcome PDF file" << endl;
7045         }
7046         else
7047         {
7048             if (e == API_ETOOMANY && e.hasExtraInfo())
7049             {
7050                 cout << "Failed to open link: " << getExtraInfoErrorString(e) << endl;
7051             }
7052             else
7053             {
7054                 cout << "Failed to open link: " << errorstring(e) << endl;
7055             }
7056 
7057         }
7058     }
7059     pdf_to_import = false;
7060 }
7061 
7062 // the requested link was opened successfully - import to cwd
openfilelink_result(handle ph,const byte * key,m_off_t size,string * a,string *,int)7063 void DemoApp::openfilelink_result(handle ph, const byte* key, m_off_t size,
7064                                   string* a, string* /*fa*/, int)
7065 {
7066     Node* n;
7067 
7068     if (!key)
7069     {
7070         cout << "File is valid, but no key was provided." << endl;
7071         pdf_to_import = false;
7072         return;
7073     }
7074 
7075     // check if the file is decryptable
7076     string attrstring;
7077 
7078     attrstring.resize(a->length()*4/3+4);
7079     attrstring.resize(Base64::btoa((const byte *)a->data(), int(a->length()), (char *)attrstring.data()));
7080 
7081     SymmCipher nodeKey;
7082     nodeKey.setkey(key, FILENODE);
7083 
7084     byte *buf = Node::decryptattr(&nodeKey,attrstring.c_str(), attrstring.size());
7085     if (!buf)
7086     {
7087         cout << "The file won't be imported, the provided key is invalid." << endl;
7088         pdf_to_import = false;
7089     }
7090     else if (client->loggedin() != NOTLOGGEDIN)
7091     {
7092         if (pdf_to_import)
7093         {
7094             n = client->nodebyhandle(client->rootnodes[0]);
7095         }
7096         else
7097         {
7098             n = client->nodebyhandle(cwd);
7099         }
7100 
7101         if (!n)
7102         {
7103             cout << "Target folder not found." << endl;
7104             pdf_to_import = false;
7105             delete [] buf;
7106             return;
7107         }
7108 
7109         AttrMap attrs;
7110         JSON json;
7111         nameid name;
7112         string* t;
7113         json.begin((char*)buf + 5);
7114         NewNode* newnode = new NewNode[1];
7115 
7116         // set up new node as folder node
7117         newnode->source = NEW_PUBLIC;
7118         newnode->type = FILENODE;
7119         newnode->nodehandle = ph;
7120         newnode->parenthandle = UNDEF;
7121         newnode->nodekey.assign((char*)key, FILENODEKEYLENGTH);
7122         newnode->attrstring.reset(new string(*a));
7123 
7124         while ((name = json.getnameid()) != EOO && json.storeobject((t = &attrs.map[name])))
7125         {
7126             JSON::unescape(t);
7127 
7128             if (name == 'n')
7129             {
7130                 client->fsaccess->normalize(t);
7131             }
7132         }
7133 
7134         attr_map::iterator it = attrs.map.find('n');
7135         if (it != attrs.map.end())
7136         {
7137             Node *ovn = client->childnodebyname(n, it->second.c_str(), true);
7138             if (ovn)
7139             {
7140                 attr_map::iterator it2 = attrs.map.find('c');
7141                 if (it2 != attrs.map.end())
7142                 {
7143                     FileFingerprint ffp;
7144                     if (ffp.unserializefingerprint(&it2->second))
7145                     {
7146                         ffp.size = size;
7147                         if (ffp.isvalid && ovn->isvalid && ffp == *(FileFingerprint*)ovn)
7148                         {
7149                             cout << "Success. (identical node skipped)" << endl;
7150                             pdf_to_import = false;
7151                             delete [] buf;
7152                             return;
7153                         }
7154                     }
7155                 }
7156 
7157                 newnode->ovhandle = !client->versions_disabled ? ovn->nodehandle : UNDEF;
7158             }
7159         }
7160 
7161         client->putnodes(n->nodehandle, newnode, 1);
7162     }
7163     else
7164     {
7165         cout << "Need to be logged in to import file links." << endl;
7166         pdf_to_import = false;
7167     }
7168 
7169     delete [] buf;
7170 }
7171 
folderlinkinfo_result(error e,handle owner,handle,string * attr,string * k,m_off_t currentSize,uint32_t numFiles,uint32_t numFolders,m_off_t versionsSize,uint32_t numVersions)7172 void DemoApp::folderlinkinfo_result(error e, handle owner, handle /*ph*/, string *attr, string* k, m_off_t currentSize, uint32_t numFiles, uint32_t numFolders, m_off_t versionsSize, uint32_t numVersions)
7173 {
7174     if (e != API_OK)
7175     {
7176         cout << "Retrieval of public folder link information failed: " << e << endl;
7177         return;
7178     }
7179 
7180     handle ph;
7181     byte folderkey[FOLDERNODEKEYLENGTH];
7182     #ifndef NDEBUG
7183     error eaux =
7184     #endif
7185     client->parsepubliclink(publiclink.c_str(), ph, folderkey, true);
7186     assert(eaux == API_OK);
7187 
7188     // Decrypt nodekey with the key of the folder link
7189     SymmCipher cipher;
7190     cipher.setkey(folderkey);
7191     const char *nodekeystr = k->data() + 9;    // skip the userhandle(8) and the `:`
7192     byte nodekey[FOLDERNODEKEYLENGTH];
7193     if (client->decryptkey(nodekeystr, nodekey, sizeof(nodekey), &cipher, 0, UNDEF))
7194     {
7195         // Decrypt node attributes with the nodekey
7196         cipher.setkey(nodekey);
7197         byte* buf = Node::decryptattr(&cipher, attr->c_str(), attr->size());
7198         if (buf)
7199         {
7200             AttrMap attrs;
7201             string fileName;
7202             string fingerprint;
7203             FileFingerprint ffp;
7204             m_time_t mtime = 0;
7205             Node::parseattr(buf, attrs, currentSize, mtime, fileName, fingerprint, ffp);
7206 
7207             // Normalize node name to UTF-8 string
7208             attr_map::iterator it = attrs.map.find('n');
7209             if (it != attrs.map.end() && !it->second.empty())
7210             {
7211                 client->fsaccess->normalize(&(it->second));
7212                 fileName = it->second.c_str();
7213             }
7214 
7215             std::string ownerStr, ownerBin((const char *)&owner, sizeof(owner));
7216             Base64::btoa(ownerBin, ownerStr);
7217 
7218             cout << "Folder link information:" << publiclink << endl;
7219             cout << "\tFolder name: " << fileName << endl;
7220             cout << "\tOwner: " << ownerStr << endl;
7221             cout << "\tNum files: " << numFiles << endl;
7222             cout << "\tNum folders: " << numFolders - 1 << endl;
7223             cout << "\tNum versions: " << numVersions << endl;
7224 
7225             delete [] buf;
7226         }
7227         else
7228         {
7229             cout << "folderlink: error decrypting node attributes with decrypted nodekey" << endl;
7230         }
7231     }
7232     else
7233     {
7234         cout << "folderlink: error decrypting nodekey with folder link key";
7235     }
7236 
7237     publiclink.clear();
7238 }
7239 
checkfile_result(handle,const Error & e)7240 void DemoApp::checkfile_result(handle /*h*/, const Error& e)
7241 {
7242     if (e == API_ETOOMANY && e.hasExtraInfo())
7243     {
7244          cout << "Link check failed: " << getExtraInfoErrorString(e) << endl;
7245     }
7246     else
7247     {
7248         cout << "Link check failed: " << errorstring(e) << endl;
7249     }
7250 }
7251 
checkfile_result(handle h,error e,byte * filekey,m_off_t size,m_time_t,m_time_t tm,string * filename,string * fingerprint,string * fileattrstring)7252 void DemoApp::checkfile_result(handle h, error e, byte* filekey, m_off_t size, m_time_t /*ts*/, m_time_t tm, string* filename,
7253                                string* fingerprint, string* fileattrstring)
7254 {
7255     cout << "Name: " << *filename << ", size: " << size;
7256 
7257     if (fingerprint->size())
7258     {
7259         cout << ", fingerprint available";
7260     }
7261 
7262     if (fileattrstring->size())
7263     {
7264         cout << ", has attributes";
7265     }
7266 
7267     cout << endl;
7268 
7269     if (e)
7270     {
7271         cout << "Not available: " << errorstring(e) << endl;
7272     }
7273     else
7274     {
7275         cout << "Initiating download..." << endl;
7276 
7277         DBTableTransactionCommitter committer(client->tctable);
7278         AppFileGet* f = new AppFileGet(NULL, h, filekey, size, tm, filename, fingerprint);
7279         f->appxfer_it = appxferq[GET].insert(appxferq[GET].end(), f);
7280         client->startxfer(GET, f, committer);
7281     }
7282 }
7283 
pread_data(byte * data,m_off_t len,m_off_t pos,m_off_t,m_off_t,void *)7284 bool DemoApp::pread_data(byte* data, m_off_t len, m_off_t pos, m_off_t, m_off_t, void* /*appdata*/)
7285 {
7286     if (pread_file)
7287     {
7288         pread_file->write((const char*)data, (size_t)len);
7289         cout << "Received " << len << " partial read byte(s) at position " << pos << endl;
7290         if (pread_file_end == pos + len)
7291         {
7292             delete pread_file;
7293             pread_file = NULL;
7294             cout << "Completed pread" << endl;
7295         }
7296     }
7297     else
7298     {
7299         cout << "Received " << len << " partial read byte(s) at position " << pos << ": ";
7300         fwrite(data, 1, size_t(len), stdout);
7301         cout << endl;
7302     }
7303     return true;
7304 }
7305 
pread_failure(const Error & e,int retry,void *,dstime)7306 dstime DemoApp::pread_failure(const Error &e, int retry, void* /*appdata*/, dstime)
7307 {
7308     if (retry < 5 && !(e == API_ETOOMANY && e.hasExtraInfo()))
7309     {
7310         cout << "Retrying read (" << errorstring(e) << ", attempt #" << retry << ")" << endl;
7311         return (dstime)(retry*10);
7312     }
7313     else
7314     {
7315         cout << "Too many failures (" << errorstring(e) << "), giving up" << endl;
7316         if (pread_file)
7317         {
7318             delete pread_file;
7319             pread_file = NULL;
7320         }
7321         return ~(dstime)0;
7322     }
7323 }
7324 
7325 // reload needed
reload(const char * reason)7326 void DemoApp::reload(const char* reason)
7327 {
7328     cout << "Reload suggested (" << reason << ") - use 'reload' to trigger" << endl;
7329 }
7330 
7331 // reload initiated
clearing()7332 void DemoApp::clearing()
7333 {
7334     LOG_debug << "Clearing all nodes/users...";
7335 }
7336 
7337 // nodes have been modified
7338 // (nodes with their removed flag set will be deleted immediately after returning from this call,
7339 // at which point their pointers will become invalid at that point.)
nodes_updated(Node ** n,int count)7340 void DemoApp::nodes_updated(Node** n, int count)
7341 {
7342     int c[2][6] = { { 0 } };
7343 
7344     if (n)
7345     {
7346         while (count--)
7347         {
7348             if ((*n)->type < 6)
7349             {
7350                 c[!(*n)->changed.removed][(*n)->type]++;
7351                 n++;
7352             }
7353         }
7354     }
7355     else
7356     {
7357         for (node_map::iterator it = client->nodes.begin(); it != client->nodes.end(); it++)
7358         {
7359             if (it->second->type < 6)
7360             {
7361                 c[1][it->second->type]++;
7362             }
7363         }
7364     }
7365 
7366     nodestats(c[1], "added or updated");
7367     nodestats(c[0], "removed");
7368 
7369     if (ISUNDEF(cwd))
7370     {
7371         cwd = client->rootnodes[0];
7372     }
7373 }
7374 
7375 // nodes now (almost) current, i.e. no server-client notifications pending
nodes_current()7376 void DemoApp::nodes_current()
7377 {
7378     LOG_debug << "Nodes current.";
7379 }
7380 
account_updated()7381 void DemoApp::account_updated()
7382 {
7383     if (client->loggedin() == EPHEMERALACCOUNT)
7384     {
7385         LOG_debug << "Account has been confirmed by another client. Proceed to login with credentials.";
7386     }
7387     else
7388     {
7389         LOG_debug << "Account has been upgraded/downgraded.";
7390     }
7391 }
7392 
notify_confirmation(const char * email)7393 void DemoApp::notify_confirmation(const char *email)
7394 {
7395     if (client->loggedin() == EPHEMERALACCOUNT)
7396     {
7397         LOG_debug << "Account has been confirmed with email " << email << ". Proceed to login with credentials.";
7398     }
7399 }
7400 
enumeratequotaitems_result(unsigned,handle,unsigned,int,int,unsigned,unsigned,unsigned,const char *,const char *,const char *,const char *)7401 void DemoApp::enumeratequotaitems_result(unsigned, handle, unsigned, int, int, unsigned, unsigned, unsigned, const char*, const char*, const char*, const char*)
7402 {
7403     // FIXME: implement
7404 }
7405 
enumeratequotaitems_result(error)7406 void DemoApp::enumeratequotaitems_result(error)
7407 {
7408     // FIXME: implement
7409 }
7410 
additem_result(error)7411 void DemoApp::additem_result(error)
7412 {
7413     // FIXME: implement
7414 }
7415 
checkout_result(const char *,error)7416 void DemoApp::checkout_result(const char*, error)
7417 {
7418     // FIXME: implement
7419 }
7420 
getmegaachievements_result(AchievementsDetails * details,error)7421 void DemoApp::getmegaachievements_result(AchievementsDetails *details, error /*e*/)
7422 {
7423     // FIXME: implement display of values
7424     delete details;
7425 }
7426 
getwelcomepdf_result(handle ph,string * k,error e)7427 void DemoApp::getwelcomepdf_result(handle ph, string *k, error e)
7428 {
7429     if (e)
7430     {
7431         cout << "Failed to get Welcome PDF. Error: " << e << endl;
7432         pdf_to_import = false;
7433     }
7434     else
7435     {
7436         cout << "Importing Welcome PDF file. Public handle: " << LOG_NODEHANDLE(ph) << endl;
7437         client->reqs.add(new CommandGetPH(client, ph, (const byte *)k->data(), 1));
7438     }
7439 }
7440 
7441 #ifdef ENABLE_CHAT
richlinkrequest_result(string * json,error e)7442 void DemoApp::richlinkrequest_result(string *json, error e)
7443 {
7444     if (!e)
7445     {
7446         cout << "Result:" << endl << *json << endl;
7447     }
7448     else
7449     {
7450         cout << "Failed to request rich link. Error: " << e << endl;
7451 
7452     }
7453 }
7454 #endif
7455 
contactlinkcreate_result(error e,handle h)7456 void DemoApp::contactlinkcreate_result(error e, handle h)
7457 {
7458     if (e)
7459     {
7460         cout << "Failed to create contact link. Error: " << e << endl;
7461     }
7462     else
7463     {
7464         cout << "Contact link created successfully: " << LOG_NODEHANDLE(h) << endl;
7465     }
7466 }
7467 
contactlinkquery_result(error e,handle h,string * email,string * fn,string * ln,string *)7468 void DemoApp::contactlinkquery_result(error e, handle h, string *email, string *fn, string *ln, string* /*avatar*/)
7469 {
7470     if (e)
7471     {
7472         cout << "Failed to get contact link details. Error: " << e << endl;
7473     }
7474     else
7475     {
7476         cout << "Contact link created successfully: " << endl;
7477         cout << "\tUserhandle: " << LOG_HANDLE(h) << endl;
7478         cout << "\tEmail: " << *email << endl;
7479         cout << "\tFirstname: " << Base64::atob(*fn) << endl;
7480         cout << "\tLastname: " << Base64::atob(*ln) << endl;
7481     }
7482 }
7483 
contactlinkdelete_result(error e)7484 void DemoApp::contactlinkdelete_result(error e)
7485 {
7486     if (e)
7487     {
7488         cout << "Failed to delete contact link. Error: " << e << endl;
7489     }
7490     else
7491     {
7492         cout << "Contact link deleted successfully." << endl;
7493     }
7494 }
7495 
7496 // display account details/history
account_details(AccountDetails * ad,bool storage,bool transfer,bool pro,bool purchases,bool transactions,bool sessions)7497 void DemoApp::account_details(AccountDetails* ad, bool storage, bool transfer, bool pro, bool purchases,
7498                               bool transactions, bool sessions)
7499 {
7500     char timebuf[32], timebuf2[32];
7501 
7502     if (storage)
7503     {
7504         cout << "\tAvailable storage: " << ad->storage_max << " byte(s)  used:  " << ad->storage_used << " available: " << (ad->storage_max - ad->storage_used) << endl;
7505 
7506         for (unsigned i = 0; i < sizeof rootnodenames/sizeof *rootnodenames; i++)
7507         {
7508             NodeStorage* ns = &ad->storage[client->rootnodes[i]];
7509 
7510             cout << "\t\tIn " << rootnodenames[i] << ": " << ns->bytes << " byte(s) in " << ns->files << " file(s) and " << ns->folders << " folder(s)" << endl;
7511             cout << "\t\tUsed storage by versions: " << ns->version_bytes << " byte(s) in " << ns->version_files << " file(s)" << endl;
7512         }
7513     }
7514 
7515     if (transfer)
7516     {
7517         if (ad->transfer_max)
7518         {
7519             long long transferFreeUsed = 0;
7520             for (unsigned i = 0; i < ad->transfer_hist.size(); i++)
7521             {
7522                 transferFreeUsed += ad->transfer_hist[i];
7523             }
7524 
7525             cout << "\tTransfer in progress: " << ad->transfer_own_reserved << "/" << ad->transfer_srv_reserved << endl;
7526             cout << "\tTransfer completed: " << ad->transfer_own_used << "/" << ad->transfer_srv_used << "/" << transferFreeUsed << " of "
7527                  << ad->transfer_max << " ("
7528                  << (100 * (ad->transfer_own_used + ad->transfer_srv_used + transferFreeUsed) / ad->transfer_max) << "%)" << endl;
7529             cout << "\tServing bandwidth ratio: " << ad->srv_ratio << "%" << endl;
7530         }
7531 
7532         if (ad->transfer_hist_starttime)
7533         {
7534             m_time_t t = m_time() - ad->transfer_hist_starttime;
7535 
7536             cout << "\tTransfer history:\n";
7537 
7538             for (unsigned i = 0; i < ad->transfer_hist.size(); i++)
7539             {
7540                 cout << "\t\t" << t;
7541                 t -= ad->transfer_hist_interval;
7542                 if (t < 0)
7543                 {
7544                     cout << " second(s) ago until now: ";
7545                 }
7546                 else
7547                 {
7548                     cout << "-" << t << " second(s) ago: ";
7549                 }
7550                 cout << ad->transfer_hist[i] << " byte(s)" << endl;
7551             }
7552         }
7553     }
7554 
7555     if (pro)
7556     {
7557         cout << "\tPro level: " << ad->pro_level << endl;
7558         cout << "\tSubscription type: " << ad->subscription_type << endl;
7559         cout << "\tAccount balance:" << endl;
7560 
7561         for (vector<AccountBalance>::iterator it = ad->balances.begin(); it != ad->balances.end(); it++)
7562         {
7563             printf("\tBalance: %.3s %.02f\n", it->currency, it->amount);
7564         }
7565     }
7566 
7567     if (purchases)
7568     {
7569         cout << "Purchase history:" << endl;
7570 
7571         for (vector<AccountPurchase>::iterator it = ad->purchases.begin(); it != ad->purchases.end(); it++)
7572         {
7573             time_t ts = it->timestamp;
7574             strftime(timebuf, sizeof timebuf, "%c", localtime(&ts));
7575             printf("\tID: %.11s Time: %s Amount: %.3s %.02f Payment method: %d\n", it->handle, timebuf, it->currency,
7576                    it->amount, it->method);
7577         }
7578     }
7579 
7580     if (transactions)
7581     {
7582         cout << "Transaction history:" << endl;
7583 
7584         for (vector<AccountTransaction>::iterator it = ad->transactions.begin(); it != ad->transactions.end(); it++)
7585         {
7586             time_t ts = it->timestamp;
7587             strftime(timebuf, sizeof timebuf, "%c", localtime(&ts));
7588             printf("\tID: %.11s Time: %s Delta: %.3s %.02f\n", it->handle, timebuf, it->currency, it->delta);
7589         }
7590     }
7591 
7592     if (sessions)
7593     {
7594         cout << "Currently Active Sessions:" << endl;
7595         for (vector<AccountSession>::iterator it = ad->sessions.begin(); it != ad->sessions.end(); it++)
7596         {
7597             if (it->alive)
7598             {
7599                 time_t ts = it->timestamp;
7600                 strftime(timebuf, sizeof timebuf, "%c", localtime(&ts));
7601                 ts = it->mru;
7602                 strftime(timebuf2, sizeof timebuf, "%c", localtime(&ts));
7603 
7604                 Base64Str<MegaClient::SESSIONHANDLE> id(it->id);
7605 
7606                 if (it->current)
7607                 {
7608                     printf("\t* Current Session\n");
7609                 }
7610                 printf("\tSession ID: %s\n\tSession start: %s\n\tMost recent activity: %s\n\tIP: %s\n\tCountry: %.2s\n\tUser-Agent: %s\n\t-----\n",
7611                         id.chars, timebuf, timebuf2, it->ip.c_str(), it->country, it->useragent.c_str());
7612             }
7613         }
7614 
7615         if(client->debugstate())
7616         {
7617             cout << endl << "Full Session history:" << endl;
7618 
7619             for (vector<AccountSession>::iterator it = ad->sessions.begin(); it != ad->sessions.end(); it++)
7620             {
7621                 time_t ts = it->timestamp;
7622                 strftime(timebuf, sizeof timebuf, "%c", localtime(&ts));
7623                 ts = it->mru;
7624                 strftime(timebuf2, sizeof timebuf, "%c", localtime(&ts));
7625                 printf("\tSession start: %s\n\tMost recent activity: %s\n\tIP: %s\n\tCountry: %.2s\n\tUser-Agent: %s\n\t-----\n",
7626                         timebuf, timebuf2, it->ip.c_str(), it->country, it->useragent.c_str());
7627             }
7628         }
7629     }
7630 }
7631 
7632 // account details could not be retrieved
account_details(AccountDetails *,error e)7633 void DemoApp::account_details(AccountDetails* /*ad*/, error e)
7634 {
7635     if (e)
7636     {
7637         cout << "Account details retrieval failed (" << errorstring(e) << ")" << endl;
7638     }
7639 }
7640 
7641 // account details could not be retrieved
sessions_killed(handle sessionid,error e)7642 void DemoApp::sessions_killed(handle sessionid, error e)
7643 {
7644     if (e)
7645     {
7646         cout << "Session killing failed (" << errorstring(e) << ")" << endl;
7647         return;
7648     }
7649 
7650     if (sessionid == UNDEF)
7651     {
7652         cout << "All sessions except current have been killed" << endl;
7653     }
7654     else
7655     {
7656         Base64Str<MegaClient::SESSIONHANDLE> id(sessionid);
7657         cout << "Session with id " << id << " has been killed" << endl;
7658     }
7659 }
7660 
smsverificationsend_result(error e)7661 void DemoApp::smsverificationsend_result(error e)
7662 {
7663     if (e)
7664     {
7665         cout << "SMS send failed: " << e << endl;
7666     }
7667     else
7668     {
7669         cout << "SMS send succeeded" << endl;
7670     }
7671 }
7672 
smsverificationcheck_result(error e,string * phoneNumber)7673 void DemoApp::smsverificationcheck_result(error e, string *phoneNumber)
7674 {
7675     if (e)
7676     {
7677         cout << "SMS verification failed: " << e << endl;
7678     }
7679     else
7680     {
7681         cout << "SMS verification succeeded" << endl;
7682         if (phoneNumber)
7683         {
7684             cout << "Phone number: " << *phoneNumber << ")" << endl;
7685         }
7686     }
7687 }
7688 
7689 // user attribute update notification
userattr_update(User * u,int priv,const char * n)7690 void DemoApp::userattr_update(User* u, int priv, const char* n)
7691 {
7692     cout << "Notification: User " << u->email << " -" << (priv ? " private" : "") << " attribute "
7693           << n << " added or updated" << endl;
7694 }
7695 
resetSmsVerifiedPhoneNumber_result(error e)7696 void DemoApp::resetSmsVerifiedPhoneNumber_result(error e)
7697 {
7698     if (e)
7699     {
7700         cout << "Reset verified phone number failed: " << e << endl;
7701     }
7702     else
7703     {
7704         cout << "Reset verified phone number succeeded" << endl;
7705     }
7706 }
7707 
7708 #ifndef NO_READLINE
longestCommonPrefix(ac::CompletionState & acs)7709 char* longestCommonPrefix(ac::CompletionState& acs)
7710 {
7711     string s = acs.completions[0].s;
7712     for (size_t i = acs.completions.size(); i--; )
7713     {
7714         for (unsigned j = 0; j < s.size() && j < acs.completions[i].s.size(); ++j)
7715         {
7716             if (s[j] != acs.completions[i].s[j])
7717             {
7718                 s.erase(j, string::npos);
7719                 break;
7720             }
7721         }
7722     }
7723     return strdup(s.c_str());
7724 }
7725 
my_rl_completion(const char *,int,int end)7726 char** my_rl_completion(const char */*text*/, int /*start*/, int end)
7727 {
7728     rl_attempted_completion_over = 1;
7729 
7730     std::string line(rl_line_buffer, end);
7731     ac::CompletionState acs = ac::autoComplete(line, line.size(), autocompleteTemplate, true);
7732 
7733     if (acs.completions.empty())
7734     {
7735         return NULL;
7736     }
7737 
7738     if (acs.completions.size() == 1 && !acs.completions[0].couldExtend)
7739     {
7740         acs.completions[0].s += " ";
7741     }
7742 
7743     char** result = (char**)malloc((sizeof(char*)*(2+acs.completions.size())));
7744     for (size_t i = acs.completions.size(); i--; )
7745     {
7746         result[i+1] = strdup(acs.completions[i].s.c_str());
7747     }
7748     result[acs.completions.size()+1] = NULL;
7749     result[0] = longestCommonPrefix(acs);
7750     //for (int i = 0; i <= acs.completions.size(); ++i)
7751     //{
7752     //    cout << "i " << i << ": " << result[i] << endl;
7753     //}
7754     rl_completion_suppress_append = true;
7755     rl_basic_word_break_characters = " \r\n";
7756     rl_completer_word_break_characters = strdup(" \r\n");
7757     rl_completer_quote_characters = "";
7758     rl_special_prefixes = "";
7759     return result;
7760 }
7761 #endif
7762 
7763 // main loop
megacli()7764 void megacli()
7765 {
7766 #ifndef NO_READLINE
7767     char *saved_line = NULL;
7768     int saved_point = 0;
7769     rl_attempted_completion_function = my_rl_completion;
7770 
7771     rl_save_prompt();
7772 
7773 #elif defined(WIN32) && defined(NO_READLINE)
7774 
7775     static_cast<WinConsole*>(console)->setShellConsole(CP_UTF8, GetConsoleOutputCP());
7776 
7777     COORD fontSize;
7778     string fontname = static_cast<WinConsole*>(console)->getConsoleFont(fontSize);
7779     cout << "Using font '" << fontname << "', " << fontSize.X << "x" << fontSize.Y
7780          << ". <CHAR/hex> will be used for absent characters.  If seen, try the 'codepage' command or a different font." << endl;
7781 
7782 #else
7783     #error non-windows platforms must use the readline library
7784 #endif
7785 
7786     for (;;)
7787     {
7788         if (prompt == COMMAND)
7789         {
7790             // display put/get transfer speed in the prompt
7791             if (client->tslots.size() || responseprogress >= 0)
7792             {
7793                 unsigned xferrate[2] = { 0 };
7794                 Waiter::bumpds();
7795 
7796                 for (transferslot_list::iterator it = client->tslots.begin(); it != client->tslots.end(); it++)
7797                 {
7798                     if ((*it)->fa)
7799                     {
7800                         xferrate[(*it)->transfer->type]
7801                             += unsigned( (*it)->progressreported * 10 / (1024 * (Waiter::ds - (*it)->starttime + 1)) );
7802                     }
7803                 }
7804 
7805                 strcpy(dynamicprompt, "MEGA");
7806 
7807                 if (xferrate[GET] || xferrate[PUT] || responseprogress >= 0)
7808                 {
7809                     strcpy(dynamicprompt + 4, " (");
7810 
7811                     if (xferrate[GET])
7812                     {
7813                         sprintf(dynamicprompt + 6, "In: %u KB/s", xferrate[GET]);
7814 
7815                         if (xferrate[PUT])
7816                         {
7817                             strcat(dynamicprompt + 9, "/");
7818                         }
7819                     }
7820 
7821                     if (xferrate[PUT])
7822                     {
7823                         sprintf(strchr(dynamicprompt, 0), "Out: %u KB/s", xferrate[PUT]);
7824                     }
7825 
7826                     if (responseprogress >= 0)
7827                     {
7828                         sprintf(strchr(dynamicprompt, 0), "%d%%", responseprogress);
7829                     }
7830 
7831                     strcat(dynamicprompt + 6, ")");
7832                 }
7833 
7834                 strcat(dynamicprompt + 4, "> ");
7835             }
7836             else
7837             {
7838                 *dynamicprompt = 0;
7839             }
7840 
7841 #if defined(WIN32) && defined(NO_READLINE)
7842             static_cast<WinConsole*>(console)->updateInputPrompt(*dynamicprompt ? dynamicprompt : prompts[COMMAND]);
7843 #else
7844             rl_callback_handler_install(*dynamicprompt ? dynamicprompt : prompts[COMMAND], store_line);
7845 
7846             // display prompt
7847             if (saved_line)
7848             {
7849                 rl_replace_line(saved_line, 0);
7850                 free(saved_line);
7851             }
7852 
7853             rl_point = saved_point;
7854             rl_redisplay();
7855 #endif
7856         }
7857 
7858         // command editing loop - exits when a line is submitted or the engine requires the CPU
7859         for (;;)
7860         {
7861             int w = client->wait();
7862 
7863             if (w & Waiter::HAVESTDIN)
7864             {
7865 #if defined(WIN32) && defined(NO_READLINE)
7866                 line = static_cast<WinConsole*>(console)->checkForCompletedInputLine();
7867 #else
7868                 if (prompt == COMMAND)
7869                 {
7870                     rl_callback_read_char();
7871                 }
7872                 else
7873                 {
7874                     console->readpwchar(pw_buf, sizeof pw_buf, &pw_buf_pos, &line);
7875                 }
7876 #endif
7877             }
7878 
7879             if (w & Waiter::NEEDEXEC || line)
7880             {
7881                 break;
7882             }
7883         }
7884 
7885 #ifndef NO_READLINE
7886         // save line
7887         saved_point = rl_point;
7888         saved_line = rl_copy_text(0, rl_end);
7889 
7890         // remove prompt
7891         rl_save_prompt();
7892         rl_replace_line("", 0);
7893         rl_redisplay();
7894 #endif
7895 
7896         if (line)
7897         {
7898             // execute user command
7899             if (*line)
7900             {
7901                 process_line(line);
7902             }
7903             free(line);
7904             line = NULL;
7905 
7906             if (quit_flag)
7907             {
7908                 return;
7909             }
7910 
7911             if (!cerr)
7912             {
7913                 cerr.clear();
7914                 cerr << "Console error output failed, perhaps on a font related utf8 error or on NULL.  It is now reset." << endl;
7915             }
7916             if (!cout)
7917             {
7918                 cout.clear();
7919                 cerr << "Console output failed, perhaps on a font related utf8 error or on NULL.  It is now reset." << endl;
7920             }
7921         }
7922 
7923 
7924         auto puts = appxferq[PUT].size();
7925         auto gets = appxferq[GET].size();
7926 
7927         // pass the CPU to the engine (nonblocking)
7928         client->exec();
7929 
7930         if (puts && !appxferq[PUT].size())
7931         {
7932             cout << "Uploads complete" << endl;
7933         }
7934         if (gets && !appxferq[GET].size())
7935         {
7936             cout << "Downloads complete" << endl;
7937         }
7938 
7939 
7940         if (clientFolder)
7941         {
7942             clientFolder->exec();
7943         }
7944     }
7945 }
7946 
main()7947 int main()
7948 {
7949 #ifdef _WIN32
7950     SimpleLogger::setLogLevel(logMax);  // warning and stronger to console; info and weaker to VS output window
7951     SimpleLogger::setOutputClass(&gLogger);
7952 #else
7953     SimpleLogger::setOutputClass(&gLogger);
7954 #endif
7955 
7956     console = new CONSOLE_CLASS;
7957 
7958     // instantiate app components: the callback processor (DemoApp),
7959     // the HTTP I/O engine (WinHttpIO) and the MegaClient itself
7960     client = new MegaClient(new DemoApp,
7961 #ifdef WIN32
7962                             new CONSOLE_WAIT_CLASS(static_cast<CONSOLE_CLASS*>(console)),
7963 #else
7964                             new CONSOLE_WAIT_CLASS,
7965 #endif
7966                             new HTTPIO_CLASS, new FSACCESS_CLASS,
7967 #ifdef DBACCESS_CLASS
7968                             new DBACCESS_CLASS,
7969 #else
7970                             NULL,
7971 #endif
7972 #ifdef GFX_CLASS
7973                             new GFX_CLASS,
7974 #else
7975                             NULL,
7976 #endif
7977                             "Gk8DyQBS",
7978                             "megacli/" TOSTRING(MEGA_MAJOR_VERSION)
7979                             "." TOSTRING(MEGA_MINOR_VERSION)
7980                             "." TOSTRING(MEGA_MICRO_VERSION),
7981                             2);
7982 
7983     ac::ACN acs = autocompleteSyntax();
7984 #if defined(WIN32) && defined(NO_READLINE)
7985     static_cast<WinConsole*>(console)->setAutocompleteSyntax((acs));
7986 #endif
7987 
7988     clientFolder = NULL;    // additional for folder links
7989     megacli();
7990 }
7991 
7992 
login_result(error e)7993 void DemoAppFolder::login_result(error e)
7994 {
7995     if (e)
7996     {
7997         cout << "Failed to load the folder link: " << errorstring(e) << endl;
7998     }
7999     else
8000     {
8001         cout << "Folder link loaded, retrieving account..." << endl;
8002         clientFolder->fetchnodes();
8003     }
8004 }
8005 
fetchnodes_result(const Error & e)8006 void DemoAppFolder::fetchnodes_result(const Error& e)
8007 {
8008     if (e)
8009     {
8010         if (e == API_ENOENT && e.hasExtraInfo())
8011         {
8012             cout << "File/folder retrieval failed: " << getExtraInfoErrorString(e) << endl;
8013         }
8014         else
8015         {
8016             cout << "File/folder retrieval failed (" << errorstring(e) << ")" << endl;
8017         }
8018 
8019         pdf_to_import = false;
8020     }
8021     else
8022     {
8023         // check if we fetched a folder link and the key is invalid
8024         handle h = clientFolder->getrootpublicfolder();
8025         if (h != UNDEF)
8026         {
8027             Node *n = clientFolder->nodebyhandle(h);
8028             if (n && (n->attrs.map.find('n') == n->attrs.map.end()))
8029             {
8030                 cout << "File/folder retrieval succeed, but encryption key is wrong." << endl;
8031             }
8032         }
8033         else
8034         {
8035             cout << "Failed to load folder link" << endl;
8036 
8037             delete clientFolder;
8038             clientFolder = NULL;
8039         }
8040 
8041         if (pdf_to_import)
8042         {
8043             client->getwelcomepdf();
8044         }
8045     }
8046 }
8047 
nodes_updated(Node ** n,int count)8048 void DemoAppFolder::nodes_updated(Node **n, int count)
8049 {
8050     int c[2][6] = { { 0 } };
8051 
8052     if (n)
8053     {
8054         while (count--)
8055         {
8056             if ((*n)->type < 6)
8057             {
8058                 c[!(*n)->changed.removed][(*n)->type]++;
8059                 n++;
8060             }
8061         }
8062     }
8063     else
8064     {
8065         for (node_map::iterator it = clientFolder->nodes.begin(); it != clientFolder->nodes.end(); it++)
8066         {
8067             if (it->second->type < 6)
8068             {
8069                 c[1][it->second->type]++;
8070             }
8071         }
8072     }
8073 
8074     cout << "The folder link contains ";
8075     nodestats(c[1], "");
8076 }
8077 
exec_metamac(autocomplete::ACState & s)8078 void exec_metamac(autocomplete::ACState& s)
8079 {
8080     Node *node = nodebypath(s.words[2].s.c_str());
8081     if (!node || node->type != FILENODE)
8082     {
8083         cerr << s.words[2].s
8084              << (node ? ": No such file or directory"
8085                       : ": Not a file")
8086              << endl;
8087         return;
8088     }
8089 
8090     auto ifAccess = client->fsaccess->newfileaccess();
8091     {
8092         auto localPath = LocalPath::fromName(s.words[1].s, *client->fsaccess, client->fsaccess->getFilesystemType(LocalPath::fromPath(s.words[1].s, *client->fsaccess)));
8093         if (!ifAccess->fopen(localPath, 1, 0))
8094         {
8095             cerr << "Failed to open: " << s.words[1].s << endl;
8096             return;
8097         }
8098     }
8099 
8100     SymmCipher cipher;
8101     int64_t remoteIv;
8102     int64_t remoteMac;
8103 
8104     {
8105         std::string remoteKey = node->nodekey();
8106         const char *iva = &remoteKey[SymmCipher::KEYLENGTH];
8107 
8108         cipher.setkey((byte*)&remoteKey[0], node->type);
8109         remoteIv = MemAccess::get<int64_t>(iva);
8110         remoteMac = MemAccess::get<int64_t>(iva + sizeof(int64_t));
8111     }
8112 
8113     auto result = generateMetaMac(cipher, *ifAccess, remoteIv);
8114     if (!result.first)
8115     {
8116         cerr << "Failed to generate metamac for: "
8117              << s.words[1].s
8118              << endl;
8119     }
8120     else
8121     {
8122         const std::ios::fmtflags flags = cout.flags();
8123 
8124         cout << s.words[2].s
8125              << " (remote): "
8126              << std::hex
8127              << (uint64_t)remoteMac
8128              << "\n"
8129              << s.words[1].s
8130              << " (local): "
8131              << (uint64_t)result.second
8132              << endl;
8133 
8134         cout.flags(flags);
8135     }
8136 }
8137 
exec_resetverifiedphonenumber(autocomplete::ACState & s)8138 void exec_resetverifiedphonenumber(autocomplete::ACState& s)
8139 {
8140     client->resetSmsVerifiedPhoneNumber();
8141 }
8142