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