1 /**
2 * @file megaclient.cpp
3 * @brief Client access engine core logic
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 "mega/mediafileattribute.h"
24 #include <cctype>
25 #include <algorithm>
26 #include <future>
27
28 #undef min // avoid issues with std::min and std::max
29 #undef max
30
31 namespace mega {
32
33 // FIXME: generate cr element for file imports
34 // FIXME: support invite links (including responding to sharekey requests)
35 // FIXME: instead of copying nodes, move if the source is in the rubbish to reduce node creation load on the servers
36 // FIXME: prevent synced folder from being moved into another synced folder
37
38
39 bool MegaClient::disablepkp = false;
40
41 // root URL for API access
42 string MegaClient::APIURL = "https://g.api.mega.co.nz/";
43
44 // root URL for GeLB requests
45 string MegaClient::GELBURL = "https://gelb.karere.mega.nz/";
46
47 // root URL for chat stats
48 string MegaClient::CHATSTATSURL = "https://stats.karere.mega.nz";
49
50 // maximum number of concurrent transfers (uploads + downloads)
51 const unsigned MegaClient::MAXTOTALTRANSFERS = 48;
52
53 // maximum number of concurrent transfers (uploads or downloads)
54 const unsigned MegaClient::MAXTRANSFERS = 32;
55
56 // maximum number of queued putfa before halting the upload queue
57 const int MegaClient::MAXQUEUEDFA = 30;
58
59 // maximum number of concurrent putfa
60 const int MegaClient::MAXPUTFA = 10;
61
62 #ifdef ENABLE_SYNC
63 // //bin/SyncDebris/yyyy-mm-dd base folder name
64 const char* const MegaClient::SYNCDEBRISFOLDERNAME = "SyncDebris";
65 #endif
66
67 // exported link marker
68 const char* const MegaClient::EXPORTEDLINK = "EXP";
69
70 // public key to send payment details
71 const char MegaClient::PAYMENT_PUBKEY[] =
72 "CADB-9t4WSMCs6we8CNcAmq97_bP-eXa9pn7SwGPxXpTuScijDrLf_ooneCQnnRBDvE"
73 "MNqTK3ULj1Q3bt757SQKDZ0snjbwlU2_D-rkBBbjWCs-S61R0Vlg8AI5q6oizH0pjpD"
74 "eOhpsv2DUlvCa4Hjgy_bRpX8v9fJvbKI2bT3GXJWE7tu8nlKHgz8Q7NE3Ycj5XuUfCW"
75 "GgOvPGBC-8qPOyg98Vloy53vja2mBjw4ycodx-ZFCt8i8b9Z8KongRMROmvoB4jY8ge"
76 "ym1mA5iSSsMroGLypv9PueOTfZlG3UTpD83v6F3w8uGHY9phFZ-k2JbCd_-s-7gyfBE"
77 "TpPvuz-oZABEBAAE";
78
79 // default number of seconds to wait after a bandwidth overquota
80 dstime MegaClient::DEFAULT_BW_OVERQUOTA_BACKOFF_SECS = 3600;
81
82 // default number of seconds to wait after a bandwidth overquota
83 dstime MegaClient::USER_DATA_EXPIRATION_BACKOFF_SECS = 86400; // 1 day
84
85 // stats id
86 std::string MegaClient::statsid;
87
88 // decrypt key (symmetric or asymmetric), rewrite asymmetric to symmetric key
decryptkey(const char * sk,byte * tk,int tl,SymmCipher * sc,int type,handle node)89 bool MegaClient::decryptkey(const char* sk, byte* tk, int tl, SymmCipher* sc, int type, handle node)
90 {
91 int sl;
92 const char* ptr = sk;
93
94 // measure key length
95 while (*ptr && *ptr != '"' && *ptr != '/')
96 {
97 ptr++;
98 }
99
100 sl = int(ptr - sk);
101
102 if (sl > 4 * FILENODEKEYLENGTH / 3 + 1)
103 {
104 // RSA-encrypted key - decrypt and update on the server to save space & client CPU time
105 sl = sl / 4 * 3 + 3;
106
107 if (sl > 4096)
108 {
109 return false;
110 }
111
112 byte* buf = new byte[sl];
113
114 sl = Base64::atob(sk, buf, sl);
115
116 // decrypt and set session ID for subsequent API communication
117 if (!asymkey.decrypt(buf, sl, tk, tl))
118 {
119 delete[] buf;
120 LOG_warn << "Corrupt or invalid RSA node key";
121 return false;
122 }
123
124 delete[] buf;
125
126 if (!ISUNDEF(node))
127 {
128 if (type)
129 {
130 sharekeyrewrite.push_back(node);
131 }
132 else
133 {
134 nodekeyrewrite.push_back(node);
135 }
136 }
137 }
138 else
139 {
140 if (Base64::atob(sk, tk, tl) != tl)
141 {
142 LOG_warn << "Corrupt or invalid symmetric node key";
143 return false;
144 }
145
146 sc->ecb_decrypt(tk, tl);
147 }
148
149 return true;
150 }
151
152 // apply queued new shares
mergenewshares(bool notify)153 void MegaClient::mergenewshares(bool notify)
154 {
155 newshare_list::iterator it;
156
157 for (it = newshares.begin(); it != newshares.end(); )
158 {
159 NewShare* s = *it;
160
161 mergenewshare(s, notify);
162
163 delete s;
164 newshares.erase(it++);
165 }
166 }
167
mergenewshare(NewShare * s,bool notify)168 void MegaClient::mergenewshare(NewShare *s, bool notify)
169 {
170 bool skreceived = false;
171 Node* n;
172
173 if ((n = nodebyhandle(s->h)))
174 {
175 if (s->have_key && (!n->sharekey || memcmp(s->key, n->sharekey->key, SymmCipher::KEYLENGTH)))
176 {
177 // setting an outbound sharekey requires node authentication
178 // unless coming from a trusted source (the local cache)
179 bool auth = true;
180
181 if (s->outgoing > 0)
182 {
183 if (!checkaccess(n, OWNERPRELOGIN))
184 {
185 LOG_warn << "Attempt to create dislocated outbound share foiled";
186 auth = false;
187 }
188 else
189 {
190 byte buf[SymmCipher::KEYLENGTH];
191
192 handleauth(s->h, buf);
193
194 if (memcmp(buf, s->auth, sizeof buf))
195 {
196 LOG_warn << "Attempt to create forged outbound share foiled";
197 auth = false;
198 }
199 }
200 }
201
202 if (auth)
203 {
204 if (n->sharekey)
205 {
206 if (!fetchingnodes)
207 {
208 sendevent(99428,"Replacing share key", 0);
209 }
210 delete n->sharekey;
211 }
212 n->sharekey = new SymmCipher(s->key);
213 skreceived = true;
214 }
215 }
216
217 if (s->access == ACCESS_UNKNOWN && !s->have_key)
218 {
219 // share was deleted
220 if (s->outgoing)
221 {
222 bool found = false;
223 if (n->outshares)
224 {
225 // outgoing share to user u deleted
226 share_map::iterator shareit = n->outshares->find(s->peer);
227 if (shareit != n->outshares->end())
228 {
229 Share *delshare = shareit->second;
230 n->outshares->erase(shareit);
231 found = true;
232 if (notify)
233 {
234 n->changed.outshares = true;
235 notifynode(n);
236 }
237 delete delshare;
238 }
239
240 if (!n->outshares->size())
241 {
242 delete n->outshares;
243 n->outshares = NULL;
244 }
245 }
246 if (n->pendingshares && !found && s->pending)
247 {
248 // delete the pending share
249 share_map::iterator shareit = n->pendingshares->find(s->pending);
250 if (shareit != n->pendingshares->end())
251 {
252 Share *delshare = shareit->second;
253 n->pendingshares->erase(shareit);
254 found = true;
255 if (notify)
256 {
257 n->changed.pendingshares = true;
258 notifynode(n);
259 }
260 delete delshare;
261 }
262
263 if (!n->pendingshares->size())
264 {
265 delete n->pendingshares;
266 n->pendingshares = NULL;
267 }
268 }
269
270 // Erase sharekey if no outgoing shares (incl pending) exist
271 if (s->remove_key && !n->outshares && !n->pendingshares)
272 {
273 rewriteforeignkeys(n);
274
275 delete n->sharekey;
276 n->sharekey = NULL;
277 }
278 }
279 else
280 {
281 // incoming share deleted - remove tree
282 if (!n->parent)
283 {
284 TreeProcDel td;
285 proctree(n, &td, true);
286 }
287 else
288 {
289 if (n->inshare)
290 {
291 n->inshare->user->sharing.erase(n->nodehandle);
292 notifyuser(n->inshare->user);
293 n->inshare = NULL;
294 }
295 }
296 }
297 }
298 else
299 {
300 if (s->outgoing)
301 {
302 if ((!s->upgrade_pending_to_full && (!ISUNDEF(s->peer) || !ISUNDEF(s->pending)))
303 || (s->upgrade_pending_to_full && !ISUNDEF(s->peer) && !ISUNDEF(s->pending)))
304 {
305 // perform mandatory verification of outgoing shares:
306 // only on own nodes and signed unless read from cache
307 if (checkaccess(n, OWNERPRELOGIN))
308 {
309 Share** sharep;
310 if (!ISUNDEF(s->pending))
311 {
312 // Pending share
313 if (!n->pendingshares)
314 {
315 n->pendingshares = new share_map();
316 }
317
318 if (s->upgrade_pending_to_full)
319 {
320 share_map::iterator shareit = n->pendingshares->find(s->pending);
321 if (shareit != n->pendingshares->end())
322 {
323 // This is currently a pending share that needs to be upgraded to a full share
324 // erase from pending shares & delete the pending share list if needed
325 Share *delshare = shareit->second;
326 n->pendingshares->erase(shareit);
327 if (notify)
328 {
329 n->changed.pendingshares = true;
330 notifynode(n);
331 }
332 delete delshare;
333 }
334
335 if (!n->pendingshares->size())
336 {
337 delete n->pendingshares;
338 n->pendingshares = NULL;
339 }
340
341 // clear this so we can fall through to below and have it re-create the share in
342 // the outshares list
343 s->pending = UNDEF;
344
345 // create the outshares list if needed
346 if (!n->outshares)
347 {
348 n->outshares = new share_map();
349 }
350
351 sharep = &((*n->outshares)[s->peer]);
352 }
353 else
354 {
355 sharep = &((*n->pendingshares)[s->pending]);
356 }
357 }
358 else
359 {
360 // Normal outshare
361 if (!n->outshares)
362 {
363 n->outshares = new share_map();
364 }
365
366 sharep = &((*n->outshares)[s->peer]);
367 }
368
369 // modification of existing share or new share
370 if (*sharep)
371 {
372 (*sharep)->update(s->access, s->ts, findpcr(s->pending));
373 }
374 else
375 {
376 *sharep = new Share(ISUNDEF(s->peer) ? NULL : finduser(s->peer, 1), s->access, s->ts, findpcr(s->pending));
377 }
378
379 if (notify)
380 {
381 if (!ISUNDEF(s->pending))
382 {
383 n->changed.pendingshares = true;
384 }
385 else
386 {
387 n->changed.outshares = true;
388 }
389 notifynode(n);
390 }
391 }
392 }
393 else
394 {
395 LOG_debug << "Merging share without peer information.";
396 // Outgoing shares received during fetchnodes are merged in two steps:
397 // 1. From readok(), a NewShare is created with the 'sharekey'
398 // 2. From readoutshares(), a NewShare is created with the 'peer' information
399 }
400 }
401 else
402 {
403 if (!ISUNDEF(s->peer))
404 {
405 if (s->peer)
406 {
407 if (!checkaccess(n, OWNERPRELOGIN))
408 {
409 // modification of existing share or new share
410 if (n->inshare)
411 {
412 n->inshare->update(s->access, s->ts);
413 }
414 else
415 {
416 n->inshare = new Share(finduser(s->peer, 1), s->access, s->ts, NULL);
417 n->inshare->user->sharing.insert(n->nodehandle);
418 mNodeCounters[n->nodehandle] = n->subnodeCounts();
419 }
420
421 if (notify)
422 {
423 n->changed.inshare = true;
424 notifynode(n);
425 }
426 }
427 else
428 {
429 LOG_warn << "Invalid inbound share location";
430 }
431 }
432 else
433 {
434 LOG_warn << "Invalid null peer on inbound share";
435 }
436 }
437 else
438 {
439 if (skreceived && notify)
440 {
441 TreeProcApplyKey td;
442 proctree(n, &td);
443 }
444 }
445 }
446 }
447 #ifdef ENABLE_SYNC
448 if (n->inshare && s->access != FULL)
449 {
450 // check if the low(ered) access level is affecting any syncs
451 // a) have we just cut off full access to a subtree of a sync?
452 do {
453 if (n->localnode && (n->localnode->sync->state == SYNC_ACTIVE || n->localnode->sync->state == SYNC_INITIALSCAN))
454 {
455 LOG_warn << "Existing inbound share sync or part thereof lost full access";
456 n->localnode->sync->errorcode = API_EACCESS;
457 n->localnode->sync->changestate(SYNC_FAILED);
458 }
459 } while ((n = n->parent));
460
461 // b) have we just lost full access to the subtree a sync is in?
462 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
463 {
464 if ((*it)->inshare && ((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN) && !checkaccess((*it)->localroot->node, FULL))
465 {
466 LOG_warn << "Existing inbound share sync lost full access";
467 (*it)->errorcode = API_EACCESS;
468 (*it)->changestate(SYNC_FAILED);
469 }
470 }
471
472 }
473 #endif
474 }
475 }
476
477 // configure for full account session access
setsid(const byte * newsid,unsigned len)478 void MegaClient::setsid(const byte* newsid, unsigned len)
479 {
480 auth = "&sid=";
481
482 size_t t = auth.size();
483 auth.resize(t + len * 4 / 3 + 4);
484 auth.resize(t + Base64::btoa(newsid, len, (char*)(auth.c_str() + t)));
485
486 sid.assign((const char*)newsid, len);
487 }
488
489 // configure for exported folder links access
setrootnode(handle h)490 void MegaClient::setrootnode(handle h)
491 {
492 char buf[12];
493
494 Base64::btoa((byte*)&h, NODEHANDLE, buf);
495
496 auth = "&n=";
497 auth.append(buf);
498 publichandle = h;
499
500 if (accountauth.size())
501 {
502 auth.append("&sid=");
503 auth.append(accountauth);
504 }
505 }
506
setlang(string * code)507 bool MegaClient::setlang(string *code)
508 {
509 if (code && code->size() == 2)
510 {
511 lang = "&lang=";
512 lang.append(*code);
513 return true;
514 }
515
516 lang.clear();
517 LOG_err << "Invalid language code: " << (code ? *code : "(null)");
518 return false;
519 }
520
getrootpublicfolder()521 handle MegaClient::getrootpublicfolder()
522 {
523 // if we logged into a folder...
524 if (auth.find("&n=") != auth.npos)
525 {
526 return rootnodes[0];
527 }
528 else
529 {
530 return UNDEF;
531 }
532 }
533
getpublicfolderhandle()534 handle MegaClient::getpublicfolderhandle()
535 {
536 return publichandle;
537 }
538
getrootnode(Node * node)539 Node *MegaClient::getrootnode(Node *node)
540 {
541 if (!node)
542 {
543 return NULL;
544 }
545
546 Node *n = node;
547 while (n->parent)
548 {
549 n = n->parent;
550 }
551 return n;
552 }
553
isPrivateNode(handle h)554 bool MegaClient::isPrivateNode(handle h)
555 {
556 Node *node = nodebyhandle(h);
557 if (!node)
558 {
559 return false;
560 }
561
562 handle rootnode = getrootnode(node)->nodehandle;
563 return (rootnode == rootnodes[0] || rootnode == rootnodes[1] || rootnode == rootnodes[2]);
564 }
565
isForeignNode(handle h)566 bool MegaClient::isForeignNode(handle h)
567 {
568 Node *node = nodebyhandle(h);
569 if (!node)
570 {
571 return false;
572 }
573
574 handle rootnode = getrootnode(node)->nodehandle;
575 return (rootnode != rootnodes[0] && rootnode != rootnodes[1] && rootnode != rootnodes[2]);
576 }
577
SCSN()578 SCSN::SCSN()
579 {
580 clear();
581 }
582
clear()583 void SCSN::clear()
584 {
585 memset(scsn, 0, sizeof(scsn));
586 stopsc = false;
587 }
588
589 // set server-client sequence number
setScsn(JSON * j)590 bool SCSN::setScsn(JSON* j)
591 {
592 handle t;
593
594 if (j->storebinary((byte*)&t, sizeof t) != sizeof t)
595 {
596 return false;
597 }
598
599 setScsn(t);
600
601 return true;
602 }
603
setScsn(handle h)604 void SCSN::setScsn(handle h)
605 {
606 Base64::btoa((byte*)&h, sizeof h, scsn);
607 }
608
stopScsn()609 void SCSN::stopScsn()
610 {
611 memset(scsn, 0, sizeof(scsn));
612 stopsc = true;
613 }
614
ready() const615 bool SCSN::ready() const
616 {
617 return !stopsc && *scsn;
618 }
619
stopped() const620 bool SCSN::stopped() const
621 {
622 return stopsc;
623 }
624
text() const625 const char* SCSN::text() const
626 {
627 assert(ready());
628 return scsn;
629 }
630
getHandle() const631 handle SCSN::getHandle() const
632 {
633 assert(ready());
634 handle t;
635 Base64::atob(scsn, (byte*)&t, sizeof t);
636
637 return t;
638 }
639
operator <<(std::ostream & os,const SCSN & scsn)640 std::ostream& operator<<(std::ostream &os, const SCSN &scsn)
641 {
642 os << scsn.text();
643 return os;
644 }
645
646
nextreqtag()647 int MegaClient::nextreqtag()
648 {
649 return ++reqtag;
650 }
651
hexval(char c)652 int MegaClient::hexval(char c)
653 {
654 return c > '9' ? c - 'a' + 10 : c - '0';
655 }
656
exportDatabase(string filename)657 void MegaClient::exportDatabase(string filename)
658 {
659 FILE *fp = NULL;
660 fp = fopen(filename.c_str(), "w");
661 if (!fp)
662 {
663 LOG_warn << "Cannot export DB to file \"" << filename << "\"";
664 return;
665 }
666
667 LOG_info << "Exporting database...";
668
669 sctable->rewind();
670
671 uint32_t id;
672 string data;
673
674 std::map<uint32_t, string> entries;
675 while (sctable->next(&id, &data, &key))
676 {
677 entries.insert(std::pair<uint32_t, string>(id,data));
678 }
679
680 for (map<uint32_t, string>::iterator it = entries.begin(); it != entries.end(); it++)
681 {
682 fprintf(fp, "%8." PRIu32 "\t%s\n", it->first, it->second.c_str());
683 }
684
685 fclose(fp);
686
687 LOG_info << "Database exported successfully to \"" << filename << "\"";
688 }
689
compareDatabases(string filename1,string filename2)690 bool MegaClient::compareDatabases(string filename1, string filename2)
691 {
692 LOG_info << "Comparing databases: \"" << filename1 << "\" and \"" << filename2 << "\"";
693 FILE *fp1 = fopen(filename1.data(), "r");
694 if (!fp1)
695 {
696 LOG_info << "Cannot open " << filename1;
697 return false;
698 }
699
700 FILE *fp2 = fopen(filename2.data(), "r");
701 if (!fp2)
702 {
703 fclose(fp1);
704
705 LOG_info << "Cannot open " << filename2;
706 return false;
707 }
708
709 const int N = 8192;
710 char buf1[N];
711 char buf2[N];
712
713 do
714 {
715 size_t r1 = fread(buf1, 1, N, fp1);
716 size_t r2 = fread(buf2, 1, N, fp2);
717
718 if (r1 != r2 || memcmp(buf1, buf2, r1))
719 {
720 fclose(fp1);
721 fclose(fp2);
722
723 LOG_info << "Databases are different";
724 return false;
725 }
726 }
727 while (!feof(fp1) || !feof(fp2));
728
729 fclose(fp1);
730 fclose(fp2);
731
732 LOG_info << "Databases are equal";
733 return true;
734 }
735
getrecoverylink(const char * email,bool hasMasterkey)736 void MegaClient::getrecoverylink(const char *email, bool hasMasterkey)
737 {
738 reqs.add(new CommandGetRecoveryLink(this, email,
739 hasMasterkey ? RECOVER_WITH_MASTERKEY : RECOVER_WITHOUT_MASTERKEY));
740 }
741
queryrecoverylink(const char * code)742 void MegaClient::queryrecoverylink(const char *code)
743 {
744 reqs.add(new CommandQueryRecoveryLink(this, code));
745 }
746
getprivatekey(const char * code)747 void MegaClient::getprivatekey(const char *code)
748 {
749 reqs.add(new CommandGetPrivateKey(this, code));
750 }
751
confirmrecoverylink(const char * code,const char * email,const char * password,const byte * masterkeyptr,int accountversion)752 void MegaClient::confirmrecoverylink(const char *code, const char *email, const char *password, const byte *masterkeyptr, int accountversion)
753 {
754 if (accountversion == 1)
755 {
756 byte pwkey[SymmCipher::KEYLENGTH];
757 pw_key(password, pwkey);
758 SymmCipher pwcipher(pwkey);
759
760 string emailstr = email;
761 uint64_t loginHash = stringhash64(&emailstr, &pwcipher);
762
763 if (masterkeyptr)
764 {
765 // encrypt provided masterkey using the new password
766 byte encryptedMasterKey[SymmCipher::KEYLENGTH];
767 memcpy(encryptedMasterKey, masterkeyptr, sizeof encryptedMasterKey);
768 pwcipher.ecb_encrypt(encryptedMasterKey);
769
770 reqs.add(new CommandConfirmRecoveryLink(this, code, (byte*)&loginHash, sizeof(loginHash), NULL, encryptedMasterKey, NULL));
771 }
772 else
773 {
774 // create a new masterkey
775 byte newmasterkey[SymmCipher::KEYLENGTH];
776 rng.genblock(newmasterkey, sizeof newmasterkey);
777
778 // generate a new session
779 byte initialSession[2 * SymmCipher::KEYLENGTH];
780 rng.genblock(initialSession, sizeof initialSession);
781 key.setkey(newmasterkey);
782 key.ecb_encrypt(initialSession, initialSession + SymmCipher::KEYLENGTH, SymmCipher::KEYLENGTH);
783
784 // and encrypt the master key to the new password
785 pwcipher.ecb_encrypt(newmasterkey);
786
787 reqs.add(new CommandConfirmRecoveryLink(this, code, (byte*)&loginHash, sizeof(loginHash), NULL, newmasterkey, initialSession));
788 }
789 }
790 else
791 {
792 byte clientkey[SymmCipher::KEYLENGTH];
793 rng.genblock(clientkey, sizeof(clientkey));
794
795 string salt;
796 HashSHA256 hasher;
797 string buffer = "mega.nz";
798 buffer.resize(200, 'P');
799 buffer.append((char *)clientkey, sizeof(clientkey));
800 hasher.add((const byte*)buffer.data(), unsigned(buffer.size()));
801 hasher.get(&salt);
802
803 byte derivedKey[2 * SymmCipher::KEYLENGTH];
804 CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA512> pbkdf2;
805 pbkdf2.DeriveKey(derivedKey, sizeof(derivedKey), 0, (byte *)password, strlen(password),
806 (const byte *)salt.data(), salt.size(), 100000);
807
808 string hashedauthkey;
809 byte *authkey = derivedKey + SymmCipher::KEYLENGTH;
810 hasher.add(authkey, SymmCipher::KEYLENGTH);
811 hasher.get(&hashedauthkey);
812 hashedauthkey.resize(SymmCipher::KEYLENGTH);
813
814 SymmCipher cipher;
815 cipher.setkey(derivedKey);
816
817 if (masterkeyptr)
818 {
819 // encrypt provided masterkey using the new password
820 byte encryptedMasterKey[SymmCipher::KEYLENGTH];
821 memcpy(encryptedMasterKey, masterkeyptr, sizeof encryptedMasterKey);
822 cipher.ecb_encrypt(encryptedMasterKey);
823 reqs.add(new CommandConfirmRecoveryLink(this, code, (byte*)hashedauthkey.data(), SymmCipher::KEYLENGTH, clientkey, encryptedMasterKey, NULL));
824 }
825 else
826 {
827 // create a new masterkey
828 byte newmasterkey[SymmCipher::KEYLENGTH];
829 rng.genblock(newmasterkey, sizeof newmasterkey);
830
831 // generate a new session
832 byte initialSession[2 * SymmCipher::KEYLENGTH];
833 rng.genblock(initialSession, sizeof initialSession);
834 key.setkey(newmasterkey);
835 key.ecb_encrypt(initialSession, initialSession + SymmCipher::KEYLENGTH, SymmCipher::KEYLENGTH);
836
837 // and encrypt the master key to the new password
838 cipher.ecb_encrypt(newmasterkey);
839 reqs.add(new CommandConfirmRecoveryLink(this, code, (byte*)hashedauthkey.data(), SymmCipher::KEYLENGTH, clientkey, newmasterkey, initialSession));
840 }
841 }
842 }
843
getcancellink(const char * email,const char * pin)844 void MegaClient::getcancellink(const char *email, const char *pin)
845 {
846 reqs.add(new CommandGetRecoveryLink(this, email, CANCEL_ACCOUNT, pin));
847 }
848
confirmcancellink(const char * code)849 void MegaClient::confirmcancellink(const char *code)
850 {
851 reqs.add(new CommandConfirmCancelLink(this, code));
852 }
853
getemaillink(const char * email,const char * pin)854 void MegaClient::getemaillink(const char *email, const char *pin)
855 {
856 reqs.add(new CommandGetEmailLink(this, email, 1, pin));
857 }
858
confirmemaillink(const char * code,const char * email,const byte * pwkey)859 void MegaClient::confirmemaillink(const char *code, const char *email, const byte *pwkey)
860 {
861 if (pwkey)
862 {
863 SymmCipher pwcipher(pwkey);
864 string emailstr = email;
865 uint64_t loginHash = stringhash64(&emailstr, &pwcipher);
866 reqs.add(new CommandConfirmEmailLink(this, code, email, (const byte*)&loginHash, true));
867 }
868 else
869 {
870 reqs.add(new CommandConfirmEmailLink(this, code, email, NULL, true));
871 }
872 }
873
contactlinkcreate(bool renew)874 void MegaClient::contactlinkcreate(bool renew)
875 {
876 reqs.add(new CommandContactLinkCreate(this, renew));
877 }
878
contactlinkquery(handle h)879 void MegaClient::contactlinkquery(handle h)
880 {
881 reqs.add(new CommandContactLinkQuery(this, h));
882 }
883
contactlinkdelete(handle h)884 void MegaClient::contactlinkdelete(handle h)
885 {
886 reqs.add(new CommandContactLinkDelete(this, h));
887 }
888
multifactorauthsetup(const char * pin)889 void MegaClient::multifactorauthsetup(const char *pin)
890 {
891 reqs.add(new CommandMultiFactorAuthSetup(this, pin));
892 }
893
multifactorauthcheck(const char * email)894 void MegaClient::multifactorauthcheck(const char *email)
895 {
896 reqs.add(new CommandMultiFactorAuthCheck(this, email));
897 }
898
multifactorauthdisable(const char * pin)899 void MegaClient::multifactorauthdisable(const char *pin)
900 {
901 reqs.add(new CommandMultiFactorAuthDisable(this, pin));
902 }
903
fetchtimezone()904 void MegaClient::fetchtimezone()
905 {
906 string timeoffset;
907 m_time_t rawtime = m_time(NULL);
908 if (rawtime != -1)
909 {
910 struct tm lt, ut, it;
911 memset(<, 0, sizeof(struct tm));
912 memset(&ut, 0, sizeof(struct tm));
913 memset(&it, 0, sizeof(struct tm));
914 m_localtime(rawtime, <);
915 m_gmtime(rawtime, &ut);
916 if (memcmp(&ut, &it, sizeof(struct tm)) && memcmp(<, &it, sizeof(struct tm)))
917 {
918 m_time_t local_time = m_mktime(<);
919 m_time_t utc_time = m_mktime(&ut);
920 if (local_time != -1 && utc_time != -1)
921 {
922 double foffset = difftime(local_time, utc_time);
923 int offset = int(fabs(foffset));
924 if (offset <= 43200)
925 {
926 ostringstream oss;
927 oss << ((foffset >= 0) ? "+" : "-");
928 oss << (offset / 3600) << ":";
929 int minutes = ((offset % 3600) / 60);
930 if (minutes < 10)
931 {
932 oss << "0";
933 }
934 oss << minutes;
935 timeoffset = oss.str();
936 }
937 }
938 }
939 }
940
941 reqs.add(new CommandFetchTimeZone(this, "", timeoffset.c_str()));
942 }
943
keepmealive(int type,bool enable)944 void MegaClient::keepmealive(int type, bool enable)
945 {
946 reqs.add(new CommandKeepMeAlive(this, type, enable));
947 }
948
getpsa()949 void MegaClient::getpsa()
950 {
951 reqs.add(new CommandGetPSA(this));
952 }
953
acknowledgeuseralerts()954 void MegaClient::acknowledgeuseralerts()
955 {
956 useralerts.acknowledgeAll();
957 }
958
activateoverquota(dstime timeleft,bool isPaywall)959 void MegaClient::activateoverquota(dstime timeleft, bool isPaywall)
960 {
961 if (timeleft)
962 {
963 assert(!isPaywall);
964 LOG_warn << "Bandwidth overquota for " << timeleft << " seconds";
965 overquotauntil = Waiter::ds + timeleft;
966
967 for (transfer_map::iterator it = transfers[GET].begin(); it != transfers[GET].end(); it++)
968 {
969 Transfer *t = it->second;
970 t->bt.backoff(timeleft);
971 if (t->slot && (t->state != TRANSFERSTATE_RETRYING
972 || !t->slot->retrying
973 || t->slot->retrybt.nextset() != overquotauntil))
974 {
975 t->state = TRANSFERSTATE_RETRYING;
976 t->slot->retrybt.backoff(timeleft);
977 t->slot->retrying = true;
978 app->transfer_failed(t, API_EOVERQUOTA, timeleft);
979 ++performanceStats.transferTempErrors;
980 }
981 }
982 }
983 else if (setstoragestatus(isPaywall ? STORAGE_PAYWALL : STORAGE_RED))
984 {
985 LOG_warn << "Storage overquota";
986 int start = (isPaywall) ? GET : PUT; // in Paywall state, none DLs/UPs can progress
987 for (int d = start; d <= PUT; d += PUT - GET)
988 {
989 for (transfer_map::iterator it = transfers[d].begin(); it != transfers[d].end(); it++)
990 {
991 Transfer *t = it->second;
992 t->bt.backoff(NEVER);
993 if (t->slot)
994 {
995 t->state = TRANSFERSTATE_RETRYING;
996 t->slot->retrybt.backoff(NEVER);
997 t->slot->retrying = true;
998 app->transfer_failed(t, isPaywall ? API_EPAYWALL : API_EOVERQUOTA, 0);
999 ++performanceStats.transferTempErrors;
1000 }
1001 }
1002 }
1003 }
1004 looprequested = true;
1005 }
1006
getDeviceid() const1007 std::string MegaClient::getDeviceid() const
1008 {
1009 if (MegaClient::statsid.empty())
1010 {
1011 fsaccess->statsid(&MegaClient::statsid);
1012 }
1013
1014 return MegaClient::statsid;
1015 }
1016
1017 // set warn level
warn(const char * msg)1018 void MegaClient::warn(const char* msg)
1019 {
1020 LOG_warn << msg;
1021 warned = true;
1022 }
1023
1024 // reset and return warnlevel
warnlevel()1025 bool MegaClient::warnlevel()
1026 {
1027 return warned ? (warned = false) | true : false;
1028 }
1029
1030 // returns a matching child node by UTF-8 name (does not resolve name clashes)
1031 // folder nodes take precedence over file nodes
childnodebyname(Node * p,const char * name,bool skipfolders)1032 Node* MegaClient::childnodebyname(Node* p, const char* name, bool skipfolders)
1033 {
1034 string nname = name;
1035 Node *found = NULL;
1036
1037 if (!p || p->type == FILENODE)
1038 {
1039 return NULL;
1040 }
1041
1042 fsaccess->normalize(&nname);
1043
1044 for (node_list::iterator it = p->children.begin(); it != p->children.end(); it++)
1045 {
1046 if (!strcmp(nname.c_str(), (*it)->displayname()))
1047 {
1048 if ((*it)->type != FILENODE && !skipfolders)
1049 {
1050 return *it;
1051 }
1052
1053 found = *it;
1054 if (skipfolders)
1055 {
1056 return found;
1057 }
1058 }
1059 }
1060
1061 return found;
1062 }
1063
1064 // returns all the matching child nodes by UTF-8 name
childnodesbyname(Node * p,const char * name,bool skipfolders)1065 vector<Node*> MegaClient::childnodesbyname(Node* p, const char* name, bool skipfolders)
1066 {
1067 string nname = name;
1068 vector<Node*> found;
1069
1070 if (!p || p->type == FILENODE)
1071 {
1072 return found;
1073 }
1074
1075 fsaccess->normalize(&nname);
1076
1077 for (node_list::iterator it = p->children.begin(); it != p->children.end(); it++)
1078 {
1079 if (nname == (*it)->displayname())
1080 {
1081 if ((*it)->type == FILENODE || !skipfolders)
1082 {
1083 found.push_back(*it);
1084 }
1085 }
1086 }
1087
1088 return found;
1089 }
1090
init()1091 void MegaClient::init()
1092 {
1093 warned = false;
1094 csretrying = false;
1095 chunkfailed = false;
1096 statecurrent = false;
1097 totalNodes = 0;
1098 mAppliedKeyNodeCount = 0;
1099 faretrying = false;
1100
1101 #ifdef ENABLE_SYNC
1102 syncactivity = false;
1103 syncops = false;
1104 syncdebrisadding = false;
1105 syncdebrisminute = 0;
1106 syncscanfailed = false;
1107 syncfslockretry = false;
1108 syncfsopsfailed = false;
1109 syncdownretry = false;
1110 syncnagleretry = false;
1111 syncextraretry = false;
1112 syncsup = true;
1113 syncdownrequired = false;
1114 syncuprequired = false;
1115
1116 if (syncscanstate)
1117 {
1118 app->syncupdate_scanning(false);
1119 syncscanstate = false;
1120 }
1121
1122 resetSyncConfigs();
1123 #endif
1124
1125 for (int i = sizeof rootnodes / sizeof *rootnodes; i--; )
1126 {
1127 rootnodes[i] = UNDEF;
1128 }
1129
1130 pendingsc.reset();
1131 pendingscUserAlerts.reset();
1132 mBlocked = false;
1133
1134 btcs.reset();
1135 btsc.reset();
1136 btpfa.reset();
1137 btbadhost.reset();
1138
1139 abortlockrequest();
1140 transferHttpCounter = 0;
1141
1142 jsonsc.pos = NULL;
1143 insca = false;
1144 insca_notlast = false;
1145 scnotifyurl.clear();
1146 scsn.clear();
1147
1148 notifyStorageChangeOnStateCurrent = false;
1149 mNotifiedSumSize = 0;
1150 mNodeCounters = NodeCounterMap();
1151 mOptimizePurgeNodes = false;
1152 }
1153
MegaClient(MegaApp * a,Waiter * w,HttpIO * h,FileSystemAccess * f,DbAccess * d,GfxProc * g,const char * k,const char * u,unsigned workerThreadCount)1154 MegaClient::MegaClient(MegaApp* a, Waiter* w, HttpIO* h, FileSystemAccess* f, DbAccess* d, GfxProc* g, const char* k, const char* u, unsigned workerThreadCount)
1155 : useralerts(*this), btugexpiration(rng), btcs(rng), btbadhost(rng), btworkinglock(rng), btsc(rng), btpfa(rng)
1156 #ifdef ENABLE_SYNC
1157 ,syncfslockretrybt(rng), syncdownbt(rng), syncnaglebt(rng), syncextrabt(rng), syncscanbt(rng)
1158 #endif
1159 , mAsyncQueue(*w, workerThreadCount)
1160 {
1161 sctable = NULL;
1162 pendingsccommit = false;
1163 tctable = NULL;
1164 me = UNDEF;
1165 publichandle = UNDEF;
1166 followsymlinks = false;
1167 usealtdownport = false;
1168 usealtupport = false;
1169 retryessl = false;
1170 workinglockcs = NULL;
1171 scpaused = false;
1172 asyncfopens = 0;
1173 achievements_enabled = false;
1174 isNewSession = false;
1175 tsLogin = 0;
1176 versions_disabled = false;
1177 accountsince = 0;
1178 accountversion = 0;
1179 gmfa_enabled = false;
1180 gfxdisabled = false;
1181 ssrs_enabled = false;
1182 nsr_enabled = false;
1183 aplvp_enabled = false;
1184 mSmsVerificationState = SMS_STATE_UNKNOWN;
1185 loggingout = 0;
1186 loggedout = false;
1187 cachedug = false;
1188 minstreamingrate = -1;
1189 ephemeralSession = false;
1190
1191 #ifndef EMSCRIPTEN
1192 autodownport = true;
1193 autoupport = true;
1194 usehttps = false;
1195 orderdownloadedchunks = false;
1196 #else
1197 autodownport = false;
1198 autoupport = false;
1199 usehttps = true;
1200 orderdownloadedchunks = true;
1201 #endif
1202
1203 fetchingnodes = false;
1204 fetchnodestag = 0;
1205
1206 #ifdef ENABLE_SYNC
1207 syncscanstate = false;
1208 syncadding = 0;
1209 currsyncid = 0;
1210 totalLocalNodes = 0;
1211 #endif
1212
1213 pendingcs = NULL;
1214
1215 xferpaused[PUT] = false;
1216 xferpaused[GET] = false;
1217 putmbpscap = 0;
1218 mBizGracePeriodTs = 0;
1219 mBizExpirationTs = 0;
1220 mBizMode = BIZ_MODE_UNKNOWN;
1221 mBizStatus = BIZ_STATUS_UNKNOWN;
1222
1223 overquotauntil = 0;
1224 ststatus = STORAGE_UNKNOWN;
1225 mOverquotaDeadlineTs = 0;
1226 looprequested = false;
1227
1228 mFetchingAuthrings = false;
1229 fetchingkeys = false;
1230 signkey = NULL;
1231 chatkey = NULL;
1232
1233 init();
1234
1235 f->client = this;
1236 f->waiter = w;
1237 transferlist.client = this;
1238
1239 if ((app = a))
1240 {
1241 a->client = this;
1242 }
1243
1244 waiter = w;
1245 httpio = h;
1246 fsaccess = f;
1247 dbaccess = d;
1248
1249 if ((gfx = g))
1250 {
1251 g->client = this;
1252 }
1253
1254 slotit = tslots.end();
1255
1256 userid = 0;
1257
1258 connections[PUT] = 3;
1259 connections[GET] = 4;
1260
1261 int i;
1262
1263 // initialize random client application instance ID (for detecting own
1264 // actions in server-client stream)
1265 for (i = sizeof sessionid; i--; )
1266 {
1267 sessionid[i] = static_cast<char>('a' + rng.genuint32(26));
1268 }
1269
1270 // initialize random API request sequence ID (server API is idempotent)
1271 for (i = sizeof reqid; i--; )
1272 {
1273 reqid[i] = static_cast<char>('a' + rng.genuint32(26));
1274 }
1275
1276 nextuh = 0;
1277 reqtag = 0;
1278
1279 badhostcs = NULL;
1280
1281 scsn.clear();
1282 cachedscsn = UNDEF;
1283
1284 snprintf(appkey, sizeof appkey, "&ak=%s", k);
1285
1286 // initialize useragent
1287 useragent = u;
1288
1289 useragent.append(" (");
1290 fsaccess->osversion(&useragent, true);
1291
1292 useragent.append(") MegaClient/" TOSTRING(MEGA_MAJOR_VERSION)
1293 "." TOSTRING(MEGA_MINOR_VERSION)
1294 "." TOSTRING(MEGA_MICRO_VERSION));
1295 useragent += sizeof(char*) == 8 ? "/64" : (sizeof(char*) == 4 ? "/32" : "");
1296
1297 LOG_debug << "User-Agent: " << useragent;
1298 LOG_debug << "Cryptopp version: " << CRYPTOPP_VERSION;
1299
1300 h->setuseragent(&useragent);
1301 h->setmaxdownloadspeed(0);
1302 h->setmaxuploadspeed(0);
1303 }
1304
~MegaClient()1305 MegaClient::~MegaClient()
1306 {
1307 destructorRunning = true;
1308 locallogout(false);
1309
1310 delete pendingcs;
1311 delete badhostcs;
1312 delete workinglockcs;
1313 delete sctable;
1314 delete tctable;
1315 delete dbaccess;
1316 }
1317
1318 #ifdef ENABLE_SYNC
resetSyncConfigs()1319 void MegaClient::resetSyncConfigs()
1320 {
1321 syncConfigs.reset();
1322 if (dbaccess && !uid.empty())
1323 {
1324 syncConfigs.reset(new SyncConfigBag{*dbaccess, *fsaccess, rng, uid});
1325 }
1326 }
1327 #endif
1328
getPublicLink(bool newLinkFormat,nodetype_t type,handle ph,const char * key)1329 std::string MegaClient::getPublicLink(bool newLinkFormat, nodetype_t type, handle ph, const char *key)
1330 {
1331 string strlink = "https://mega.nz/";
1332 string nodeType;
1333 if (newLinkFormat)
1334 {
1335 nodeType = (type == FOLDERNODE ? "folder/" : "file/");
1336 }
1337 else
1338 {
1339 nodeType = (type == FOLDERNODE ? "#F!" : "#!");
1340 }
1341
1342 strlink += nodeType;
1343
1344 Base64Str<MegaClient::NODEHANDLE> base64ph(ph);
1345 strlink += base64ph;
1346 strlink += (newLinkFormat ? "#" : "");
1347
1348 if (key)
1349 {
1350 strlink += (newLinkFormat ? "" : "!");
1351 strlink += key;
1352 }
1353
1354 return strlink;
1355 }
1356
1357 // nonblocking state machine executing all operations currently in progress
exec()1358 void MegaClient::exec()
1359 {
1360 CodeCounter::ScopeTimer ccst(performanceStats.execFunction);
1361
1362 WAIT_CLASS::bumpds();
1363
1364 if (overquotauntil && overquotauntil < Waiter::ds)
1365 {
1366 overquotauntil = 0;
1367 }
1368
1369 if (httpio->inetisback())
1370 {
1371 LOG_info << "Internet connectivity returned - resetting all backoff timers";
1372 abortbackoff(overquotauntil <= Waiter::ds);
1373 }
1374
1375 if (EVER(httpio->lastdata) && Waiter::ds >= httpio->lastdata + HttpIO::NETWORKTIMEOUT
1376 && !pendingcs)
1377 {
1378 LOG_debug << "Network timeout. Reconnecting";
1379 disconnect();
1380 }
1381 else if (EVER(disconnecttimestamp))
1382 {
1383 if (disconnecttimestamp <= Waiter::ds)
1384 {
1385 sendevent(99427, "Timeout (server idle)", 0);
1386
1387 disconnect();
1388 }
1389 }
1390 else if (pendingcs && EVER(pendingcs->lastdata) && !requestLock && !fetchingnodes
1391 && Waiter::ds >= pendingcs->lastdata + HttpIO::REQUESTTIMEOUT)
1392 {
1393 LOG_debug << "Request timeout. Triggering a lock request";
1394 requestLock = true;
1395 }
1396
1397 // successful network operation with a failed transfer chunk: increment error count
1398 // and continue transfers
1399 if (httpio->success && chunkfailed)
1400 {
1401 chunkfailed = false;
1402
1403 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); it++)
1404 {
1405 if ((*it)->failure)
1406 {
1407 (*it)->lasterror = API_EFAILED;
1408 (*it)->errorcount++;
1409 (*it)->failure = false;
1410 (*it)->lastdata = Waiter::ds;
1411 LOG_warn << "Transfer error count raised: " << (*it)->errorcount;
1412 }
1413 }
1414 }
1415
1416 bool first = true;
1417 do
1418 {
1419 if (!first)
1420 {
1421 WAIT_CLASS::bumpds();
1422 }
1423 first = false;
1424
1425 looprequested = false;
1426
1427 if (cachedug && btugexpiration.armed())
1428 {
1429 LOG_debug << "Cached user data expired";
1430 getuserdata();
1431 fetchtimezone();
1432 }
1433
1434 if (pendinghttp.size())
1435 {
1436 pendinghttp_map::iterator it = pendinghttp.begin();
1437 while (it != pendinghttp.end())
1438 {
1439 GenericHttpReq *req = it->second;
1440 switch (static_cast<reqstatus_t>(req->status))
1441 {
1442 case REQ_FAILURE:
1443 if (!req->httpstatus && (!req->maxretries || (req->numretry + 1) < req->maxretries))
1444 {
1445 req->numretry++;
1446 req->status = REQ_PREPARED;
1447 req->bt.backoff();
1448 req->isbtactive = true;
1449 LOG_warn << "Request failed (" << req->posturl << ") retrying ("
1450 << (req->numretry + 1) << " of " << req->maxretries << ")";
1451 it++;
1452 break;
1453 }
1454 // no retry -> fall through
1455 case REQ_SUCCESS:
1456 restag = it->first;
1457 app->http_result(req->httpstatus ? API_OK : API_EFAILED,
1458 req->httpstatus,
1459 req->buf ? (byte *)req->buf : (byte *)req->in.data(),
1460 int(req->buf ? req->bufpos : req->in.size()));
1461 delete req;
1462 pendinghttp.erase(it++);
1463 break;
1464 case REQ_PREPARED:
1465 if (req->bt.armed())
1466 {
1467 req->isbtactive = false;
1468 LOG_debug << "Sending retry for " << req->posturl;
1469 switch (req->method)
1470 {
1471 case METHOD_GET:
1472 req->get(this);
1473 break;
1474 case METHOD_POST:
1475 req->post(this);
1476 break;
1477 case METHOD_NONE:
1478 req->dns(this);
1479 break;
1480 }
1481 it++;
1482 break;
1483 }
1484 // no retry -> fall through
1485 case REQ_INFLIGHT:
1486 if (req->maxbt.nextset() && req->maxbt.armed())
1487 {
1488 LOG_debug << "Max total time exceeded for request: " << req->posturl;
1489 restag = it->first;
1490 app->http_result(API_EFAILED, 0, NULL, 0);
1491 delete req;
1492 pendinghttp.erase(it++);
1493 break;
1494 }
1495 default:
1496 it++;
1497 }
1498 }
1499 }
1500
1501 // file attribute puts (handled sequentially as a FIFO)
1502 if (activefa.size())
1503 {
1504 putfa_list::iterator curfa = activefa.begin();
1505 while (curfa != activefa.end())
1506 {
1507 HttpReqCommandPutFA* fa = *curfa;
1508 m_off_t p = fa->transferred(this);
1509 if (fa->progressreported < p)
1510 {
1511 httpio->updateuploadspeed(p - fa->progressreported);
1512 fa->progressreported = p;
1513 }
1514
1515 switch (static_cast<reqstatus_t>(fa->status))
1516 {
1517 case REQ_SUCCESS:
1518 if (fa->in.size() == sizeof(handle))
1519 {
1520 LOG_debug << "File attribute uploaded OK - " << fa->th;
1521
1522 // successfully wrote file attribute - store handle &
1523 // remove from list
1524 handle fah = MemAccess::get<handle>(fa->in.data());
1525
1526 if (fa->th == UNDEF)
1527 {
1528 // client app requested the upload without a node yet, and it will use the fa handle
1529 app->putfa_result(fah, fa->type, nullptr);
1530 }
1531 else
1532 {
1533 Node* n;
1534 handle h;
1535 handlepair_set::iterator it;
1536
1537 // do we have a valid upload handle?
1538 h = fa->th;
1539
1540 it = uhnh.lower_bound(pair<handle, handle>(h, 0));
1541
1542 if (it != uhnh.end() && it->first == h)
1543 {
1544 h = it->second;
1545 }
1546
1547 // are we updating a live node? issue command directly.
1548 // otherwise, queue for processing upon upload
1549 // completion.
1550 if ((n = nodebyhandle(h)) || (n = nodebyhandle(fa->th)))
1551 {
1552 LOG_debug << "Attaching file attribute";
1553 reqs.add(new CommandAttachFA(this, n->nodehandle, fa->type, fah, fa->tag));
1554 }
1555 else
1556 {
1557 pendingfa[pair<handle, fatype>(fa->th, fa->type)] = pair<handle, int>(fah, fa->tag);
1558 LOG_debug << "Queueing pending file attribute. Total: " << pendingfa.size();
1559 checkfacompletion(fa->th);
1560 }
1561 }
1562 }
1563 else
1564 {
1565 LOG_warn << "Error attaching attribute";
1566 Transfer *transfer = NULL;
1567 handletransfer_map::iterator htit = faputcompletion.find(fa->th);
1568 if (htit != faputcompletion.end())
1569 {
1570 // the failed attribute belongs to a pending upload
1571 transfer = htit->second;
1572 }
1573 else
1574 {
1575 // check if the failed attribute belongs to an active upload
1576 for (transfer_map::iterator it = transfers[PUT].begin(); it != transfers[PUT].end(); it++)
1577 {
1578 if (it->second->uploadhandle == fa->th)
1579 {
1580 transfer = it->second;
1581 break;
1582 }
1583 }
1584 }
1585
1586 if (transfer)
1587 {
1588 // reduce the number of required attributes to let the upload continue
1589 transfer->minfa--;
1590 checkfacompletion(fa->th);
1591 sendevent(99407,"Attribute attach failed during active upload", 0);
1592 }
1593 else
1594 {
1595 LOG_debug << "Transfer related to failed attribute not found: " << fa->th;
1596 }
1597 }
1598
1599 delete fa;
1600 curfa = activefa.erase(curfa);
1601 LOG_debug << "Remaining file attributes: " << activefa.size() << " active, " << queuedfa.size() << " queued";
1602 btpfa.reset();
1603 faretrying = false;
1604 break;
1605
1606 case REQ_FAILURE:
1607 // repeat request with exponential backoff
1608 LOG_warn << "Error setting file attribute";
1609 curfa = activefa.erase(curfa);
1610 fa->status = REQ_READY;
1611 queuedfa.push_back(fa);
1612 btpfa.backoff();
1613 faretrying = true;
1614 break;
1615
1616 default:
1617 curfa++;
1618 }
1619 }
1620 }
1621
1622 if (btpfa.armed())
1623 {
1624 faretrying = false;
1625 while (queuedfa.size() && activefa.size() < MAXPUTFA)
1626 {
1627 // dispatch most recent file attribute put
1628 putfa_list::iterator curfa = queuedfa.begin();
1629 HttpReqCommandPutFA* fa = *curfa;
1630 queuedfa.erase(curfa);
1631 activefa.push_back(fa);
1632
1633 LOG_debug << "Adding file attribute to the request queue";
1634 fa->status = REQ_INFLIGHT;
1635 reqs.add(fa);
1636 }
1637 }
1638
1639 if (fafcs.size())
1640 {
1641 // file attribute fetching (handled in parallel on a per-cluster basis)
1642 // cluster channels are never purged
1643 fafc_map::iterator cit;
1644 FileAttributeFetchChannel* fc;
1645
1646 for (cit = fafcs.begin(); cit != fafcs.end(); cit++)
1647 {
1648 fc = cit->second;
1649
1650 // is this request currently in flight?
1651 switch (static_cast<reqstatus_t>(fc->req.status))
1652 {
1653 case REQ_SUCCESS:
1654 if (fc->req.contenttype.find("text/html") != string::npos
1655 && !memcmp(fc->req.posturl.c_str(), "http:", 5))
1656 {
1657 LOG_warn << "Invalid Content-Type detected downloading file attr: " << fc->req.contenttype;
1658 fc->urltime = 0;
1659 usehttps = true;
1660 app->notify_change_to_https();
1661
1662 sendevent(99436, "Automatic change to HTTPS", 0);
1663 }
1664 else
1665 {
1666 fc->parse(cit->first, true);
1667 }
1668
1669 // notify app in case some attributes were not returned, then redispatch
1670 fc->failed();
1671 fc->req.disconnect();
1672 fc->req.status = REQ_PREPARED;
1673 fc->timeout.reset();
1674 fc->bt.reset();
1675 break;
1676
1677 case REQ_INFLIGHT:
1678 if (!fc->req.httpio)
1679 {
1680 break;
1681 }
1682
1683 if (fc->inbytes != fc->req.in.size())
1684 {
1685 httpio->lock();
1686 fc->parse(cit->first, false);
1687 httpio->unlock();
1688
1689 fc->timeout.backoff(100);
1690
1691 fc->inbytes = fc->req.in.size();
1692 }
1693
1694 if (!fc->timeout.armed()) break;
1695
1696 LOG_warn << "Timeout getting file attr";
1697 // timeout! fall through...
1698 case REQ_FAILURE:
1699 LOG_warn << "Error getting file attr";
1700
1701 if (fc->req.httpstatus && fc->req.contenttype.find("text/html") != string::npos
1702 && !memcmp(fc->req.posturl.c_str(), "http:", 5))
1703 {
1704 LOG_warn << "Invalid Content-Type detected on failed file attr: " << fc->req.contenttype;
1705 usehttps = true;
1706 app->notify_change_to_https();
1707
1708 sendevent(99436, "Automatic change to HTTPS", 0);
1709 }
1710
1711 fc->failed();
1712 fc->timeout.reset();
1713 fc->bt.backoff();
1714 fc->urltime = 0;
1715 fc->req.disconnect();
1716 fc->req.status = REQ_PREPARED;
1717 default:
1718 ;
1719 }
1720
1721 if (fc->req.status != REQ_INFLIGHT && fc->bt.armed() && (fc->fafs[1].size() || fc->fafs[0].size()))
1722 {
1723 fc->req.in.clear();
1724
1725 if (!fc->urltime || (Waiter::ds - fc->urltime) > 600)
1726 {
1727 // fetches pending for this unconnected channel - dispatch fresh connection
1728 LOG_debug << "Getting fresh download URL";
1729 fc->timeout.reset();
1730 reqs.add(new CommandGetFA(this, cit->first, fc->fahref));
1731 fc->req.status = REQ_INFLIGHT;
1732 }
1733 else
1734 {
1735 // redispatch cached URL if not older than one minute
1736 LOG_debug << "Using cached download URL";
1737 fc->dispatch();
1738 }
1739 }
1740 }
1741 }
1742
1743 // handle API client-server requests
1744 for (;;)
1745 {
1746 // do we have an API request outstanding?
1747 if (pendingcs)
1748 {
1749 // handle retry reason for requests
1750 retryreason_t reason = RETRY_NONE;
1751
1752 if (pendingcs->status == REQ_SUCCESS || pendingcs->status == REQ_FAILURE)
1753 {
1754 performanceStats.csRequestWaitTime.stop();
1755 }
1756
1757 switch (static_cast<reqstatus_t>(pendingcs->status))
1758 {
1759 case REQ_READY:
1760 break;
1761
1762 case REQ_INFLIGHT:
1763 if (pendingcs->contentlength > 0)
1764 {
1765 if (fetchingnodes && fnstats.timeToFirstByte == NEVER
1766 && pendingcs->bufpos > 10)
1767 {
1768 WAIT_CLASS::bumpds();
1769 fnstats.timeToFirstByte = WAIT_CLASS::ds - fnstats.startTime;
1770 }
1771
1772 if (pendingcs->bufpos > pendingcs->notifiedbufpos)
1773 {
1774 abortlockrequest();
1775 app->request_response_progress(pendingcs->bufpos, pendingcs->contentlength);
1776 pendingcs->notifiedbufpos = pendingcs->bufpos;
1777 }
1778 }
1779 break;
1780
1781 case REQ_SUCCESS:
1782 abortlockrequest();
1783 app->request_response_progress(pendingcs->bufpos, -1);
1784
1785 if (pendingcs->in != "-3" && pendingcs->in != "-4")
1786 {
1787 if (*pendingcs->in.c_str() == '[')
1788 {
1789 if (fetchingnodes && fnstats.timeToFirstByte == NEVER)
1790 {
1791 WAIT_CLASS::bumpds();
1792 fnstats.timeToFirstByte = WAIT_CLASS::ds - fnstats.startTime;
1793 }
1794
1795 if (csretrying)
1796 {
1797 app->notify_retry(0, RETRY_NONE);
1798 csretrying = false;
1799 }
1800
1801 // request succeeded, process result array
1802 reqs.serverresponse(std::move(pendingcs->in), this);
1803
1804 WAIT_CLASS::bumpds();
1805
1806 delete pendingcs;
1807 pendingcs = NULL;
1808
1809 notifypurge();
1810 if (sctable && pendingsccommit && !reqs.cmdspending())
1811 {
1812 LOG_debug << "Executing postponed DB commit";
1813 sctable->commit();
1814 sctable->begin();
1815 app->notify_dbcommit();
1816 pendingsccommit = false;
1817 }
1818
1819 // increment unique request ID
1820 for (int i = sizeof reqid; i--; )
1821 {
1822 if (reqid[i]++ < 'z')
1823 {
1824 break;
1825 }
1826 else
1827 {
1828 reqid[i] = 'a';
1829 }
1830 }
1831
1832 if (loggedout)
1833 {
1834 locallogout(true);
1835 app->logout_result(API_OK);
1836 }
1837 }
1838 else
1839 {
1840 // request failed
1841 JSON json;
1842 json.pos = pendingcs->in.c_str();
1843 std::string requestError;
1844 error e;
1845 bool valid = json.storeobject(&requestError);
1846 if (valid)
1847 {
1848 if (strncmp(requestError.c_str(), "{\"err\":", 7) == 0)
1849 {
1850 e = (error)atoi(requestError.c_str() + 7);
1851 }
1852 else
1853 {
1854 e = (error)atoi(requestError.c_str());
1855 }
1856 }
1857 else
1858 {
1859 e = API_EINTERNAL;
1860 requestError = std::to_string(e);
1861 }
1862
1863 if (!e)
1864 {
1865 e = API_EINTERNAL;
1866 requestError = std::to_string(e);
1867 }
1868
1869 if (e == API_EBLOCKED && sid.size())
1870 {
1871 block();
1872 }
1873
1874 app->request_error(e);
1875 delete pendingcs;
1876 pendingcs = NULL;
1877 csretrying = false;
1878
1879 reqs.servererror(requestError, this);
1880 break;
1881 }
1882
1883 btcs.reset();
1884 break;
1885 }
1886 else
1887 {
1888 if (pendingcs->in == "-3")
1889 {
1890 reason = RETRY_API_LOCK;
1891 }
1892 else
1893 {
1894 reason = RETRY_RATE_LIMIT;
1895 }
1896 if (fetchingnodes)
1897 {
1898 fnstats.eAgainCount++;
1899 }
1900 }
1901
1902 // fall through
1903 case REQ_FAILURE:
1904 if (!reason && pendingcs->httpstatus != 200)
1905 {
1906 if (pendingcs->httpstatus == 500)
1907 {
1908 reason = RETRY_SERVERS_BUSY;
1909 }
1910 else if (pendingcs->httpstatus == 0)
1911 {
1912 reason = RETRY_CONNECTIVITY;
1913 }
1914 else
1915 {
1916 reason = RETRY_UNKNOWN;
1917 }
1918 }
1919
1920 if (fetchingnodes && pendingcs->httpstatus != 200)
1921 {
1922 if (pendingcs->httpstatus == 500)
1923 {
1924 fnstats.e500Count++;
1925 }
1926 else
1927 {
1928 fnstats.eOthersCount++;
1929 }
1930 }
1931
1932 abortlockrequest();
1933 if (pendingcs->sslcheckfailed)
1934 {
1935 sendevent(99453, "Invalid public key");
1936 sslfakeissuer = pendingcs->sslfakeissuer;
1937 app->request_error(API_ESSL);
1938 sslfakeissuer.clear();
1939
1940 if (!retryessl)
1941 {
1942 delete pendingcs;
1943 pendingcs = NULL;
1944 csretrying = false;
1945
1946 reqs.servererror(std::to_string(API_ESSL), this);
1947 break;
1948 }
1949 }
1950
1951 // failure, repeat with capped exponential backoff
1952 app->request_response_progress(pendingcs->bufpos, -1);
1953
1954 delete pendingcs;
1955 pendingcs = NULL;
1956
1957 btcs.backoff();
1958 app->notify_retry(btcs.retryin(), reason);
1959 csretrying = true;
1960
1961 reqs.requeuerequest();
1962
1963 default:
1964 ;
1965 }
1966
1967 if (pendingcs)
1968 {
1969 break;
1970 }
1971 }
1972
1973 if (btcs.armed())
1974 {
1975 if (reqs.cmdspending())
1976 {
1977 pendingcs = new HttpReq();
1978 pendingcs->protect = true;
1979 pendingcs->logname = clientname + "cs ";
1980
1981 bool suppressSID = true;
1982 reqs.serverrequest(pendingcs->out, suppressSID, pendingcs->includesFetchingNodes);
1983
1984 pendingcs->posturl = APIURL;
1985
1986 pendingcs->posturl.append("cs?id=");
1987 pendingcs->posturl.append(reqid, sizeof reqid);
1988 if (!suppressSID)
1989 {
1990 pendingcs->posturl.append(auth);
1991 }
1992 pendingcs->posturl.append(appkey);
1993
1994 string version = "v=2";
1995 pendingcs->posturl.append("&" + version);
1996 if (lang.size())
1997 {
1998 pendingcs->posturl.append("&");
1999 pendingcs->posturl.append(lang);
2000 }
2001 pendingcs->type = REQ_JSON;
2002
2003 performanceStats.csRequestWaitTime.start();
2004 pendingcs->post(this);
2005 continue;
2006 }
2007 else
2008 {
2009 btcs.reset();
2010 }
2011 }
2012 break;
2013 }
2014
2015 // handle the request for the last 50 UserAlerts
2016 if (pendingscUserAlerts)
2017 {
2018 switch (static_cast<reqstatus_t>(pendingscUserAlerts->status))
2019 {
2020 case REQ_SUCCESS:
2021 if (*pendingscUserAlerts->in.c_str() == '{')
2022 {
2023 JSON json;
2024 json.begin(pendingscUserAlerts->in.c_str());
2025 json.enterobject();
2026 if (useralerts.procsc_useralert(json))
2027 {
2028 // NULL vector: "notify all elements"
2029 app->useralerts_updated(NULL, int(useralerts.alerts.size()));
2030 }
2031 pendingscUserAlerts.reset();
2032 break;
2033 }
2034
2035 // fall through
2036 case REQ_FAILURE:
2037 if (pendingscUserAlerts->httpstatus == 200)
2038 {
2039 error e = (error)atoi(pendingscUserAlerts->in.c_str());
2040 if (e == API_EAGAIN || e == API_ERATELIMIT)
2041 {
2042 btsc.backoff();
2043 pendingscUserAlerts.reset();
2044 LOG_warn << "Backing off before retrying useralerts request: " << btsc.retryin();
2045 break;
2046 }
2047 LOG_err << "Unexpected sc response: " << pendingscUserAlerts->in;
2048 }
2049 LOG_err << "Useralerts request failed, continuing without them";
2050 if (useralerts.begincatchup)
2051 {
2052 useralerts.begincatchup = false;
2053 useralerts.catchupdone = true;
2054 }
2055 pendingscUserAlerts.reset();
2056 break;
2057
2058 default:
2059 break;
2060 }
2061 }
2062
2063 // handle API server-client requests
2064 if (!jsonsc.pos && !pendingscUserAlerts && pendingsc && !loggingout)
2065 {
2066 switch (static_cast<reqstatus_t>(pendingsc->status))
2067 {
2068 case REQ_SUCCESS:
2069 pendingscTimedOut = false;
2070 if (pendingsc->contentlength == 1
2071 && pendingsc->in.size()
2072 && pendingsc->in[0] == '0')
2073 {
2074 LOG_debug << "SC keep-alive received";
2075 pendingsc.reset();
2076 btsc.reset();
2077 break;
2078 }
2079
2080 if (*pendingsc->in.c_str() == '{')
2081 {
2082 insca = false;
2083 insca_notlast = false;
2084 jsonsc.begin(pendingsc->in.c_str());
2085 jsonsc.enterobject();
2086 break;
2087 }
2088 else
2089 {
2090 error e = (error)atoi(pendingsc->in.c_str());
2091 if (e == API_ESID)
2092 {
2093 app->request_error(API_ESID);
2094 scsn.stopScsn();
2095 }
2096 else if (e == API_ETOOMANY)
2097 {
2098 LOG_warn << "Too many pending updates - reloading local state";
2099 int creqtag = reqtag;
2100 reqtag = fetchnodestag; // associate with ongoing request, if any
2101 fetchingnodes = false;
2102 fetchnodestag = 0;
2103 fetchnodes(true);
2104 reqtag = creqtag;
2105 }
2106 else if (e == API_EAGAIN || e == API_ERATELIMIT)
2107 {
2108 if (!statecurrent)
2109 {
2110 fnstats.eAgainCount++;
2111 }
2112 }
2113 else if (e == API_EBLOCKED)
2114 {
2115 app->request_error(API_EBLOCKED);
2116 block(true);
2117 }
2118 else
2119 {
2120 LOG_err << "Unexpected sc response: " << pendingsc->in;
2121 scsn.stopScsn();
2122 }
2123 }
2124
2125 // fall through
2126 case REQ_FAILURE:
2127 pendingscTimedOut = false;
2128 if (pendingsc)
2129 {
2130 if (!statecurrent && pendingsc->httpstatus != 200)
2131 {
2132 if (pendingsc->httpstatus == 500)
2133 {
2134 fnstats.e500Count++;
2135 }
2136 else
2137 {
2138 fnstats.eOthersCount++;
2139 }
2140 }
2141
2142 if (pendingsc->sslcheckfailed)
2143 {
2144 sendevent(99453, "Invalid public key");
2145 sslfakeissuer = pendingsc->sslfakeissuer;
2146 app->request_error(API_ESSL);
2147 sslfakeissuer.clear();
2148
2149 if (!retryessl)
2150 {
2151 scsn.stopScsn();
2152 }
2153 }
2154
2155 pendingsc.reset();
2156 }
2157
2158 if (scsn.stopped())
2159 {
2160 btsc.backoff(NEVER);
2161 }
2162 else
2163 {
2164 // failure, repeat with capped exponential backoff
2165 btsc.backoff();
2166 }
2167 break;
2168
2169 case REQ_INFLIGHT:
2170 if (!pendingscTimedOut && Waiter::ds >= (pendingsc->lastdata + HttpIO::SCREQUESTTIMEOUT))
2171 {
2172 LOG_debug << "sc timeout expired";
2173 // In almost all cases the server won't take more than SCREQUESTTIMEOUT seconds. But if it does, break the cycle of endless requests for the same thing
2174 pendingscTimedOut = true;
2175 pendingsc.reset();
2176 btsc.reset();
2177 }
2178 break;
2179 default:
2180 break;
2181 }
2182 }
2183
2184 #ifdef ENABLE_SYNC
2185 if (syncactivity)
2186 {
2187 syncops = true;
2188 }
2189 syncactivity = false;
2190
2191 if (scsn.stopped() || mBlocked || scpaused || !statecurrent || !syncsup)
2192 {
2193 LOG_verbose << " Megaclient exec is pending resolutions."
2194 << " scpaused=" << scpaused
2195 << " stopsc=" << scsn.stopped()
2196 << " mBlocked=" << mBlocked
2197 << " jsonsc.pos=" << jsonsc.pos
2198 << " syncsup=" << syncsup
2199 << " statecurrent=" << statecurrent
2200 << " syncadding=" << syncadding
2201 << " syncactivity=" << syncactivity
2202 << " syncdownrequired=" << syncdownrequired
2203 << " syncdownretry=" << syncdownretry;
2204 }
2205
2206 // do not process the SC result until all preconfigured syncs are up and running
2207 // except if SC packets are required to complete a fetchnodes
2208 if (!scpaused && jsonsc.pos && (syncsup || !statecurrent) && !syncdownrequired && !syncdownretry)
2209 #else
2210 if (!scpaused && jsonsc.pos)
2211 #endif
2212 {
2213 // FIXME: reload in case of bad JSON
2214 bool r = procsc();
2215
2216 if (r)
2217 {
2218 // completed - initiate next SC request
2219 pendingsc.reset();
2220 btsc.reset();
2221 }
2222 #ifdef ENABLE_SYNC
2223 else
2224 {
2225 // remote changes require immediate attention of syncdown()
2226 syncdownrequired = true;
2227 syncactivity = true;
2228 }
2229 #endif
2230 }
2231
2232 if (!pendingsc && !pendingscUserAlerts && scsn.ready() && btsc.armed() && !mBlocked)
2233 {
2234 if (useralerts.begincatchup)
2235 {
2236 assert(!fetchingnodes);
2237 pendingscUserAlerts.reset(new HttpReq());
2238 pendingscUserAlerts->logname = clientname + "sc50 ";
2239 pendingscUserAlerts->protect = true;
2240 pendingscUserAlerts->posturl = APIURL;
2241 pendingscUserAlerts->posturl.append("sc"); // notifications/useralerts on sc rather than wsc, no timeout
2242 pendingscUserAlerts->posturl.append("?c=50");
2243 pendingscUserAlerts->posturl.append(auth);
2244 pendingscUserAlerts->type = REQ_JSON;
2245 pendingscUserAlerts->post(this);
2246 }
2247 else
2248 {
2249 pendingsc.reset(new HttpReq());
2250 pendingsc->logname = clientname + "sc ";
2251 if (scnotifyurl.size())
2252 {
2253 pendingsc->posturl = scnotifyurl;
2254 }
2255 else
2256 {
2257 pendingsc->posturl = APIURL;
2258 pendingsc->posturl.append("wsc");
2259 }
2260
2261 pendingsc->protect = true;
2262 pendingsc->posturl.append("?sn=");
2263 pendingsc->posturl.append(scsn.text());
2264
2265 pendingsc->posturl.append(auth);
2266
2267 pendingsc->type = REQ_JSON;
2268 pendingsc->post(this);
2269 }
2270 jsonsc.pos = NULL;
2271 }
2272
2273 if (badhostcs)
2274 {
2275 if (badhostcs->status == REQ_SUCCESS)
2276 {
2277 LOG_debug << "Successful badhost report";
2278 btbadhost.reset();
2279 delete badhostcs;
2280 badhostcs = NULL;
2281 }
2282 else if(badhostcs->status == REQ_FAILURE
2283 || (badhostcs->status == REQ_INFLIGHT && Waiter::ds >= (badhostcs->lastdata + HttpIO::REQUESTTIMEOUT)))
2284 {
2285 LOG_debug << "Failed badhost report. Retrying...";
2286 btbadhost.backoff();
2287 badhosts = badhostcs->outbuf;
2288 delete badhostcs;
2289 badhostcs = NULL;
2290 }
2291 }
2292
2293 if (workinglockcs)
2294 {
2295 if (workinglockcs->status == REQ_SUCCESS)
2296 {
2297 LOG_debug << "Successful lock request";
2298 btworkinglock.reset();
2299
2300 if (workinglockcs->in == "1")
2301 {
2302 LOG_warn << "Timeout (server idle)";
2303 disconnecttimestamp = Waiter::ds + HttpIO::CONNECTTIMEOUT;
2304 }
2305 else if (workinglockcs->in == "0")
2306 {
2307 sendevent(99425, "Timeout (server busy)", 0);
2308 pendingcs->lastdata = Waiter::ds;
2309 }
2310 else
2311 {
2312 LOG_err << "Error in lock request: " << workinglockcs->in;
2313 disconnecttimestamp = Waiter::ds + HttpIO::CONNECTTIMEOUT;
2314 }
2315
2316 delete workinglockcs;
2317 workinglockcs = NULL;
2318 requestLock = false;
2319 }
2320 else if (workinglockcs->status == REQ_FAILURE
2321 || (workinglockcs->status == REQ_INFLIGHT && Waiter::ds >= (workinglockcs->lastdata + HttpIO::REQUESTTIMEOUT)))
2322 {
2323 LOG_warn << "Failed lock request. Retrying...";
2324 btworkinglock.backoff();
2325 delete workinglockcs;
2326 workinglockcs = NULL;
2327 }
2328 }
2329
2330 // fill transfer slots from the queue
2331 if (lastDispatchTransfersDs != Waiter::ds)
2332 {
2333 // don't run this too often or it may use a lot of cpu without starting new transfers, if the list is long
2334 lastDispatchTransfersDs = Waiter::ds;
2335
2336 size_t lastCount = 0;
2337 size_t transferCount = transfers[GET].size() + transfers[PUT].size();
2338 do
2339 {
2340 lastCount = transferCount;
2341
2342 // Check the list of transfers and start a few big files, and many small, up to configured limits.
2343 dispatchTransfers();
2344
2345 // if we are cancelling a lot of transfers (eg. nodes to download were deleted), keep going. Avoid stalling when no transfers are active and all queued fail
2346 transferCount = transfers[GET].size() + transfers[PUT].size();
2347 } while (transferCount < lastCount);
2348 }
2349
2350 #ifndef EMSCRIPTEN
2351 assert(!asyncfopens);
2352 #endif
2353
2354 slotit = tslots.begin();
2355
2356
2357 if (!mBlocked) // handle active unpaused transfers
2358 {
2359 DBTableTransactionCommitter committer(tctable);
2360
2361 while (slotit != tslots.end())
2362 {
2363 transferslot_list::iterator it = slotit;
2364
2365 slotit++;
2366
2367 if (!xferpaused[(*it)->transfer->type] && (!(*it)->retrying || (*it)->retrybt.armed()))
2368 {
2369 (*it)->doio(this, committer);
2370 }
2371 }
2372 }
2373 else
2374 {
2375 LOG_debug << "skipping slots doio while blocked";
2376 }
2377
2378 #ifdef ENABLE_SYNC
2379 // verify filesystem fingerprints, disable deviating syncs
2380 // (this covers mountovers, some device removals and some failures)
2381 sync_list::iterator it;
2382 for (it = syncs.begin(); it != syncs.end(); it++)
2383 {
2384 if ((*it)->fsfp)
2385 {
2386 fsfp_t current = (*it)->dirnotify->fsfingerprint();
2387 if ((*it)->fsfp != current)
2388 {
2389 LOG_err << "Local fingerprint mismatch. Previous: " << (*it)->fsfp
2390 << " Current: " << current;
2391 (*it)->errorcode = API_EFAILED;
2392 (*it)->changestate(SYNC_FAILED);
2393 }
2394 }
2395 }
2396
2397 if (!syncsup)
2398 {
2399 // set syncsup if there are no initializing syncs
2400 // this will allow incoming server-client commands to trigger the filesystem
2401 // actions that have occurred while the sync app was not running
2402 for (it = syncs.begin(); it != syncs.end(); it++)
2403 {
2404 if ((*it)->state == SYNC_INITIALSCAN)
2405 {
2406 break;
2407 }
2408 }
2409
2410 if (it == syncs.end())
2411 {
2412 syncsup = true;
2413 syncactivity = true;
2414 syncdownrequired = true;
2415 }
2416 }
2417
2418 // process active syncs
2419 // sync timer: full rescan in case of filesystem notification failures
2420 if (syncscanfailed && syncscanbt.armed())
2421 {
2422 syncscanfailed = false;
2423 syncops = true;
2424 }
2425
2426 // sync timer: file change upload delay timeouts (Nagle algorithm)
2427 if (syncnagleretry && syncnaglebt.armed())
2428 {
2429 syncnagleretry = false;
2430 syncops = true;
2431 }
2432
2433 if (syncextraretry && syncextrabt.armed())
2434 {
2435 syncextraretry = false;
2436 syncops = true;
2437 }
2438
2439 // sync timer: read lock retry
2440 if (syncfslockretry && syncfslockretrybt.armed())
2441 {
2442 syncfslockretrybt.backoff(Sync::SCANNING_DELAY_DS);
2443 }
2444
2445 // halt all syncing while the local filesystem is pending a lock-blocked operation
2446 // or while we are fetching nodes
2447 // FIXME: indicate by callback
2448 if (!syncdownretry && !syncadding && statecurrent && !syncdownrequired && !fetchingnodes)
2449 {
2450 // process active syncs, stop doing so while transient local fs ops are pending
2451 if (syncs.size() || syncactivity)
2452 {
2453 bool prevpending = false;
2454 for (int q = syncfslockretry ? DirNotify::RETRY : DirNotify::DIREVENTS; q >= DirNotify::DIREVENTS; q--)
2455 {
2456 for (it = syncs.begin(); it != syncs.end(); )
2457 {
2458 Sync* sync = *it++;
2459 prevpending = prevpending || sync->dirnotify->notifyq[q].size();
2460 if (prevpending)
2461 {
2462 break;
2463 }
2464 }
2465 if (prevpending)
2466 {
2467 break;
2468 }
2469 }
2470
2471 dstime nds = NEVER;
2472 dstime mindelay = NEVER;
2473 for (it = syncs.begin(); it != syncs.end(); )
2474 {
2475 Sync* sync = *it++;
2476 if (sync->isnetwork && (sync->state == SYNC_ACTIVE || sync->state == SYNC_INITIALSCAN))
2477 {
2478 Notification notification;
2479 while (sync->dirnotify->notifyq[DirNotify::EXTRA].popFront(notification))
2480 {
2481 dstime dsmin = Waiter::ds - Sync::EXTRA_SCANNING_DELAY_DS;
2482 if (notification.timestamp <= dsmin)
2483 {
2484 LOG_debug << "Processing extra fs notification: " << notification.path.toPath(*fsaccess);
2485 sync->dirnotify->notify(DirNotify::DIREVENTS, notification.localnode, std::move(notification.path));
2486 }
2487 else
2488 {
2489 sync->dirnotify->notifyq[DirNotify::EXTRA].unpopFront(notification);
2490 dstime delay = (notification.timestamp - dsmin) + 1;
2491 if (delay < mindelay)
2492 {
2493 mindelay = delay;
2494 }
2495 break;
2496 }
2497 }
2498 }
2499 }
2500 if (EVER(mindelay))
2501 {
2502 syncextrabt.backoff(mindelay);
2503 syncextraretry = true;
2504 }
2505 else
2506 {
2507 syncextraretry = false;
2508 }
2509
2510 for (int q = syncfslockretry ? DirNotify::RETRY : DirNotify::DIREVENTS; q >= DirNotify::DIREVENTS; q--)
2511 {
2512 if (!syncfsopsfailed)
2513 {
2514 syncfslockretry = false;
2515
2516 // not retrying local operations: process pending notifyqs
2517 for (it = syncs.begin(); it != syncs.end(); )
2518 {
2519 Sync* sync = *it++;
2520
2521 if (sync->state == SYNC_CANCELED || sync->state == SYNC_FAILED)
2522 {
2523 delete sync;
2524 continue;
2525 }
2526 else if (sync->state == SYNC_ACTIVE || sync->state == SYNC_INITIALSCAN)
2527 {
2528 // process items from the notifyq until depleted
2529 if (sync->dirnotify->notifyq[q].size())
2530 {
2531 dstime dsretry;
2532
2533 syncops = true;
2534
2535 if ((dsretry = sync->procscanq(q)))
2536 {
2537 // we resume processing after dsretry has elapsed
2538 // (to avoid open-after-creation races with e.g. MS Office)
2539 if (EVER(dsretry))
2540 {
2541 if (!syncnagleretry || (dsretry + 1) < syncnaglebt.backoffdelta())
2542 {
2543 syncnaglebt.backoff(dsretry + 1);
2544 }
2545
2546 syncnagleretry = true;
2547 }
2548 else
2549 {
2550 if (syncnagleretry)
2551 {
2552 syncnaglebt.arm();
2553 }
2554 syncactivity = true;
2555 }
2556
2557 if (syncadding)
2558 {
2559 break;
2560 }
2561 }
2562 else
2563 {
2564 LOG_debug << "Pending MEGA nodes: " << synccreate.size();
2565 if (!syncadding)
2566 {
2567 LOG_debug << "Running syncup to create missing folders";
2568 syncup(sync->localroot.get(), &nds);
2569 sync->cachenodes();
2570 }
2571
2572 // we interrupt processing the notifyq if the completion
2573 // of a node creation is required to continue
2574 break;
2575 }
2576 }
2577
2578 if (sync->state == SYNC_INITIALSCAN && q == DirNotify::DIREVENTS && !sync->dirnotify->notifyq[q].size())
2579 {
2580 sync->changestate(SYNC_ACTIVE);
2581
2582 // scan for items that were deleted while the sync was stopped
2583 // FIXME: defer this until RETRY queue is processed
2584 sync->scanseqno++;
2585 sync->deletemissing(sync->localroot.get());
2586 }
2587 }
2588 }
2589
2590 if (syncadding)
2591 {
2592 break;
2593 }
2594 }
2595 }
2596
2597 size_t totalpending = 0;
2598 size_t scanningpending = 0;
2599 for (int q = DirNotify::RETRY; q >= DirNotify::DIREVENTS; q--)
2600 {
2601 for (it = syncs.begin(); it != syncs.end(); )
2602 {
2603 Sync* sync = *it++;
2604 sync->cachenodes();
2605
2606 totalpending += sync->dirnotify->notifyq[q].size();
2607 Notification notification;
2608 if (q == DirNotify::DIREVENTS)
2609 {
2610 scanningpending += sync->dirnotify->notifyq[q].size();
2611 }
2612 else if (!syncfslockretry && sync->dirnotify->notifyq[DirNotify::RETRY].peekFront(notification))
2613 {
2614 syncfslockretrybt.backoff(Sync::SCANNING_DELAY_DS);
2615 blockedfile = notification.path;
2616 syncfslockretry = true;
2617 }
2618 }
2619 }
2620
2621 if (!syncfslockretry && !syncfsopsfailed)
2622 {
2623 blockedfile.clear();
2624 }
2625
2626 if (syncadding)
2627 {
2628 // do not continue processing syncs while adding nodes
2629 // just go to evaluate the main do-while loop
2630 notifypurge();
2631 continue;
2632 }
2633
2634 // delete files that were overwritten by folders in checkpath()
2635 execsyncdeletions();
2636
2637 if (synccreate.size())
2638 {
2639 syncupdate();
2640 }
2641
2642 // notify the app of the length of the pending scan queue
2643 if (scanningpending < 4)
2644 {
2645 if (syncscanstate)
2646 {
2647 LOG_debug << "Scanning finished";
2648 app->syncupdate_scanning(false);
2649 syncscanstate = false;
2650 }
2651 }
2652 else if (scanningpending > 10)
2653 {
2654 if (!syncscanstate)
2655 {
2656 LOG_debug << "Scanning started";
2657 app->syncupdate_scanning(true);
2658 syncscanstate = true;
2659 }
2660 }
2661
2662 if (prevpending && !totalpending)
2663 {
2664 LOG_debug << "Scan queue processed, triggering a scan";
2665 syncdownrequired = true;
2666 }
2667
2668 notifypurge();
2669
2670 if (!syncadding && (syncactivity || syncops))
2671 {
2672 for (it = syncs.begin(); it != syncs.end(); it++)
2673 {
2674 // make sure that the remote synced folder still exists
2675 if (!(*it)->localroot->node)
2676 {
2677 LOG_err << "The remote root node doesn't exist";
2678 (*it)->errorcode = API_ENOENT;
2679 (*it)->changestate(SYNC_FAILED);
2680 }
2681 }
2682
2683 // perform aggregate ops that require all scanqs to be fully processed
2684 for (it = syncs.begin(); it != syncs.end(); it++)
2685 {
2686 if ((*it)->dirnotify->notifyq[DirNotify::DIREVENTS].size()
2687 || (*it)->dirnotify->notifyq[DirNotify::RETRY].size())
2688 {
2689 if (!syncnagleretry && !syncfslockretry)
2690 {
2691 syncactivity = true;
2692 }
2693
2694 break;
2695 }
2696 }
2697
2698 if (it == syncs.end())
2699 {
2700 // execution of notified deletions - these are held in localsyncnotseen and
2701 // kept pending until all creations (that might reference them for the purpose of
2702 // copying) have completed and all notification queues have run empty (to ensure
2703 // that moves are not executed as deletions+additions.
2704 if (localsyncnotseen.size() && !synccreate.size())
2705 {
2706 // ... execute all pending deletions
2707 LocalPath path;
2708 auto fa = fsaccess->newfileaccess();
2709 while (localsyncnotseen.size())
2710 {
2711 LocalNode* l = *localsyncnotseen.begin();
2712 unlinkifexists(l, fa.get(), path);
2713 delete l;
2714 }
2715 }
2716
2717 // process filesystem notifications for active syncs unless we
2718 // are retrying local fs writes
2719 if (!syncfsopsfailed)
2720 {
2721 LOG_verbose << "syncops: " << syncactivity << syncnagleretry
2722 << syncfslockretry << synccreate.size();
2723 syncops = false;
2724
2725 // FIXME: only syncup for subtrees that were actually
2726 // updated to reduce CPU load
2727 bool repeatsyncup = false;
2728 bool syncupdone = false;
2729 for (it = syncs.begin(); it != syncs.end(); it++)
2730 {
2731 if (((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN)
2732 && !syncadding && syncuprequired && !syncnagleretry)
2733 {
2734 LOG_debug << "Running syncup on demand";
2735 repeatsyncup |= !syncup((*it)->localroot.get(), &nds);
2736 syncupdone = true;
2737 (*it)->cachenodes();
2738 }
2739 }
2740 syncuprequired = !syncupdone || repeatsyncup;
2741
2742 if (EVER(nds))
2743 {
2744 if (!syncnagleretry || (nds - Waiter::ds) < syncnaglebt.backoffdelta())
2745 {
2746 syncnaglebt.backoff(nds - Waiter::ds);
2747 }
2748
2749 syncnagleretry = true;
2750 syncuprequired = true;
2751 }
2752
2753 // delete files that were overwritten by folders in syncup()
2754 execsyncdeletions();
2755
2756 if (synccreate.size())
2757 {
2758 syncupdate();
2759 }
2760
2761 unsigned totalnodes = 0;
2762
2763 // we have no sync-related operations pending - trigger processing if at least one
2764 // filesystem item is notified or initiate a full rescan if there has been
2765 // an event notification failure (or event notification is unavailable)
2766 bool scanfailed = false;
2767 for (it = syncs.begin(); it != syncs.end(); it++)
2768 {
2769 Sync* sync = *it;
2770
2771 totalnodes += sync->localnodes[FILENODE] + sync->localnodes[FOLDERNODE];
2772
2773 if (sync->state == SYNC_ACTIVE || sync->state == SYNC_INITIALSCAN)
2774 {
2775 if (sync->dirnotify->notifyq[DirNotify::DIREVENTS].size()
2776 || sync->dirnotify->notifyq[DirNotify::RETRY].size())
2777 {
2778 break;
2779 }
2780 else
2781 {
2782 if (sync->fullscan)
2783 {
2784 // recursively delete all LocalNodes that were deleted (not moved or renamed!)
2785 sync->deletemissing(sync->localroot.get());
2786 sync->cachenodes();
2787 }
2788
2789 // if the directory events notification subsystem is permanently unavailable or
2790 // has signaled a temporary error, initiate a full rescan
2791 if (sync->state == SYNC_ACTIVE)
2792 {
2793 sync->fullscan = false;
2794
2795 string failedReason;
2796 auto failed = sync->dirnotify->getFailed(failedReason);
2797
2798 if (syncscanbt.armed()
2799 && (failed || fsaccess->notifyfailed
2800 || sync->dirnotify->mErrorCount.load() || fsaccess->notifyerr))
2801 {
2802 LOG_warn << "Sync scan failed " << failed
2803 << " " << fsaccess->notifyfailed
2804 << " " << sync->dirnotify->mErrorCount.load()
2805 << " " << fsaccess->notifyerr;
2806 if (failed)
2807 {
2808 LOG_warn << "The cause was: " << failedReason;
2809 }
2810 scanfailed = true;
2811
2812 sync->scan(&sync->localroot->localname, NULL);
2813 sync->dirnotify->mErrorCount = 0;
2814 sync->fullscan = true;
2815 sync->scanseqno++;
2816 }
2817 }
2818 }
2819 }
2820 }
2821
2822 if (scanfailed)
2823 {
2824 fsaccess->notifyerr = false;
2825 dstime backoff = 300 + totalnodes / 128;
2826 syncscanbt.backoff(backoff);
2827 syncscanfailed = true;
2828 LOG_warn << "Next full scan in " << backoff << " ds";
2829 }
2830
2831 // clear pending global notification error flag if all syncs were marked
2832 // to be rescanned
2833 if (fsaccess->notifyerr && it == syncs.end())
2834 {
2835 fsaccess->notifyerr = false;
2836 }
2837
2838 execsyncdeletions();
2839 }
2840 }
2841 }
2842 }
2843 }
2844 else
2845 {
2846 notifypurge();
2847
2848 // sync timer: retry syncdown() ops in case of local filesystem lock clashes
2849 if (syncdownretry && syncdownbt.armed())
2850 {
2851 syncdownretry = false;
2852 syncdownrequired = true;
2853 }
2854
2855 if (syncdownrequired)
2856 {
2857 syncdownrequired = false;
2858 if (!fetchingnodes)
2859 {
2860 LOG_verbose << "Running syncdown";
2861 bool success = true;
2862 for (it = syncs.begin(); it != syncs.end(); it++)
2863 {
2864 // make sure that the remote synced folder still exists
2865 if (!(*it)->localroot->node)
2866 {
2867 LOG_err << "The remote root node doesn't exist";
2868 (*it)->errorcode = API_ENOENT;
2869 (*it)->changestate(SYNC_FAILED);
2870 }
2871 else
2872 {
2873 LocalPath localpath = (*it)->localroot->localname;
2874 if ((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN)
2875 {
2876 LOG_debug << "Running syncdown on demand";
2877 if (!syncdown((*it)->localroot.get(), localpath, true))
2878 {
2879 // a local filesystem item was locked - schedule periodic retry
2880 // and force a full rescan afterwards as the local item may
2881 // be subject to changes that are notified with obsolete paths
2882 success = false;
2883 (*it)->dirnotify->mErrorCount = true;
2884 }
2885
2886 (*it)->cachenodes();
2887 }
2888 }
2889 }
2890
2891 // notify the app if a lock is being retried
2892 if (success)
2893 {
2894 syncuprequired = true;
2895 syncdownretry = false;
2896 syncactivity = true;
2897
2898 if (syncfsopsfailed)
2899 {
2900 syncfsopsfailed = false;
2901 app->syncupdate_local_lockretry(false);
2902 }
2903 }
2904 else
2905 {
2906 if (!syncfsopsfailed)
2907 {
2908 syncfsopsfailed = true;
2909 app->syncupdate_local_lockretry(true);
2910 }
2911
2912 syncdownretry = true;
2913 syncdownbt.backoff(50);
2914 }
2915 }
2916 else
2917 {
2918 LOG_err << "Syncdown requested while fetchingnodes is set";
2919 }
2920 }
2921 }
2922 #endif
2923
2924 notifypurge();
2925
2926 if (!badhostcs && badhosts.size() && btbadhost.armed())
2927 {
2928 // report hosts affected by failed requests
2929 LOG_debug << "Sending badhost report: " << badhosts;
2930 badhostcs = new HttpReq();
2931 badhostcs->posturl = APIURL;
2932 badhostcs->posturl.append("pf?h");
2933 badhostcs->outbuf = badhosts;
2934 badhostcs->type = REQ_JSON;
2935 badhostcs->post(this);
2936 badhosts.clear();
2937 }
2938
2939 if (!workinglockcs && requestLock && btworkinglock.armed())
2940 {
2941 LOG_debug << "Sending lock request";
2942 workinglockcs = new HttpReq();
2943 workinglockcs->posturl = APIURL;
2944 workinglockcs->posturl.append("cs?");
2945 workinglockcs->posturl.append(auth);
2946 workinglockcs->posturl.append("&wlt=1");
2947 workinglockcs->type = REQ_JSON;
2948 workinglockcs->post(this);
2949 }
2950
2951
2952 for (vector<TimerWithBackoff *>::iterator it = bttimers.begin(); it != bttimers.end(); )
2953 {
2954 TimerWithBackoff *bttimer = *it;
2955 if (bttimer->armed())
2956 {
2957 restag = bttimer->tag;
2958 app->timer_result(API_OK);
2959 delete bttimer;
2960 it = bttimers.erase(it);
2961 }
2962 else
2963 {
2964 ++it;
2965 }
2966 }
2967
2968 httpio->updatedownloadspeed();
2969 httpio->updateuploadspeed();
2970 } while (httpio->doio() || execdirectreads() || (!pendingcs && reqs.cmdspending() && btcs.armed()) || looprequested);
2971
2972
2973 NodeCounter storagesum;
2974 for (auto& nc : mNodeCounters)
2975 {
2976 if (nc.first == rootnodes[0] || nc.first == rootnodes[1] || nc.first == rootnodes[2])
2977 {
2978 storagesum += nc.second;
2979 }
2980 }
2981 if (mNotifiedSumSize != storagesum.storage)
2982 {
2983 mNotifiedSumSize = storagesum.storage;
2984 app->storagesum_changed(mNotifiedSumSize);
2985 }
2986
2987 #ifdef MEGA_MEASURE_CODE
2988 performanceStats.transfersActiveTime.start(!tslots.empty() && !performanceStats.transfersActiveTime.inprogress());
2989 performanceStats.transfersActiveTime.stop(tslots.empty() && performanceStats.transfersActiveTime.inprogress());
2990
2991 static auto lasttime = Waiter::ds;
2992 if (Waiter::ds > lasttime + 1200)
2993 {
2994 lasttime = Waiter::ds;
2995 LOG_info << performanceStats.report(false, httpio, waiter, reqs);
2996 }
2997 #endif
2998 }
2999
3000 // get next event time from all subsystems, then invoke the waiter if needed
3001 // returns true if an engine-relevant event has occurred, false otherwise
wait()3002 int MegaClient::wait()
3003 {
3004 int r = preparewait();
3005 if (r)
3006 {
3007 return r;
3008 }
3009 r |= dowait();
3010 r |= checkevents();
3011 return r;
3012 }
3013
preparewait()3014 int MegaClient::preparewait()
3015 {
3016 CodeCounter::ScopeTimer ccst(performanceStats.prepareWait);
3017
3018 dstime nds;
3019
3020 // get current dstime and clear wait events
3021 WAIT_CLASS::bumpds();
3022
3023 #ifdef ENABLE_SYNC
3024 // sync directory scans in progress or still processing sc packet without having
3025 // encountered a locally locked item? don't wait.
3026 if (syncactivity || syncdownrequired || (!scpaused && jsonsc.pos && (syncsup || !statecurrent) && !syncdownretry))
3027 {
3028 nds = Waiter::ds;
3029 }
3030 else
3031 #endif
3032 {
3033 // next retry of a failed transfer
3034 nds = NEVER;
3035
3036 if (httpio->success && chunkfailed)
3037 {
3038 // there is a pending transfer retry, don't wait
3039 nds = Waiter::ds;
3040 }
3041
3042 nexttransferretry(PUT, &nds);
3043 nexttransferretry(GET, &nds);
3044
3045 // retry transferslots
3046 transferSlotsBackoff.update(&nds, false);
3047
3048 for (pendinghttp_map::iterator it = pendinghttp.begin(); it != pendinghttp.end(); it++)
3049 {
3050 if (it->second->isbtactive)
3051 {
3052 it->second->bt.update(&nds);
3053 }
3054
3055 if (it->second->maxbt.nextset())
3056 {
3057 it->second->maxbt.update(&nds);
3058 }
3059 }
3060
3061 // retry failed client-server requests
3062 if (!pendingcs)
3063 {
3064 btcs.update(&nds);
3065 }
3066
3067 // retry failed server-client requests
3068 if (!pendingsc && !pendingscUserAlerts && scsn.ready() && !mBlocked)
3069 {
3070 btsc.update(&nds);
3071 }
3072
3073 // retry failed badhost requests
3074 if (!badhostcs && badhosts.size())
3075 {
3076 btbadhost.update(&nds);
3077 }
3078
3079 if (!workinglockcs && requestLock)
3080 {
3081 btworkinglock.update(&nds);
3082 }
3083
3084 for (vector<TimerWithBackoff *>::iterator cit = bttimers.begin(); cit != bttimers.end(); cit++)
3085 {
3086 (*cit)->update(&nds);
3087 }
3088
3089 // retry failed file attribute puts
3090 if (faretrying)
3091 {
3092 btpfa.update(&nds);
3093 }
3094
3095 // retry failed file attribute gets
3096 for (fafc_map::iterator cit = fafcs.begin(); cit != fafcs.end(); cit++)
3097 {
3098 if (cit->second->req.status == REQ_INFLIGHT)
3099 {
3100 cit->second->timeout.update(&nds);
3101 }
3102 else if (cit->second->fafs[1].size() || cit->second->fafs[0].size())
3103 {
3104 cit->second->bt.update(&nds);
3105 }
3106 }
3107
3108 // next pending pread event
3109 if (!dsdrns.empty())
3110 {
3111 if (dsdrns.begin()->first < nds)
3112 {
3113 if (dsdrns.begin()->first <= Waiter::ds)
3114 {
3115 nds = Waiter::ds;
3116 }
3117 else
3118 {
3119 nds = dsdrns.begin()->first;
3120 }
3121 }
3122 }
3123
3124 if (cachedug)
3125 {
3126 btugexpiration.update(&nds);
3127 }
3128
3129 #ifdef ENABLE_SYNC
3130 // sync rescan
3131 if (syncscanfailed)
3132 {
3133 syncscanbt.update(&nds);
3134 }
3135
3136 // retrying of transient failed read ops
3137 if (syncfslockretry && !syncdownretry && !syncadding
3138 && statecurrent && !syncdownrequired && !syncfsopsfailed)
3139 {
3140 LOG_debug << "Waiting for a temporary error checking filesystem notification";
3141 syncfslockretrybt.update(&nds);
3142 }
3143
3144 // retrying of transiently failed syncdown() updates
3145 if (syncdownretry)
3146 {
3147 syncdownbt.update(&nds);
3148 }
3149
3150 // triggering of Nagle-delayed sync PUTs
3151 if (syncnagleretry)
3152 {
3153 syncnaglebt.update(&nds);
3154 }
3155
3156 if (syncextraretry)
3157 {
3158 syncextrabt.update(&nds);
3159 }
3160 #endif
3161
3162 // detect stuck network
3163 if (EVER(httpio->lastdata) && !pendingcs)
3164 {
3165 dstime timeout = httpio->lastdata + HttpIO::NETWORKTIMEOUT;
3166
3167 if (timeout > Waiter::ds && timeout < nds)
3168 {
3169 nds = timeout;
3170 }
3171 else if (timeout <= Waiter::ds)
3172 {
3173 nds = 0;
3174 }
3175 }
3176
3177 if (pendingcs && EVER(pendingcs->lastdata))
3178 {
3179 if (EVER(disconnecttimestamp))
3180 {
3181 if (disconnecttimestamp > Waiter::ds && disconnecttimestamp < nds)
3182 {
3183 nds = disconnecttimestamp;
3184 }
3185 else if (disconnecttimestamp <= Waiter::ds)
3186 {
3187 nds = 0;
3188 }
3189 }
3190 else if (!requestLock && !fetchingnodes)
3191 {
3192 dstime timeout = pendingcs->lastdata + HttpIO::REQUESTTIMEOUT;
3193 if (timeout > Waiter::ds && timeout < nds)
3194 {
3195 nds = timeout;
3196 }
3197 else if (timeout <= Waiter::ds)
3198 {
3199 nds = 0;
3200 }
3201 }
3202 else if (workinglockcs && EVER(workinglockcs->lastdata)
3203 && workinglockcs->status == REQ_INFLIGHT)
3204 {
3205 dstime timeout = workinglockcs->lastdata + HttpIO::REQUESTTIMEOUT;
3206 if (timeout > Waiter::ds && timeout < nds)
3207 {
3208 nds = timeout;
3209 }
3210 else if (timeout <= Waiter::ds)
3211 {
3212 nds = 0;
3213 }
3214 }
3215 }
3216
3217
3218 if (badhostcs && EVER(badhostcs->lastdata)
3219 && badhostcs->status == REQ_INFLIGHT)
3220 {
3221 dstime timeout = badhostcs->lastdata + HttpIO::REQUESTTIMEOUT;
3222 if (timeout > Waiter::ds && timeout < nds)
3223 {
3224 nds = timeout;
3225 }
3226 else if (timeout <= Waiter::ds)
3227 {
3228 nds = 0;
3229 }
3230 }
3231
3232 if (!pendingscTimedOut && !jsonsc.pos && pendingsc && pendingsc->status == REQ_INFLIGHT)
3233 {
3234 dstime timeout = pendingsc->lastdata + HttpIO::SCREQUESTTIMEOUT;
3235 if (timeout > Waiter::ds && timeout < nds)
3236 {
3237 nds = timeout;
3238 }
3239 else if (timeout <= Waiter::ds)
3240 {
3241 nds = 0;
3242 }
3243 }
3244 }
3245
3246 // immediate action required?
3247 if (!nds)
3248 {
3249 ++performanceStats.prepwaitImmediate;
3250 return Waiter::NEEDEXEC;
3251 }
3252
3253 // nds is either MAX_INT (== no pending events) or > Waiter::ds
3254 if (EVER(nds))
3255 {
3256 nds -= Waiter::ds;
3257 }
3258
3259 #ifdef MEGA_MEASURE_CODE
3260 bool reasonGiven = false;
3261 if (nds == 0)
3262 {
3263 ++performanceStats.prepwaitZero;
3264 reasonGiven = true;
3265 }
3266 #endif
3267
3268 waiter->init(nds);
3269
3270 // set subsystem wakeup criteria (WinWaiter assumes httpio to be set first!)
3271 waiter->wakeupby(httpio, Waiter::NEEDEXEC);
3272
3273 #ifdef MEGA_MEASURE_CODE
3274 if (waiter->maxds == 0 && !reasonGiven)
3275 {
3276 ++performanceStats.prepwaitHttpio;
3277 reasonGiven = true;
3278 }
3279 #endif
3280
3281 waiter->wakeupby(fsaccess, Waiter::NEEDEXEC);
3282
3283 #ifdef MEGA_MEASURE_CODE
3284 if (waiter->maxds == 0 && !reasonGiven)
3285 {
3286 ++performanceStats.prepwaitFsaccess;
3287 reasonGiven = true;
3288 }
3289 if (!reasonGiven)
3290 {
3291 ++performanceStats.nonzeroWait;
3292 }
3293 #endif
3294
3295 return 0;
3296 }
3297
dowait()3298 int MegaClient::dowait()
3299 {
3300 CodeCounter::ScopeTimer ccst(performanceStats.doWait);
3301
3302 return waiter->wait();
3303 }
3304
checkevents()3305 int MegaClient::checkevents()
3306 {
3307 CodeCounter::ScopeTimer ccst(performanceStats.checkEvents);
3308
3309 int r = httpio->checkevents(waiter);
3310 r |= fsaccess->checkevents(waiter);
3311 if (gfx)
3312 {
3313 r |= gfx->checkevents(waiter);
3314 }
3315 return r;
3316 }
3317
3318 // reset all backoff timers and transfer retry counters
abortbackoff(bool includexfers)3319 bool MegaClient::abortbackoff(bool includexfers)
3320 {
3321 bool r = false;
3322
3323 WAIT_CLASS::bumpds();
3324
3325 if (includexfers)
3326 {
3327 overquotauntil = 0;
3328 if (ststatus != STORAGE_PAYWALL) // in ODQ Paywall, ULs/DLs are not allowed
3329 {
3330 // in ODQ Red, only ULs are disallowed
3331 int end = (ststatus != STORAGE_RED) ? PUT : GET;
3332 for (int d = GET; d <= end; d += PUT - GET)
3333 {
3334 for (transfer_map::iterator it = transfers[d].begin(); it != transfers[d].end(); it++)
3335 {
3336 if (it->second->bt.arm())
3337 {
3338 r = true;
3339 }
3340
3341 if (it->second->slot && it->second->slot->retrying)
3342 {
3343 if (it->second->slot->retrybt.arm())
3344 {
3345 r = true;
3346 }
3347 }
3348 }
3349 }
3350
3351 for (handledrn_map::iterator it = hdrns.begin(); it != hdrns.end();)
3352 {
3353 (it++)->second->retry(API_OK);
3354 }
3355 }
3356 }
3357
3358 for (pendinghttp_map::iterator it = pendinghttp.begin(); it != pendinghttp.end(); it++)
3359 {
3360 if (it->second->bt.arm())
3361 {
3362 r = true;
3363 }
3364 }
3365
3366 if (btcs.arm())
3367 {
3368 r = true;
3369 }
3370
3371 if (btbadhost.arm())
3372 {
3373 r = true;
3374 }
3375
3376 if (btworkinglock.arm())
3377 {
3378 r = true;
3379 }
3380
3381 if (!pendingsc && !pendingscUserAlerts && btsc.arm())
3382 {
3383 r = true;
3384 }
3385
3386 if (activefa.size() < MAXPUTFA && btpfa.arm())
3387 {
3388 r = true;
3389 }
3390
3391 for (fafc_map::iterator it = fafcs.begin(); it != fafcs.end(); it++)
3392 {
3393 if (it->second->req.status != REQ_INFLIGHT && it->second->bt.arm())
3394 {
3395 r = true;
3396 }
3397 }
3398
3399 return r;
3400 }
3401
3402 // activate enough queued transfers as necessary to keep the system busy - but not too busy
dispatchTransfers()3403 void MegaClient::dispatchTransfers()
3404 {
3405 // do we have any transfer slots available?
3406 if (!slotavail())
3407 {
3408 LOG_verbose << "No slots available";
3409 return;
3410 }
3411
3412 CodeCounter::ScopeTimer ccst(performanceStats.dispatchTransfers);
3413
3414 struct counter
3415 {
3416 m_off_t remainingsum = 0;
3417 unsigned total = 0;
3418 unsigned added = 0;
3419 bool hasVeryBig = false;
3420
3421 void addexisting(m_off_t size, m_off_t progressed)
3422 {
3423 remainingsum += size - progressed;
3424 total += 1;
3425 if (size > 100 * 1024 * 1024 && (size - progressed) > 5 * 1024 * 1024)
3426 {
3427 hasVeryBig = true;
3428 }
3429 }
3430 void addnew(m_off_t size)
3431 {
3432 addexisting(size, 0);
3433 added += 1;
3434 }
3435 };
3436 std::array<counter, 6> counters;
3437
3438 // Determine average speed and total amount of data remaining for the given direction/size-category
3439 // We prepare data for put/get in index 0..1, and the put/get/big/small combinations in index 2..5
3440 for (TransferSlot* ts : tslots)
3441 {
3442 assert(ts->transfer->type == PUT || ts->transfer->type == GET);
3443 TransferCategory tc(ts->transfer);
3444 counters[tc.index()].addexisting(ts->transfer->size, ts->progressreported);
3445 counters[tc.directionIndex()].addexisting(ts->transfer->size, ts->progressreported);
3446 }
3447
3448 std::function<bool(Transfer*)> testAddTransferFunction = [&counters, this](Transfer* t)
3449 {
3450 TransferCategory tc(t);
3451
3452 // hard limit on puts/gets
3453 if (counters[tc.directionIndex()].total >= MAXTRANSFERS)
3454 {
3455 return false;
3456 }
3457
3458 // only request half the max at most, to get a quicker response from the API and get overlap with transfers going
3459 if (counters[tc.directionIndex()].added >= MAXTRANSFERS/2)
3460 {
3461 return false;
3462 }
3463
3464 // If we have one very big file, that is enough to max out the bandwidth by itself; get that one done quickly (without preventing more small files).
3465 if (counters[tc.index()].hasVeryBig)
3466 {
3467 return false;
3468 }
3469
3470 // queue up enough transfers that we can expect to keep busy for at least the next 30 seconds in this category
3471 m_off_t speed = (tc.direction == GET) ? httpio->downloadSpeed : httpio->uploadSpeed;
3472 m_off_t targetOutstanding = 30 * speed;
3473 targetOutstanding = std::max<m_off_t>(targetOutstanding, 2 * 1024 * 1024);
3474 targetOutstanding = std::min<m_off_t>(targetOutstanding, 100 * 1024 * 1024);
3475
3476 if (counters[tc.index()].remainingsum >= targetOutstanding)
3477 {
3478 return false;
3479 }
3480
3481 counters[tc.index()].addnew(t->size);
3482 counters[tc.directionIndex()].addnew(t->size);
3483
3484 return true;
3485 };
3486
3487 std::array<vector<Transfer*>, 6> nextInCategory = transferlist.nexttransfers(testAddTransferFunction);
3488
3489 // Iterate the 4 combinations in this order:
3490 static const TransferCategory categoryOrder[] = {
3491 TransferCategory(PUT, LARGEFILE),
3492 TransferCategory(GET, LARGEFILE),
3493 TransferCategory(PUT, SMALLFILE),
3494 TransferCategory(GET, SMALLFILE),
3495 };
3496
3497 DBTableTransactionCommitter committer(tctable);
3498
3499 for (auto category : categoryOrder)
3500 {
3501 for (Transfer *nexttransfer : nextInCategory[category.index()])
3502 {
3503 if (!slotavail())
3504 {
3505 return;
3506 }
3507
3508 if (category.direction == PUT && queuedfa.size() > MAXQUEUEDFA)
3509 {
3510 // file attribute jam? halt uploads.
3511 LOG_warn << "Attribute queue full: " << queuedfa.size();
3512 break;
3513 }
3514
3515 if (nexttransfer->localfilename.empty())
3516 {
3517 // this is a fresh transfer rather than the resumption of a partly
3518 // completed and deferred one
3519 if (nexttransfer->type == PUT)
3520 {
3521 // generate fresh random encryption key/CTR IV for this file
3522 byte keyctriv[SymmCipher::KEYLENGTH + sizeof(int64_t)];
3523 rng.genblock(keyctriv, sizeof keyctriv);
3524 memcpy(nexttransfer->transferkey.data(), keyctriv, SymmCipher::KEYLENGTH);
3525 nexttransfer->ctriv = MemAccess::get<uint64_t>((const char*)keyctriv + SymmCipher::KEYLENGTH);
3526 }
3527 else
3528 {
3529 // set up keys for the decryption of this file (k == NULL => private node)
3530 const byte* k = NULL;
3531 bool missingPrivateNode = false;
3532
3533 // locate suitable template file
3534 for (file_list::iterator it = nexttransfer->files.begin(); it != nexttransfer->files.end(); it++)
3535 {
3536 if ((*it)->hprivate && !(*it)->hforeign)
3537 {
3538 // Make sure we have the size field
3539 Node* n = nodebyhandle((*it)->h);
3540 if (!n)
3541 {
3542 missingPrivateNode = true;
3543 }
3544 else if (n->type == FILENODE)
3545 {
3546 k = (const byte*)n->nodekey().data();
3547 nexttransfer->size = n->size;
3548 }
3549 }
3550 else
3551 {
3552 k = (*it)->filekey;
3553 nexttransfer->size = (*it)->size;
3554 }
3555
3556 if (k)
3557 {
3558 memcpy(nexttransfer->transferkey.data(), k, SymmCipher::KEYLENGTH);
3559 SymmCipher::xorblock(k + SymmCipher::KEYLENGTH, nexttransfer->transferkey.data());
3560 nexttransfer->ctriv = MemAccess::get<int64_t>((const char*)k + SymmCipher::KEYLENGTH);
3561 nexttransfer->metamac = MemAccess::get<int64_t>((const char*)k + SymmCipher::KEYLENGTH + sizeof(int64_t));
3562 break;
3563 }
3564 }
3565
3566 if (!k)
3567 {
3568 // there are no keys to decrypt this download - if it's because the node to download doesn't exist anymore, fail the transfer (otherwise wait for keys to become available)
3569 if (missingPrivateNode)
3570 {
3571 nexttransfer->failed(API_EARGS, committer);
3572 }
3573 continue;
3574 }
3575 }
3576
3577 nexttransfer->localfilename.clear();
3578
3579 // set file localnames (ultimate target) and one transfer-wide temp
3580 // localname
3581 for (file_list::iterator it = nexttransfer->files.begin();
3582 nexttransfer->localfilename.empty() && it != nexttransfer->files.end(); it++)
3583 {
3584 (*it)->prepare();
3585 }
3586
3587 // app-side transfer preparations (populate localname, create thumbnail...)
3588 app->transfer_prepare(nexttransfer);
3589 }
3590
3591 bool openok = false;
3592 bool openfinished = false;
3593
3594 // verify that a local path was given and start/resume transfer
3595 if (!nexttransfer->localfilename.empty())
3596 {
3597 TransferSlot *ts = nullptr;
3598
3599 if (!nexttransfer->slot)
3600 {
3601 // allocate transfer slot
3602 ts = new TransferSlot(nexttransfer);
3603 }
3604 else
3605 {
3606 ts = nexttransfer->slot;
3607 }
3608
3609 if (ts->fa->asyncavailable())
3610 {
3611 if (!nexttransfer->asyncopencontext)
3612 {
3613 LOG_debug << "Starting async open";
3614
3615 // try to open file (PUT transfers: open in nonblocking mode)
3616 nexttransfer->asyncopencontext = (nexttransfer->type == PUT)
3617 ? ts->fa->asyncfopen(nexttransfer->localfilename)
3618 : ts->fa->asyncfopen(nexttransfer->localfilename, false, true, nexttransfer->size);
3619 asyncfopens++;
3620 }
3621
3622 if (nexttransfer->asyncopencontext->finished)
3623 {
3624 LOG_debug << "Async open finished";
3625 openok = !nexttransfer->asyncopencontext->failed;
3626 openfinished = true;
3627 delete nexttransfer->asyncopencontext;
3628 nexttransfer->asyncopencontext = NULL;
3629 asyncfopens--;
3630 }
3631
3632 assert(!asyncfopens);
3633 //FIXME: Improve the management of asynchronous fopen when they can
3634 //be really asynchronous. All transfers could open its file in this
3635 //stage (not good) and, if we limit it, the transfer queue could hang because
3636 //it's full of transfers in that state. Transfer moves also complicates
3637 //the management because transfers that haven't been opened could be
3638 //placed over transfers that are already being opened.
3639 //Probably, the best approach is to add the slot of these transfers to
3640 //the queue and ensure that all operations (transfer moves, pauses)
3641 //are correctly cancelled when needed
3642 }
3643 else
3644 {
3645 // try to open file (PUT transfers: open in nonblocking mode)
3646 openok = (nexttransfer->type == PUT)
3647 ? ts->fa->fopen(nexttransfer->localfilename)
3648 : ts->fa->fopen(nexttransfer->localfilename, false, true);
3649 openfinished = true;
3650 }
3651
3652 if (openfinished && openok)
3653 {
3654 handle h = UNDEF;
3655 bool hprivate = true;
3656 const char *privauth = NULL;
3657 const char *pubauth = NULL;
3658 const char *chatauth = NULL;
3659
3660 nexttransfer->pos = 0;
3661 nexttransfer->progresscompleted = 0;
3662
3663 if (nexttransfer->type == GET || nexttransfer->tempurls.size())
3664 {
3665 m_off_t p = 0;
3666
3667 // resume at the end of the last contiguous completed block
3668 nexttransfer->chunkmacs.calcprogress(nexttransfer->size, nexttransfer->pos, nexttransfer->progresscompleted, &p);
3669
3670 if (nexttransfer->progresscompleted > nexttransfer->size)
3671 {
3672 LOG_err << "Invalid transfer progress!";
3673 nexttransfer->pos = nexttransfer->size;
3674 nexttransfer->progresscompleted = nexttransfer->size;
3675 }
3676
3677 ts->updatecontiguousprogress();
3678 LOG_debug << "Resuming transfer at " << nexttransfer->pos
3679 << " Completed: " << nexttransfer->progresscompleted
3680 << " Contiguous: " << ts->progresscontiguous
3681 << " Partial: " << p << " Size: " << nexttransfer->size
3682 << " ultoken: " << (nexttransfer->ultoken != NULL);
3683 }
3684 else
3685 {
3686 nexttransfer->chunkmacs.clear();
3687 }
3688
3689 ts->progressreported = nexttransfer->progresscompleted;
3690
3691 if (nexttransfer->type == PUT)
3692 {
3693 if (ts->fa->mtime != nexttransfer->mtime || ts->fa->size != nexttransfer->size)
3694 {
3695 LOG_warn << "Modification detected starting upload. Size: " << nexttransfer->size << " Mtime: " << nexttransfer->mtime
3696 << " FaSize: " << ts->fa->size << " FaMtime: " << ts->fa->mtime;
3697 nexttransfer->failed(API_EREAD, committer);
3698 continue;
3699 }
3700
3701 // create thumbnail/preview imagery, if applicable (FIXME: do not re-create upon restart)
3702 if (!nexttransfer->localfilename.empty() && !nexttransfer->uploadhandle)
3703 {
3704 nexttransfer->uploadhandle = getuploadhandle();
3705
3706 if (!gfxdisabled && gfx && gfx->isgfx(nexttransfer->localfilename.editStringDirect()))
3707 {
3708 // we want all imagery to be safely tucked away before completing the upload, so we bump minfa
3709 nexttransfer->minfa += gfx->gendimensionsputfa(ts->fa, nexttransfer->localfilename.editStringDirect(), nexttransfer->uploadhandle, nexttransfer->transfercipher(), -1, false);
3710 }
3711 }
3712 }
3713 else
3714 {
3715 for (file_list::iterator it = nexttransfer->files.begin();
3716 it != nexttransfer->files.end(); it++)
3717 {
3718 if (!(*it)->hprivate || (*it)->hforeign || nodebyhandle((*it)->h))
3719 {
3720 h = (*it)->h;
3721 hprivate = (*it)->hprivate;
3722 privauth = (*it)->privauth.size() ? (*it)->privauth.c_str() : NULL;
3723 pubauth = (*it)->pubauth.size() ? (*it)->pubauth.c_str() : NULL;
3724 chatauth = (*it)->chatauth;
3725 break;
3726 }
3727 else
3728 {
3729 LOG_err << "Unexpected node ownership";
3730 }
3731 }
3732 }
3733
3734 // dispatch request for temporary source/target URL
3735 if (nexttransfer->tempurls.size())
3736 {
3737 ts->transferbuf.setIsRaid(nexttransfer, nexttransfer->tempurls, nexttransfer->pos, ts->maxRequestSize);
3738 app->transfer_prepare(nexttransfer);
3739 }
3740 else
3741 {
3742 reqs.add((ts->pendingcmd = (nexttransfer->type == PUT)
3743 ? (Command*)new CommandPutFile(this, ts, putmbpscap)
3744 : (Command*)new CommandGetFile(this, ts, NULL, h, hprivate, privauth, pubauth, chatauth)));
3745 }
3746
3747 LOG_debug << "Activating transfer";
3748 ts->slots_it = tslots.insert(tslots.begin(), ts);
3749
3750 // notify the app about the starting transfer
3751 for (file_list::iterator it = nexttransfer->files.begin();
3752 it != nexttransfer->files.end(); it++)
3753 {
3754 (*it)->start();
3755 }
3756 app->transfer_update(nexttransfer);
3757
3758 performanceStats.transferStarts += 1;
3759 }
3760 else if (openfinished)
3761 {
3762 string utf8path = nexttransfer->localfilename.toPath(*fsaccess);
3763 if (nexttransfer->type == GET)
3764 {
3765 LOG_err << "Error dispatching transfer. Temporary file not writable: " << utf8path;
3766 nexttransfer->failed(API_EWRITE, committer);
3767 }
3768 else if (!ts->fa->retry)
3769 {
3770 LOG_err << "Error dispatching transfer. Local file permanently unavailable: " << utf8path;
3771 nexttransfer->failed(API_EREAD, committer);
3772 }
3773 else
3774 {
3775 LOG_warn << "Error dispatching transfer. Local file temporarily unavailable: " << utf8path;
3776 nexttransfer->failed(API_EREAD, committer);
3777 }
3778 }
3779 }
3780 else
3781 {
3782 LOG_err << "Error preparing transfer. No localfilename";
3783 nexttransfer->failed(API_EREAD, committer);
3784 }
3785 }
3786 }
3787 }
3788
3789 // generate upload handle for this upload
3790 // (after 65536 uploads, a node handle clash is possible, but far too unlikely
3791 // to be of real-world concern)
getuploadhandle()3792 handle MegaClient::getuploadhandle()
3793 {
3794 byte* ptr = (byte*)(&nextuh + 1);
3795
3796 while (!++*--ptr);
3797
3798 return nextuh;
3799 }
3800
3801 // do we have an upload that is still waiting for file attributes before being completed?
checkfacompletion(handle th,Transfer * t)3802 void MegaClient::checkfacompletion(handle th, Transfer* t)
3803 {
3804 if (th)
3805 {
3806 bool delayedcompletion;
3807 handletransfer_map::iterator htit;
3808
3809 if ((delayedcompletion = !t))
3810 {
3811 // abort if upload still running
3812 if ((htit = faputcompletion.find(th)) == faputcompletion.end())
3813 {
3814 LOG_debug << "Upload still running checking a file attribute - " << th;
3815 return;
3816 }
3817
3818 t = htit->second;
3819 }
3820
3821 int facount = 0;
3822
3823 // do we have the pre-set threshold number of file attributes available? complete upload.
3824 for (fa_map::iterator it = pendingfa.lower_bound(pair<handle, fatype>(th, fatype(0)));
3825 it != pendingfa.end() && it->first.first == th; it++)
3826 {
3827 facount++;
3828 }
3829
3830 if (facount < t->minfa)
3831 {
3832 LOG_debug << "Pending file attributes for upload - " << th << " : " << (t->minfa < facount);
3833 if (!delayedcompletion)
3834 {
3835 // we have insufficient file attributes available: remove transfer and put on hold
3836 t->faputcompletion_it = faputcompletion.insert(pair<handle, Transfer*>(th, t)).first;
3837
3838 transfers[t->type].erase(t->transfers_it);
3839 t->transfers_it = transfers[t->type].end();
3840
3841 delete t->slot;
3842 t->slot = NULL;
3843
3844 LOG_debug << "Transfer put on hold. Total: " << faputcompletion.size();
3845 }
3846
3847 return;
3848 }
3849 }
3850 else
3851 {
3852 LOG_warn << "NULL file attribute handle";
3853 }
3854
3855 LOG_debug << "Transfer finished, sending callbacks - " << th;
3856 t->state = TRANSFERSTATE_COMPLETED;
3857 t->completefiles();
3858 looprequested = true;
3859 app->transfer_complete(t);
3860 delete t;
3861 }
3862
3863 // clear transfer queue
freeq(direction_t d)3864 void MegaClient::freeq(direction_t d)
3865 {
3866 DBTableTransactionCommitter committer(tctable);
3867 for (auto transferPtr : transfers[d])
3868 {
3869 transferPtr.second->mOptimizedDelete = true; // so it doesn't remove itself from this list while deleting
3870 app->transfer_removed(transferPtr.second);
3871 delete transferPtr.second;
3872 }
3873 transfers[d].clear();
3874 transferlist.transfers[GET].clear();
3875 transferlist.transfers[PUT].clear();
3876 }
3877
isFetchingNodesPendingCS()3878 bool MegaClient::isFetchingNodesPendingCS()
3879 {
3880 return pendingcs && pendingcs->includesFetchingNodes;
3881 }
3882
3883 #ifdef ENABLE_SYNC
resumeResumableSyncs()3884 void MegaClient::resumeResumableSyncs()
3885 {
3886
3887 if (!syncConfigs || !allowAutoResumeSyncs)
3888 {
3889 return;
3890 }
3891 for (const auto& config : syncConfigs->all())
3892 {
3893 if (!config.isResumable())
3894 {
3895 continue;
3896 }
3897 const auto e = addsync(config, DEBRISFOLDER, nullptr);
3898 if (e == 0)
3899 {
3900 app->sync_auto_resumed(config.getLocalPath(), config.getRemoteNode(),
3901 static_cast<long long>(config.getLocalFingerprint()),
3902 config.getRegExps());
3903 }
3904 }
3905 }
3906 #endif
3907 // determine next scheduled transfer retry
nexttransferretry(direction_t d,dstime * dsmin)3908 void MegaClient::nexttransferretry(direction_t d, dstime* dsmin)
3909 {
3910 if (!xferpaused[d]) // avoid setting the timer's next=1 if it won't be processed
3911 {
3912 transferRetryBackoffs[d].update(dsmin, true);
3913 }
3914 }
3915
3916 // disconnect all HTTP connections (slows down operations, but is semantically neutral)
disconnect()3917 void MegaClient::disconnect()
3918 {
3919 if (pendingcs)
3920 {
3921 app->request_response_progress(-1, -1);
3922 pendingcs->disconnect();
3923 }
3924
3925 if (pendingsc)
3926 {
3927 pendingsc->disconnect();
3928 }
3929
3930 if (pendingscUserAlerts)
3931 {
3932 pendingscUserAlerts->disconnect();
3933 }
3934
3935 abortlockrequest();
3936
3937 for (pendinghttp_map::iterator it = pendinghttp.begin(); it != pendinghttp.end(); it++)
3938 {
3939 it->second->disconnect();
3940 }
3941
3942 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); it++)
3943 {
3944 (*it)->disconnect();
3945 }
3946
3947 for (handledrn_map::iterator it = hdrns.begin(); it != hdrns.end();)
3948 {
3949 (it++)->second->retry(API_OK);
3950 }
3951
3952 for (putfa_list::iterator it = activefa.begin(); it != activefa.end(); it++)
3953 {
3954 (*it)->disconnect();
3955 }
3956
3957 for (fafc_map::iterator it = fafcs.begin(); it != fafcs.end(); it++)
3958 {
3959 it->second->req.disconnect();
3960 }
3961
3962 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); it++)
3963 {
3964 (*it)->errorcount = 0;
3965 }
3966
3967 if (badhostcs)
3968 {
3969 badhostcs->disconnect();
3970 }
3971
3972 httpio->lastdata = NEVER;
3973 httpio->disconnect();
3974
3975 app->notify_disconnect();
3976 }
3977
3978 // force retrieval of pending actionpackets immediately
3979 // by closing pending sc, reset backoff and clear waitd URL
catchup()3980 void MegaClient::catchup()
3981 {
3982 if (pendingsc)
3983 {
3984 pendingsc->disconnect();
3985
3986 pendingsc.reset();
3987 }
3988 btsc.reset();
3989 scnotifyurl.clear();
3990 }
3991
abortlockrequest()3992 void MegaClient::abortlockrequest()
3993 {
3994 delete workinglockcs;
3995 workinglockcs = NULL;
3996 btworkinglock.reset();
3997 requestLock = false;
3998 disconnecttimestamp = NEVER;
3999 }
4000
logout()4001 void MegaClient::logout()
4002 {
4003 if (loggedin() != FULLACCOUNT)
4004 {
4005 locallogout(true);
4006
4007 restag = reqtag;
4008 app->logout_result(API_OK);
4009 return;
4010 }
4011
4012 loggingout++;
4013 reqs.add(new CommandLogout(this));
4014 }
4015
locallogout(bool removecaches)4016 void MegaClient::locallogout(bool removecaches)
4017 {
4018 mAsyncQueue.clearDiscardable();
4019
4020 if (removecaches)
4021 {
4022 removeCaches();
4023 }
4024
4025 delete sctable;
4026 sctable = NULL;
4027 pendingsccommit = false;
4028
4029 me = UNDEF;
4030 uid.clear();
4031 unshareablekey.clear();
4032 publichandle = UNDEF;
4033 cachedscsn = UNDEF;
4034 achievements_enabled = false;
4035 isNewSession = false;
4036 tsLogin = 0;
4037 versions_disabled = false;
4038 accountsince = 0;
4039 gmfa_enabled = false;
4040 ssrs_enabled = false;
4041 nsr_enabled = false;
4042 aplvp_enabled = false;
4043 mNewLinkFormat = false;
4044 mSmsVerificationState = SMS_STATE_UNKNOWN;
4045 mSmsVerifiedPhone.clear();
4046 loggingout = 0;
4047 loggedout = false;
4048 cachedug = false;
4049 minstreamingrate = -1;
4050 ephemeralSession = false;
4051 #ifdef USE_MEDIAINFO
4052 mediaFileInfo = MediaFileInfo();
4053 #endif
4054
4055 // remove any cached transfers older than two days that have not been resumed (updates transfer list)
4056 purgeOrphanTransfers();
4057
4058 // delete all remaining transfers (optimized not to remove from transfer list one by one)
4059 // transfer destructors update the transfer in the cache database
4060 freeq(GET);
4061 freeq(PUT);
4062
4063 // close the transfer cache database.
4064 disconnect();
4065 closetc();
4066
4067 freeq(GET); // freeq after closetc due to optimizations
4068 freeq(PUT);
4069
4070 purgenodesusersabortsc(false);
4071
4072 reqs.clear();
4073
4074 delete pendingcs;
4075 pendingcs = NULL;
4076 scsn.clear();
4077 mBlocked = false;
4078
4079 for (putfa_list::iterator it = queuedfa.begin(); it != queuedfa.end(); it++)
4080 {
4081 delete *it;
4082 }
4083
4084 for (putfa_list::iterator it = activefa.begin(); it != activefa.end(); it++)
4085 {
4086 delete *it;
4087 }
4088
4089 for (pendinghttp_map::iterator it = pendinghttp.begin(); it != pendinghttp.end(); it++)
4090 {
4091 delete it->second;
4092 }
4093
4094 for (vector<TimerWithBackoff *>::iterator it = bttimers.begin(); it != bttimers.end(); it++)
4095 {
4096 delete *it;
4097 }
4098
4099 queuedfa.clear();
4100 activefa.clear();
4101 pendinghttp.clear();
4102 bttimers.clear();
4103 xferpaused[PUT] = false;
4104 xferpaused[GET] = false;
4105 putmbpscap = 0;
4106 fetchingnodes = false;
4107 fetchnodestag = 0;
4108 ststatus = STORAGE_UNKNOWN;
4109 overquotauntil = 0;
4110 mOverquotaDeadlineTs = 0;
4111 mOverquotaWarningTs.clear();
4112 mBizGracePeriodTs = 0;
4113 mBizExpirationTs = 0;
4114 mBizMode = BIZ_MODE_UNKNOWN;
4115 mBizStatus = BIZ_STATUS_UNKNOWN;
4116 mBizMasters.clear();
4117 mPublicLinks.clear();
4118 scpaused = false;
4119
4120 for (fafc_map::iterator cit = fafcs.begin(); cit != fafcs.end(); cit++)
4121 {
4122 for (int i = 2; i--; )
4123 {
4124 for (faf_map::iterator it = cit->second->fafs[i].begin(); it != cit->second->fafs[i].end(); it++)
4125 {
4126 delete it->second;
4127 }
4128 }
4129
4130 delete cit->second;
4131 }
4132
4133 fafcs.clear();
4134
4135 pendingfa.clear();
4136
4137 // erase keys & session ID
4138 resetKeyring();
4139
4140 key.setkey(SymmCipher::zeroiv);
4141 tckey.setkey(SymmCipher::zeroiv);
4142 asymkey.resetkey();
4143 mPrivKey.clear();
4144 pubk.resetkey();
4145 resetKeyring();
4146 memset((char*)auth.c_str(), 0, auth.size());
4147 auth.clear();
4148 sessionkey.clear();
4149 accountversion = 0;
4150 accountsalt.clear();
4151 sid.clear();
4152 k.clear();
4153
4154 mAuthRings.clear();
4155 mAuthRingsTemp.clear();
4156 mFetchingAuthrings = false;
4157
4158 init();
4159
4160 if (dbaccess)
4161 {
4162 dbaccess->currentDbVersion = DbAccess::LEGACY_DB_VERSION;
4163 }
4164
4165 #ifdef ENABLE_SYNC
4166 syncadding = 0;
4167 totalLocalNodes = 0;
4168 #endif
4169
4170 fetchingkeys = false;
4171 }
4172
removeCaches()4173 void MegaClient::removeCaches()
4174 {
4175 if (sctable)
4176 {
4177 sctable->remove();
4178 delete sctable;
4179 sctable = NULL;
4180 pendingsccommit = false;
4181 }
4182
4183 #ifdef ENABLE_SYNC
4184 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
4185 {
4186 if ((*it)->statecachetable)
4187 {
4188 (*it)->statecachetable->remove();
4189 delete (*it)->statecachetable;
4190 (*it)->statecachetable = NULL;
4191 }
4192 }
4193 if (syncConfigs)
4194 {
4195 syncConfigs->clear();
4196 }
4197 #endif
4198
4199 disabletransferresumption();
4200 }
4201
version()4202 const char *MegaClient::version()
4203 {
4204 return TOSTRING(MEGA_MAJOR_VERSION)
4205 "." TOSTRING(MEGA_MINOR_VERSION)
4206 "." TOSTRING(MEGA_MICRO_VERSION);
4207 }
4208
getlastversion(const char * appKey)4209 void MegaClient::getlastversion(const char *appKey)
4210 {
4211 reqs.add(new CommandGetVersion(this, appKey));
4212 }
4213
getlocalsslcertificate()4214 void MegaClient::getlocalsslcertificate()
4215 {
4216 reqs.add(new CommandGetLocalSSLCertificate(this));
4217 }
4218
dnsrequest(const char * hostname)4219 void MegaClient::dnsrequest(const char *hostname)
4220 {
4221 GenericHttpReq *req = new GenericHttpReq(rng);
4222 req->tag = reqtag;
4223 req->maxretries = 0;
4224 pendinghttp[reqtag] = req;
4225 req->posturl = (usehttps ? string("https://") : string("http://")) + hostname;
4226 req->dns(this);
4227 }
4228
gelbrequest(const char * service,int timeoutds,int retries)4229 void MegaClient::gelbrequest(const char *service, int timeoutds, int retries)
4230 {
4231 GenericHttpReq *req = new GenericHttpReq(rng);
4232 req->tag = reqtag;
4233 req->maxretries = retries;
4234 if (timeoutds > 0)
4235 {
4236 req->maxbt.backoff(timeoutds);
4237 }
4238 pendinghttp[reqtag] = req;
4239 req->posturl = GELBURL;
4240 req->posturl.append("?service=");
4241 req->posturl.append(service);
4242 req->protect = true;
4243 req->get(this);
4244 }
4245
sendchatstats(const char * json,int port)4246 void MegaClient::sendchatstats(const char *json, int port)
4247 {
4248 GenericHttpReq *req = new GenericHttpReq(rng);
4249 req->tag = reqtag;
4250 req->maxretries = 0;
4251 pendinghttp[reqtag] = req;
4252 req->posturl = CHATSTATSURL;
4253 if (port > 0)
4254 {
4255 req->posturl.append(":");
4256 char stringPort[6];
4257 sprintf(stringPort, "%d", port);
4258 req->posturl.append(stringPort);
4259 }
4260 req->posturl.append("/stats");
4261 req->protect = true;
4262 req->out->assign(json);
4263 req->post(this);
4264 }
4265
sendchatlogs(const char * json,const char * aid,int port)4266 void MegaClient::sendchatlogs(const char *json, const char *aid, int port)
4267 {
4268 GenericHttpReq *req = new GenericHttpReq(rng);
4269 req->tag = reqtag;
4270 req->maxretries = 0;
4271 pendinghttp[reqtag] = req;
4272 req->posturl = CHATSTATSURL;
4273 if (port > 0)
4274 {
4275 req->posturl.append(":");
4276 char stringPort[6];
4277 sprintf(stringPort, "%d", port);
4278 req->posturl.append(stringPort);
4279 }
4280 req->posturl.append("/msglog?aid=");
4281 req->posturl.append(aid);
4282 req->posturl.append("&t=e");
4283 req->protect = true;
4284 req->out->assign(json);
4285 req->post(this);
4286 }
4287
httprequest(const char * url,int method,bool binary,const char * json,int retries)4288 void MegaClient::httprequest(const char *url, int method, bool binary, const char *json, int retries)
4289 {
4290 GenericHttpReq *req = new GenericHttpReq(rng, binary);
4291 req->tag = reqtag;
4292 req->maxretries = retries;
4293 pendinghttp[reqtag] = req;
4294 if (method == METHOD_GET)
4295 {
4296 req->posturl = url;
4297 req->get(this);
4298 }
4299 else
4300 {
4301 req->posturl = url;
4302 if (json)
4303 {
4304 req->out->assign(json);
4305 }
4306 req->post(this);
4307 }
4308 }
4309
4310 // process server-client request
procsc()4311 bool MegaClient::procsc()
4312 {
4313 CodeCounter::ScopeTimer ccst(performanceStats.scProcessingTime);
4314
4315 nameid name;
4316
4317 #ifdef ENABLE_SYNC
4318 char test[] = "},{\"a\":\"t\",\"i\":\"";
4319 char test2[32] = "\",\"t\":{\"f\":[{\"h\":\"";
4320 bool stop = false;
4321 bool newnodes = false;
4322 #endif
4323 Node* dn = NULL;
4324
4325 for (;;)
4326 {
4327 if (!insca)
4328 {
4329 switch (jsonsc.getnameid())
4330 {
4331 case 'w':
4332 jsonsc.storeobject(&scnotifyurl);
4333 break;
4334
4335 case MAKENAMEID2('i', 'r'):
4336 // when spoonfeeding is in action, there may still be more actionpackets to be delivered.
4337 insca_notlast = jsonsc.getint() == 1;
4338 break;
4339
4340 case MAKENAMEID2('s', 'n'):
4341 // the sn element is guaranteed to be the last in sequence (except for notification requests (c=50))
4342 scsn.setScsn(&jsonsc);
4343 notifypurge();
4344 if (sctable)
4345 {
4346 if (!pendingcs && !csretrying && !reqs.cmdspending())
4347 {
4348 sctable->commit();
4349 sctable->begin();
4350 app->notify_dbcommit();
4351 pendingsccommit = false;
4352 }
4353 else
4354 {
4355 LOG_debug << "Postponing DB commit until cs requests finish";
4356 pendingsccommit = true;
4357 }
4358 }
4359 break;
4360
4361 case EOO:
4362 LOG_debug << "Processing of action packets finished. More to follow: " << insca_notlast;
4363 mergenewshares(1);
4364 applykeys();
4365
4366 if (!statecurrent && !insca_notlast) // with actionpacket spoonfeeding, just finishing a batch does not mean we are up to date yet - keep going while "ir":1
4367 {
4368 if (fetchingnodes)
4369 {
4370 notifypurge();
4371 if (sctable)
4372 {
4373 sctable->commit();
4374 sctable->begin();
4375 pendingsccommit = false;
4376 }
4377
4378 WAIT_CLASS::bumpds();
4379 fnstats.timeToResult = Waiter::ds - fnstats.startTime;
4380 fnstats.timeToCurrent = fnstats.timeToResult;
4381
4382 fetchingnodes = false;
4383 restag = fetchnodestag;
4384 fetchnodestag = 0;
4385 #ifdef ENABLE_SYNC
4386 resumeResumableSyncs();
4387 #endif
4388
4389 app->fetchnodes_result(API_OK);
4390 app->notify_dbcommit();
4391
4392 WAIT_CLASS::bumpds();
4393 fnstats.timeToSyncsResumed = Waiter::ds - fnstats.startTime;
4394 }
4395 else
4396 {
4397 WAIT_CLASS::bumpds();
4398 fnstats.timeToCurrent = Waiter::ds - fnstats.startTime;
4399 }
4400 fnstats.nodesCurrent = nodes.size();
4401
4402 statecurrent = true;
4403 app->nodes_current();
4404 LOG_debug << "Local filesystem up to date";
4405
4406 if (notifyStorageChangeOnStateCurrent)
4407 {
4408 app->notify_storage(STORAGE_CHANGE);
4409 notifyStorageChangeOnStateCurrent = false;
4410 }
4411
4412 if (tctable && cachedfiles.size())
4413 {
4414 DBTableTransactionCommitter committer(tctable);
4415 for (unsigned int i = 0; i < cachedfiles.size(); i++)
4416 {
4417 direction_t type = NONE;
4418 File *file = app->file_resume(&cachedfiles.at(i), &type);
4419 if (!file || (type != GET && type != PUT))
4420 {
4421 tctable->del(cachedfilesdbids.at(i));
4422 continue;
4423 }
4424 nextreqtag();
4425 file->dbid = cachedfilesdbids.at(i);
4426 if (!startxfer(type, file, committer))
4427 {
4428 tctable->del(cachedfilesdbids.at(i));
4429 continue;
4430 }
4431 }
4432 cachedfiles.clear();
4433 cachedfilesdbids.clear();
4434 }
4435
4436 WAIT_CLASS::bumpds();
4437 fnstats.timeToTransfersResumed = Waiter::ds - fnstats.startTime;
4438
4439 string report;
4440 fnstats.toJsonArray(&report);
4441
4442 sendevent(99426, report.c_str(), 0); // Treeproc performance log
4443
4444 // NULL vector: "notify all elements"
4445 app->nodes_updated(NULL, int(nodes.size()));
4446 app->users_updated(NULL, int(users.size()));
4447 app->pcrs_updated(NULL, int(pcrindex.size()));
4448 #ifdef ENABLE_CHAT
4449 app->chats_updated(NULL, int(chats.size()));
4450 #endif
4451 for (node_map::iterator it = nodes.begin(); it != nodes.end(); it++)
4452 {
4453 memset(&(it->second->changed), 0, sizeof it->second->changed);
4454 }
4455
4456 if (!loggedinfolderlink())
4457 {
4458 // historic user alerts are not supported for public folders
4459 // now that we have loaded cached state, and caught up actionpackets since that state
4460 // (or just fetched everything if there was no cache), our next sc request can be for useralerts
4461 useralerts.begincatchup = true;
4462 }
4463 }
4464
4465 if (!insca_notlast)
4466 {
4467 app->catchup_result();
4468 }
4469 return true;
4470
4471 case 'a':
4472 if (jsonsc.enterarray())
4473 {
4474 LOG_debug << "Processing action packets";
4475 insca = true;
4476 break;
4477 }
4478 // fall through
4479 default:
4480 if (!jsonsc.storeobject())
4481 {
4482 LOG_err << "Error parsing sc request";
4483 return true;
4484 }
4485 }
4486 }
4487
4488 if (insca)
4489 {
4490 if (jsonsc.enterobject())
4491 {
4492 // the "a" attribute is guaranteed to be the first in the object
4493 if (jsonsc.getnameid() == 'a')
4494 {
4495 if (!statecurrent)
4496 {
4497 fnstats.actionPackets++;
4498 }
4499
4500 name = jsonsc.getnameid();
4501
4502 // only process server-client request if not marked as
4503 // self-originating ("i" marker element guaranteed to be following
4504 // "a" element if present)
4505 if (fetchingnodes || memcmp(jsonsc.pos, "\"i\":\"", 5)
4506 || memcmp(jsonsc.pos + 5, sessionid, sizeof sessionid)
4507 || jsonsc.pos[5 + sizeof sessionid] != '"')
4508 {
4509 #ifdef ENABLE_CHAT
4510 bool readingPublicChat = false;
4511 #endif
4512 switch (name)
4513 {
4514 case 'u':
4515 // node update
4516 sc_updatenode();
4517 #ifdef ENABLE_SYNC
4518 if (!fetchingnodes)
4519 {
4520 // run syncdown() before continuing
4521 applykeys();
4522 return false;
4523 }
4524 #endif
4525 break;
4526
4527 case 't':
4528 #ifdef ENABLE_SYNC
4529 if (!fetchingnodes && !stop)
4530 {
4531 for (int i=4; jsonsc.pos[i] && jsonsc.pos[i] != ']'; i++)
4532 {
4533 if (!memcmp(&jsonsc.pos[i-4], "\"t\":1", 5))
4534 {
4535 stop = true;
4536 break;
4537 }
4538 }
4539 }
4540 #endif
4541
4542 // node addition
4543 {
4544 useralerts.beginNotingSharedNodes();
4545 handle originatingUser = sc_newnodes();
4546 mergenewshares(1);
4547 useralerts.convertNotedSharedNodes(true, originatingUser);
4548 }
4549
4550 #ifdef ENABLE_SYNC
4551 if (!fetchingnodes)
4552 {
4553 if (stop)
4554 {
4555 // run syncdown() before continuing
4556 applykeys();
4557 return false;
4558 }
4559 else
4560 {
4561 newnodes = true;
4562 }
4563 }
4564 #endif
4565 break;
4566
4567 case 'd':
4568 // node deletion
4569 dn = sc_deltree();
4570
4571 #ifdef ENABLE_SYNC
4572 if (fetchingnodes)
4573 {
4574 break;
4575 }
4576
4577 if (dn && !memcmp(jsonsc.pos, test, 16))
4578 {
4579 Base64::btoa((byte *)&dn->nodehandle, sizeof(dn->nodehandle), &test2[18]);
4580 if (!memcmp(&jsonsc.pos[26], test2, 26))
4581 {
4582 // it's a move operation, stop parsing after completing it
4583 stop = true;
4584 break;
4585 }
4586 }
4587
4588 // run syncdown() to process the deletion before continuing
4589 applykeys();
4590 return false;
4591 #endif
4592 break;
4593
4594 case 's':
4595 case MAKENAMEID2('s', '2'):
4596 // share addition/update/revocation
4597 if (sc_shares())
4598 {
4599 int creqtag = reqtag;
4600 reqtag = 0;
4601 mergenewshares(1);
4602 reqtag = creqtag;
4603 }
4604 break;
4605
4606 case 'c':
4607 // contact addition/update
4608 sc_contacts();
4609 break;
4610
4611 case 'k':
4612 // crypto key request
4613 sc_keys();
4614 break;
4615
4616 case MAKENAMEID2('f', 'a'):
4617 // file attribute update
4618 sc_fileattr();
4619 break;
4620
4621 case MAKENAMEID2('u', 'a'):
4622 // user attribute update
4623 sc_userattr();
4624 break;
4625
4626 case MAKENAMEID4('p', 's', 't', 's'):
4627 if (sc_upgrade())
4628 {
4629 app->account_updated();
4630 abortbackoff(true);
4631 }
4632 break;
4633
4634 case MAKENAMEID4('p', 's', 'e', 's'):
4635 sc_paymentreminder();
4636 break;
4637
4638 case MAKENAMEID3('i', 'p', 'c'):
4639 // incoming pending contact request (to us)
4640 sc_ipc();
4641 break;
4642
4643 case MAKENAMEID3('o', 'p', 'c'):
4644 // outgoing pending contact request (from us)
4645 sc_opc();
4646 break;
4647
4648 case MAKENAMEID4('u', 'p', 'c', 'i'):
4649 // incoming pending contact request update (accept/deny/ignore)
4650 sc_upc(true);
4651 break;
4652
4653 case MAKENAMEID4('u', 'p', 'c', 'o'):
4654 // outgoing pending contact request update (from them, accept/deny/ignore)
4655 sc_upc(false);
4656 break;
4657
4658 case MAKENAMEID2('p','h'):
4659 // public links handles
4660 sc_ph();
4661 break;
4662
4663 case MAKENAMEID2('s','e'):
4664 // set email
4665 sc_se();
4666 break;
4667 #ifdef ENABLE_CHAT
4668 case MAKENAMEID4('m', 'c', 'p', 'c'): // fall-through
4669 {
4670 readingPublicChat = true;
4671 }
4672 case MAKENAMEID3('m', 'c', 'c'):
4673 // chat creation / peer's invitation / peer's removal
4674 sc_chatupdate(readingPublicChat);
4675 break;
4676
4677 case MAKENAMEID5('m', 'c', 'f', 'p', 'c'): // fall-through
4678 case MAKENAMEID4('m', 'c', 'f', 'c'):
4679 // chat flags update
4680 sc_chatflags();
4681 break;
4682
4683 case MAKENAMEID5('m', 'c', 'p', 'n', 'a'): // fall-through
4684 case MAKENAMEID4('m', 'c', 'n', 'a'):
4685 // granted / revoked access to a node
4686 sc_chatnode();
4687 break;
4688 #endif
4689 case MAKENAMEID3('u', 'a', 'c'):
4690 sc_uac();
4691 break;
4692
4693 case MAKENAMEID2('l', 'a'):
4694 // last acknowledged
4695 sc_la();
4696 break;
4697
4698 case MAKENAMEID2('u', 'b'):
4699 // business account update
4700 sc_ub();
4701 break;
4702 }
4703 }
4704 }
4705
4706 jsonsc.leaveobject();
4707 }
4708 else
4709 {
4710 jsonsc.leavearray();
4711 insca = false;
4712
4713 #ifdef ENABLE_SYNC
4714 if (!fetchingnodes && newnodes)
4715 {
4716 applykeys();
4717 return false;
4718 }
4719 #endif
4720 }
4721 }
4722 }
4723 }
4724
4725 // update the user's local state cache, on completion of the fetchnodes command
4726 // (note that if immediate-completion commands have been issued in the
4727 // meantime, the state of the affected nodes
4728 // may be ahead of the recorded scsn - their consistency will be checked by
4729 // subsequent server-client commands.)
4730 // initsc() is called after all initial decryption has been performed, so we
4731 // are tolerant towards incomplete/faulty nodes.
initsc()4732 void MegaClient::initsc()
4733 {
4734 if (sctable)
4735 {
4736 bool complete;
4737
4738 sctable->begin();
4739 sctable->truncate();
4740
4741 // 1. write current scsn
4742 handle tscsn = scsn.getHandle();
4743 complete = sctable->put(CACHEDSCSN, (char*)&tscsn, sizeof tscsn);
4744
4745 if (complete)
4746 {
4747 // 2. write all users
4748 for (user_map::iterator it = users.begin(); it != users.end(); it++)
4749 {
4750 if (!(complete = sctable->put(CACHEDUSER, &it->second, &key)))
4751 {
4752 break;
4753 }
4754 }
4755 }
4756
4757 if (complete)
4758 {
4759 // 3. write new or modified nodes, purge deleted nodes
4760 for (node_map::iterator it = nodes.begin(); it != nodes.end(); it++)
4761 {
4762 if (!(complete = sctable->put(CACHEDNODE, it->second, &key)))
4763 {
4764 break;
4765 }
4766 }
4767 }
4768
4769 if (complete)
4770 {
4771 // 4. write new or modified pcrs, purge deleted pcrs
4772 for (handlepcr_map::iterator it = pcrindex.begin(); it != pcrindex.end(); it++)
4773 {
4774 if (!(complete = sctable->put(CACHEDPCR, it->second, &key)))
4775 {
4776 break;
4777 }
4778 }
4779 }
4780
4781 #ifdef ENABLE_CHAT
4782 if (complete)
4783 {
4784 // 5. write new or modified chats
4785 for (textchat_map::iterator it = chats.begin(); it != chats.end(); it++)
4786 {
4787 if (!(complete = sctable->put(CACHEDCHAT, it->second, &key)))
4788 {
4789 break;
4790 }
4791 }
4792 }
4793 LOG_debug << "Saving SCSN " << scsn.text() << " with " << nodes.size() << " nodes, " << users.size() << " users, " << pcrindex.size() << " pcrs and " << chats.size() << " chats to local cache (" << complete << ")";
4794 #else
4795
4796 LOG_debug << "Saving SCSN " << scsn.text() << " with " << nodes.size() << " nodes and " << users.size() << " users and " << pcrindex.size() << " pcrs to local cache (" << complete << ")";
4797 #endif
4798 finalizesc(complete);
4799 }
4800 }
4801
4802 // erase and and fill user's local state cache
updatesc()4803 void MegaClient::updatesc()
4804 {
4805 if (sctable)
4806 {
4807 string t;
4808
4809 sctable->get(CACHEDSCSN, &t);
4810
4811 if (t.size() != sizeof cachedscsn)
4812 {
4813 if (t.size())
4814 {
4815 LOG_err << "Invalid scsn size";
4816 }
4817 return;
4818 }
4819
4820 if (!scsn.ready())
4821 {
4822 LOG_err << "scsn not known, not updating database";
4823 return;
4824 }
4825
4826 bool complete;
4827
4828 // 1. update associated scsn
4829 handle tscsn = scsn.getHandle();
4830 complete = sctable->put(CACHEDSCSN, (char*)&tscsn, sizeof tscsn);
4831
4832 if (complete)
4833 {
4834 // 2. write new or update modified users
4835 for (user_vector::iterator it = usernotify.begin(); it != usernotify.end(); it++)
4836 {
4837 char base64[12];
4838 if ((*it)->show == INACTIVE && (*it)->userhandle != me)
4839 {
4840 if ((*it)->dbid)
4841 {
4842 LOG_verbose << "Removing inactive user from database: " << (Base64::btoa((byte*)&((*it)->userhandle),MegaClient::USERHANDLE,base64) ? base64 : "");
4843 if (!(complete = sctable->del((*it)->dbid)))
4844 {
4845 break;
4846 }
4847 }
4848 }
4849 else
4850 {
4851 LOG_verbose << "Adding/updating user to database: " << (Base64::btoa((byte*)&((*it)->userhandle),MegaClient::USERHANDLE,base64) ? base64 : "");
4852 if (!(complete = sctable->put(CACHEDUSER, *it, &key)))
4853 {
4854 break;
4855 }
4856 }
4857 }
4858 }
4859
4860 if (complete)
4861 {
4862 // 3. write new or modified nodes, purge deleted nodes
4863 for (node_vector::iterator it = nodenotify.begin(); it != nodenotify.end(); it++)
4864 {
4865 char base64[12];
4866 if ((*it)->changed.removed)
4867 {
4868 if ((*it)->dbid)
4869 {
4870 LOG_verbose << "Removing node from database: " << (Base64::btoa((byte*)&((*it)->nodehandle),MegaClient::NODEHANDLE,base64) ? base64 : "");
4871 if (!(complete = sctable->del((*it)->dbid)))
4872 {
4873 break;
4874 }
4875 }
4876 }
4877 else
4878 {
4879 LOG_verbose << "Adding node to database: " << (Base64::btoa((byte*)&((*it)->nodehandle),MegaClient::NODEHANDLE,base64) ? base64 : "");
4880 if (!(complete = sctable->put(CACHEDNODE, *it, &key)))
4881 {
4882 break;
4883 }
4884 }
4885 }
4886 }
4887
4888 if (complete)
4889 {
4890 // 4. write new or modified pcrs, purge deleted pcrs
4891 for (pcr_vector::iterator it = pcrnotify.begin(); it != pcrnotify.end(); it++)
4892 {
4893 char base64[12];
4894 if ((*it)->removed())
4895 {
4896 if ((*it)->dbid)
4897 {
4898 LOG_verbose << "Removing pcr from database: " << (Base64::btoa((byte*)&((*it)->id),MegaClient::PCRHANDLE,base64) ? base64 : "");
4899 if (!(complete = sctable->del((*it)->dbid)))
4900 {
4901 break;
4902 }
4903 }
4904 }
4905 else if (!(*it)->removed())
4906 {
4907 LOG_verbose << "Adding pcr to database: " << (Base64::btoa((byte*)&((*it)->id),MegaClient::PCRHANDLE,base64) ? base64 : "");
4908 if (!(complete = sctable->put(CACHEDPCR, *it, &key)))
4909 {
4910 break;
4911 }
4912 }
4913 }
4914 }
4915
4916 #ifdef ENABLE_CHAT
4917 if (complete)
4918 {
4919 // 5. write new or modified chats
4920 for (textchat_map::iterator it = chatnotify.begin(); it != chatnotify.end(); it++)
4921 {
4922 char base64[12];
4923 LOG_verbose << "Adding chat to database: " << (Base64::btoa((byte*)&(it->second->id),MegaClient::CHATHANDLE,base64) ? base64 : "");
4924 if (!(complete = sctable->put(CACHEDCHAT, it->second, &key)))
4925 {
4926 break;
4927 }
4928 }
4929 }
4930 LOG_debug << "Saving SCSN " << scsn.text() << " with " << nodenotify.size() << " modified nodes, " << usernotify.size() << " users, " << pcrnotify.size() << " pcrs and " << chatnotify.size() << " chats to local cache (" << complete << ")";
4931 #else
4932 LOG_debug << "Saving SCSN " << scsn.text() << " with " << nodenotify.size() << " modified nodes, " << usernotify.size() << " users and " << pcrnotify.size() << " pcrs to local cache (" << complete << ")";
4933 #endif
4934 finalizesc(complete);
4935 }
4936 }
4937
4938 // commit or purge local state cache
finalizesc(bool complete)4939 void MegaClient::finalizesc(bool complete)
4940 {
4941 if (complete)
4942 {
4943 cachedscsn = scsn.getHandle();
4944 }
4945 else
4946 {
4947 sctable->remove();
4948
4949 LOG_err << "Cache update DB write error - disabling caching";
4950
4951 delete sctable;
4952 sctable = NULL;
4953 pendingsccommit = false;
4954 }
4955 }
4956
4957 // queue node file attribute for retrieval or cancel retrieval
getfa(handle h,string * fileattrstring,const string & nodekey,fatype t,int cancel)4958 error MegaClient::getfa(handle h, string *fileattrstring, const string &nodekey, fatype t, int cancel)
4959 {
4960 // locate this file attribute type in the nodes's attribute string
4961 handle fah;
4962 int p, pp;
4963
4964 // find position of file attribute or 0 if not present
4965 if (!(p = Node::hasfileattribute(fileattrstring, t)))
4966 {
4967 return API_ENOENT;
4968 }
4969
4970 pp = p - 1;
4971
4972 while (pp && fileattrstring->at(pp - 1) >= '0' && fileattrstring->at(pp - 1) <= '9')
4973 {
4974 pp--;
4975 }
4976
4977 if (p == pp)
4978 {
4979 return API_ENOENT;
4980 }
4981
4982 if (Base64::atob(strchr(fileattrstring->c_str() + p, '*') + 1, (byte*)&fah, sizeof(fah)) != sizeof(fah))
4983 {
4984 return API_ENOENT;
4985 }
4986
4987 int c = atoi(fileattrstring->c_str() + pp);
4988
4989 if (cancel)
4990 {
4991 // cancel pending request
4992 fafc_map::iterator cit;
4993
4994 if ((cit = fafcs.find(c)) != fafcs.end())
4995 {
4996 faf_map::iterator it;
4997
4998 for (int i = 2; i--; )
4999 {
5000 if ((it = cit->second->fafs[i].find(fah)) != cit->second->fafs[i].end())
5001 {
5002 delete it->second;
5003 cit->second->fafs[i].erase(it);
5004
5005 // none left: tear down connection
5006 if (!cit->second->fafs[1].size() && cit->second->req.status == REQ_INFLIGHT)
5007 {
5008 cit->second->req.disconnect();
5009 }
5010
5011 return API_OK;
5012 }
5013 }
5014 }
5015
5016 return API_ENOENT;
5017 }
5018 else
5019 {
5020 // add file attribute cluster channel and set cluster reference node handle
5021 FileAttributeFetchChannel** fafcp = &fafcs[c];
5022
5023 if (!*fafcp)
5024 {
5025 *fafcp = new FileAttributeFetchChannel(this);
5026 }
5027
5028 if (!(*fafcp)->fafs[1].count(fah))
5029 {
5030 (*fafcp)->fahref = fah;
5031
5032 // map returned handle to type/node upon retrieval response
5033 FileAttributeFetch** fafp = &(*fafcp)->fafs[0][fah];
5034
5035 if (!*fafp)
5036 {
5037 *fafp = new FileAttributeFetch(h, nodekey, t, reqtag);
5038 }
5039 else
5040 {
5041 restag = (*fafp)->tag;
5042 return API_EEXIST;
5043 }
5044 }
5045 else
5046 {
5047 FileAttributeFetch** fafp = &(*fafcp)->fafs[1][fah];
5048 restag = (*fafp)->tag;
5049 return API_EEXIST;
5050 }
5051
5052 return API_OK;
5053 }
5054 }
5055
5056 // build pending attribute string for this handle and remove
pendingattrstring(handle h,string * fa)5057 void MegaClient::pendingattrstring(handle h, string* fa)
5058 {
5059 char buf[128];
5060
5061 for (fa_map::iterator it = pendingfa.lower_bound(pair<handle, fatype>(h, fatype(0)));
5062 it != pendingfa.end() && it->first.first == h; )
5063 {
5064 if (it->first.second != fa_media)
5065 {
5066 sprintf(buf, "/%u*", (unsigned)it->first.second);
5067 Base64::btoa((byte*)&it->second.first, sizeof(it->second.first), strchr(buf + 3, 0));
5068 fa->append(buf + !fa->size());
5069 LOG_debug << "Added file attribute to putnodes. Remaining: " << pendingfa.size()-1;
5070 }
5071 pendingfa.erase(it++);
5072 }
5073 }
5074
5075 // attach file attribute to a file (th can be upload or node handle)
5076 // FIXME: to avoid unnecessary roundtrips to the attribute servers, also cache locally
putfa(handle th,fatype t,SymmCipher * key,std::unique_ptr<string> data,bool checkAccess)5077 void MegaClient::putfa(handle th, fatype t, SymmCipher* key, std::unique_ptr<string> data, bool checkAccess)
5078 {
5079 // CBC-encrypt attribute data (padded to next multiple of BLOCKSIZE)
5080 data->resize((data->size() + SymmCipher::BLOCKSIZE - 1) & -SymmCipher::BLOCKSIZE);
5081 key->cbc_encrypt((byte*)data->data(), data->size());
5082
5083 queuedfa.push_back(new HttpReqCommandPutFA(this, th, t, std::move(data), checkAccess));
5084 LOG_debug << "File attribute added to queue - " << th << " : " << queuedfa.size() << " queued, " << activefa.size() << " active";
5085
5086 // no other file attribute storage request currently in progress? POST this one.
5087 while (activefa.size() < MAXPUTFA && queuedfa.size())
5088 {
5089 putfa_list::iterator curfa = queuedfa.begin();
5090 HttpReqCommandPutFA *fa = *curfa;
5091 queuedfa.erase(curfa);
5092 activefa.push_back(fa);
5093 fa->status = REQ_INFLIGHT;
5094 reqs.add(fa);
5095 }
5096 }
5097
5098 // has the limit of concurrent transfer tslots been reached?
slotavail() const5099 bool MegaClient::slotavail() const
5100 {
5101 return !mBlocked && tslots.size() < MAXTOTALTRANSFERS;
5102 }
5103
setstoragestatus(storagestatus_t status)5104 bool MegaClient::setstoragestatus(storagestatus_t status)
5105 {
5106 // transition from paywall to red should not happen
5107 assert(status != STORAGE_RED || ststatus != STORAGE_PAYWALL);
5108
5109 if (ststatus != status && (status != STORAGE_RED || ststatus != STORAGE_PAYWALL))
5110 {
5111 storagestatus_t pststatus = ststatus;
5112 ststatus = status;
5113 if (pststatus == STORAGE_PAYWALL)
5114 {
5115 mOverquotaDeadlineTs = 0;
5116 mOverquotaWarningTs.clear();
5117 }
5118 app->notify_storage(ststatus);
5119 if (pststatus == STORAGE_RED || pststatus == STORAGE_PAYWALL)
5120 {
5121 abortbackoff(true);
5122 }
5123 return true;
5124 }
5125 return false;
5126 }
5127
getpubliclinkinfo(handle h)5128 void MegaClient::getpubliclinkinfo(handle h)
5129 {
5130 reqs.add(new CommandFolderLinkInfo(this, h));
5131 }
5132
smsverificationsend(const string & phoneNumber,bool reVerifyingWhitelisted)5133 error MegaClient::smsverificationsend(const string& phoneNumber, bool reVerifyingWhitelisted)
5134 {
5135 if (!CommandSMSVerificationSend::isPhoneNumber(phoneNumber))
5136 {
5137 return API_EARGS;
5138 }
5139
5140 reqs.add(new CommandSMSVerificationSend(this, phoneNumber, reVerifyingWhitelisted));
5141 if (reVerifyingWhitelisted)
5142 {
5143 reqs.add(new CommandGetUserData(this));
5144 }
5145
5146 return API_OK;
5147 }
5148
smsverificationcheck(const std::string & verificationCode)5149 error MegaClient::smsverificationcheck(const std::string &verificationCode)
5150 {
5151 if (!CommandSMSVerificationCheck::isVerificationCode(verificationCode))
5152 {
5153 return API_EARGS;
5154 }
5155
5156 reqs.add(new CommandSMSVerificationCheck(this, verificationCode));
5157
5158 return API_OK;
5159 }
5160
5161 // server-client node update processing
sc_updatenode()5162 void MegaClient::sc_updatenode()
5163 {
5164 handle h = UNDEF;
5165 handle u = 0;
5166 const char* a = NULL;
5167 m_time_t ts = -1;
5168
5169 for (;;)
5170 {
5171 switch (jsonsc.getnameid())
5172 {
5173 case 'n':
5174 h = jsonsc.gethandle();
5175 break;
5176
5177 case 'u':
5178 u = jsonsc.gethandle(USERHANDLE);
5179 break;
5180
5181 case MAKENAMEID2('a', 't'):
5182 a = jsonsc.getvalue();
5183 break;
5184
5185 case MAKENAMEID2('t', 's'):
5186 ts = jsonsc.getint();
5187 break;
5188
5189 case EOO:
5190 if (!ISUNDEF(h))
5191 {
5192 Node* n;
5193 bool notify = false;
5194
5195 if ((n = nodebyhandle(h)))
5196 {
5197 if (u && n->owner != u)
5198 {
5199 n->owner = u;
5200 n->changed.owner = true;
5201 notify = true;
5202 }
5203
5204 if (a && ((n->attrstring && strcmp(n->attrstring->c_str(), a)) || !n->attrstring))
5205 {
5206 if (!n->attrstring)
5207 {
5208 n->attrstring.reset(new string);
5209 }
5210 Node::copystring(n->attrstring.get(), a);
5211 n->changed.attrs = true;
5212 notify = true;
5213 }
5214
5215 if (ts != -1 && n->ctime != ts)
5216 {
5217 n->ctime = ts;
5218 n->changed.ctime = true;
5219 notify = true;
5220 }
5221
5222 n->applykey();
5223 n->setattr();
5224
5225 if (notify)
5226 {
5227 notifynode(n);
5228 }
5229 }
5230 }
5231 return;
5232
5233 default:
5234 if (!jsonsc.storeobject())
5235 {
5236 return;
5237 }
5238 }
5239 }
5240 }
5241
5242 // read tree object (nodes and users)
readtree(JSON * j)5243 void MegaClient::readtree(JSON* j)
5244 {
5245 if (j->enterobject())
5246 {
5247 for (;;)
5248 {
5249 switch (jsonsc.getnameid())
5250 {
5251 case 'f':
5252 readnodes(j, 1);
5253 break;
5254
5255 case MAKENAMEID2('f', '2'):
5256 readnodes(j, 1);
5257 break;
5258
5259 case 'u':
5260 readusers(j, true);
5261 break;
5262
5263 case EOO:
5264 j->leaveobject();
5265 return;
5266
5267 default:
5268 if (!jsonsc.storeobject())
5269 {
5270 return;
5271 }
5272 }
5273 }
5274 }
5275 }
5276
5277 // server-client newnodes processing
sc_newnodes()5278 handle MegaClient::sc_newnodes()
5279 {
5280 handle originatingUser = UNDEF;
5281 for (;;)
5282 {
5283 switch (jsonsc.getnameid())
5284 {
5285 case 't':
5286 readtree(&jsonsc);
5287 break;
5288
5289 case 'u':
5290 readusers(&jsonsc, true);
5291 break;
5292
5293 case MAKENAMEID2('o', 'u'):
5294 originatingUser = jsonsc.gethandle(USERHANDLE);
5295 break;
5296
5297 case EOO:
5298 return originatingUser;
5299
5300 default:
5301 if (!jsonsc.storeobject())
5302 {
5303 return originatingUser;
5304 }
5305 }
5306 }
5307 }
5308
5309 // share requests come in the following flavours:
5310 // - n/k (set share key) (always symmetric)
5311 // - n/o/u[/okd] (share deletion)
5312 // - n/o/u/k/r/ts[/ok][/ha] (share addition) (k can be asymmetric)
5313 // returns 0 in case of a share addition or error, 1 otherwise
sc_shares()5314 bool MegaClient::sc_shares()
5315 {
5316 handle h = UNDEF;
5317 handle oh = UNDEF;
5318 handle uh = UNDEF;
5319 handle p = UNDEF;
5320 handle ou = UNDEF;
5321 bool upgrade_pending_to_full = false;
5322 const char* k = NULL;
5323 const char* ok = NULL;
5324 bool okremoved = false;
5325 byte ha[SymmCipher::BLOCKSIZE];
5326 byte sharekey[SymmCipher::BLOCKSIZE];
5327 int have_ha = 0;
5328 accesslevel_t r = ACCESS_UNKNOWN;
5329 m_time_t ts = 0;
5330 int outbound;
5331
5332 for (;;)
5333 {
5334 switch (jsonsc.getnameid())
5335 {
5336 case 'p': // Pending contact request handle for an s2 packet
5337 p = jsonsc.gethandle(PCRHANDLE);
5338 break;
5339
5340 case MAKENAMEID2('o', 'p'):
5341 upgrade_pending_to_full = true;
5342 break;
5343
5344 case 'n': // share node
5345 h = jsonsc.gethandle();
5346 break;
5347
5348 case 'o': // owner user
5349 oh = jsonsc.gethandle(USERHANDLE);
5350 break;
5351
5352 case 'u': // target user
5353 uh = jsonsc.is(EXPORTEDLINK) ? 0 : jsonsc.gethandle(USERHANDLE);
5354 break;
5355
5356 case MAKENAMEID2('o', 'u'):
5357 ou = jsonsc.gethandle(USERHANDLE);
5358 break;
5359
5360 case MAKENAMEID2('o', 'k'): // owner key
5361 ok = jsonsc.getvalue();
5362 break;
5363
5364 case MAKENAMEID3('o', 'k', 'd'):
5365 okremoved = (jsonsc.getint() == 1); // owner key removed
5366 break;
5367
5368 case MAKENAMEID2('h', 'a'): // outgoing share signature
5369 have_ha = Base64::atob(jsonsc.getvalue(), ha, sizeof ha) == sizeof ha;
5370 break;
5371
5372 case 'r': // share access level
5373 r = (accesslevel_t)jsonsc.getint();
5374 break;
5375
5376 case MAKENAMEID2('t', 's'): // share timestamp
5377 ts = jsonsc.getint();
5378 break;
5379
5380 case 'k': // share key
5381 k = jsonsc.getvalue();
5382 break;
5383
5384 case EOO:
5385 // we do not process share commands unless logged into a full
5386 // account
5387 if (loggedin() < FULLACCOUNT)
5388 {
5389 return false;
5390 }
5391
5392 // need a share node
5393 if (ISUNDEF(h))
5394 {
5395 return false;
5396 }
5397
5398 // ignore unrelated share packets (should never be triggered)
5399 outbound = (oh == me);
5400 if (!ISUNDEF(oh) && !outbound && (uh != me))
5401 {
5402 return false;
5403 }
5404
5405 // am I the owner of the share? use ok, otherwise k.
5406 if (ok && oh == me)
5407 {
5408 k = ok;
5409 }
5410
5411 if (k)
5412 {
5413 if (!decryptkey(k, sharekey, sizeof sharekey, &key, 1, h))
5414 {
5415 return false;
5416 }
5417
5418 if (ISUNDEF(oh) && ISUNDEF(uh))
5419 {
5420 // share key update on inbound share
5421 newshares.push_back(new NewShare(h, 0, UNDEF, ACCESS_UNKNOWN, 0, sharekey));
5422 return true;
5423 }
5424
5425 if (!ISUNDEF(oh) && (!ISUNDEF(uh) || !ISUNDEF(p)))
5426 {
5427 if (!outbound && statecurrent)
5428 {
5429 User* u = finduser(oh);
5430 // only new shares should be notified (skip permissions changes)
5431 bool newShare = u && u->sharing.find(h) == u->sharing.end();
5432 if (newShare)
5433 {
5434 useralerts.add(new UserAlert::NewShare(h, oh, u->email, ts, useralerts.nextId()));
5435 useralerts.ignoreNextSharedNodesUnder(h); // no need to alert on nodes already in the new share, which are delivered next
5436 }
5437 }
5438
5439 // new share - can be inbound or outbound
5440 newshares.push_back(new NewShare(h, outbound,
5441 outbound ? uh : oh,
5442 r, ts, sharekey,
5443 have_ha ? ha : NULL,
5444 p, upgrade_pending_to_full));
5445
5446 //Returns false because as this is a new share, the node
5447 //could not have been received yet
5448 return false;
5449 }
5450 }
5451 else
5452 {
5453 if (!ISUNDEF(oh) && (!ISUNDEF(uh) || !ISUNDEF(p)))
5454 {
5455 handle peer = outbound ? uh : oh;
5456 if (peer != me && peer && !ISUNDEF(peer) && statecurrent && ou != me)
5457 {
5458 User* u = finduser(peer);
5459 useralerts.add(new UserAlert::DeletedShare(peer, u ? u->email : "", oh, h, ts == 0 ? m_time() : ts, useralerts.nextId()));
5460 }
5461
5462 // share revocation or share without key
5463 newshares.push_back(new NewShare(h, outbound,
5464 peer, r, 0, NULL, NULL, p, false, okremoved));
5465 return r == ACCESS_UNKNOWN;
5466 }
5467 }
5468
5469 return false;
5470
5471 default:
5472 if (!jsonsc.storeobject())
5473 {
5474 return false;
5475 }
5476 }
5477 }
5478 }
5479
sc_upgrade()5480 bool MegaClient::sc_upgrade()
5481 {
5482 string result;
5483 bool success = false;
5484 int proNumber = 0;
5485 int itemclass = 0;
5486
5487 for (;;)
5488 {
5489 switch (jsonsc.getnameid())
5490 {
5491 case MAKENAMEID2('i', 't'):
5492 itemclass = int(jsonsc.getint()); // itemclass. For now, it's always 0.
5493 break;
5494
5495 case 'p':
5496 proNumber = int(jsonsc.getint()); //pro type
5497 break;
5498
5499 case 'r':
5500 jsonsc.storeobject(&result);
5501 if (result == "s")
5502 {
5503 success = true;
5504 }
5505 break;
5506
5507 case EOO:
5508 if (itemclass == 0 && statecurrent)
5509 {
5510 useralerts.add(new UserAlert::Payment(success, proNumber, m_time(), useralerts.nextId()));
5511 }
5512 return success;
5513
5514 default:
5515 if (!jsonsc.storeobject())
5516 {
5517 return false;
5518 }
5519 }
5520 }
5521 }
5522
sc_paymentreminder()5523 void MegaClient::sc_paymentreminder()
5524 {
5525 m_time_t expiryts = 0;
5526
5527 for (;;)
5528 {
5529 switch (jsonsc.getnameid())
5530 {
5531 case MAKENAMEID2('t', 's'):
5532 expiryts = int(jsonsc.getint()); // timestamp
5533 break;
5534
5535 case EOO:
5536 if (statecurrent)
5537 {
5538 useralerts.add(new UserAlert::PaymentReminder(expiryts ? expiryts : m_time(), useralerts.nextId()));
5539 }
5540 return;
5541
5542 default:
5543 if (!jsonsc.storeobject())
5544 {
5545 return;
5546 }
5547 }
5548 }
5549 }
5550
5551 // user/contact updates come in the following format:
5552 // u:[{c/m/ts}*] - Add/modify user/contact
sc_contacts()5553 void MegaClient::sc_contacts()
5554 {
5555 handle ou = UNDEF;
5556
5557 for (;;)
5558 {
5559 switch (jsonsc.getnameid())
5560 {
5561 case 'u':
5562 useralerts.startprovisional();
5563 readusers(&jsonsc, true);
5564 break;
5565
5566 case MAKENAMEID2('o', 'u'):
5567 ou = jsonsc.gethandle(MegaClient::USERHANDLE);
5568 break;
5569
5570 case EOO:
5571 useralerts.evalprovisional(ou);
5572 return;
5573
5574 default:
5575 if (!jsonsc.storeobject())
5576 {
5577 return;
5578 }
5579 }
5580 }
5581 }
5582
5583 // server-client key requests/responses
sc_keys()5584 void MegaClient::sc_keys()
5585 {
5586 handle h;
5587 Node* n = NULL;
5588 node_vector kshares;
5589 node_vector knodes;
5590
5591 for (;;)
5592 {
5593 switch (jsonsc.getnameid())
5594 {
5595 case MAKENAMEID2('s', 'r'):
5596 procsr(&jsonsc);
5597 break;
5598
5599 case 'h':
5600 if (!ISUNDEF(h = jsonsc.gethandle()) && (n = nodebyhandle(h)) && n->sharekey)
5601 {
5602 kshares.push_back(n); // n->inshare is checked in cr_response
5603 }
5604 break;
5605
5606 case 'n':
5607 if (jsonsc.enterarray())
5608 {
5609 while (!ISUNDEF(h = jsonsc.gethandle()) && (n = nodebyhandle(h)))
5610 {
5611 knodes.push_back(n);
5612 }
5613
5614 jsonsc.leavearray();
5615 }
5616 break;
5617
5618 case MAKENAMEID2('c', 'r'):
5619 proccr(&jsonsc);
5620 break;
5621
5622 case EOO:
5623 cr_response(&kshares, &knodes, NULL);
5624 return;
5625
5626 default:
5627 if (!jsonsc.storeobject())
5628 {
5629 return;
5630 }
5631 }
5632 }
5633 }
5634
5635 // server-client file attribute update
sc_fileattr()5636 void MegaClient::sc_fileattr()
5637 {
5638 Node* n = NULL;
5639 const char* fa = NULL;
5640
5641 for (;;)
5642 {
5643 switch (jsonsc.getnameid())
5644 {
5645 case MAKENAMEID2('f', 'a'):
5646 fa = jsonsc.getvalue();
5647 break;
5648
5649 case 'n':
5650 handle h;
5651 if (!ISUNDEF(h = jsonsc.gethandle()))
5652 {
5653 n = nodebyhandle(h);
5654 }
5655 break;
5656
5657 case EOO:
5658 if (fa && n)
5659 {
5660 Node::copystring(&n->fileattrstring, fa);
5661 n->changed.fileattrstring = true;
5662 notifynode(n);
5663 }
5664 return;
5665
5666 default:
5667 if (!jsonsc.storeobject())
5668 {
5669 return;
5670 }
5671 }
5672 }
5673 }
5674
5675 // server-client user attribute update notification
sc_userattr()5676 void MegaClient::sc_userattr()
5677 {
5678 handle uh = UNDEF;
5679 User *u = NULL;
5680
5681 string ua, uav;
5682 string_vector ualist; // stores attribute names
5683 string_vector uavlist; // stores attribute versions
5684 string_vector::const_iterator itua, ituav;
5685
5686 for (;;)
5687 {
5688 switch (jsonsc.getnameid())
5689 {
5690 case 'u':
5691 uh = jsonsc.gethandle(USERHANDLE);
5692 break;
5693
5694 case MAKENAMEID2('u', 'a'):
5695 if (jsonsc.enterarray())
5696 {
5697 while (jsonsc.storeobject(&ua))
5698 {
5699 ualist.push_back(ua);
5700 }
5701 jsonsc.leavearray();
5702 }
5703 break;
5704
5705 case 'v':
5706 if (jsonsc.enterarray())
5707 {
5708 while (jsonsc.storeobject(&uav))
5709 {
5710 uavlist.push_back(uav);
5711 }
5712 jsonsc.leavearray();
5713 }
5714 break;
5715
5716 case EOO:
5717 if (ISUNDEF(uh))
5718 {
5719 LOG_err << "Failed to parse the user :" << uh;
5720 }
5721 else if (!(u = finduser(uh)))
5722 {
5723 LOG_debug << "User attributes update for non-existing user";
5724 }
5725 else if (ualist.size() == uavlist.size())
5726 {
5727 assert(ualist.size() && uavlist.size());
5728
5729 // invalidate only out-of-date attributes
5730 for (itua = ualist.begin(), ituav = uavlist.begin();
5731 itua != ualist.end();
5732 itua++, ituav++)
5733 {
5734 attr_t type = User::string2attr(itua->c_str());
5735 const string *cacheduav = u->getattrversion(type);
5736 if (cacheduav)
5737 {
5738 if (*cacheduav != *ituav)
5739 {
5740 u->invalidateattr(type);
5741 switch(type)
5742 {
5743 case ATTR_KEYRING:
5744 {
5745 resetKeyring();
5746 break;
5747 }
5748 case ATTR_AUTHRING: // fall-through
5749 case ATTR_AUTHCU255: // fall-through
5750 case ATTR_AUTHRSA:
5751 {
5752 LOG_debug << User::attr2string(type) << " has changed externally. Fetching...";
5753 mAuthRings.erase(type);
5754 getua(u, type, 0);
5755 break;
5756 }
5757 default:
5758 break;
5759 }
5760 }
5761 else
5762 {
5763 LOG_info << "User attribute already up to date";
5764 return;
5765 }
5766 }
5767 else
5768 {
5769 u->setChanged(type);
5770
5771 // if this attr was just created, add it to cache with empty value and set it as invalid
5772 // (it will allow to detect if the attr exists upon resumption from cache, in case the value wasn't received yet)
5773 if (type == ATTR_DISABLE_VERSIONS && !u->getattr(type))
5774 {
5775 string emptyStr;
5776 u->setattr(type, &emptyStr, &emptyStr);
5777 u->invalidateattr(type);
5778 }
5779 }
5780
5781 if (!fetchingnodes)
5782 {
5783 // silently fetch-upon-update these critical attributes
5784 if (type == ATTR_DISABLE_VERSIONS || type == ATTR_PUSH_SETTINGS)
5785 {
5786 getua(u, type, 0);
5787 }
5788 else if (type == ATTR_STORAGE_STATE)
5789 {
5790 if (!statecurrent)
5791 {
5792 notifyStorageChangeOnStateCurrent = true;
5793 }
5794 else
5795 {
5796 LOG_debug << "Possible storage status change";
5797 app->notify_storage(STORAGE_CHANGE);
5798 }
5799 }
5800 }
5801 }
5802 u->setTag(0);
5803 notifyuser(u);
5804 }
5805 else // different number of attributes than versions --> error
5806 {
5807 LOG_err << "Unpaired user attributes and versions";
5808 }
5809 return;
5810
5811 default:
5812 if (!jsonsc.storeobject())
5813 {
5814 return;
5815 }
5816 }
5817 }
5818 }
5819
5820 // Incoming pending contact additions or updates, always triggered by the creator (reminders, deletes, etc)
sc_ipc()5821 void MegaClient::sc_ipc()
5822 {
5823 // fields: m, ts, uts, rts, dts, msg, p, ps
5824 m_time_t ts = 0;
5825 m_time_t uts = 0;
5826 m_time_t rts = 0;
5827 m_time_t dts = 0;
5828 m_off_t clv = 0;
5829 const char *m = NULL;
5830 const char *msg = NULL;
5831 handle p = UNDEF;
5832 PendingContactRequest *pcr;
5833
5834 bool done = false;
5835 while (!done)
5836 {
5837 switch (jsonsc.getnameid())
5838 {
5839 case 'm':
5840 m = jsonsc.getvalue();
5841 break;
5842 case MAKENAMEID2('t', 's'):
5843 ts = jsonsc.getint();
5844 break;
5845 case MAKENAMEID3('u', 't', 's'):
5846 uts = jsonsc.getint();
5847 break;
5848 case MAKENAMEID3('r', 't', 's'):
5849 rts = jsonsc.getint();
5850 break;
5851 case MAKENAMEID3('d', 't', 's'):
5852 dts = jsonsc.getint();
5853 break;
5854 case MAKENAMEID3('m', 's', 'g'):
5855 msg = jsonsc.getvalue();
5856 break;
5857 case MAKENAMEID3('c', 'l', 'v'):
5858 clv = jsonsc.getint();
5859 break;
5860 case 'p':
5861 p = jsonsc.gethandle(MegaClient::PCRHANDLE);
5862 break;
5863 case EOO:
5864 done = true;
5865 if (ISUNDEF(p))
5866 {
5867 LOG_err << "p element not provided";
5868 break;
5869 }
5870
5871 if (m && statecurrent)
5872 {
5873 string email;
5874 Node::copystring(&email, m);
5875 useralerts.add(new UserAlert::IncomingPendingContact(dts, rts, p, email, ts, useralerts.nextId()));
5876 }
5877
5878 pcr = pcrindex.count(p) ? pcrindex[p] : (PendingContactRequest *) NULL;
5879
5880 if (dts != 0)
5881 {
5882 //Trying to remove an ignored request
5883 if (pcr)
5884 {
5885 // this is a delete, find the existing object in state
5886 pcr->uts = dts;
5887 pcr->changed.deleted = true;
5888 }
5889 }
5890 else if (pcr && rts != 0)
5891 {
5892 // reminder
5893 if (uts == 0)
5894 {
5895 LOG_err << "uts element not provided";
5896 break;
5897 }
5898
5899 pcr->uts = uts;
5900 pcr->changed.reminded = true;
5901 }
5902 else
5903 {
5904 // new
5905 if (!m)
5906 {
5907 LOG_err << "m element not provided";
5908 break;
5909 }
5910 if (ts == 0)
5911 {
5912 LOG_err << "ts element not provided";
5913 break;
5914 }
5915 if (uts == 0)
5916 {
5917 LOG_err << "uts element not provided";
5918 break;
5919 }
5920
5921 pcr = new PendingContactRequest(p, m, NULL, ts, uts, msg, false);
5922 mappcr(p, pcr);
5923 pcr->autoaccepted = clv;
5924 }
5925 notifypcr(pcr);
5926
5927 break;
5928 default:
5929 if (!jsonsc.storeobject())
5930 {
5931 return;
5932 }
5933 }
5934 }
5935 }
5936
5937 // Outgoing pending contact additions or updates, always triggered by the creator (reminders, deletes, etc)
sc_opc()5938 void MegaClient::sc_opc()
5939 {
5940 // fields: e, m, ts, uts, rts, dts, msg, p
5941 m_time_t ts = 0;
5942 m_time_t uts = 0;
5943 m_time_t rts = 0;
5944 m_time_t dts = 0;
5945 const char *e = NULL;
5946 const char *m = NULL;
5947 const char *msg = NULL;
5948 handle p = UNDEF;
5949 PendingContactRequest *pcr;
5950
5951 bool done = false;
5952 while (!done)
5953 {
5954 switch (jsonsc.getnameid())
5955 {
5956 case 'e':
5957 e = jsonsc.getvalue();
5958 break;
5959 case 'm':
5960 m = jsonsc.getvalue();
5961 break;
5962 case MAKENAMEID2('t', 's'):
5963 ts = jsonsc.getint();
5964 break;
5965 case MAKENAMEID3('u', 't', 's'):
5966 uts = jsonsc.getint();
5967 break;
5968 case MAKENAMEID3('r', 't', 's'):
5969 rts = jsonsc.getint();
5970 break;
5971 case MAKENAMEID3('d', 't', 's'):
5972 dts = jsonsc.getint();
5973 break;
5974 case MAKENAMEID3('m', 's', 'g'):
5975 msg = jsonsc.getvalue();
5976 break;
5977 case 'p':
5978 p = jsonsc.gethandle(MegaClient::PCRHANDLE);
5979 break;
5980 case EOO:
5981 done = true;
5982 if (ISUNDEF(p))
5983 {
5984 LOG_err << "p element not provided";
5985 break;
5986 }
5987
5988 pcr = pcrindex.count(p) ? pcrindex[p] : (PendingContactRequest *) NULL;
5989
5990 if (dts != 0) // delete PCR
5991 {
5992 // this is a delete, find the existing object in state
5993 if (pcr)
5994 {
5995 pcr->uts = dts;
5996 pcr->changed.deleted = true;
5997 }
5998 }
5999 else if (!e || !m || ts == 0 || uts == 0)
6000 {
6001 LOG_err << "Pending Contact Request is incomplete.";
6002 break;
6003 }
6004 else if (ts == uts) // add PCR
6005 {
6006 pcr = new PendingContactRequest(p, e, m, ts, uts, msg, true);
6007 mappcr(p, pcr);
6008 }
6009 else // remind PCR
6010 {
6011 if (rts == 0)
6012 {
6013 LOG_err << "Pending Contact Request is incomplete (rts element).";
6014 break;
6015 }
6016
6017 if (pcr)
6018 {
6019 pcr->uts = rts;
6020 pcr->changed.reminded = true;
6021 }
6022 }
6023 notifypcr(pcr);
6024
6025 break;
6026 default:
6027 if (!jsonsc.storeobject())
6028 {
6029 return;
6030 }
6031 }
6032 }
6033 }
6034
6035 // Incoming pending contact request updates, always triggered by the receiver of the request (accepts, denies, etc)
sc_upc(bool incoming)6036 void MegaClient::sc_upc(bool incoming)
6037 {
6038 // fields: p, uts, s, m
6039 m_time_t uts = 0;
6040 int s = 0;
6041 const char *m = NULL;
6042 handle p = UNDEF, ou = UNDEF;
6043 PendingContactRequest *pcr;
6044
6045 bool done = false;
6046 while (!done)
6047 {
6048 switch (jsonsc.getnameid())
6049 {
6050 case 'm':
6051 m = jsonsc.getvalue();
6052 break;
6053 case MAKENAMEID3('u', 't', 's'):
6054 uts = jsonsc.getint();
6055 break;
6056 case 's':
6057 s = int(jsonsc.getint());
6058 break;
6059 case 'p':
6060 p = jsonsc.gethandle(MegaClient::PCRHANDLE);
6061 break;
6062 case MAKENAMEID2('o', 'u'):
6063 ou = jsonsc.gethandle(MegaClient::PCRHANDLE);
6064 break;
6065 case EOO:
6066 done = true;
6067 if (ISUNDEF(p))
6068 {
6069 LOG_err << "p element not provided";
6070 break;
6071 }
6072
6073 pcr = pcrindex.count(p) ? pcrindex[p] : (PendingContactRequest *) NULL;
6074
6075 if (!pcr)
6076 {
6077 // As this was an update triggered by us, on an object we must know about, this is kinda a problem.
6078 LOG_err << "upci PCR not found, huge massive problem";
6079 break;
6080 }
6081 else
6082 {
6083 if (!m)
6084 {
6085 LOG_err << "m element not provided";
6086 break;
6087 }
6088 if (s == 0)
6089 {
6090 LOG_err << "s element not provided";
6091 break;
6092 }
6093 if (uts == 0)
6094 {
6095 LOG_err << "uts element not provided";
6096 break;
6097 }
6098
6099 switch (s)
6100 {
6101 case 1:
6102 // ignored
6103 pcr->changed.ignored = true;
6104 break;
6105 case 2:
6106 // accepted
6107 pcr->changed.accepted = true;
6108 break;
6109 case 3:
6110 // denied
6111 pcr->changed.denied = true;
6112 break;
6113 }
6114 pcr->uts = uts;
6115 }
6116
6117 if (statecurrent && ou != me && (incoming || s != 2))
6118 {
6119 string email;
6120 Node::copystring(&email, m);
6121 using namespace UserAlert;
6122 useralerts.add(incoming ? (Base*) new UpdatedPendingContactIncoming(s, p, email, uts, useralerts.nextId())
6123 : (Base*) new UpdatedPendingContactOutgoing(s, p, email, uts, useralerts.nextId()));
6124 }
6125
6126 notifypcr(pcr);
6127
6128 break;
6129 default:
6130 if (!jsonsc.storeobject())
6131 {
6132 return;
6133 }
6134 }
6135 }
6136 }
6137 // Public links updates
sc_ph()6138 void MegaClient::sc_ph()
6139 {
6140 // fields: h, ph, d, n, ets
6141 handle h = UNDEF;
6142 handle ph = UNDEF;
6143 bool deleted = false;
6144 bool created = false;
6145 bool updated = false;
6146 bool takendown = false;
6147 bool reinstated = false;
6148 m_time_t ets = 0;
6149 m_time_t cts = 0;
6150 Node *n;
6151
6152 bool done = false;
6153 while (!done)
6154 {
6155 switch (jsonsc.getnameid())
6156 {
6157 case 'h':
6158 h = jsonsc.gethandle(MegaClient::NODEHANDLE);
6159 break;
6160 case MAKENAMEID2('p','h'):
6161 ph = jsonsc.gethandle(MegaClient::NODEHANDLE);
6162 break;
6163 case 'd':
6164 deleted = (jsonsc.getint() == 1);
6165 break;
6166 case 'n':
6167 created = (jsonsc.getint() == 1);
6168 break;
6169 case 'u':
6170 updated = (jsonsc.getint() == 1);
6171 break;
6172 case MAKENAMEID4('d', 'o', 'w', 'n'):
6173 {
6174 int down = int(jsonsc.getint());
6175 takendown = (down == 1);
6176 reinstated = (down == 0);
6177 }
6178 break;
6179 case MAKENAMEID3('e', 't', 's'):
6180 ets = jsonsc.getint();
6181 break;
6182 case MAKENAMEID2('t', 's'):
6183 cts = jsonsc.getint();
6184 break;
6185 case EOO:
6186 done = true;
6187 if (ISUNDEF(h))
6188 {
6189 LOG_err << "h element not provided";
6190 break;
6191 }
6192 if (ISUNDEF(ph))
6193 {
6194 LOG_err << "ph element not provided";
6195 break;
6196 }
6197 if (!deleted && !created && !updated && !takendown)
6198 {
6199 LOG_err << "d/n/u/down element not provided";
6200 break;
6201 }
6202 if (!deleted && !cts)
6203 {
6204 LOG_err << "creation timestamp element not provided";
6205 break;
6206 }
6207
6208 n = nodebyhandle(h);
6209 if (n)
6210 {
6211 if ((takendown || reinstated) && !ISUNDEF(h) && statecurrent)
6212 {
6213 useralerts.add(new UserAlert::Takedown(takendown, reinstated, n->type, h, m_time(), useralerts.nextId()));
6214 }
6215
6216 if (deleted) // deletion
6217 {
6218 if (n->plink)
6219 {
6220 mPublicLinks.erase(n->nodehandle);
6221 delete n->plink;
6222 n->plink = NULL;
6223 }
6224 }
6225 else
6226 {
6227 n->setpubliclink(ph, cts, ets, takendown);
6228 }
6229
6230 n->changed.publiclink = true;
6231 notifynode(n);
6232 }
6233 else
6234 {
6235 LOG_warn << "node for public link not found";
6236 }
6237
6238 break;
6239 default:
6240 if (!jsonsc.storeobject())
6241 {
6242 return;
6243 }
6244 }
6245 }
6246 }
6247
sc_se()6248 void MegaClient::sc_se()
6249 {
6250 // fields: e, s
6251 string email;
6252 int status = -1;
6253 handle uh = UNDEF;
6254 User *u;
6255
6256 bool done = false;
6257 while (!done)
6258 {
6259 switch (jsonsc.getnameid())
6260 {
6261 case 'e':
6262 jsonsc.storeobject(&email);
6263 break;
6264 case 'u':
6265 uh = jsonsc.gethandle(USERHANDLE);
6266 break;
6267 case 's':
6268 status = int(jsonsc.getint());
6269 break;
6270 case EOO:
6271 done = true;
6272 if (email.empty())
6273 {
6274 LOG_err << "e element not provided";
6275 break;
6276 }
6277 if (uh == UNDEF)
6278 {
6279 LOG_err << "u element not provided";
6280 break;
6281 }
6282 if (status == -1)
6283 {
6284 LOG_err << "s element not provided";
6285 break;
6286 }
6287 if (status != EMAIL_REMOVED &&
6288 status != EMAIL_PENDING_REMOVED &&
6289 status != EMAIL_PENDING_ADDED &&
6290 status != EMAIL_FULLY_ACCEPTED)
6291 {
6292 LOG_err << "unknown value for s element: " << status;
6293 break;
6294 }
6295
6296 u = finduser(uh);
6297 if (!u)
6298 {
6299 LOG_warn << "user for email change not found. Not a contact?";
6300 }
6301 else if (status == EMAIL_FULLY_ACCEPTED)
6302 {
6303 LOG_debug << "Email changed from `" << u->email << "` to `" << email << "`";
6304
6305 mapuser(uh, email.c_str()); // update email used as index for user's map
6306 u->changed.email = true;
6307 notifyuser(u);
6308 }
6309 // TODO: manage different status once multiple-emails is supported
6310
6311 break;
6312 default:
6313 if (!jsonsc.storeobject())
6314 {
6315 return;
6316 }
6317 }
6318 }
6319 }
6320
6321 #ifdef ENABLE_CHAT
sc_chatupdate(bool readingPublicChat)6322 void MegaClient::sc_chatupdate(bool readingPublicChat)
6323 {
6324 // fields: id, u, cs, n, g, ou, ct, ts, m, ck
6325 handle chatid = UNDEF;
6326 userpriv_vector *userpriv = NULL;
6327 int shard = -1;
6328 userpriv_vector *upnotif = NULL;
6329 bool group = false;
6330 handle ou = UNDEF;
6331 string title;
6332 m_time_t ts = -1;
6333 bool publicchat = false;
6334 string unifiedkey;
6335
6336 bool done = false;
6337 while (!done)
6338 {
6339 switch (jsonsc.getnameid())
6340 {
6341 case MAKENAMEID2('i','d'):
6342 chatid = jsonsc.gethandle(MegaClient::CHATHANDLE);
6343 break;
6344
6345 case 'u': // list of users participating in the chat (+privileges)
6346 userpriv = readuserpriv(&jsonsc);
6347 break;
6348
6349 case MAKENAMEID2('c','s'):
6350 shard = int(jsonsc.getint());
6351 break;
6352
6353 case 'n': // the new user, for notification purposes (not used)
6354 upnotif = readuserpriv(&jsonsc);
6355 break;
6356
6357 case 'g':
6358 group = jsonsc.getint();
6359 break;
6360
6361 case MAKENAMEID2('o','u'):
6362 ou = jsonsc.gethandle(MegaClient::USERHANDLE);
6363 break;
6364
6365 case MAKENAMEID2('c','t'):
6366 jsonsc.storeobject(&title);
6367 break;
6368
6369 case MAKENAMEID2('t', 's'): // actual creation timestamp
6370 ts = jsonsc.getint();
6371 break;
6372
6373 case 'm':
6374 assert(readingPublicChat);
6375 publicchat = jsonsc.getint();
6376 break;
6377
6378 case MAKENAMEID2('c','k'):
6379 assert(readingPublicChat);
6380 jsonsc.storeobject(&unifiedkey);
6381 break;
6382
6383 case EOO:
6384 done = true;
6385
6386 if (ISUNDEF(chatid))
6387 {
6388 LOG_err << "Cannot read handle of the chat";
6389 }
6390 else if (ISUNDEF(ou))
6391 {
6392 LOG_err << "Cannot read originating user of action packet";
6393 }
6394 else if (shard == -1)
6395 {
6396 LOG_err << "Cannot read chat shard";
6397 }
6398 else
6399 {
6400 bool mustHaveUK = false;
6401 privilege_t oldPriv = PRIV_UNKNOWN;
6402 if (chats.find(chatid) == chats.end())
6403 {
6404 chats[chatid] = new TextChat();
6405 mustHaveUK = true;
6406 }
6407 else
6408 {
6409 oldPriv = chats[chatid]->priv;
6410 }
6411
6412 TextChat *chat = chats[chatid];
6413 chat->id = chatid;
6414 chat->shard = shard;
6415 chat->group = group;
6416 chat->priv = PRIV_UNKNOWN;
6417 chat->ou = ou;
6418 chat->title = title;
6419 // chat->flags = ?; --> flags are received in other AP: mcfc
6420 if (ts != -1)
6421 {
6422 chat->ts = ts; // only in APs related to chat creation or when you're added to
6423 }
6424
6425 bool found = false;
6426 userpriv_vector::iterator upvit;
6427 if (userpriv)
6428 {
6429 // find 'me' in the list of participants, get my privilege and remove from peer's list
6430 for (upvit = userpriv->begin(); upvit != userpriv->end(); upvit++)
6431 {
6432 if (upvit->first == me)
6433 {
6434 found = true;
6435 mustHaveUK = (oldPriv <= PRIV_RM && upvit->second > PRIV_RM);
6436 chat->priv = upvit->second;
6437 userpriv->erase(upvit);
6438 if (userpriv->empty())
6439 {
6440 delete userpriv;
6441 userpriv = NULL;
6442 }
6443 break;
6444 }
6445 }
6446 }
6447 // if `me` is not found among participants list and there's a notification list...
6448 if (!found && upnotif)
6449 {
6450 // ...then `me` may have been removed from the chat: get the privilege level=PRIV_RM
6451 for (upvit = upnotif->begin(); upvit != upnotif->end(); upvit++)
6452 {
6453 if (upvit->first == me)
6454 {
6455 mustHaveUK = (oldPriv <= PRIV_RM && upvit->second > PRIV_RM);
6456 chat->priv = upvit->second;
6457 break;
6458 }
6459 }
6460 }
6461
6462 if (chat->priv == PRIV_RM)
6463 {
6464 // clear the list of peers because API still includes peers in the
6465 // actionpacket, but not in a fresh fetchnodes
6466 delete userpriv;
6467 userpriv = NULL;
6468 }
6469
6470 delete chat->userpriv; // discard any existing `userpriv`
6471 chat->userpriv = userpriv;
6472
6473 if (readingPublicChat)
6474 {
6475 chat->setMode(publicchat);
6476 if (!unifiedkey.empty()) // not all actionpackets include it
6477 {
6478 chat->unifiedKey = unifiedkey;
6479 }
6480 else if (mustHaveUK)
6481 {
6482 LOG_err << "Public chat without unified key detected";
6483 }
6484 }
6485
6486 chat->setTag(0); // external change
6487 notifychat(chat);
6488 }
6489
6490 delete upnotif;
6491 break;
6492
6493 default:
6494 if (!jsonsc.storeobject())
6495 {
6496 delete upnotif;
6497 return;
6498 }
6499 }
6500 }
6501 }
6502
sc_chatnode()6503 void MegaClient::sc_chatnode()
6504 {
6505 handle chatid = UNDEF;
6506 handle h = UNDEF;
6507 handle uh = UNDEF;
6508 bool r = false;
6509 bool g = false;
6510
6511 for (;;)
6512 {
6513 switch (jsonsc.getnameid())
6514 {
6515 case 'g':
6516 // access granted
6517 g = jsonsc.getint();
6518 break;
6519
6520 case 'r':
6521 // access revoked
6522 r = jsonsc.getint();
6523 break;
6524
6525 case MAKENAMEID2('i','d'):
6526 chatid = jsonsc.gethandle(MegaClient::CHATHANDLE);
6527 break;
6528
6529 case 'n':
6530 h = jsonsc.gethandle(MegaClient::NODEHANDLE);
6531 break;
6532
6533 case 'u':
6534 uh = jsonsc.gethandle(MegaClient::USERHANDLE);
6535 break;
6536
6537 case EOO:
6538 if (chatid != UNDEF && h != UNDEF && uh != UNDEF && (r || g))
6539 {
6540 textchat_map::iterator it = chats.find(chatid);
6541 if (it == chats.end())
6542 {
6543 LOG_err << "Unknown chat for user/node access to attachment";
6544 return;
6545 }
6546
6547 TextChat *chat = it->second;
6548 if (r) // access revoked
6549 {
6550 if(!chat->setNodeUserAccess(h, uh, true))
6551 {
6552 LOG_err << "Unknown user/node at revoke access to attachment";
6553 }
6554 }
6555 else // access granted
6556 {
6557 chat->setNodeUserAccess(h, uh);
6558 }
6559
6560 chat->setTag(0); // external change
6561 notifychat(chat);
6562 }
6563 else
6564 {
6565 LOG_err << "Failed to parse attached node information";
6566 }
6567 return;
6568
6569 default:
6570 if (!jsonsc.storeobject())
6571 {
6572 return;
6573 }
6574 }
6575 }
6576 }
6577
sc_chatflags()6578 void MegaClient::sc_chatflags()
6579 {
6580 bool done = false;
6581 handle chatid = UNDEF;
6582 byte flags = 0;
6583 while(!done)
6584 {
6585 switch (jsonsc.getnameid())
6586 {
6587 case MAKENAMEID2('i','d'):
6588 chatid = jsonsc.gethandle(MegaClient::CHATHANDLE);
6589 break;
6590
6591 case 'f':
6592 flags = byte(jsonsc.getint());
6593 break;
6594
6595 case EOO:
6596 {
6597 done = true;
6598 textchat_map::iterator it = chats.find(chatid);
6599 if (it == chats.end())
6600 {
6601 string chatidB64;
6602 string tmp((const char*)&chatid, sizeof(chatid));
6603 Base64::btoa(tmp, chatidB64);
6604 LOG_err << "Received flags for unknown chatid: " << chatidB64.c_str();
6605 break;
6606 }
6607
6608 TextChat *chat = chats[chatid];
6609 chat->setFlags(flags);
6610
6611 chat->setTag(0); // external change
6612 notifychat(chat);
6613 break;
6614 }
6615
6616 default:
6617 if (!jsonsc.storeobject())
6618 {
6619 return;
6620 }
6621 break;
6622 }
6623 }
6624 }
6625
6626 #endif
6627
sc_uac()6628 void MegaClient::sc_uac()
6629 {
6630 string email;
6631 for (;;)
6632 {
6633 switch (jsonsc.getnameid())
6634 {
6635 case 'm':
6636 jsonsc.storeobject(&email);
6637 break;
6638
6639 case EOO:
6640 if (email.empty())
6641 {
6642 LOG_warn << "Missing email address in `uac` action packet";
6643 }
6644 app->account_updated();
6645 app->notify_confirmation(email.c_str());
6646 return;
6647
6648 default:
6649 if (!jsonsc.storeobject())
6650 {
6651 LOG_warn << "Failed to parse `uac` action packet";
6652 return;
6653 }
6654 }
6655 }
6656 }
6657
sc_la()6658 void MegaClient::sc_la()
6659 {
6660 for (;;)
6661 {
6662 switch (jsonsc.getnameid())
6663 {
6664 case EOO:
6665 useralerts.onAcknowledgeReceived();
6666 return;
6667
6668 default:
6669 if (!jsonsc.storeobject())
6670 {
6671 LOG_warn << "Failed to parse `la` action packet";
6672 return;
6673 }
6674 }
6675 }
6676 }
6677
sc_ub()6678 void MegaClient::sc_ub()
6679 {
6680 BizStatus status = BIZ_STATUS_UNKNOWN;
6681 BizMode mode = BIZ_MODE_UNKNOWN;
6682 BizStatus prevBizStatus = mBizStatus;
6683 for (;;)
6684 {
6685 switch (jsonsc.getnameid())
6686 {
6687 case 's':
6688 status = BizStatus(jsonsc.getint());
6689 break;
6690
6691 case 'm':
6692 mode = BizMode(jsonsc.getint());
6693 break;
6694
6695 case EOO:
6696 if ((status < BIZ_STATUS_EXPIRED || status > BIZ_STATUS_GRACE_PERIOD))
6697 {
6698 std::string err = "Missing or invalid status in `ub` action packet";
6699 LOG_err << err;
6700 sendevent(99449, err.c_str(), 0);
6701 return;
6702 }
6703 if ( (mode != BIZ_MODE_MASTER && mode != BIZ_MODE_SUBUSER)
6704 && (status != BIZ_STATUS_INACTIVE) ) // when inactive, `m` might be missing (unknown/undefined)
6705 {
6706 LOG_err << "Unexpected mode for business account at `ub`. Mode: " << mode;
6707 return;
6708 }
6709
6710 mBizStatus = status;
6711 mBizMode = mode;
6712
6713 if (mBizMode != BIZ_MODE_UNKNOWN)
6714 {
6715 LOG_info << "Disable achievements for business account type";
6716 achievements_enabled = false;
6717 }
6718
6719 // FIXME: if API decides to include the expiration ts, remove the block below
6720 if (mBizStatus == BIZ_STATUS_ACTIVE)
6721 {
6722 // If new status is active, reset timestamps of transitions
6723 mBizGracePeriodTs = 0;
6724 mBizExpirationTs = 0;
6725 }
6726
6727 app->notify_business_status(mBizStatus);
6728 if (prevBizStatus == BIZ_STATUS_INACTIVE)
6729 {
6730 app->account_updated();
6731 getuserdata(); // update account flags
6732 }
6733
6734 return;
6735
6736 default:
6737 if (!jsonsc.storeobject())
6738 {
6739 LOG_warn << "Failed to parse `ub` action packet";
6740 return;
6741 }
6742 }
6743 }
6744
6745 }
6746
6747 // scan notified nodes for
6748 // - name differences with an existing LocalNode
6749 // - appearance of new folders
6750 // - (re)appearance of files
6751 // - deletions
6752 // purge removed nodes after notification
notifypurge(void)6753 void MegaClient::notifypurge(void)
6754 {
6755 int i, t;
6756
6757 handle tscsn = cachedscsn;
6758
6759 if (scsn.ready()) tscsn = scsn.getHandle();
6760
6761 if (nodenotify.size() || usernotify.size() || pcrnotify.size()
6762 #ifdef ENABLE_CHAT
6763 || chatnotify.size()
6764 #endif
6765 || cachedscsn != tscsn)
6766 {
6767 updatesc();
6768
6769 #ifdef ENABLE_SYNC
6770 // update LocalNode <-> Node associations
6771 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
6772 {
6773 (*it)->cachenodes();
6774 }
6775 #endif
6776 }
6777
6778 if ((t = int(nodenotify.size())))
6779 {
6780 #ifdef ENABLE_SYNC
6781 // check for deleted syncs
6782 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
6783 {
6784 if (((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN)
6785 && (*it)->localroot->node->changed.removed)
6786 {
6787 delsync(*it);
6788 }
6789 }
6790 #endif
6791 applykeys();
6792
6793 if (!fetchingnodes)
6794 {
6795 app->nodes_updated(&nodenotify[0], t);
6796 }
6797
6798 // check all notified nodes for removed status and purge
6799 for (i = 0; i < t; i++)
6800 {
6801 Node* n = nodenotify[i];
6802 if (n->attrstring)
6803 {
6804 LOG_err << "NO_KEY node: " << n->type << " " << n->size << " " << n->nodehandle << " " << n->nodekeyUnchecked().size();
6805 #ifdef ENABLE_SYNC
6806 if (n->localnode)
6807 {
6808 LOG_err << "LocalNode: " << n->localnode->name << " " << n->localnode->type << " " << n->localnode->size;
6809 }
6810 #endif
6811 }
6812
6813 if (n->changed.removed)
6814 {
6815 // remove inbound share
6816 if (n->inshare)
6817 {
6818 n->inshare->user->sharing.erase(n->nodehandle);
6819 notifyuser(n->inshare->user);
6820 }
6821
6822 nodes.erase(n->nodehandle);
6823 delete n;
6824 }
6825 else
6826 {
6827 n->notified = false;
6828 memset(&(n->changed), 0, sizeof(n->changed));
6829 n->tag = 0;
6830 }
6831 }
6832
6833 nodenotify.clear();
6834 }
6835
6836 if ((t = int(pcrnotify.size())))
6837 {
6838 if (!fetchingnodes)
6839 {
6840 app->pcrs_updated(&pcrnotify[0], t);
6841 }
6842
6843 // check all notified nodes for removed status and purge
6844 for (i = 0; i < t; i++)
6845 {
6846 PendingContactRequest* pcr = pcrnotify[i];
6847
6848 if (pcr->removed())
6849 {
6850 pcrindex.erase(pcr->id);
6851 delete pcr;
6852 }
6853 else
6854 {
6855 pcr->notified = false;
6856 memset(&(pcr->changed), 0, sizeof(pcr->changed));
6857 }
6858 }
6859
6860 pcrnotify.clear();
6861 }
6862
6863 // users are never deleted (except at account cancellation)
6864 if ((t = int(usernotify.size())))
6865 {
6866 if (!fetchingnodes)
6867 {
6868 app->users_updated(&usernotify[0], t);
6869 }
6870
6871 for (i = 0; i < t; i++)
6872 {
6873 User *u = usernotify[i];
6874
6875 u->notified = false;
6876 u->resetTag();
6877 memset(&(u->changed), 0, sizeof(u->changed));
6878
6879 if (u->show == INACTIVE && u->userhandle != me)
6880 {
6881 // delete any remaining shares with this user
6882 for (handle_set::iterator it = u->sharing.begin(); it != u->sharing.end(); it++)
6883 {
6884 Node *n = nodebyhandle(*it);
6885 if (n && !n->changed.removed)
6886 {
6887 sendevent(99435, "Orphan incoming share", 0);
6888 }
6889 }
6890 u->sharing.clear();
6891
6892 discarduser(u->userhandle, false);
6893 }
6894 }
6895
6896 usernotify.clear();
6897 }
6898
6899 if ((t = int(useralerts.useralertnotify.size())))
6900 {
6901 LOG_debug << "Notifying " << t << " user alerts";
6902 app->useralerts_updated(&useralerts.useralertnotify[0], t);
6903
6904 for (i = 0; i < t; i++)
6905 {
6906 UserAlert::Base *ua = useralerts.useralertnotify[i];
6907 ua->tag = -1;
6908 }
6909
6910 useralerts.useralertnotify.clear();
6911 }
6912
6913 #ifdef ENABLE_CHAT
6914 if ((t = int(chatnotify.size())))
6915 {
6916 if (!fetchingnodes)
6917 {
6918 app->chats_updated(&chatnotify, t);
6919 }
6920
6921 for (textchat_map::iterator it = chatnotify.begin(); it != chatnotify.end(); it++)
6922 {
6923 TextChat *chat = it->second;
6924
6925 chat->notified = false;
6926 chat->resetTag();
6927 memset(&(chat->changed), 0, sizeof(chat->changed));
6928 }
6929
6930 chatnotify.clear();
6931 }
6932 #endif
6933
6934 totalNodes = nodes.size();
6935 }
6936
6937 // return node pointer derived from node handle
nodebyhandle(handle h)6938 Node* MegaClient::nodebyhandle(handle h)
6939 {
6940 node_map::iterator it;
6941
6942 if ((it = nodes.find(h)) != nodes.end())
6943 {
6944 return it->second;
6945 }
6946
6947 return NULL;
6948 }
6949
6950 // server-client deletion
sc_deltree()6951 Node* MegaClient::sc_deltree()
6952 {
6953 Node* n = NULL;
6954 handle originatingUser = UNDEF;
6955
6956 for (;;)
6957 {
6958 switch (jsonsc.getnameid())
6959 {
6960 case 'n':
6961 handle h;
6962
6963 if (!ISUNDEF((h = jsonsc.gethandle())))
6964 {
6965 n = nodebyhandle(h);
6966 }
6967 break;
6968
6969 case MAKENAMEID2('o', 'u'):
6970 originatingUser = jsonsc.gethandle(USERHANDLE);
6971 break;
6972
6973 case EOO:
6974 if (n)
6975 {
6976 TreeProcDel td;
6977 useralerts.beginNotingSharedNodes();
6978
6979 int creqtag = reqtag;
6980 reqtag = 0;
6981 proctree(n, &td);
6982 reqtag = creqtag;
6983
6984 useralerts.convertNotedSharedNodes(false, originatingUser);
6985 }
6986 return n;
6987
6988 default:
6989 if (!jsonsc.storeobject())
6990 {
6991 return NULL;
6992 }
6993 }
6994 }
6995 }
6996
6997 // generate handle authentication token
handleauth(handle h,byte * auth)6998 void MegaClient::handleauth(handle h, byte* auth)
6999 {
7000 Base64::btoa((byte*)&h, NODEHANDLE, (char*)auth);
7001 memcpy(auth + sizeof h, auth, sizeof h);
7002 key.ecb_encrypt(auth);
7003 }
7004
7005 // make attribute string; add magic number prefix
makeattr(SymmCipher * key,string * attrstring,const char * json,int l) const7006 void MegaClient::makeattr(SymmCipher* key, string* attrstring, const char* json, int l) const
7007 {
7008 if (l < 0)
7009 {
7010 l = int(strlen(json));
7011 }
7012 int ll = (l + 6 + SymmCipher::KEYLENGTH - 1) & - SymmCipher::KEYLENGTH;
7013 byte* buf = new byte[ll];
7014
7015 memcpy(buf, "MEGA{", 5); // check for the presence of the magic number "MEGA"
7016 memcpy(buf + 5, json, l);
7017 buf[l + 5] = '}';
7018 memset(buf + 6 + l, 0, ll - l - 6);
7019
7020 key->cbc_encrypt(buf, ll);
7021
7022 attrstring->assign((char*)buf, ll);
7023
7024 delete[] buf;
7025 }
7026
makeattr(SymmCipher * key,const std::unique_ptr<string> & attrstring,const char * json,int l) const7027 void MegaClient::makeattr(SymmCipher* key, const std::unique_ptr<string>& attrstring, const char* json, int l) const
7028 {
7029 makeattr(key, attrstring.get(), json, l);
7030 }
7031
7032 // update node attributes
7033 // (with speculative instant completion)
setattr(Node * n,const char * prevattr)7034 error MegaClient::setattr(Node* n, const char *prevattr)
7035 {
7036 if (ststatus == STORAGE_PAYWALL)
7037 {
7038 return API_EPAYWALL;
7039 }
7040
7041 if (!checkaccess(n, FULL))
7042 {
7043 return API_EACCESS;
7044 }
7045
7046 SymmCipher* cipher;
7047
7048 if (!(cipher = n->nodecipher()))
7049 {
7050 return API_EKEY;
7051 }
7052
7053 n->changed.attrs = true;
7054 n->tag = reqtag;
7055 notifynode(n);
7056
7057 reqs.add(new CommandSetAttr(this, n, cipher, prevattr));
7058
7059 return API_OK;
7060 }
7061
putnodes_prepareOneFolder(NewNode * newnode,std::string foldername)7062 void MegaClient::putnodes_prepareOneFolder(NewNode* newnode, std::string foldername)
7063 {
7064 string attrstring;
7065 byte buf[FOLDERNODEKEYLENGTH];
7066
7067 // set up new node as folder node
7068 newnode->source = NEW_NODE;
7069 newnode->type = FOLDERNODE;
7070 newnode->nodehandle = 0;
7071 newnode->parenthandle = UNDEF;
7072
7073 // generate fresh random key for this folder node
7074 rng.genblock(buf, FOLDERNODEKEYLENGTH);
7075 newnode->nodekey.assign((char*)buf, FOLDERNODEKEYLENGTH);
7076 tmpnodecipher.setkey(buf);
7077
7078 // generate fresh attribute object with the folder name
7079 AttrMap attrs;
7080
7081 fsaccess->normalize(&foldername);
7082 attrs.map['n'] = foldername;
7083
7084 // JSON-encode object and encrypt attribute string
7085 attrs.getjson(&attrstring);
7086 newnode->attrstring.reset(new string);
7087 makeattr(&tmpnodecipher, newnode->attrstring, attrstring.c_str());
7088 }
7089
7090 // send new nodes to API for processing
putnodes(handle h,NewNode * newnodes,int numnodes,const char * cauth)7091 void MegaClient::putnodes(handle h, NewNode* newnodes, int numnodes, const char *cauth)
7092 {
7093 reqs.add(new CommandPutNodes(this, h, NULL, newnodes, numnodes, reqtag, PUTNODES_APP, cauth));
7094 }
7095
7096 // drop nodes into a user's inbox (must have RSA keypair)
putnodes(const char * user,NewNode * newnodes,int numnodes)7097 void MegaClient::putnodes(const char* user, NewNode* newnodes, int numnodes)
7098 {
7099 User* u;
7100
7101 restag = reqtag;
7102
7103 if (!(u = finduser(user, 0)) && !user)
7104 {
7105 return app->putnodes_result(API_EARGS, USER_HANDLE, newnodes);
7106 }
7107
7108 queuepubkeyreq(user, ::mega::make_unique<PubKeyActionPutNodes>(newnodes, numnodes, reqtag));
7109 }
7110
7111 // returns 1 if node has accesslevel a or better, 0 otherwise
checkaccess(Node * n,accesslevel_t a)7112 int MegaClient::checkaccess(Node* n, accesslevel_t a)
7113 {
7114 // folder link access is always read-only - ignore login status during
7115 // initial tree fetch
7116 if ((a < OWNERPRELOGIN) && !loggedin())
7117 {
7118 return a == RDONLY;
7119 }
7120
7121 // trace back to root node (always full access) or share node
7122 while (n)
7123 {
7124 if (n->inshare)
7125 {
7126 return n->inshare->access >= a;
7127 }
7128
7129 if (!n->parent)
7130 {
7131 return n->type > FOLDERNODE;
7132 }
7133
7134 n = n->parent;
7135 }
7136
7137 return 0;
7138 }
7139
7140 // returns API_OK if a move operation is permitted, API_EACCESS or
7141 // API_ECIRCULAR otherwise. Also returns API_EPAYWALL if in PAYWALL.
checkmove(Node * fn,Node * tn)7142 error MegaClient::checkmove(Node* fn, Node* tn)
7143 {
7144 // precondition #0: not in paywall
7145 if (ststatus == STORAGE_PAYWALL)
7146 {
7147 return API_EPAYWALL;
7148 }
7149
7150 // condition #1: cannot move top-level node, must have full access to fn's
7151 // parent
7152 if (!fn->parent || !checkaccess(fn->parent, FULL))
7153 {
7154 return API_EACCESS;
7155 }
7156
7157 // condition #2: target must be folder
7158 if (tn->type == FILENODE)
7159 {
7160 return API_EACCESS;
7161 }
7162
7163 // condition #3: must have write access to target
7164 if (!checkaccess(tn, RDWR))
7165 {
7166 return API_EACCESS;
7167 }
7168
7169 // condition #4: source can't be a version
7170 if (fn->parent->type == FILENODE)
7171 {
7172 return API_EACCESS;
7173 }
7174
7175 // condition #5: tn must not be below fn (would create circular linkage)
7176 for (;;)
7177 {
7178 if (tn == fn)
7179 {
7180 return API_ECIRCULAR;
7181 }
7182
7183 if (tn->inshare || !tn->parent)
7184 {
7185 break;
7186 }
7187
7188 tn = tn->parent;
7189 }
7190
7191 // condition #6: fn and tn must be in the same tree (same ultimate parent
7192 // node or shared by the same user)
7193 for (;;)
7194 {
7195 if (fn->inshare || !fn->parent)
7196 {
7197 break;
7198 }
7199
7200 fn = fn->parent;
7201 }
7202
7203 // moves within the same tree or between the user's own trees are permitted
7204 if (fn == tn || (!fn->inshare && !tn->inshare))
7205 {
7206 return API_OK;
7207 }
7208
7209 // moves between inbound shares from the same user are permitted
7210 if (fn->inshare && tn->inshare && fn->inshare->user == tn->inshare->user)
7211 {
7212 return API_OK;
7213 }
7214
7215 return API_EACCESS;
7216 }
7217
7218 // move node to new parent node (for changing the filename, use setattr and
7219 // modify the 'n' attribute)
rename(Node * n,Node * p,syncdel_t syncdel,handle prevparent,const char * newName)7220 error MegaClient::rename(Node* n, Node* p, syncdel_t syncdel, handle prevparent, const char *newName)
7221 {
7222 error e;
7223
7224 if ((e = checkmove(n, p)))
7225 {
7226 return e;
7227 }
7228
7229 Node *prevParent = NULL;
7230 if (!ISUNDEF(prevparent))
7231 {
7232 prevParent = nodebyhandle(prevparent);
7233 }
7234 else
7235 {
7236 prevParent = n->parent;
7237 }
7238
7239 if (n->setparent(p))
7240 {
7241 bool updateNodeAttributes = false;
7242 if (prevParent)
7243 {
7244 Node *prevRoot = getrootnode(prevParent);
7245 Node *newRoot = getrootnode(p);
7246 handle rubbishHandle = rootnodes[RUBBISHNODE - ROOTNODE];
7247 nameid rrname = AttrMap::string2nameid("rr");
7248
7249 if (prevRoot->nodehandle != rubbishHandle
7250 && p->nodehandle == rubbishHandle)
7251 {
7252 // deleted node
7253 char base64Handle[12];
7254 Base64::btoa((byte*)&prevParent->nodehandle, MegaClient::NODEHANDLE, base64Handle);
7255 if (strcmp(base64Handle, n->attrs.map[rrname].c_str()))
7256 {
7257 LOG_debug << "Adding rr attribute";
7258 n->attrs.map[rrname] = base64Handle;
7259 updateNodeAttributes = true;
7260 }
7261 }
7262 else if (prevRoot->nodehandle == rubbishHandle
7263 && newRoot->nodehandle != rubbishHandle)
7264 {
7265 // undeleted node
7266 attr_map::iterator it = n->attrs.map.find(rrname);
7267 if (it != n->attrs.map.end())
7268 {
7269 LOG_debug << "Removing rr attribute";
7270 n->attrs.map.erase(it);
7271 updateNodeAttributes = true;
7272 }
7273 }
7274 }
7275
7276 if (newName)
7277 {
7278 string name(newName);
7279 fsaccess->normalize(&name);
7280 n->attrs.map['n'] = name;
7281 updateNodeAttributes = true;
7282 }
7283
7284 n->changed.parent = true;
7285 n->tag = reqtag;
7286 notifynode(n);
7287
7288 // rewrite keys of foreign nodes that are moved out of an outbound share
7289 rewriteforeignkeys(n);
7290
7291 reqs.add(new CommandMoveNode(this, n, p, syncdel, prevparent));
7292 if (updateNodeAttributes)
7293 {
7294 setattr(n);
7295 }
7296 }
7297
7298 return API_OK;
7299 }
7300
7301 // delete node tree
unlink(Node * n,bool keepversions)7302 error MegaClient::unlink(Node* n, bool keepversions)
7303 {
7304 if (!n->inshare && !checkaccess(n, FULL))
7305 {
7306 return API_EACCESS;
7307 }
7308
7309 if (mBizStatus > BIZ_STATUS_INACTIVE
7310 && mBizMode == BIZ_MODE_SUBUSER && n->inshare
7311 && mBizMasters.find(n->inshare->user->userhandle) != mBizMasters.end())
7312 {
7313 // business subusers cannot leave inshares from master biz users
7314 return API_EMASTERONLY;
7315 }
7316
7317 if (ststatus == STORAGE_PAYWALL)
7318 {
7319 return API_EPAYWALL;
7320 }
7321
7322 bool kv = (keepversions && n->type == FILENODE);
7323 reqs.add(new CommandDelNode(this, n->nodehandle, kv));
7324
7325 mergenewshares(1);
7326
7327 if (kv)
7328 {
7329 Node *newerversion = n->parent;
7330 if (n->children.size())
7331 {
7332 Node *olderversion = n->children.back();
7333 olderversion->setparent(newerversion);
7334 olderversion->changed.parent = true;
7335 olderversion->tag = reqtag;
7336 notifynode(olderversion);
7337 }
7338 }
7339
7340 TreeProcDel td;
7341 proctree(n, &td);
7342
7343 return API_OK;
7344 }
7345
unlinkversions()7346 void MegaClient::unlinkversions()
7347 {
7348 reqs.add(new CommandDelVersions(this));
7349 }
7350
7351 // Converts a string in UTF8 to array of int32 in the same way than Webclient converts a string in UTF16 to array of 32-bit elements
7352 // (returns NULL if the input is invalid UTF-8)
7353 // unfortunately, discards bits 8-31 of multibyte characters for backwards compatibility
utf8_to_a32forjs(const char * str,int * len)7354 char* MegaClient::utf8_to_a32forjs(const char* str, int* len)
7355 {
7356 if (!str)
7357 {
7358 return NULL;
7359 }
7360
7361 int t = int(strlen(str));
7362 int t2 = 4 * ((t + 3) >> 2);
7363 char* result = new char[t2]();
7364 uint32_t* a32 = (uint32_t*)result;
7365 uint32_t unicode;
7366
7367 int i = 0;
7368 int j = 0;
7369
7370 while (i < t)
7371 {
7372 char c = str[i++] & 0xff;
7373
7374 if (!(c & 0x80))
7375 {
7376 unicode = c & 0xff;
7377 }
7378 else if ((c & 0xe0) == 0xc0)
7379 {
7380 if (i >= t || (str[i] & 0xc0) != 0x80)
7381 {
7382 delete[] result;
7383 return NULL;
7384 }
7385
7386 unicode = (c & 0x1f) << 6;
7387 unicode |= str[i++] & 0x3f;
7388 }
7389 else if ((c & 0xf0) == 0xe0)
7390 {
7391 if (i + 2 > t || (str[i] & 0xc0) != 0x80 || (str[i + 1] & 0xc0) != 0x80)
7392 {
7393 delete[] result;
7394 return NULL;
7395 }
7396
7397 unicode = (c & 0x0f) << 12;
7398 unicode |= (str[i++] & 0x3f) << 6;
7399 unicode |= str[i++] & 0x3f;
7400 }
7401 else if ((c & 0xf8) == 0xf0)
7402 {
7403 if (i + 3 > t
7404 || (str[i] & 0xc0) != 0x80
7405 || (str[i + 1] & 0xc0) != 0x80
7406 || (str[i + 2] & 0xc0) != 0x80)
7407 {
7408 delete[] result;
7409 return NULL;
7410 }
7411
7412 unicode = (c & 0x07) << 18;
7413 unicode |= (str[i++] & 0x3f) << 12;
7414 unicode |= (str[i++] & 0x3f) << 6;
7415 unicode |= str[i++] & 0x3f;
7416
7417 // management of surrogate pairs like the JavaScript code
7418 uint32_t hi = 0xd800 | ((unicode >> 10) & 0x3F) | (((unicode >> 16) - 1) << 6);
7419 uint32_t low = 0xdc00 | (unicode & 0x3ff);
7420
7421 a32[j >> 2] |= htonl(hi << (24 - (j & 3) * 8));
7422 j++;
7423
7424 unicode = low;
7425 }
7426 else
7427 {
7428 delete[] result;
7429 return NULL;
7430 }
7431
7432 a32[j >> 2] |= htonl(unicode << (24 - (j & 3) * 8));
7433 j++;
7434 }
7435
7436 *len = j;
7437 return result;
7438 }
7439
7440 // compute UTF-8 password hash
pw_key(const char * utf8pw,byte * key) const7441 error MegaClient::pw_key(const char* utf8pw, byte* key) const
7442 {
7443 int t;
7444 char* pw;
7445
7446 if (!(pw = utf8_to_a32forjs(utf8pw, &t)))
7447 {
7448 return API_EARGS;
7449 }
7450
7451 int n = (t + 15) / 16;
7452 SymmCipher* keys = new SymmCipher[n];
7453
7454 for (int i = 0; i < n; i++)
7455 {
7456 int valid = (i != (n - 1)) ? SymmCipher::BLOCKSIZE : (t - SymmCipher::BLOCKSIZE * i);
7457 memcpy(key, pw + i * SymmCipher::BLOCKSIZE, valid);
7458 memset(key + valid, 0, SymmCipher::BLOCKSIZE - valid);
7459 keys[i].setkey(key);
7460 }
7461
7462 memcpy(key, "\x93\xC4\x67\xE3\x7D\xB0\xC7\xA4\xD1\xBE\x3F\x81\x01\x52\xCB\x56", SymmCipher::BLOCKSIZE);
7463
7464 for (int r = 65536; r--; )
7465 {
7466 for (int i = 0; i < n; i++)
7467 {
7468 keys[i].ecb_encrypt(key);
7469 }
7470 }
7471
7472 delete[] keys;
7473 delete[] pw;
7474
7475 return API_OK;
7476 }
7477
7478 // compute generic string hash
stringhash(const char * s,byte * hash,SymmCipher * cipher)7479 void MegaClient::stringhash(const char* s, byte* hash, SymmCipher* cipher)
7480 {
7481 int t;
7482
7483 t = strlen(s) & - SymmCipher::BLOCKSIZE;
7484
7485 strncpy((char*)hash, s + t, SymmCipher::BLOCKSIZE);
7486
7487 while (t)
7488 {
7489 t -= SymmCipher::BLOCKSIZE;
7490 SymmCipher::xorblock((byte*)s + t, hash);
7491 }
7492
7493 for (t = 16384; t--; )
7494 {
7495 cipher->ecb_encrypt(hash);
7496 }
7497
7498 memcpy(hash + 4, hash + 8, 4);
7499 }
7500
7501 // (transforms s to lowercase)
stringhash64(string * s,SymmCipher * c)7502 uint64_t MegaClient::stringhash64(string* s, SymmCipher* c)
7503 {
7504 byte hash[SymmCipher::KEYLENGTH];
7505
7506 tolower_string(*s);
7507 stringhash(s->c_str(), hash, c);
7508
7509 return MemAccess::get<uint64_t>((const char*)hash);
7510 }
7511
7512 // read and add/verify node array
readnodes(JSON * j,int notify,putsource_t source,NewNode * nn,int nnsize,int tag,bool applykeys)7513 int MegaClient::readnodes(JSON* j, int notify, putsource_t source, NewNode* nn, int nnsize, int tag, bool applykeys)
7514 {
7515 if (!j->enterarray())
7516 {
7517 return 0;
7518 }
7519
7520 node_vector dp;
7521 Node* n;
7522
7523 while (j->enterobject())
7524 {
7525 handle h = UNDEF, ph = UNDEF;
7526 handle u = 0, su = UNDEF;
7527 nodetype_t t = TYPE_UNKNOWN;
7528 const char* a = NULL;
7529 const char* k = NULL;
7530 const char* fa = NULL;
7531 const char *sk = NULL;
7532 accesslevel_t rl = ACCESS_UNKNOWN;
7533 m_off_t s = NEVER;
7534 m_time_t ts = -1, sts = -1;
7535 nameid name;
7536 int nni = -1;
7537
7538 while ((name = j->getnameid()) != EOO)
7539 {
7540 switch (name)
7541 {
7542 case 'h': // new node: handle
7543 h = j->gethandle();
7544 break;
7545
7546 case 'p': // parent node
7547 ph = j->gethandle();
7548 break;
7549
7550 case 'u': // owner user
7551 u = j->gethandle(USERHANDLE);
7552 break;
7553
7554 case 't': // type
7555 t = (nodetype_t)j->getint();
7556 break;
7557
7558 case 'a': // attributes
7559 a = j->getvalue();
7560 break;
7561
7562 case 'k': // key(s)
7563 k = j->getvalue();
7564 break;
7565
7566 case 's': // file size
7567 s = j->getint();
7568 break;
7569
7570 case 'i': // related source NewNode index
7571 nni = int(j->getint());
7572 break;
7573
7574 case MAKENAMEID2('t', 's'): // actual creation timestamp
7575 ts = j->getint();
7576 break;
7577
7578 case MAKENAMEID2('f', 'a'): // file attributes
7579 fa = j->getvalue();
7580 break;
7581
7582 // inbound share attributes
7583 case 'r': // share access level
7584 rl = (accesslevel_t)j->getint();
7585 break;
7586
7587 case MAKENAMEID2('s', 'k'): // share key
7588 sk = j->getvalue();
7589 break;
7590
7591 case MAKENAMEID2('s', 'u'): // sharing user
7592 su = j->gethandle(USERHANDLE);
7593 break;
7594
7595 case MAKENAMEID3('s', 't', 's'): // share timestamp
7596 sts = j->getint();
7597 break;
7598
7599 default:
7600 if (!j->storeobject())
7601 {
7602 return 0;
7603 }
7604 }
7605 }
7606
7607 if (ISUNDEF(h))
7608 {
7609 warn("Missing node handle");
7610 }
7611 else
7612 {
7613 if (t == TYPE_UNKNOWN)
7614 {
7615 warn("Unknown node type");
7616 }
7617 else if (t == FILENODE || t == FOLDERNODE)
7618 {
7619 if (ISUNDEF(ph))
7620 {
7621 warn("Missing parent");
7622 }
7623 else if (!a)
7624 {
7625 warn("Missing node attributes");
7626 }
7627 else if (!k)
7628 {
7629 warn("Missing node key");
7630 }
7631
7632 if (t == FILENODE && ISUNDEF(s))
7633 {
7634 warn("File node without file size");
7635 }
7636 }
7637 }
7638
7639 if (fa && t != FILENODE)
7640 {
7641 warn("Spurious file attributes");
7642 }
7643
7644 if (!warnlevel())
7645 {
7646 if ((n = nodebyhandle(h)))
7647 {
7648 Node* p = NULL;
7649 if (!ISUNDEF(ph))
7650 {
7651 p = nodebyhandle(ph);
7652 }
7653
7654 if (n->changed.removed)
7655 {
7656 // node marked for deletion is being resurrected, possibly
7657 // with a new parent (server-client move operation)
7658 n->changed.removed = false;
7659 }
7660 else
7661 {
7662 // node already present - check for race condition
7663 if ((n->parent && ph != n->parent->nodehandle && p && p->type != FILENODE) || n->type != t)
7664 {
7665 app->reload("Node inconsistency");
7666
7667 static bool reloadnotified = false;
7668 if (!reloadnotified)
7669 {
7670 sendevent(99437, "Node inconsistency", 0);
7671 reloadnotified = true;
7672 }
7673 }
7674 }
7675
7676 if (!ISUNDEF(ph))
7677 {
7678 if (p)
7679 {
7680 n->setparent(p);
7681 n->changed.parent = true;
7682 }
7683 else
7684 {
7685 n->setparent(NULL);
7686 n->parenthandle = ph;
7687 dp.push_back(n);
7688 }
7689 }
7690
7691 if (a && k && n->attrstring)
7692 {
7693 LOG_warn << "Updating the key of a NO_KEY node";
7694 Node::copystring(n->attrstring.get(), a);
7695 n->setkeyfromjson(k);
7696 }
7697 }
7698 else
7699 {
7700 byte buf[SymmCipher::KEYLENGTH];
7701
7702 if (!ISUNDEF(su))
7703 {
7704 if (t != FOLDERNODE)
7705 {
7706 warn("Invalid share node type");
7707 }
7708
7709 if (rl == ACCESS_UNKNOWN)
7710 {
7711 warn("Missing access level");
7712 }
7713
7714 if (!sk)
7715 {
7716 LOG_warn << "Missing share key for inbound share";
7717 }
7718
7719 if (warnlevel())
7720 {
7721 su = UNDEF;
7722 }
7723 else
7724 {
7725 if (sk)
7726 {
7727 decryptkey(sk, buf, sizeof buf, &key, 1, h);
7728 }
7729 }
7730 }
7731
7732 string fas;
7733
7734 Node::copystring(&fas, fa);
7735
7736 // fallback timestamps
7737 if (!(ts + 1))
7738 {
7739 ts = m_time();
7740 }
7741
7742 if (!(sts + 1))
7743 {
7744 sts = ts;
7745 }
7746
7747 n = new Node(this, &dp, h, ph, t, s, u, fas.c_str(), ts);
7748 n->changed.newnode = true;
7749
7750 n->tag = tag;
7751
7752 n->attrstring.reset(new string);
7753 Node::copystring(n->attrstring.get(), a);
7754 n->setkeyfromjson(k);
7755
7756 if (!ISUNDEF(su))
7757 {
7758 newshares.push_back(new NewShare(h, 0, su, rl, sts, sk ? buf : NULL));
7759 }
7760
7761 if (u != me && !ISUNDEF(u) && !fetchingnodes)
7762 {
7763 useralerts.noteSharedNode(u, t, ts, n);
7764 }
7765
7766 if (nn && nni >= 0 && nni < nnsize)
7767 {
7768 nn[nni].added = true;
7769
7770 #ifdef ENABLE_SYNC
7771 if (source == PUTNODES_SYNC)
7772 {
7773 if (nn[nni].localnode)
7774 {
7775 // overwrites/updates: associate LocalNode with newly created Node
7776 nn[nni].localnode->setnode(n);
7777 nn[nni].localnode->treestate(TREESTATE_SYNCED);
7778
7779 // updates cache with the new node associated
7780 nn[nni].localnode->sync->statecacheadd(nn[nni].localnode);
7781 nn[nni].localnode->newnode.reset(); // localnode ptr now null also
7782 }
7783 }
7784 #endif
7785
7786 if (nn[nni].source == NEW_UPLOAD)
7787 {
7788 handle uh = nn[nni].uploadhandle;
7789
7790 // do we have pending file attributes for this upload? set them.
7791 for (fa_map::iterator it = pendingfa.lower_bound(pair<handle, fatype>(uh, fatype(0)));
7792 it != pendingfa.end() && it->first.first == uh; )
7793 {
7794 reqs.add(new CommandAttachFA(this, h, it->first.second, it->second.first, it->second.second));
7795 pendingfa.erase(it++);
7796 }
7797
7798 // FIXME: only do this for in-flight FA writes
7799 uhnh.insert(pair<handle, handle>(uh, h));
7800 }
7801 }
7802 }
7803
7804 if (notify)
7805 {
7806 notifynode(n);
7807 }
7808
7809 if (applykeys)
7810 {
7811 n->applykey();
7812 }
7813 }
7814 }
7815
7816 // any child nodes that arrived before their parents?
7817 for (size_t i = dp.size(); i--; )
7818 {
7819 if ((n = nodebyhandle(dp[i]->parenthandle)))
7820 {
7821 dp[i]->setparent(n);
7822 }
7823 }
7824
7825 return j->leavearray();
7826 }
7827
7828 // decrypt and set encrypted sharekey
setkey(SymmCipher * c,const char * k)7829 void MegaClient::setkey(SymmCipher* c, const char* k)
7830 {
7831 byte newkey[SymmCipher::KEYLENGTH];
7832
7833 if (Base64::atob(k, newkey, sizeof newkey) == sizeof newkey)
7834 {
7835 key.ecb_decrypt(newkey);
7836 c->setkey(newkey);
7837 }
7838 }
7839
7840 // read outbound share keys
readok(JSON * j)7841 void MegaClient::readok(JSON* j)
7842 {
7843 if (j->enterarray())
7844 {
7845 while (j->enterobject())
7846 {
7847 readokelement(j);
7848 }
7849
7850 j->leavearray();
7851
7852 mergenewshares(0);
7853 }
7854 }
7855
7856 // - h/ha/k (outbound sharekeys, always symmetric)
readokelement(JSON * j)7857 void MegaClient::readokelement(JSON* j)
7858 {
7859 handle h = UNDEF;
7860 byte ha[SymmCipher::BLOCKSIZE];
7861 byte buf[SymmCipher::BLOCKSIZE];
7862 int have_ha = 0;
7863 const char* k = NULL;
7864
7865 for (;;)
7866 {
7867 switch (j->getnameid())
7868 {
7869 case 'h':
7870 h = j->gethandle();
7871 break;
7872
7873 case MAKENAMEID2('h', 'a'): // share authentication tag
7874 have_ha = Base64::atob(j->getvalue(), ha, sizeof ha) == sizeof ha;
7875 break;
7876
7877 case 'k': // share key(s)
7878 k = j->getvalue();
7879 break;
7880
7881 case EOO:
7882 if (ISUNDEF(h))
7883 {
7884 LOG_warn << "Missing outgoing share handle in ok element";
7885 return;
7886 }
7887
7888 if (!k)
7889 {
7890 LOG_warn << "Missing outgoing share key in ok element";
7891 return;
7892 }
7893
7894 if (!have_ha)
7895 {
7896 LOG_warn << "Missing outbound share signature";
7897 return;
7898 }
7899
7900 if (decryptkey(k, buf, SymmCipher::KEYLENGTH, &key, 1, h))
7901 {
7902 newshares.push_back(new NewShare(h, 1, UNDEF, ACCESS_UNKNOWN, 0, buf, ha));
7903 }
7904 return;
7905
7906 default:
7907 if (!j->storeobject())
7908 {
7909 return;
7910 }
7911 }
7912 }
7913 }
7914
7915 // read outbound shares and pending shares
readoutshares(JSON * j)7916 void MegaClient::readoutshares(JSON* j)
7917 {
7918 if (j->enterarray())
7919 {
7920 while (j->enterobject())
7921 {
7922 readoutshareelement(j);
7923 }
7924
7925 j->leavearray();
7926
7927 mergenewshares(0);
7928 }
7929 }
7930
7931 // - h/u/r/ts/p (outbound share or pending share)
readoutshareelement(JSON * j)7932 void MegaClient::readoutshareelement(JSON* j)
7933 {
7934 handle h = UNDEF;
7935 handle uh = UNDEF;
7936 handle p = UNDEF;
7937 accesslevel_t r = ACCESS_UNKNOWN;
7938 m_time_t ts = 0;
7939
7940 for (;;)
7941 {
7942 switch (j->getnameid())
7943 {
7944 case 'h':
7945 h = j->gethandle();
7946 break;
7947
7948 case 'p':
7949 p = j->gethandle(PCRHANDLE);
7950 break;
7951
7952 case 'u': // share target user
7953 uh = j->is(EXPORTEDLINK) ? 0 : j->gethandle(USERHANDLE);
7954 break;
7955
7956 case 'r': // access
7957 r = (accesslevel_t)j->getint();
7958 break;
7959
7960 case MAKENAMEID2('t', 's'): // timestamp
7961 ts = j->getint();
7962 break;
7963
7964 case EOO:
7965 if (ISUNDEF(h))
7966 {
7967 LOG_warn << "Missing outgoing share node";
7968 return;
7969 }
7970
7971 if (ISUNDEF(uh) && ISUNDEF(p))
7972 {
7973 LOG_warn << "Missing outgoing share user";
7974 return;
7975 }
7976
7977 if (r == ACCESS_UNKNOWN)
7978 {
7979 LOG_warn << "Missing outgoing share access";
7980 return;
7981 }
7982
7983 newshares.push_back(new NewShare(h, 1, uh, r, ts, NULL, NULL, p));
7984 return;
7985
7986 default:
7987 if (!j->storeobject())
7988 {
7989 return;
7990 }
7991 }
7992 }
7993 }
7994
readipc(JSON * j)7995 void MegaClient::readipc(JSON *j)
7996 {
7997 // fields: ps, m, ts, uts, msg, p
7998 if (j->enterarray())
7999 {
8000 while (j->enterobject())
8001 {
8002 m_time_t ts = 0;
8003 m_time_t uts = 0;
8004 const char *m = NULL;
8005 const char *msg = NULL;
8006 handle p = UNDEF;
8007
8008 bool done = false;
8009 while (!done)
8010 {
8011 switch (j->getnameid()) {
8012 case 'm':
8013 m = j->getvalue();
8014 break;
8015 case MAKENAMEID2('t', 's'):
8016 ts = j->getint();
8017 break;
8018 case MAKENAMEID3('u', 't', 's'):
8019 uts = j->getint();
8020 break;
8021 case MAKENAMEID3('m', 's', 'g'):
8022 msg = j->getvalue();
8023 break;
8024 case 'p':
8025 p = j->gethandle(MegaClient::PCRHANDLE);
8026 break;
8027 case EOO:
8028 done = true;
8029 if (ISUNDEF(p))
8030 {
8031 LOG_err << "p element not provided";
8032 break;
8033 }
8034 if (!m)
8035 {
8036 LOG_err << "m element not provided";
8037 break;
8038 }
8039 if (ts == 0)
8040 {
8041 LOG_err << "ts element not provided";
8042 break;
8043 }
8044 if (uts == 0)
8045 {
8046 LOG_err << "uts element not provided";
8047 break;
8048 }
8049
8050 if (pcrindex[p] != NULL)
8051 {
8052 pcrindex[p]->update(m, NULL, ts, uts, msg, false);
8053 }
8054 else
8055 {
8056 pcrindex[p] = new PendingContactRequest(p, m, NULL, ts, uts, msg, false);
8057 }
8058
8059 break;
8060 default:
8061 if (!j->storeobject())
8062 {
8063 return;
8064 }
8065 }
8066 }
8067 }
8068
8069 j->leavearray();
8070 }
8071 }
8072
readopc(JSON * j)8073 void MegaClient::readopc(JSON *j)
8074 {
8075 // fields: e, m, ts, uts, rts, msg, p
8076 if (j->enterarray())
8077 {
8078 while (j->enterobject())
8079 {
8080 m_time_t ts = 0;
8081 m_time_t uts = 0;
8082 const char *e = NULL;
8083 const char *m = NULL;
8084 const char *msg = NULL;
8085 handle p = UNDEF;
8086
8087 bool done = false;
8088 while (!done)
8089 {
8090 switch (j->getnameid())
8091 {
8092 case 'e':
8093 e = j->getvalue();
8094 break;
8095 case 'm':
8096 m = j->getvalue();
8097 break;
8098 case MAKENAMEID2('t', 's'):
8099 ts = j->getint();
8100 break;
8101 case MAKENAMEID3('u', 't', 's'):
8102 uts = j->getint();
8103 break;
8104 case MAKENAMEID3('m', 's', 'g'):
8105 msg = j->getvalue();
8106 break;
8107 case 'p':
8108 p = j->gethandle(MegaClient::PCRHANDLE);
8109 break;
8110 case EOO:
8111 done = true;
8112 if (!e)
8113 {
8114 LOG_err << "e element not provided";
8115 break;
8116 }
8117 if (!m)
8118 {
8119 LOG_err << "m element not provided";
8120 break;
8121 }
8122 if (ts == 0)
8123 {
8124 LOG_err << "ts element not provided";
8125 break;
8126 }
8127 if (uts == 0)
8128 {
8129 LOG_err << "uts element not provided";
8130 break;
8131 }
8132
8133 if (pcrindex[p] != NULL)
8134 {
8135 pcrindex[p]->update(e, m, ts, uts, msg, true);
8136 }
8137 else
8138 {
8139 pcrindex[p] = new PendingContactRequest(p, e, m, ts, uts, msg, true);
8140 }
8141
8142 break;
8143 default:
8144 if (!j->storeobject())
8145 {
8146 return;
8147 }
8148 }
8149 }
8150 }
8151
8152 j->leavearray();
8153 }
8154 }
8155
readmiscflags(JSON * json)8156 error MegaClient::readmiscflags(JSON *json)
8157 {
8158 while (1)
8159 {
8160 switch (json->getnameid())
8161 {
8162 // mcs:1 --> MegaChat enabled
8163 case MAKENAMEID3('a', 'c', 'h'):
8164 achievements_enabled = bool(json->getint()); // Mega Achievements enabled
8165 break;
8166 case MAKENAMEID4('m', 'f', 'a', 'e'): // multi-factor authentication enabled
8167 gmfa_enabled = bool(json->getint());
8168 break;
8169 case MAKENAMEID4('s', 's', 'r', 's'): // server-side rubish-bin scheduler (only available when logged in)
8170 ssrs_enabled = bool(json->getint());
8171 break;
8172 case MAKENAMEID4('n', 's', 'r', 'e'): // new secure registration enabled
8173 nsr_enabled = bool(json->getint());
8174 break;
8175 case MAKENAMEID5('a', 'p', 'l', 'v', 'p'): // apple VOIP push enabled (only available when logged in)
8176 aplvp_enabled = bool(json->getint());
8177 break;
8178 case MAKENAMEID5('s', 'm', 's', 'v', 'e'): // 2 = Opt-in and unblock SMS allowed 1 = Only unblock SMS allowed 0 = No SMS allowed
8179 mSmsVerificationState = static_cast<SmsVerificationState>(json->getint());
8180 break;
8181 case MAKENAMEID4('n', 'l', 'f', 'e'): // new link format enabled
8182 mNewLinkFormat = static_cast<bool>(json->getint());
8183 break;
8184 case EOO:
8185 return API_OK;
8186 default:
8187 if (!json->storeobject())
8188 {
8189 return API_EINTERNAL;
8190 }
8191 }
8192 }
8193 }
8194
procph(JSON * j)8195 void MegaClient::procph(JSON *j)
8196 {
8197 // fields: h, ph, ets
8198 if (j->enterarray())
8199 {
8200 while (j->enterobject())
8201 {
8202 handle h = UNDEF;
8203 handle ph = UNDEF;
8204 m_time_t ets = 0;
8205 m_time_t cts = 0;
8206 Node *n = NULL;
8207 bool takendown = false;
8208
8209 bool done = false;
8210 while (!done)
8211 {
8212 switch (j->getnameid())
8213 {
8214 case 'h':
8215 h = j->gethandle(MegaClient::NODEHANDLE);
8216 break;
8217 case MAKENAMEID2('p','h'):
8218 ph = j->gethandle(MegaClient::NODEHANDLE);
8219 break;
8220 case MAKENAMEID3('e', 't', 's'):
8221 ets = j->getint();
8222 break;
8223 case MAKENAMEID2('t', 's'):
8224 cts = j->getint();
8225 break;
8226 case MAKENAMEID4('d','o','w','n'):
8227 takendown = (j->getint() == 1);
8228 break;
8229 case EOO:
8230 done = true;
8231 if (ISUNDEF(h))
8232 {
8233 LOG_err << "h element not provided";
8234 break;
8235 }
8236 if (ISUNDEF(ph))
8237 {
8238 LOG_err << "ph element not provided";
8239 break;
8240 }
8241 if (!cts)
8242 {
8243 LOG_err << "creation timestamp element not provided";
8244 break;
8245 }
8246
8247 n = nodebyhandle(h);
8248 if (n)
8249 {
8250 n->setpubliclink(ph, cts, ets, takendown);
8251 }
8252 else
8253 {
8254 LOG_warn << "node for public link not found";
8255 }
8256
8257 break;
8258 default:
8259 if (!j->storeobject())
8260 {
8261 return;
8262 }
8263 }
8264 }
8265 }
8266
8267 j->leavearray();
8268 }
8269 }
8270
applykeys()8271 void MegaClient::applykeys()
8272 {
8273 CodeCounter::ScopeTimer ccst(performanceStats.applyKeys);
8274
8275 int noKeyExpected = (rootnodes[0] != UNDEF) + (rootnodes[1] != UNDEF) + (rootnodes[2] != UNDEF);
8276
8277 if (nodes.size() > size_t(mAppliedKeyNodeCount + noKeyExpected))
8278 {
8279 for (auto& it : nodes)
8280 {
8281 it.second->applykey();
8282 }
8283 }
8284
8285 sendkeyrewrites();
8286 }
8287
sendkeyrewrites()8288 void MegaClient::sendkeyrewrites()
8289 {
8290 if (sharekeyrewrite.size())
8291 {
8292 reqs.add(new CommandShareKeyUpdate(this, &sharekeyrewrite));
8293 sharekeyrewrite.clear();
8294 }
8295
8296 if (nodekeyrewrite.size())
8297 {
8298 reqs.add(new CommandNodeKeyUpdate(this, &nodekeyrewrite));
8299 nodekeyrewrite.clear();
8300 }
8301 }
8302
8303 // user/contact list
readusers(JSON * j,bool actionpackets)8304 bool MegaClient::readusers(JSON* j, bool actionpackets)
8305 {
8306 if (!j->enterarray())
8307 {
8308 return 0;
8309 }
8310
8311 while (j->enterobject())
8312 {
8313 handle uh = 0;
8314 visibility_t v = VISIBILITY_UNKNOWN; // new share objects do not override existing visibility
8315 m_time_t ts = 0;
8316 const char* m = NULL;
8317 nameid name;
8318 BizMode bizMode = BIZ_MODE_UNKNOWN;
8319
8320 while ((name = j->getnameid()) != EOO)
8321 {
8322 switch (name)
8323 {
8324 case 'u': // new node: handle
8325 uh = j->gethandle(USERHANDLE);
8326 break;
8327
8328 case 'c': // visibility
8329 v = (visibility_t)j->getint();
8330 break;
8331
8332 case 'm': // email
8333 m = j->getvalue();
8334 break;
8335
8336 case MAKENAMEID2('t', 's'):
8337 ts = j->getint();
8338 break;
8339
8340 case 'b':
8341 {
8342 if (j->enterobject())
8343 {
8344 nameid businessName;
8345 while ((businessName = j->getnameid()) != EOO)
8346 {
8347 switch (businessName)
8348 {
8349 case 'm':
8350 bizMode = static_cast<BizMode>(j->getint());
8351 break;
8352 default:
8353 if (!j->storeobject())
8354 return false;
8355 break;
8356 }
8357 }
8358
8359 j->leaveobject();
8360 }
8361
8362 break;
8363 }
8364
8365 default:
8366 if (!j->storeobject())
8367 {
8368 return false;
8369 }
8370 }
8371 }
8372
8373 if (ISUNDEF(uh))
8374 {
8375 warn("Missing contact user handle");
8376 }
8377
8378 if (!m)
8379 {
8380 warn("Unknown contact user e-mail address");
8381 }
8382
8383 if (!warnlevel())
8384 {
8385 if (actionpackets && v >= 0 && v <= 3 && statecurrent)
8386 {
8387 string email;
8388 Node::copystring(&email, m);
8389 useralerts.add(new UserAlert::ContactChange(v, uh, email, ts, useralerts.nextId()));
8390 }
8391 User* u = finduser(uh, 0);
8392 bool notify = !u;
8393 if (u || (u = finduser(uh, 1)))
8394 {
8395 const string oldEmail = u->email;
8396 mapuser(uh, m);
8397
8398 u->mBizMode = bizMode;
8399
8400 if (v != VISIBILITY_UNKNOWN)
8401 {
8402 if (u->show != v || u->ctime != ts)
8403 {
8404 if (u->show == HIDDEN && v == VISIBLE)
8405 {
8406 u->invalidateattr(ATTR_FIRSTNAME);
8407 u->invalidateattr(ATTR_LASTNAME);
8408 if (oldEmail != u->email)
8409 {
8410 u->changed.email = true;
8411 }
8412 }
8413 else if (u->show == VISIBILITY_UNKNOWN && v == VISIBLE
8414 && uh != me
8415 && !fetchingnodes)
8416 {
8417 // new user --> fetch keys
8418 fetchContactKeys(u);
8419 }
8420
8421 u->set(v, ts);
8422 notify = true;
8423 }
8424 }
8425
8426 if (notify)
8427 {
8428 notifyuser(u);
8429 }
8430 }
8431 }
8432 }
8433
8434 return j->leavearray();
8435 }
8436
8437 // Supported formats:
8438 // - file links: #!<ph>[!<key>]
8439 // <ph>[!<key>]
8440 // /file/<ph>[<params>][#<key>]
8441 //
8442 // - folder links: #F!<ph>[!<key>]
8443 // /folder/<ph>[<params>][#<key>]
parsepubliclink(const char * link,handle & ph,byte * key,bool isFolderLink)8444 error MegaClient::parsepubliclink(const char* link, handle& ph, byte* key, bool isFolderLink)
8445 {
8446 bool isFolder;
8447 const char* ptr = nullptr;
8448 if ((ptr = strstr(link, "#F!")))
8449 {
8450 ptr += 3;
8451 isFolder = true;
8452 }
8453 else if ((ptr = strstr(link, "folder/")))
8454 {
8455 ptr += 7;
8456 isFolder = true;
8457 }
8458 else if ((ptr = strstr(link, "#!")))
8459 {
8460 ptr += 2;
8461 isFolder = false;
8462 }
8463 else if ((ptr = strstr(link, "file/")))
8464 {
8465 ptr += 5;
8466 isFolder = false;
8467 }
8468 else // legacy file link format without '#'
8469 {
8470 ptr = link;
8471 isFolder = false;
8472 }
8473
8474 if (isFolder != isFolderLink)
8475 {
8476 return API_EARGS; // type of link mismatch
8477 }
8478
8479 if (strlen(ptr) < 8) // no public handle in the link
8480 {
8481 return API_EARGS;
8482 }
8483
8484 ph = 0; //otherwise atob will give an unexpected result
8485 if (Base64::atob(ptr, (byte*)&ph, NODEHANDLE) == NODEHANDLE)
8486 {
8487 ptr += 8;
8488
8489 // skip any tracking parameter introduced by third-party websites
8490 while(*ptr && *ptr != '!' && *ptr != '#')
8491 {
8492 ptr++;
8493 }
8494
8495 if (!*ptr || ((*ptr == '#' || *ptr == '!') && *(ptr + 1) == '\0')) // no key provided
8496 {
8497 return API_EINCOMPLETE;
8498 }
8499
8500 if (*ptr == '!' || *ptr == '#')
8501 {
8502 const char *k = ptr + 1; // skip '!' or '#' separator
8503 int keylen = isFolderLink ? FOLDERNODEKEYLENGTH : FILENODEKEYLENGTH;
8504 if (Base64::atob(k, key, keylen) == keylen)
8505 {
8506 return API_OK;
8507 }
8508 }
8509 }
8510
8511 return API_EARGS;
8512 }
8513
folderaccess(const char * folderlink)8514 error MegaClient::folderaccess(const char *folderlink)
8515 {
8516 handle h = UNDEF;
8517 byte folderkey[FOLDERNODEKEYLENGTH];
8518
8519 error e;
8520 if ((e = parsepubliclink(folderlink, h, folderkey, true)) == API_OK)
8521 {
8522 setrootnode(h);
8523 key.setkey(folderkey);
8524 }
8525
8526 return e;
8527 }
8528
prelogin(const char * email)8529 void MegaClient::prelogin(const char *email)
8530 {
8531 reqs.add(new CommandPrelogin(this, email));
8532 }
8533
8534 // create new session
login(const char * email,const byte * pwkey,const char * pin)8535 void MegaClient::login(const char* email, const byte* pwkey, const char* pin)
8536 {
8537 string lcemail(email);
8538
8539 key.setkey((byte*)pwkey);
8540
8541 uint64_t emailhash = stringhash64(&lcemail, &key);
8542
8543 byte sek[SymmCipher::KEYLENGTH];
8544 rng.genblock(sek, sizeof sek);
8545
8546 reqs.add(new CommandLogin(this, email, (byte*)&emailhash, sizeof(emailhash), sek, 0, pin));
8547 }
8548
8549 // create new session (v2)
login2(const char * email,const char * password,string * salt,const char * pin)8550 void MegaClient::login2(const char *email, const char *password, string *salt, const char *pin)
8551 {
8552 string bsalt;
8553 Base64::atob(*salt, bsalt);
8554
8555 byte derivedKey[2 * SymmCipher::KEYLENGTH];
8556 CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA512> pbkdf2;
8557 pbkdf2.DeriveKey(derivedKey, sizeof(derivedKey), 0, (byte *)password, strlen(password),
8558 (const byte *)bsalt.data(), bsalt.size(), 100000);
8559
8560 login2(email, derivedKey, pin);
8561 }
8562
login2(const char * email,const byte * derivedKey,const char * pin)8563 void MegaClient::login2(const char *email, const byte *derivedKey, const char* pin)
8564 {
8565 key.setkey((byte*)derivedKey);
8566 const byte *authKey = derivedKey + SymmCipher::KEYLENGTH;
8567
8568 byte sek[SymmCipher::KEYLENGTH];
8569 rng.genblock(sek, sizeof sek);
8570
8571 reqs.add(new CommandLogin(this, email, authKey, SymmCipher::KEYLENGTH, sek, 0, pin));
8572 }
8573
fastlogin(const char * email,const byte * pwkey,uint64_t emailhash)8574 void MegaClient::fastlogin(const char* email, const byte* pwkey, uint64_t emailhash)
8575 {
8576 key.setkey((byte*)pwkey);
8577
8578 byte sek[SymmCipher::KEYLENGTH];
8579 rng.genblock(sek, sizeof sek);
8580
8581 reqs.add(new CommandLogin(this, email, (byte*)&emailhash, sizeof(emailhash), sek));
8582 }
8583
getuserdata()8584 void MegaClient::getuserdata()
8585 {
8586 cachedug = false;
8587 reqs.add(new CommandGetUserData(this));
8588 }
8589
getmiscflags()8590 void MegaClient::getmiscflags()
8591 {
8592 reqs.add(new CommandGetMiscFlags(this));
8593 }
8594
getpubkey(const char * user)8595 void MegaClient::getpubkey(const char *user)
8596 {
8597 queuepubkeyreq(user, ::mega::make_unique<PubKeyActionNotifyApp>(reqtag));
8598 }
8599
8600 // resume session - load state from local cache, if available
login(const byte * session,int size)8601 void MegaClient::login(const byte* session, int size)
8602 {
8603 int sessionversion = 0;
8604 if (size == sizeof key.key + SIDLEN + 1)
8605 {
8606 sessionversion = session[0];
8607
8608 if (sessionversion != 1)
8609 {
8610 restag = reqtag;
8611 app->login_result(API_EARGS);
8612 return;
8613 }
8614
8615 session++;
8616 size--;
8617 }
8618
8619 if (size == sizeof key.key + SIDLEN)
8620 {
8621 string t;
8622
8623 key.setkey(session);
8624 setsid(session + sizeof key.key, size - sizeof key.key);
8625
8626 opensctable();
8627
8628 if (sctable && sctable->get(CACHEDSCSN, &t) && t.size() == sizeof cachedscsn)
8629 {
8630 cachedscsn = MemAccess::get<handle>(t.data());
8631 }
8632
8633 byte sek[SymmCipher::KEYLENGTH];
8634 rng.genblock(sek, sizeof sek);
8635
8636 reqs.add(new CommandLogin(this, NULL, NULL, 0, sek, sessionversion));
8637 getuserdata();
8638 fetchtimezone();
8639 }
8640 else
8641 {
8642 restag = reqtag;
8643 app->login_result(API_EARGS);
8644 }
8645 }
8646
8647 // check password's integrity
validatepwd(const byte * pwkey)8648 error MegaClient::validatepwd(const byte *pwkey)
8649 {
8650 User *u = finduser(me);
8651 if (!u)
8652 {
8653 return API_EACCESS;
8654 }
8655
8656 SymmCipher pwcipher(pwkey);
8657 pwcipher.setkey((byte*)pwkey);
8658
8659 string lcemail(u->email.c_str());
8660 uint64_t emailhash = stringhash64(&lcemail, &pwcipher);
8661
8662 reqs.add(new CommandValidatePassword(this, lcemail.c_str(), emailhash));
8663
8664 return API_OK;
8665 }
8666
dumpsession(byte * session,size_t size)8667 int MegaClient::dumpsession(byte* session, size_t size)
8668 {
8669 if (loggedin() == NOTLOGGEDIN)
8670 {
8671 return 0;
8672 }
8673
8674 if (size < sid.size() + sizeof key.key)
8675 {
8676 return API_ERANGE;
8677 }
8678
8679 if (sessionkey.size())
8680 {
8681 if (size < sid.size() + sizeof key.key + 1)
8682 {
8683 return API_ERANGE;
8684 }
8685
8686 size = sid.size() + sizeof key.key + 1;
8687
8688 session[0] = 1;
8689 session++;
8690
8691 byte k[SymmCipher::KEYLENGTH];
8692 SymmCipher cipher;
8693 cipher.setkey((const byte *)sessionkey.data(), int(sessionkey.size()));
8694 cipher.ecb_encrypt(key.key, k);
8695 memcpy(session, k, sizeof k);
8696 }
8697 else
8698 {
8699 size = sid.size() + sizeof key.key;
8700 memcpy(session, key.key, sizeof key.key);
8701 }
8702
8703 memcpy(session + sizeof key.key, sid.data(), sid.size());
8704
8705 return int(size);
8706 }
8707
resendverificationemail()8708 void MegaClient::resendverificationemail()
8709 {
8710 reqs.add(new CommandResendVerificationEmail(this));
8711 }
8712
resetSmsVerifiedPhoneNumber()8713 void MegaClient::resetSmsVerifiedPhoneNumber()
8714 {
8715 reqs.add(new CommandResetSmsVerifiedPhoneNumber(this));
8716 }
8717
copysession()8718 void MegaClient::copysession()
8719 {
8720 reqs.add(new CommandCopySession(this));
8721 }
8722
sessiontransferdata(const char * url,string * session)8723 string *MegaClient::sessiontransferdata(const char *url, string *session)
8724 {
8725 if (!session && loggedin() != FULLACCOUNT)
8726 {
8727 return NULL;
8728 }
8729
8730 std::stringstream ss;
8731
8732 // open array
8733 ss << "[";
8734
8735 // add AES key
8736 string aeskey;
8737 key.serializekeyforjs(&aeskey);
8738 ss << aeskey << ",\"";
8739
8740 // add session ID
8741 if (session)
8742 {
8743 ss << *session;
8744 }
8745 else
8746 {
8747 string sids;
8748 sids.resize(sid.size() * 4 / 3 + 4);
8749 sids.resize(Base64::btoa((byte *)sid.data(), int(sid.size()), (char *)sids.data()));
8750 ss << sids;
8751 }
8752 ss << "\",\"";
8753
8754 // add URL
8755 if (url)
8756 {
8757 ss << url;
8758 }
8759 ss << "\",false]";
8760
8761 // standard Base64 encoding
8762 string json = ss.str();
8763 string *base64 = new string;
8764 base64->resize(json.size() * 4 / 3 + 4);
8765 base64->resize(Base64::btoa((byte *)json.data(), int(json.size()), (char *)base64->data()));
8766 std::replace(base64->begin(), base64->end(), '-', '+');
8767 std::replace(base64->begin(), base64->end(), '_', '/');
8768 return base64;
8769 }
8770
killsession(handle session)8771 void MegaClient::killsession(handle session)
8772 {
8773 reqs.add(new CommandKillSessions(this, session));
8774 }
8775
8776 // Kill all sessions (except current)
killallsessions()8777 void MegaClient::killallsessions()
8778 {
8779 reqs.add(new CommandKillSessions(this));
8780 }
8781
opensctable()8782 void MegaClient::opensctable()
8783 {
8784 if (dbaccess && !sctable)
8785 {
8786 string dbname;
8787
8788 if (sid.size() >= SIDLEN)
8789 {
8790 dbname.resize((SIDLEN - sizeof key.key) * 4 / 3 + 3);
8791 dbname.resize(Base64::btoa((const byte*)sid.data() + sizeof key.key, SIDLEN - sizeof key.key, (char*)dbname.c_str()));
8792 }
8793 else if (loggedinfolderlink())
8794 {
8795 dbname.resize(NODEHANDLE * 4 / 3 + 3);
8796 dbname.resize(Base64::btoa((const byte*)&publichandle, NODEHANDLE, (char*)dbname.c_str()));
8797 }
8798
8799 if (dbname.size())
8800 {
8801 sctable = dbaccess->open(rng, fsaccess, &dbname, false, false);
8802 pendingsccommit = false;
8803 }
8804 }
8805 }
8806
8807 // verify a static symmetric password challenge
checktsid(byte * sidbuf,unsigned len)8808 int MegaClient::checktsid(byte* sidbuf, unsigned len)
8809 {
8810 if (len != SIDLEN)
8811 {
8812 return 0;
8813 }
8814
8815 key.ecb_encrypt(sidbuf);
8816
8817 return !memcmp(sidbuf, sidbuf + SIDLEN - SymmCipher::KEYLENGTH, SymmCipher::KEYLENGTH);
8818 }
8819
8820 // locate user by e-mail address or ASCII handle
finduser(const char * uid,int add)8821 User* MegaClient::finduser(const char* uid, int add)
8822 {
8823 // null user for folder links?
8824 if (!uid || !*uid)
8825 {
8826 return NULL;
8827 }
8828
8829 if (!strchr(uid, '@'))
8830 {
8831 // not an e-mail address: must be ASCII handle
8832 handle uh;
8833
8834 if (Base64::atob(uid, (byte*)&uh, sizeof uh) == sizeof uh)
8835 {
8836 return finduser(uh, add);
8837 }
8838
8839 return NULL;
8840 }
8841
8842 string nuid;
8843 User* u;
8844
8845 // convert e-mail address to lowercase (ASCII only)
8846 Node::copystring(&nuid, uid);
8847 tolower_string(nuid);
8848
8849 um_map::iterator it = umindex.find(nuid);
8850
8851 if (it == umindex.end())
8852 {
8853 if (!add)
8854 {
8855 return NULL;
8856 }
8857
8858 // add user by lowercase e-mail address
8859 u = &users[++userid];
8860 u->uid = nuid;
8861 Node::copystring(&u->email, nuid.c_str());
8862 umindex[nuid] = userid;
8863
8864 return u;
8865 }
8866 else
8867 {
8868 return &users[it->second];
8869 }
8870 }
8871
8872 // locate user by binary handle
finduser(handle uh,int add)8873 User* MegaClient::finduser(handle uh, int add)
8874 {
8875 if (!uh)
8876 {
8877 return NULL;
8878 }
8879
8880 User* u;
8881 uh_map::iterator it = uhindex.find(uh);
8882
8883 if (it == uhindex.end())
8884 {
8885 if (!add)
8886 {
8887 return NULL;
8888 }
8889
8890 // add user by binary handle
8891 u = &users[++userid];
8892
8893 char uid[12];
8894 Base64::btoa((byte*)&uh, MegaClient::USERHANDLE, uid);
8895 u->uid.assign(uid, 11);
8896
8897 uhindex[uh] = userid;
8898 u->userhandle = uh;
8899
8900 return u;
8901 }
8902 else
8903 {
8904 return &users[it->second];
8905 }
8906 }
8907
ownuser()8908 User *MegaClient::ownuser()
8909 {
8910 return finduser(me);
8911 }
8912
8913 // add missing mapping (handle or email)
8914 // reduce uid to ASCII uh if only known by email
mapuser(handle uh,const char * email)8915 void MegaClient::mapuser(handle uh, const char* email)
8916 {
8917 if (!email || !*email)
8918 {
8919 return;
8920 }
8921
8922 User* u;
8923 string nuid;
8924
8925 Node::copystring(&nuid, email);
8926 tolower_string(nuid);
8927
8928 // does user uh exist?
8929 uh_map::iterator hit = uhindex.find(uh);
8930
8931 if (hit != uhindex.end())
8932 {
8933 // yes: add email reference
8934 u = &users[hit->second];
8935
8936 um_map::iterator mit = umindex.find(nuid);
8937 if (mit != umindex.end() && mit->second != hit->second && (users[mit->second].show != INACTIVE || users[mit->second].userhandle == me))
8938 {
8939 // duplicated user: one by email, one by handle
8940 discardnotifieduser(&users[mit->second]);
8941 assert(!users[mit->second].sharing.size());
8942 users.erase(mit->second);
8943 }
8944
8945 // if mapping a different email, remove old index
8946 if (strcmp(u->email.c_str(), nuid.c_str()))
8947 {
8948 if (u->email.size())
8949 {
8950 umindex.erase(u->email);
8951 }
8952
8953 Node::copystring(&u->email, nuid.c_str());
8954 }
8955
8956 umindex[nuid] = hit->second;
8957
8958 return;
8959 }
8960
8961 // does user email exist?
8962 um_map::iterator mit = umindex.find(nuid);
8963
8964 if (mit != umindex.end())
8965 {
8966 // yes: add uh reference
8967 u = &users[mit->second];
8968
8969 uhindex[uh] = mit->second;
8970 u->userhandle = uh;
8971
8972 char uid[12];
8973 Base64::btoa((byte*)&uh, MegaClient::USERHANDLE, uid);
8974 u->uid.assign(uid, 11);
8975 }
8976 }
8977
discarduser(handle uh,bool discardnotified)8978 void MegaClient::discarduser(handle uh, bool discardnotified)
8979 {
8980 User *u = finduser(uh);
8981 if (!u)
8982 {
8983 return;
8984 }
8985
8986 while (u->pkrs.size()) // protect any pending pubKey request
8987 {
8988 auto& pka = u->pkrs.front();
8989 if(pka->cmd)
8990 {
8991 pka->cmd->invalidateUser();
8992 }
8993 pka->proc(this, u);
8994 u->pkrs.pop_front();
8995 }
8996
8997 if (discardnotified)
8998 {
8999 discardnotifieduser(u);
9000 }
9001
9002 umindex.erase(u->email);
9003 users.erase(uhindex[uh]);
9004 uhindex.erase(uh);
9005 }
9006
discarduser(const char * email)9007 void MegaClient::discarduser(const char *email)
9008 {
9009 User *u = finduser(email);
9010 if (!u)
9011 {
9012 return;
9013 }
9014
9015 while (u->pkrs.size()) // protect any pending pubKey request
9016 {
9017 auto& pka = u->pkrs.front();
9018 if(pka->cmd)
9019 {
9020 pka->cmd->invalidateUser();
9021 }
9022 pka->proc(this, u);
9023 u->pkrs.pop_front();
9024 }
9025
9026 discardnotifieduser(u);
9027
9028 uhindex.erase(u->userhandle);
9029 users.erase(umindex[email]);
9030 umindex.erase(email);
9031 }
9032
findpcr(handle p)9033 PendingContactRequest* MegaClient::findpcr(handle p)
9034 {
9035 if (ISUNDEF(p))
9036 {
9037 return NULL;
9038 }
9039
9040 PendingContactRequest* pcr = pcrindex[p];
9041 if (!pcr)
9042 {
9043 pcr = new PendingContactRequest(p);
9044 pcrindex[p] = pcr;
9045 assert(fetchingnodes);
9046 // while fetchingnodes, outgoing shares reference an "empty" PCR that is completed when `opc` is parsed
9047 }
9048
9049 return pcrindex[p];
9050 }
9051
mappcr(handle id,PendingContactRequest * pcr)9052 void MegaClient::mappcr(handle id, PendingContactRequest *pcr)
9053 {
9054 delete pcrindex[id];
9055 pcrindex[id] = pcr;
9056 }
9057
discardnotifieduser(User * u)9058 bool MegaClient::discardnotifieduser(User *u)
9059 {
9060 for (user_vector::iterator it = usernotify.begin(); it != usernotify.end(); it++)
9061 {
9062 if (*it == u)
9063 {
9064 usernotify.erase(it);
9065 return true; // no duplicated users in the notify vector
9066 }
9067 }
9068 return false;
9069 }
9070
9071 // sharekey distribution request - walk array consisting of {node,user+}+ handle tuples
9072 // and submit public key requests
procsr(JSON * j)9073 void MegaClient::procsr(JSON* j)
9074 {
9075 User* u;
9076 handle sh, uh;
9077
9078 if (!j->enterarray())
9079 {
9080 return;
9081 }
9082
9083 while (j->ishandle() && (sh = j->gethandle()))
9084 {
9085 if (nodebyhandle(sh))
9086 {
9087 // process pending requests
9088 while (j->ishandle(USERHANDLE) && (uh = j->gethandle(USERHANDLE)))
9089 {
9090 if ((u = finduser(uh)))
9091 {
9092 queuepubkeyreq(u, ::mega::make_unique<PubKeyActionSendShareKey>(sh));
9093 }
9094 }
9095 }
9096 else
9097 {
9098 // unknown node: skip
9099 while (j->ishandle(USERHANDLE) && (uh = j->gethandle(USERHANDLE)));
9100 }
9101 }
9102
9103 j->leavearray();
9104 }
9105
clearKeys()9106 void MegaClient::clearKeys()
9107 {
9108 User *u = finduser(me);
9109
9110 u->invalidateattr(ATTR_KEYRING);
9111 u->invalidateattr(ATTR_ED25519_PUBK);
9112 u->invalidateattr(ATTR_CU25519_PUBK);
9113 u->invalidateattr(ATTR_SIG_RSA_PUBK);
9114 u->invalidateattr(ATTR_SIG_CU255_PUBK);
9115
9116 fetchingkeys = false;
9117 }
9118
resetKeyring()9119 void MegaClient::resetKeyring()
9120 {
9121 delete signkey;
9122 signkey = NULL;
9123
9124 delete chatkey;
9125 chatkey = NULL;
9126 }
9127
9128 // process node tree (bottom up)
proctree(Node * n,TreeProc * tp,bool skipinshares,bool skipversions)9129 void MegaClient::proctree(Node* n, TreeProc* tp, bool skipinshares, bool skipversions)
9130 {
9131 if (!skipversions || n->type != FILENODE)
9132 {
9133 for (node_list::iterator it = n->children.begin(); it != n->children.end(); )
9134 {
9135 Node *child = *it++;
9136 if (!(skipinshares && child->inshare))
9137 {
9138 proctree(child, tp, skipinshares);
9139 }
9140 }
9141 }
9142
9143 tp->proc(this, n);
9144 }
9145
9146 // queue PubKeyAction request to be triggered upon availability of the user's
9147 // public key
queuepubkeyreq(User * u,std::unique_ptr<PubKeyAction> pka)9148 void MegaClient::queuepubkeyreq(User* u, std::unique_ptr<PubKeyAction> pka)
9149 {
9150 if (!u || u->pubk.isvalid())
9151 {
9152 restag = pka->tag;
9153 pka->proc(this, u);
9154 }
9155 else
9156 {
9157 u->pkrs.push_back(std::move(pka));
9158
9159 if (!u->pubkrequested)
9160 {
9161 u->pkrs.back()->cmd = new CommandPubKeyRequest(this, u);
9162 reqs.add(u->pkrs.back()->cmd);
9163 u->pubkrequested = true;
9164 }
9165 }
9166 }
9167
queuepubkeyreq(const char * uid,std::unique_ptr<PubKeyAction> pka)9168 void MegaClient::queuepubkeyreq(const char *uid, std::unique_ptr<PubKeyAction> pka)
9169 {
9170 User *u = finduser(uid, 0);
9171 if (!u && uid)
9172 {
9173 if (strchr(uid, '@')) // uid is an e-mail address
9174 {
9175 string nuid;
9176 Node::copystring(&nuid, uid);
9177 tolower_string(nuid);
9178
9179 u = new User(nuid.c_str());
9180 u->uid = nuid;
9181 u->isTemporary = true;
9182 }
9183 else // not an e-mail address: must be ASCII handle
9184 {
9185 handle uh;
9186 if (Base64::atob(uid, (byte*)&uh, sizeof uh) == sizeof uh)
9187 {
9188 u = new User(NULL);
9189 u->userhandle = uh;
9190 u->uid = uid;
9191 u->isTemporary = true;
9192 }
9193 }
9194 }
9195
9196 queuepubkeyreq(u, std::move(pka));
9197 }
9198
9199 // rewrite keys of foreign nodes due to loss of underlying shareufskey
rewriteforeignkeys(Node * n)9200 void MegaClient::rewriteforeignkeys(Node* n)
9201 {
9202 TreeProcForeignKeys rewrite;
9203 proctree(n, &rewrite);
9204
9205 if (nodekeyrewrite.size())
9206 {
9207 reqs.add(new CommandNodeKeyUpdate(this, &nodekeyrewrite));
9208 nodekeyrewrite.clear();
9209 }
9210 }
9211
9212 // if user has a known public key, complete instantly
9213 // otherwise, queue and request public key if not already pending
setshare(Node * n,const char * user,accesslevel_t a,const char * personal_representation)9214 void MegaClient::setshare(Node* n, const char* user, accesslevel_t a, const char* personal_representation)
9215 {
9216 size_t total = n->outshares ? n->outshares->size() : 0;
9217 total += n->pendingshares ? n->pendingshares->size() : 0;
9218 if (a == ACCESS_UNKNOWN && total == 1)
9219 {
9220 // rewrite keys of foreign nodes located in the outbound share that is getting canceled
9221 // FIXME: verify that it is really getting canceled to prevent benign premature rewrite
9222 rewriteforeignkeys(n);
9223 }
9224
9225 queuepubkeyreq(user, ::mega::make_unique<PubKeyActionCreateShare>(n->nodehandle, a, reqtag, personal_representation));
9226 }
9227
9228 // Add/delete/remind outgoing pending contact request
setpcr(const char * temail,opcactions_t action,const char * msg,const char * oemail,handle contactLink)9229 void MegaClient::setpcr(const char* temail, opcactions_t action, const char* msg, const char* oemail, handle contactLink)
9230 {
9231 reqs.add(new CommandSetPendingContact(this, temail, action, msg, oemail, contactLink));
9232 }
9233
updatepcr(handle p,ipcactions_t action)9234 void MegaClient::updatepcr(handle p, ipcactions_t action)
9235 {
9236 reqs.add(new CommandUpdatePendingContact(this, p, action));
9237 }
9238
9239 // enumerate Pro account purchase options (not fully implemented)
purchase_enumeratequotaitems()9240 void MegaClient::purchase_enumeratequotaitems()
9241 {
9242 reqs.add(new CommandEnumerateQuotaItems(this));
9243 }
9244
9245 // begin a new purchase (FIXME: not fully implemented)
purchase_begin()9246 void MegaClient::purchase_begin()
9247 {
9248 purchase_basket.clear();
9249 }
9250
9251 // submit purchased product for payment
purchase_additem(int itemclass,handle item,unsigned price,const char * currency,unsigned tax,const char * country,handle lastPublicHandle,int phtype,int64_t ts)9252 void MegaClient::purchase_additem(int itemclass, handle item, unsigned price,
9253 const char* currency, unsigned tax, const char* country,
9254 handle lastPublicHandle, int phtype, int64_t ts)
9255 {
9256 reqs.add(new CommandPurchaseAddItem(this, itemclass, item, price, currency, tax, country, lastPublicHandle, phtype, ts));
9257 }
9258
9259 // obtain payment URL for given provider
purchase_checkout(int gateway)9260 void MegaClient::purchase_checkout(int gateway)
9261 {
9262 reqs.add(new CommandPurchaseCheckout(this, gateway));
9263 }
9264
submitpurchasereceipt(int type,const char * receipt,handle lph,int phtype,int64_t ts)9265 void MegaClient::submitpurchasereceipt(int type, const char *receipt, handle lph, int phtype, int64_t ts)
9266 {
9267 reqs.add(new CommandSubmitPurchaseReceipt(this, type, receipt, lph, phtype, ts));
9268 }
9269
creditcardstore(const char * ccplain)9270 error MegaClient::creditcardstore(const char *ccplain)
9271 {
9272 if (!ccplain)
9273 {
9274 return API_EARGS;
9275 }
9276
9277 string ccnumber, expm, expy, cv2, ccode;
9278 if (!JSON::extractstringvalue(ccplain, "card_number", &ccnumber)
9279 || (ccnumber.size() < 10)
9280 || !JSON::extractstringvalue(ccplain, "expiry_date_month", &expm)
9281 || (expm.size() != 2)
9282 || !JSON::extractstringvalue(ccplain, "expiry_date_year", &expy)
9283 || (expy.size() != 4)
9284 || !JSON::extractstringvalue(ccplain, "cv2", &cv2)
9285 || (cv2.size() != 3)
9286 || !JSON::extractstringvalue(ccplain, "country_code", &ccode)
9287 || (ccode.size() != 2))
9288 {
9289 return API_EARGS;
9290 }
9291
9292 string::iterator it = find_if(ccnumber.begin(), ccnumber.end(), char_is_not_digit);
9293 if (it != ccnumber.end())
9294 {
9295 return API_EARGS;
9296 }
9297
9298 it = find_if(expm.begin(), expm.end(), char_is_not_digit);
9299 if (it != expm.end() || atol(expm.c_str()) > 12)
9300 {
9301 return API_EARGS;
9302 }
9303
9304 it = find_if(expy.begin(), expy.end(), char_is_not_digit);
9305 if (it != expy.end() || atol(expy.c_str()) < 2015)
9306 {
9307 return API_EARGS;
9308 }
9309
9310 it = find_if(cv2.begin(), cv2.end(), char_is_not_digit);
9311 if (it != cv2.end())
9312 {
9313 return API_EARGS;
9314 }
9315
9316
9317 //Luhn algorithm
9318 int odd = 1, sum = 0;
9319 for (size_t i = ccnumber.size(); i--; odd = !odd)
9320 {
9321 int digit = ccnumber[i] - '0';
9322 sum += odd ? digit : ((digit < 5) ? 2 * digit : 2 * (digit - 5) + 1);
9323 }
9324
9325 if (sum % 10)
9326 {
9327 return API_EARGS;
9328 }
9329
9330 byte pubkdata[sizeof(PAYMENT_PUBKEY) * 3 / 4 + 3];
9331 int pubkdatalen = Base64::atob(PAYMENT_PUBKEY, (byte *)pubkdata, sizeof(pubkdata));
9332
9333 string ccenc;
9334 string ccplain1 = ccplain;
9335 PayCrypter payCrypter(rng);
9336 if (!payCrypter.hybridEncrypt(&ccplain1, pubkdata, pubkdatalen, &ccenc))
9337 {
9338 return API_EARGS;
9339 }
9340
9341 string last4 = ccnumber.substr(ccnumber.size() - 4);
9342
9343 char hashstring[256];
9344 int ret = snprintf(hashstring, sizeof(hashstring), "{\"card_number\":\"%s\","
9345 "\"expiry_date_month\":\"%s\","
9346 "\"expiry_date_year\":\"%s\","
9347 "\"cv2\":\"%s\"}", ccnumber.c_str(), expm.c_str(), expy.c_str(), cv2.c_str());
9348
9349 if (ret < 0 || ret >= (int)sizeof(hashstring))
9350 {
9351 return API_EARGS;
9352 }
9353
9354 HashSHA256 hash;
9355 string binaryhash;
9356 hash.add((byte *)hashstring, int(strlen(hashstring)));
9357 hash.get(&binaryhash);
9358
9359 static const char hexchars[] = "0123456789abcdef";
9360 ostringstream oss;
9361 string hexHash;
9362 for (size_t i=0;i<binaryhash.size();++i)
9363 {
9364 oss.put(hexchars[(binaryhash[i] >> 4) & 0x0F]);
9365 oss.put(hexchars[binaryhash[i] & 0x0F]);
9366 }
9367 hexHash = oss.str();
9368
9369 string base64cc;
9370 base64cc.resize(ccenc.size()*4/3+4);
9371 base64cc.resize(Base64::btoa((byte *)ccenc.data(), int(ccenc.size()), (char *)base64cc.data()));
9372 std::replace( base64cc.begin(), base64cc.end(), '-', '+');
9373 std::replace( base64cc.begin(), base64cc.end(), '_', '/');
9374
9375 reqs.add(new CommandCreditCardStore(this, base64cc.data(), last4.c_str(), expm.c_str(), expy.c_str(), hexHash.data()));
9376 return API_OK;
9377 }
9378
creditcardquerysubscriptions()9379 void MegaClient::creditcardquerysubscriptions()
9380 {
9381 reqs.add(new CommandCreditCardQuerySubscriptions(this));
9382 }
9383
creditcardcancelsubscriptions(const char * reason)9384 void MegaClient::creditcardcancelsubscriptions(const char* reason)
9385 {
9386 reqs.add(new CommandCreditCardCancelSubscriptions(this, reason));
9387 }
9388
getpaymentmethods()9389 void MegaClient::getpaymentmethods()
9390 {
9391 reqs.add(new CommandGetPaymentMethods(this));
9392 }
9393
9394 // delete or block an existing contact
removecontact(const char * email,visibility_t show)9395 error MegaClient::removecontact(const char* email, visibility_t show)
9396 {
9397 if (!strchr(email, '@') || (show != HIDDEN && show != BLOCKED))
9398 {
9399 return API_EARGS;
9400 }
9401
9402 reqs.add(new CommandRemoveContact(this, email, show));
9403
9404 return API_OK;
9405 }
9406
9407 /**
9408 * @brief Attach/update/delete a user attribute.
9409 *
9410 * Attributes are stored as base64-encoded binary blobs. They use internal
9411 * attribute name prefixes:
9412 *
9413 * "*" - Private and encrypted. Use a TLV container (key-value)
9414 * "#" - Protected and plain text, accessible only by contacts.
9415 * "+" - Public and plain text, accessible by anyone knowing userhandle
9416 * "^" - Private and non-encrypted.
9417 *
9418 * @param at Attribute type.
9419 * @param av Attribute value.
9420 * @param avl Attribute value length.
9421 * @param ctag Tag to identify the request at intermediate layer
9422
9423 */
putua(attr_t at,const byte * av,unsigned avl,int ctag,handle lastPublicHandle,int phtype,int64_t ts)9424 void MegaClient::putua(attr_t at, const byte* av, unsigned avl, int ctag, handle lastPublicHandle, int phtype, int64_t ts)
9425 {
9426 string data;
9427
9428 if (!av)
9429 {
9430 if (at == ATTR_AVATAR) // remove avatar
9431 {
9432 data = "none";
9433 }
9434
9435 av = (const byte*) data.data();
9436 avl = unsigned(data.size());
9437 }
9438
9439 int tag = (ctag != -1) ? ctag : reqtag;
9440 User *u = ownuser();
9441 assert(u);
9442 if (!u)
9443 {
9444 LOG_err << "Own user not found when attempting to set user attributes";
9445 restag = tag;
9446 app->putua_result(API_EACCESS);
9447 return;
9448 }
9449 int needversion = u->needversioning(at);
9450 if (needversion == -1)
9451 {
9452 restag = tag;
9453 app->putua_result(API_EARGS); // attribute not recognized
9454 return;
9455 }
9456
9457 if (!needversion)
9458 {
9459 reqs.add(new CommandPutUA(this, at, av, avl, tag, lastPublicHandle, phtype, ts));
9460 }
9461 else
9462 {
9463 // if the cached value is outdated, first need to fetch the latest version
9464 if (u->getattr(at) && !u->isattrvalid(at))
9465 {
9466 restag = tag;
9467 app->putua_result(API_EEXPIRED);
9468 return;
9469 }
9470 reqs.add(new CommandPutUAVer(this, at, av, avl, tag));
9471 }
9472 }
9473
putua(userattr_map * attrs,int ctag)9474 void MegaClient::putua(userattr_map *attrs, int ctag)
9475 {
9476 int tag = (ctag != -1) ? ctag : reqtag;
9477 User *u = ownuser();
9478
9479 if (!u || !attrs || !attrs->size())
9480 {
9481 restag = tag;
9482 return app->putua_result(API_EARGS);
9483 }
9484
9485 for (userattr_map::iterator it = attrs->begin(); it != attrs->end(); it++)
9486 {
9487 attr_t type = it->first;
9488
9489 if (User::needversioning(type) != 1)
9490 {
9491 restag = tag;
9492 return app->putua_result(API_EARGS);
9493 }
9494
9495 // if the cached value is outdated, first need to fetch the latest version
9496 if (u->getattr(type) && !u->isattrvalid(type))
9497 {
9498 restag = tag;
9499 return app->putua_result(API_EEXPIRED);
9500 }
9501 }
9502
9503 reqs.add(new CommandPutMultipleUAVer(this, attrs, tag));
9504 }
9505
9506 /**
9507 * @brief Queue a user attribute retrieval.
9508 *
9509 * @param u User.
9510 * @param at Attribute type.
9511 * @param ctag Tag to identify the request at intermediate layer
9512 */
getua(User * u,const attr_t at,int ctag)9513 void MegaClient::getua(User* u, const attr_t at, int ctag)
9514 {
9515 if (at != ATTR_UNKNOWN)
9516 {
9517 // if we can solve those requests locally (cached values)...
9518 const string *cachedav = u->getattr(at);
9519 int tag = (ctag != -1) ? ctag : reqtag;
9520
9521 if (!fetchingkeys && cachedav && u->isattrvalid(at))
9522 {
9523 if (User::scope(at) == '*') // private attribute, TLV encoding
9524 {
9525 TLVstore *tlv = TLVstore::containerToTLVrecords(cachedav, &key);
9526 restag = tag;
9527 app->getua_result(tlv, at);
9528 delete tlv;
9529 return;
9530 }
9531 else
9532 {
9533 restag = tag;
9534 app->getua_result((byte*) cachedav->data(), unsigned(cachedav->size()), at);
9535 return;
9536 }
9537 }
9538 else
9539 {
9540 reqs.add(new CommandGetUA(this, u->uid.c_str(), at, NULL, tag));
9541 }
9542 }
9543 }
9544
getua(const char * email_handle,const attr_t at,const char * ph,int ctag)9545 void MegaClient::getua(const char *email_handle, const attr_t at, const char *ph, int ctag)
9546 {
9547 if (email_handle && at != ATTR_UNKNOWN)
9548 {
9549 reqs.add(new CommandGetUA(this, email_handle, at, ph,(ctag != -1) ? ctag : reqtag));
9550 }
9551 }
9552
getUserEmail(const char * uid)9553 void MegaClient::getUserEmail(const char *uid)
9554 {
9555 reqs.add(new CommandGetUserEmail(this, uid));
9556 }
9557
9558 #ifdef DEBUG
delua(const char * an)9559 void MegaClient::delua(const char *an)
9560 {
9561 if (an)
9562 {
9563 reqs.add(new CommandDelUA(this, an));
9564 }
9565 }
9566
senddevcommand(const char * command,const char * email)9567 void MegaClient::senddevcommand(const char *command, const char *email)
9568 {
9569 reqs.add(new CommandSendDevCommand(this, command, email));
9570 }
9571 #endif
9572
9573 // queue node for notification
notifynode(Node * n)9574 void MegaClient::notifynode(Node* n)
9575 {
9576 n->applykey();
9577
9578 if (!fetchingnodes)
9579 {
9580 if (n->tag && !n->changed.removed && n->attrstring)
9581 {
9582 // report a "NO_KEY" event
9583
9584 char* buf = new char[n->nodekey().size() * 4 / 3 + 4];
9585 Base64::btoa((byte *)n->nodekey().data(), int(n->nodekey().size()), buf);
9586
9587 int changed = 0;
9588 changed |= (int)n->changed.removed;
9589 changed |= n->changed.attrs << 1;
9590 changed |= n->changed.owner << 2;
9591 changed |= n->changed.ctime << 3;
9592 changed |= n->changed.fileattrstring << 4;
9593 changed |= n->changed.inshare << 5;
9594 changed |= n->changed.outshares << 6;
9595 changed |= n->changed.pendingshares << 7;
9596 changed |= n->changed.parent << 8;
9597 changed |= n->changed.publiclink << 9;
9598 changed |= n->changed.newnode << 10;
9599
9600 int attrlen = int(n->attrstring->size());
9601 string base64attrstring;
9602 base64attrstring.resize(attrlen * 4 / 3 + 4);
9603 base64attrstring.resize(Base64::btoa((byte *)n->attrstring->data(), int(n->attrstring->size()), (char *)base64attrstring.data()));
9604
9605 char report[512];
9606 Base64::btoa((const byte *)&n->nodehandle, MegaClient::NODEHANDLE, report);
9607 sprintf(report + 8, " %d %" PRIu64 " %d %X %.200s %.200s", n->type, n->size, attrlen, changed, buf, base64attrstring.c_str());
9608
9609 reportevent("NK", report, 0);
9610 sendevent(99400, report, 0);
9611
9612 delete [] buf;
9613 }
9614
9615 #ifdef ENABLE_SYNC
9616 // is this a synced node that was moved to a non-synced location? queue for
9617 // deletion from LocalNodes.
9618 if (n->localnode && n->localnode->parent && n->parent && !n->parent->localnode)
9619 {
9620 if (n->changed.removed || n->changed.parent)
9621 {
9622 if (n->type == FOLDERNODE)
9623 {
9624 app->syncupdate_remote_folder_deletion(n->localnode->sync, n);
9625 }
9626 else
9627 {
9628 app->syncupdate_remote_file_deletion(n->localnode->sync, n);
9629 }
9630 }
9631
9632 n->localnode->deleted = true;
9633 n->localnode->node = NULL;
9634 n->localnode = NULL;
9635 }
9636 else
9637 {
9638 // is this a synced node that is not a sync root, or a new node in a
9639 // synced folder?
9640 // FIXME: aggregate subtrees!
9641 if (n->localnode && n->localnode->parent)
9642 {
9643 n->localnode->deleted = n->changed.removed;
9644 }
9645
9646 if (n->parent && n->parent->localnode && (!n->localnode || (n->localnode->parent != n->parent->localnode)))
9647 {
9648 if (n->localnode)
9649 {
9650 n->localnode->deleted = n->changed.removed;
9651 }
9652
9653 if (!n->changed.removed && (n->changed.newnode || n->changed.parent))
9654 {
9655 if (!n->localnode)
9656 {
9657 if (n->type == FOLDERNODE)
9658 {
9659 app->syncupdate_remote_folder_addition(n->parent->localnode->sync, n);
9660 }
9661 else
9662 {
9663 app->syncupdate_remote_file_addition(n->parent->localnode->sync, n);
9664 }
9665 }
9666 else
9667 {
9668 app->syncupdate_remote_move(n->localnode->sync, n,
9669 n->localnode->parent ? n->localnode->parent->node : NULL);
9670 }
9671 }
9672 }
9673 else if (!n->changed.removed && n->changed.attrs && n->localnode && n->localnode->name.compare(n->displayname()))
9674 {
9675 app->syncupdate_remote_rename(n->localnode->sync, n, n->localnode->name.c_str());
9676 }
9677 }
9678 #endif
9679 }
9680
9681 if (!n->notified)
9682 {
9683 n->notified = true;
9684 nodenotify.push_back(n);
9685 }
9686 }
9687
transfercacheadd(Transfer * transfer,DBTableTransactionCommitter * committer)9688 void MegaClient::transfercacheadd(Transfer *transfer, DBTableTransactionCommitter* committer)
9689 {
9690 if (tctable && !transfer->skipserialization)
9691 {
9692 LOG_debug << "Caching transfer";
9693 tctable->checkCommitter(committer);
9694 tctable->put(MegaClient::CACHEDTRANSFER, transfer, &tckey);
9695 }
9696 }
9697
transfercachedel(Transfer * transfer,DBTableTransactionCommitter * committer)9698 void MegaClient::transfercachedel(Transfer *transfer, DBTableTransactionCommitter* committer)
9699 {
9700 if (tctable && transfer->dbid)
9701 {
9702 LOG_debug << "Removing cached transfer";
9703 tctable->checkCommitter(committer);
9704 tctable->del(transfer->dbid);
9705 }
9706 }
9707
filecacheadd(File * file,DBTableTransactionCommitter & committer)9708 void MegaClient::filecacheadd(File *file, DBTableTransactionCommitter& committer)
9709 {
9710 if (tctable && !file->syncxfer)
9711 {
9712 LOG_debug << "Caching file";
9713 tctable->checkCommitter(&committer);
9714 tctable->put(MegaClient::CACHEDFILE, file, &tckey);
9715 }
9716 }
9717
filecachedel(File * file,DBTableTransactionCommitter * committer)9718 void MegaClient::filecachedel(File *file, DBTableTransactionCommitter* committer)
9719 {
9720 if (tctable && !file->syncxfer)
9721 {
9722 LOG_debug << "Removing cached file";
9723 tctable->checkCommitter(committer);
9724 tctable->del(file->dbid);
9725 }
9726
9727 if (file->temporaryfile)
9728 {
9729 LOG_debug << "Removing temporary file";
9730 fsaccess->unlinklocal(file->localname);
9731 }
9732 }
9733
9734 // queue user for notification
notifyuser(User * u)9735 void MegaClient::notifyuser(User* u)
9736 {
9737 if (!u->notified)
9738 {
9739 u->notified = true;
9740 usernotify.push_back(u);
9741 }
9742 }
9743
9744 // queue pcr for notification
notifypcr(PendingContactRequest * pcr)9745 void MegaClient::notifypcr(PendingContactRequest* pcr)
9746 {
9747 if (pcr && !pcr->notified)
9748 {
9749 pcr->notified = true;
9750 pcrnotify.push_back(pcr);
9751 }
9752 }
9753
9754 #ifdef ENABLE_CHAT
notifychat(TextChat * chat)9755 void MegaClient::notifychat(TextChat *chat)
9756 {
9757 if (!chat->notified)
9758 {
9759 chat->notified = true;
9760 chatnotify[chat->id] = chat;
9761 }
9762 }
9763 #endif
9764
9765 // process request for share node keys
9766 // builds & emits k/cr command
9767 // returns 1 in case of a valid response, 0 otherwise
proccr(JSON * j)9768 void MegaClient::proccr(JSON* j)
9769 {
9770 node_vector shares, nodes;
9771 handle h;
9772
9773 if (j->enterobject())
9774 {
9775 for (;;)
9776 {
9777 switch (j->getnameid())
9778 {
9779 case MAKENAMEID3('s', 'n', 'k'):
9780 procsnk(j);
9781 break;
9782
9783 case MAKENAMEID3('s', 'u', 'k'):
9784 procsuk(j);
9785 break;
9786
9787 case EOO:
9788 j->leaveobject();
9789 return;
9790
9791 default:
9792 if (!j->storeobject())
9793 {
9794 return;
9795 }
9796 }
9797 }
9798
9799 return;
9800 }
9801
9802 if (!j->enterarray())
9803 {
9804 LOG_err << "Malformed CR - outer array";
9805 return;
9806 }
9807
9808 if (j->enterarray())
9809 {
9810 while (!ISUNDEF(h = j->gethandle()))
9811 {
9812 shares.push_back(nodebyhandle(h));
9813 }
9814
9815 j->leavearray();
9816
9817 if (j->enterarray())
9818 {
9819 while (!ISUNDEF(h = j->gethandle()))
9820 {
9821 nodes.push_back(nodebyhandle(h));
9822 }
9823
9824 j->leavearray();
9825 }
9826 else
9827 {
9828 LOG_err << "Malformed SNK CR - nodes part";
9829 return;
9830 }
9831
9832 if (j->enterarray())
9833 {
9834 cr_response(&shares, &nodes, j);
9835 j->leavearray();
9836 }
9837 else
9838 {
9839 LOG_err << "Malformed CR - linkage part";
9840 return;
9841 }
9842 }
9843
9844 j->leavearray();
9845 }
9846
9847 // share nodekey delivery
procsnk(JSON * j)9848 void MegaClient::procsnk(JSON* j)
9849 {
9850 if (j->enterarray())
9851 {
9852 handle sh, nh;
9853
9854 while (j->enterarray())
9855 {
9856 if (ISUNDEF((sh = j->gethandle())))
9857 {
9858 return;
9859 }
9860
9861 if (ISUNDEF((nh = j->gethandle())))
9862 {
9863 return;
9864 }
9865
9866 Node* sn = nodebyhandle(sh);
9867
9868 if (sn && sn->sharekey && checkaccess(sn, OWNER))
9869 {
9870 Node* n = nodebyhandle(nh);
9871
9872 if (n && n->isbelow(sn))
9873 {
9874 byte keybuf[FILENODEKEYLENGTH];
9875 size_t keysize = n->nodekey().size();
9876 sn->sharekey->ecb_encrypt((byte*)n->nodekey().data(), keybuf, keysize);
9877 reqs.add(new CommandSingleKeyCR(sh, nh, keybuf, keysize));
9878 }
9879 }
9880
9881 j->leavearray();
9882 }
9883
9884 j->leavearray();
9885 }
9886 }
9887
9888 // share userkey delivery
procsuk(JSON * j)9889 void MegaClient::procsuk(JSON* j)
9890 {
9891 if (j->enterarray())
9892 {
9893 while (j->enterarray())
9894 {
9895 handle sh, uh;
9896
9897 sh = j->gethandle();
9898
9899 if (!ISUNDEF(sh))
9900 {
9901 uh = j->gethandle();
9902
9903 if (!ISUNDEF(uh))
9904 {
9905 // FIXME: add support for share user key delivery
9906 }
9907 }
9908
9909 j->leavearray();
9910 }
9911
9912 j->leavearray();
9913 }
9914 }
9915
9916 #ifdef ENABLE_CHAT
procmcf(JSON * j)9917 void MegaClient::procmcf(JSON *j)
9918 {
9919 if (j->enterobject())
9920 {
9921 bool done = false;
9922 while (!done)
9923 {
9924 bool readingPublicChats = false;
9925 switch(j->getnameid())
9926 {
9927 case MAKENAMEID2('p', 'c'): // list of public and/or formerly public chatrooms
9928 {
9929 readingPublicChats = true;
9930 } // fall-through
9931 case 'c': // list of chatrooms
9932 {
9933 j->enterarray();
9934
9935 while(j->enterobject()) // while there are more chats to read...
9936 {
9937 handle chatid = UNDEF;
9938 privilege_t priv = PRIV_UNKNOWN;
9939 int shard = -1;
9940 userpriv_vector *userpriv = NULL;
9941 bool group = false;
9942 string title;
9943 string unifiedKey;
9944 m_time_t ts = -1;
9945 bool publicchat = false;
9946
9947 bool readingChat = true;
9948 while(readingChat) // read the chat information
9949 {
9950 switch (j->getnameid())
9951 {
9952 case MAKENAMEID2('i','d'):
9953 chatid = j->gethandle(MegaClient::CHATHANDLE);
9954 break;
9955
9956 case 'p':
9957 priv = (privilege_t) j->getint();
9958 break;
9959
9960 case MAKENAMEID2('c','s'):
9961 shard = int(j->getint());
9962 break;
9963
9964 case 'u': // list of users participating in the chat (+privileges)
9965 userpriv = readuserpriv(j);
9966 break;
9967
9968 case 'g':
9969 group = j->getint();
9970 break;
9971
9972 case MAKENAMEID2('c','t'):
9973 j->storeobject(&title);
9974 break;
9975
9976 case MAKENAMEID2('c', 'k'): // store unified key for public chats
9977 assert(readingPublicChats);
9978 j->storeobject(&unifiedKey);
9979 break;
9980
9981 case MAKENAMEID2('t', 's'): // actual creation timestamp
9982 ts = j->getint();
9983 break;
9984
9985 case 'm': // operation mode: 1 -> public chat; 0 -> private chat
9986 assert(readingPublicChats);
9987 publicchat = j->getint();
9988 break;
9989
9990 case EOO:
9991 if (chatid != UNDEF && priv != PRIV_UNKNOWN && shard != -1)
9992 {
9993 if (chats.find(chatid) == chats.end())
9994 {
9995 chats[chatid] = new TextChat();
9996 }
9997
9998 TextChat *chat = chats[chatid];
9999 chat->id = chatid;
10000 chat->priv = priv;
10001 chat->shard = shard;
10002 chat->group = group;
10003 chat->title = title;
10004 chat->ts = (ts != -1) ? ts : 0;
10005
10006 if (readingPublicChats)
10007 {
10008 chat->publicchat = publicchat; // true or false (formerly public, now private)
10009 chat->unifiedKey = unifiedKey;
10010
10011 if (unifiedKey.empty())
10012 {
10013 LOG_err << "Received public (or formerly public) chat without unified key";
10014 }
10015 }
10016
10017 // remove yourself from the list of users (only peers matter)
10018 if (userpriv)
10019 {
10020 if (chat->priv == PRIV_RM)
10021 {
10022 // clear the list of peers because API still includes peers in the
10023 // actionpacket, but not in a fresh fetchnodes
10024 delete userpriv;
10025 userpriv = NULL;
10026 }
10027 else
10028 {
10029 userpriv_vector::iterator upvit;
10030 for (upvit = userpriv->begin(); upvit != userpriv->end(); upvit++)
10031 {
10032 if (upvit->first == me)
10033 {
10034 userpriv->erase(upvit);
10035 if (userpriv->empty())
10036 {
10037 delete userpriv;
10038 userpriv = NULL;
10039 }
10040 break;
10041 }
10042 }
10043 }
10044 }
10045 delete chat->userpriv; // discard any existing `userpriv`
10046 chat->userpriv = userpriv;
10047 }
10048 else
10049 {
10050 LOG_err << "Failed to parse chat information";
10051 }
10052 readingChat = false;
10053 break;
10054
10055 default:
10056 if (!j->storeobject())
10057 {
10058 LOG_err << "Failed to parse chat information";
10059 readingChat = false;
10060 delete userpriv;
10061 userpriv = NULL;
10062 }
10063 break;
10064 }
10065 }
10066 j->leaveobject();
10067 }
10068
10069 j->leavearray();
10070 break;
10071 }
10072
10073 case MAKENAMEID3('p', 'c', 'f'): // list of flags for public and/or formerly public chatrooms
10074 {
10075 readingPublicChats = true;
10076 } // fall-through
10077 case MAKENAMEID2('c', 'f'):
10078 {
10079 j->enterarray();
10080
10081 while(j->enterobject()) // while there are more chatid/flag tuples to read...
10082 {
10083 handle chatid = UNDEF;
10084 byte flags = 0xFF;
10085
10086 bool readingFlags = true;
10087 while (readingFlags)
10088 {
10089 switch (j->getnameid())
10090 {
10091
10092 case MAKENAMEID2('i','d'):
10093 chatid = j->gethandle(MegaClient::CHATHANDLE);
10094 break;
10095
10096 case 'f':
10097 flags = byte(j->getint());
10098 break;
10099
10100 case EOO:
10101 if (chatid != UNDEF && flags != 0xFF)
10102 {
10103 textchat_map::iterator it = chats.find(chatid);
10104 if (it == chats.end())
10105 {
10106 string chatidB64;
10107 string tmp((const char*)&chatid, sizeof(chatid));
10108 Base64::btoa(tmp, chatidB64);
10109 LOG_err << "Received flags for unknown chatid: " << chatidB64.c_str();
10110 }
10111 else
10112 {
10113 it->second->setFlags(flags);
10114 assert(!readingPublicChats || !it->second->unifiedKey.empty());
10115 }
10116 }
10117 else
10118 {
10119 LOG_err << "Failed to parse chat flags";
10120 }
10121 readingFlags = false;
10122 break;
10123
10124 default:
10125 if (!j->storeobject())
10126 {
10127 LOG_err << "Failed to parse chat flags";
10128 readingFlags = false;
10129 }
10130 break;
10131 }
10132 }
10133
10134 j->leaveobject();
10135 }
10136
10137 j->leavearray();
10138 break;
10139 }
10140
10141 case EOO:
10142 done = true;
10143 j->leaveobject();
10144 break;
10145
10146 default:
10147 if (!j->storeobject())
10148 {
10149 return;
10150 }
10151 }
10152 }
10153 }
10154 }
10155
procmcna(JSON * j)10156 void MegaClient::procmcna(JSON *j)
10157 {
10158 if (j->enterarray())
10159 {
10160 while(j->enterobject()) // while there are more nodes to read...
10161 {
10162 handle chatid = UNDEF;
10163 handle h = UNDEF;
10164 handle uh = UNDEF;
10165
10166 bool readingNode = true;
10167 while(readingNode) // read the attached node information
10168 {
10169 switch (j->getnameid())
10170 {
10171 case MAKENAMEID2('i','d'):
10172 chatid = j->gethandle(MegaClient::CHATHANDLE);
10173 break;
10174
10175 case 'n':
10176 h = j->gethandle(MegaClient::NODEHANDLE);
10177 break;
10178
10179 case 'u':
10180 uh = j->gethandle(MegaClient::USERHANDLE);
10181 break;
10182
10183 case EOO:
10184 if (chatid != UNDEF && h != UNDEF && uh != UNDEF)
10185 {
10186 textchat_map::iterator it = chats.find(chatid);
10187 if (it == chats.end())
10188 {
10189 LOG_err << "Unknown chat for user/node access to attachment";
10190 }
10191 else
10192 {
10193 it->second->setNodeUserAccess(h, uh);
10194 }
10195 }
10196 else
10197 {
10198 LOG_err << "Failed to parse attached node information";
10199 }
10200 readingNode = false;
10201 break;
10202
10203 default:
10204 if (!j->storeobject())
10205 {
10206 LOG_err << "Failed to parse attached node information";
10207 readingNode = false;
10208 }
10209 break;
10210 }
10211 }
10212 j->leaveobject();
10213 }
10214 j->leavearray();
10215 }
10216 }
10217 #endif
10218
10219 // add node to vector, return position, deduplicate
addnode(node_vector * v,Node * n) const10220 unsigned MegaClient::addnode(node_vector* v, Node* n) const
10221 {
10222 // linear search not particularly scalable, but fine for the relatively
10223 // small real-world requests
10224 for (unsigned i = unsigned(v->size()); i--; )
10225 {
10226 if ((*v)[i] == n)
10227 {
10228 return i;
10229 }
10230 }
10231
10232 v->push_back(n);
10233 return unsigned(v->size() - 1);
10234 }
10235
10236 // generate crypto key response
10237 // if !selector, generate all shares*nodes tuples
cr_response(node_vector * shares,node_vector * nodes,JSON * selector)10238 void MegaClient::cr_response(node_vector* shares, node_vector* nodes, JSON* selector)
10239 {
10240 node_vector rshares, rnodes;
10241 unsigned si, ni = unsigned(-1);
10242 Node* sn;
10243 Node* n;
10244 string crkeys;
10245 byte keybuf[FILENODEKEYLENGTH];
10246 char buf[128];
10247 int setkey = -1;
10248
10249 // for security reasons, we only respond to key requests affecting our own
10250 // shares
10251 for (si = unsigned(shares->size()); si--; )
10252 {
10253 if ((*shares)[si] && ((*shares)[si]->inshare || !(*shares)[si]->sharekey))
10254 {
10255 // security feature: we only distribute node keys for our own outgoing shares.
10256 LOG_warn << "Attempt to obtain node key for invalid/third-party share foiled";
10257 (*shares)[si] = NULL;
10258 sendevent(99445, "Inshare key request rejected", 0);
10259 }
10260 }
10261
10262 if (!selector)
10263 {
10264 si = 0;
10265 ni = unsigned(-1);
10266 if (shares->empty() || nodes->empty())
10267 {
10268 return;
10269 }
10270 }
10271
10272 // estimate required size for requested keys
10273 // for each node: ",<index>,<index>,"<nodekey>
10274 crkeys.reserve(nodes->size() * ((5 + 4 * 2) + (FILENODEKEYLENGTH * 4 / 3 + 4)) + 1);
10275 // we reserve for indexes up to 4 digits per index
10276
10277 for (;;)
10278 {
10279 if (selector)
10280 {
10281 if (!selector->isnumeric())
10282 {
10283 break;
10284 }
10285
10286 si = (unsigned)selector->getint();
10287 ni = (unsigned)selector->getint();
10288
10289 if (si >= shares->size())
10290 {
10291 LOG_err << "Share index out of range";
10292 return;
10293 }
10294
10295 if (ni >= nodes->size())
10296 {
10297 LOG_err << "Node index out of range";
10298 return;
10299 }
10300
10301 if (selector->pos[1] == '"')
10302 {
10303 setkey = selector->storebinary(keybuf, sizeof keybuf);
10304 }
10305 else
10306 {
10307 setkey = -1;
10308 }
10309 }
10310 else
10311 {
10312 // no selector supplied
10313 ni++;
10314
10315 if (ni >= nodes->size())
10316 {
10317 ni = 0;
10318 if (++si >= shares->size())
10319 {
10320 break;
10321 }
10322 }
10323 }
10324
10325 if ((sn = (*shares)[si]) && (n = (*nodes)[ni]))
10326 {
10327 if (n->isbelow(sn))
10328 {
10329 if (setkey >= 0)
10330 {
10331 if (setkey == (int)n->nodekey().size())
10332 {
10333 sn->sharekey->ecb_decrypt(keybuf, n->nodekey().size());
10334 n->setkey(keybuf);
10335 setkey = -1;
10336 }
10337 }
10338 else
10339 {
10340 n->applykey();
10341 int keysize = int(n->nodekey().size());
10342 if (sn->sharekey && keysize == (n->type == FILENODE ? FILENODEKEYLENGTH : FOLDERNODEKEYLENGTH))
10343 {
10344 unsigned nsi, nni;
10345
10346 nsi = addnode(&rshares, sn);
10347 nni = addnode(&rnodes, n);
10348
10349 sprintf(buf, "\",%u,%u,\"", nsi, nni);
10350
10351 // generate & queue share nodekey
10352 sn->sharekey->ecb_encrypt((byte*)n->nodekey().data(), keybuf, size_t(keysize));
10353 Base64::btoa(keybuf, keysize, strchr(buf + 7, 0));
10354 crkeys.append(buf);
10355 }
10356 else
10357 {
10358 LOG_warn << "Skipping node due to an unavailable key";
10359 }
10360 }
10361 }
10362 else
10363 {
10364 LOG_warn << "Attempt to obtain key of node outside share foiled";
10365 }
10366 }
10367 }
10368
10369 if (crkeys.size())
10370 {
10371 crkeys.append("\"");
10372 reqs.add(new CommandKeyCR(this, &rshares, &rnodes, crkeys.c_str() + 2));
10373 }
10374 }
10375
getaccountdetails(AccountDetails * ad,bool storage,bool transfer,bool pro,bool transactions,bool purchases,bool sessions,int source)10376 void MegaClient::getaccountdetails(AccountDetails* ad, bool storage,
10377 bool transfer, bool pro, bool transactions,
10378 bool purchases, bool sessions, int source)
10379 {
10380 if (storage || transfer || pro)
10381 {
10382 reqs.add(new CommandGetUserQuota(this, ad, storage, transfer, pro, source));
10383 }
10384
10385 if (transactions)
10386 {
10387 reqs.add(new CommandGetUserTransactions(this, ad));
10388 }
10389
10390 if (purchases)
10391 {
10392 reqs.add(new CommandGetUserPurchases(this, ad));
10393 }
10394
10395 if (sessions)
10396 {
10397 reqs.add(new CommandGetUserSessions(this, ad));
10398 }
10399 }
10400
querytransferquota(m_off_t size)10401 void MegaClient::querytransferquota(m_off_t size)
10402 {
10403 reqs.add(new CommandQueryTransferQuota(this, size));
10404 }
10405
10406 // export node link
exportnode(Node * n,int del,m_time_t ets)10407 error MegaClient::exportnode(Node* n, int del, m_time_t ets)
10408 {
10409 if (n->plink && !del && !n->plink->takendown
10410 && (ets == n->plink->ets) && !n->plink->isExpired())
10411 {
10412 if (ststatus == STORAGE_PAYWALL)
10413 {
10414 LOG_warn << "Rejecting public link request when ODQ paywall";
10415 return API_EPAYWALL;
10416 }
10417 restag = reqtag;
10418 app->exportnode_result(n->nodehandle, n->plink->ph);
10419 return API_OK;
10420 }
10421
10422 if (!checkaccess(n, OWNER))
10423 {
10424 return API_EACCESS;
10425 }
10426
10427 // export node
10428 switch (n->type)
10429 {
10430 case FILENODE:
10431 getpubliclink(n, del, ets);
10432 break;
10433
10434 case FOLDERNODE:
10435 if (del)
10436 {
10437 // deletion of outgoing share also deletes the link automatically
10438 // need to first remove the link and then the share
10439 getpubliclink(n, del, ets);
10440 setshare(n, NULL, ACCESS_UNKNOWN);
10441 }
10442 else
10443 {
10444 // exporting folder - need to create share first
10445 setshare(n, NULL, RDONLY);
10446 // getpubliclink() is called as _result() of the share
10447 }
10448
10449 break;
10450
10451 default:
10452 return API_EACCESS;
10453 }
10454
10455 return API_OK;
10456 }
10457
getpubliclink(Node * n,int del,m_time_t ets)10458 void MegaClient::getpubliclink(Node* n, int del, m_time_t ets)
10459 {
10460 reqs.add(new CommandSetPH(this, n, del, ets));
10461 }
10462
10463 // open exported file link
10464 // formats supported: ...#!publichandle!key, publichandle!key or file/<ph>[<params>][#<key>]
openfilelink(handle ph,const byte * key,int op)10465 void MegaClient::openfilelink(handle ph, const byte *key, int op)
10466 {
10467 if (op)
10468 {
10469 reqs.add(new CommandGetPH(this, ph, key, op));
10470 }
10471 else
10472 {
10473 assert(key);
10474 reqs.add(new CommandGetFile(this, NULL, key, ph, false));
10475 }
10476 }
10477
10478 /* Format of password-protected links
10479 *
10480 * algorithm = 1 byte - A byte to identify which algorithm was used (for future upgradability), initially is set to 0
10481 * file/folder = 1 byte - A byte to identify if the link is a file or folder link (0 = folder, 1 = file)
10482 * public handle = 6 bytes - The public folder/file handle
10483 * salt = 32 bytes - A 256 bit randomly generated salt
10484 * encrypted key = 16 or 32 bytes - The encrypted actual folder or file key
10485 * MAC tag = 32 bytes - The MAC of all the previous data to ensure integrity of the link i.e. calculated as:
10486 * HMAC-SHA256(MAC key, (algorithm || file/folder || public handle || salt || encrypted key))
10487 */
decryptlink(const char * link,const char * pwd,string * decryptedLink)10488 error MegaClient::decryptlink(const char *link, const char *pwd, string* decryptedLink)
10489 {
10490 if (!pwd || !link)
10491 {
10492 LOG_err << "Empty link or empty password to decrypt link";
10493 return API_EARGS;
10494 }
10495
10496 const char* ptr = NULL;
10497 const char* end = NULL;
10498 if (!(ptr = strstr(link, "#P!")))
10499 {
10500 LOG_err << "This link is not password protected";
10501 return API_EARGS;
10502 }
10503 ptr += 3;
10504
10505 // Decode the link
10506 int linkLen = 1 + 1 + 6 + 32 + 32 + 32; // maximum size in binary, for file links
10507 string linkBin;
10508 linkBin.resize(linkLen);
10509 linkLen = Base64::atob(ptr, (byte*)linkBin.data(), linkLen);
10510
10511 ptr = (char *)linkBin.data();
10512 end = ptr + linkLen;
10513
10514 if ((ptr + 2) >= end)
10515 {
10516 LOG_err << "This link is too short";
10517 return API_EINCOMPLETE;
10518 }
10519
10520 int algorithm = *ptr++;
10521 if (algorithm != 1 && algorithm != 2)
10522 {
10523 LOG_err << "The algorithm used to encrypt this link is not supported";
10524 return API_EINTERNAL;
10525 }
10526
10527 int isFolder = !(*ptr++);
10528 if (isFolder > 1)
10529 {
10530 LOG_err << "This link doesn't reference any folder or file";
10531 return API_EARGS;
10532 }
10533
10534 size_t encKeyLen = isFolder ? FOLDERNODEKEYLENGTH : FILENODEKEYLENGTH;
10535 if ((ptr + 38 + encKeyLen + 32) > end)
10536 {
10537 LOG_err << "This link is too short";
10538 return API_EINCOMPLETE;
10539 }
10540
10541 handle ph = MemAccess::get<handle>(ptr);
10542 ptr += 6;
10543
10544 byte salt[32];
10545 memcpy((char*)salt, ptr, 32);
10546 ptr += sizeof salt;
10547
10548 string encKey;
10549 encKey.resize(encKeyLen);
10550 memcpy((byte *)encKey.data(), ptr, encKeyLen);
10551 ptr += encKeyLen;
10552
10553 byte hmac[32];
10554 memcpy((char*)&hmac, ptr, 32);
10555 ptr += 32;
10556
10557 // Derive MAC key with salt+pwd
10558 byte derivedKey[64];
10559 unsigned int iterations = 100000;
10560 PBKDF2_HMAC_SHA512 pbkdf2;
10561 pbkdf2.deriveKey(derivedKey, sizeof derivedKey,
10562 (byte*) pwd, strlen(pwd),
10563 salt, sizeof salt,
10564 iterations);
10565
10566 byte hmacComputed[32];
10567 if (algorithm == 1)
10568 {
10569 // verify HMAC with macKey(alg, f/F, ph, salt, encKey)
10570 HMACSHA256 hmacsha256((byte *)linkBin.data(), 40 + encKeyLen);
10571 hmacsha256.add(derivedKey + 32, 32);
10572 hmacsha256.get(hmacComputed);
10573 }
10574 else // algorithm == 2 (fix legacy Webclient bug: swap data and key)
10575 {
10576 // verify HMAC with macKey(alg, f/F, ph, salt, encKey)
10577 HMACSHA256 hmacsha256(derivedKey + 32, 32);
10578 hmacsha256.add((byte *)linkBin.data(), unsigned(40 + encKeyLen));
10579 hmacsha256.get(hmacComputed);
10580 }
10581 if (memcmp(hmac, hmacComputed, 32))
10582 {
10583 LOG_err << "HMAC verification failed. Possible tampered or corrupted link";
10584 return API_EKEY;
10585 }
10586
10587 if (decryptedLink)
10588 {
10589 // Decrypt encKey using X-OR with first 16/32 bytes of derivedKey
10590 byte key[FILENODEKEYLENGTH];
10591 for (unsigned int i = 0; i < encKeyLen; i++)
10592 {
10593 key[i] = encKey[i] ^ derivedKey[i];
10594 }
10595
10596 Base64Str<FILENODEKEYLENGTH> keyStr(key);
10597 decryptedLink->assign(MegaClient::getPublicLink(mNewLinkFormat, isFolder ? FOLDERNODE : FILENODE, ph, keyStr));
10598 }
10599
10600 return API_OK;
10601 }
10602
encryptlink(const char * link,const char * pwd,string * encryptedLink)10603 error MegaClient::encryptlink(const char *link, const char *pwd, string *encryptedLink)
10604 {
10605 if (!pwd || !link || !encryptedLink)
10606 {
10607 LOG_err << "Empty link or empty password to encrypt link";
10608 return API_EARGS;
10609 }
10610
10611 bool isFolder = (strstr(link, "#F!") || strstr(link, "folder/"));
10612 handle ph;
10613 size_t linkKeySize = isFolder ? FOLDERNODEKEYLENGTH : FILENODEKEYLENGTH;
10614 std::unique_ptr<byte[]> linkKey(new byte[linkKeySize]);
10615 error e = parsepubliclink(link, ph, linkKey.get(), isFolder);
10616 if (e == API_OK)
10617 {
10618 // Derive MAC key with salt+pwd
10619 byte derivedKey[64];
10620 byte salt[32];
10621 rng.genblock(salt, 32);
10622 unsigned int iterations = 100000;
10623 PBKDF2_HMAC_SHA512 pbkdf2;
10624 pbkdf2.deriveKey(derivedKey, sizeof derivedKey,
10625 (byte*) pwd, strlen(pwd),
10626 salt, sizeof salt,
10627 iterations);
10628
10629 // Prepare encryption key
10630 string encKey;
10631 encKey.resize(linkKeySize);
10632 for (unsigned int i = 0; i < linkKeySize; i++)
10633 {
10634 encKey[i] = derivedKey[i] ^ linkKey[i];
10635 }
10636
10637 // Preapare payload to derive encryption key
10638 byte algorithm = 2;
10639 byte type = isFolder ? 0 : 1;
10640 string payload;
10641 payload.append((char*) &algorithm, sizeof algorithm);
10642 payload.append((char*) &type, sizeof type);
10643 payload.append((char*) &ph, NODEHANDLE);
10644 payload.append((char*) salt, sizeof salt);
10645 payload.append(encKey);
10646
10647
10648 // Prepare HMAC
10649 byte hmac[32];
10650 if (algorithm == 1)
10651 {
10652 HMACSHA256 hmacsha256((byte *)payload.data(), payload.size());
10653 hmacsha256.add(derivedKey + 32, 32);
10654 hmacsha256.get(hmac);
10655 }
10656 else if (algorithm == 2) // fix legacy Webclient bug: swap data and key
10657 {
10658 HMACSHA256 hmacsha256(derivedKey + 32, 32);
10659 hmacsha256.add((byte *)payload.data(), unsigned(payload.size()));
10660 hmacsha256.get(hmac);
10661 }
10662 else
10663 {
10664 LOG_err << "Invalid algorithm to encrypt link";
10665 return API_EINTERNAL;
10666 }
10667
10668 // Prepare encrypted link
10669 string encLinkBytes;
10670 encLinkBytes.append((char*) &algorithm, sizeof algorithm);
10671 encLinkBytes.append((char*) &type, sizeof type);
10672 encLinkBytes.append((char*) &ph, NODEHANDLE);
10673 encLinkBytes.append((char*) salt, sizeof salt);
10674 encLinkBytes.append(encKey);
10675 encLinkBytes.append((char*) hmac, sizeof hmac);
10676
10677 string encLink;
10678 Base64::btoa(encLinkBytes, encLink);
10679
10680 encryptedLink->clear();
10681 encryptedLink->append("https://mega.nz/#P!");
10682 encryptedLink->append(encLink);
10683 }
10684
10685 return e;
10686 }
10687
loggedinfolderlink()10688 bool MegaClient::loggedinfolderlink()
10689 {
10690 return !ISUNDEF(publichandle);
10691 }
10692
loggedin()10693 sessiontype_t MegaClient::loggedin()
10694 {
10695 if (ISUNDEF(me))
10696 {
10697 return NOTLOGGEDIN;
10698 }
10699
10700 if (ephemeralSession)
10701 {
10702 return EPHEMERALACCOUNT;
10703 }
10704
10705 if (!asymkey.isvalid())
10706 {
10707 return CONFIRMEDACCOUNT;
10708 }
10709
10710 return FULLACCOUNT;
10711 }
10712
whyamiblocked()10713 void MegaClient::whyamiblocked()
10714 {
10715 // make sure the smsve flag is up to date when we get the response
10716 getmiscflags();
10717
10718 // queue the actual request
10719 reqs.add(new CommandWhyAmIblocked(this));
10720 }
10721
block(bool fromServerClientResponse)10722 void MegaClient::block(bool fromServerClientResponse)
10723 {
10724 LOG_verbose << "Blocking MegaClient, fromServerClientResponse: " << fromServerClientResponse;
10725
10726 mBlocked = true;
10727 }
10728
unblock()10729 void MegaClient::unblock()
10730 {
10731 LOG_verbose << "Unblocking MegaClient";
10732
10733 mBlocked = false;
10734 }
10735
changepw(const char * password,const char * pin)10736 error MegaClient::changepw(const char* password, const char *pin)
10737 {
10738 User* u;
10739
10740 if (!loggedin() || !(u = finduser(me)))
10741 {
10742 return API_EACCESS;
10743 }
10744
10745 if (accountversion == 1)
10746 {
10747 error e;
10748 byte newpwkey[SymmCipher::KEYLENGTH];
10749 if ((e = pw_key(password, newpwkey)))
10750 {
10751 return e;
10752 }
10753
10754 byte newkey[SymmCipher::KEYLENGTH];
10755 SymmCipher pwcipher;
10756 memcpy(newkey, key.key, sizeof newkey);
10757 pwcipher.setkey(newpwkey);
10758 pwcipher.ecb_encrypt(newkey);
10759
10760 string email = u->email;
10761 uint64_t stringhash = stringhash64(&email, &pwcipher);
10762 reqs.add(new CommandSetMasterKey(this, newkey, (const byte *)&stringhash, sizeof(stringhash), NULL, pin));
10763 return API_OK;
10764 }
10765
10766 byte clientRandomValue[SymmCipher::KEYLENGTH];
10767 rng.genblock(clientRandomValue, sizeof(clientRandomValue));
10768
10769 string salt;
10770 HashSHA256 hasher;
10771 string buffer = "mega.nz";
10772 buffer.resize(200, 'P');
10773 buffer.append((char *)clientRandomValue, sizeof(clientRandomValue));
10774 hasher.add((const byte*)buffer.data(), unsigned(buffer.size()));
10775 hasher.get(&salt);
10776
10777 byte derivedKey[2 * SymmCipher::KEYLENGTH];
10778 CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA512> pbkdf2;
10779 pbkdf2.DeriveKey(derivedKey, sizeof(derivedKey), 0, (byte *)password, strlen(password),
10780 (const byte *)salt.data(), salt.size(), 100000);
10781
10782 byte encmasterkey[SymmCipher::KEYLENGTH];
10783 SymmCipher cipher;
10784 cipher.setkey(derivedKey);
10785 cipher.ecb_encrypt(key.key, encmasterkey);
10786
10787 string hashedauthkey;
10788 byte *authkey = derivedKey + SymmCipher::KEYLENGTH;
10789 hasher.add(authkey, SymmCipher::KEYLENGTH);
10790 hasher.get(&hashedauthkey);
10791 hashedauthkey.resize(SymmCipher::KEYLENGTH);
10792
10793 // Pass the salt and apply to this->accountsalt if the command succeed to allow posterior checks of the password without getting it from the server
10794 reqs.add(new CommandSetMasterKey(this, encmasterkey, (byte*)hashedauthkey.data(), SymmCipher::KEYLENGTH, clientRandomValue, pin, &salt));
10795 return API_OK;
10796 }
10797
10798 // create ephemeral session
createephemeral()10799 void MegaClient::createephemeral()
10800 {
10801 ephemeralSession = true;
10802 byte keybuf[SymmCipher::KEYLENGTH];
10803 byte pwbuf[SymmCipher::KEYLENGTH];
10804 byte sscbuf[2 * SymmCipher::KEYLENGTH];
10805
10806 rng.genblock(keybuf, sizeof keybuf);
10807 rng.genblock(pwbuf, sizeof pwbuf);
10808 rng.genblock(sscbuf, sizeof sscbuf);
10809
10810 key.setkey(keybuf);
10811 key.ecb_encrypt(sscbuf, sscbuf + SymmCipher::KEYLENGTH, SymmCipher::KEYLENGTH);
10812
10813 key.setkey(pwbuf);
10814 key.ecb_encrypt(keybuf);
10815
10816 reqs.add(new CommandCreateEphemeralSession(this, keybuf, pwbuf, sscbuf));
10817 }
10818
resumeephemeral(handle uh,const byte * pw,int ctag)10819 void MegaClient::resumeephemeral(handle uh, const byte* pw, int ctag)
10820 {
10821 ephemeralSession = true;
10822 reqs.add(new CommandResumeEphemeralSession(this, uh, pw, ctag ? ctag : reqtag));
10823 }
10824
cancelsignup()10825 void MegaClient::cancelsignup()
10826 {
10827 reqs.add(new CommandCancelSignup(this));
10828 }
10829
sendsignuplink(const char * email,const char * name,const byte * pwhash)10830 void MegaClient::sendsignuplink(const char* email, const char* name, const byte* pwhash)
10831 {
10832 SymmCipher pwcipher(pwhash);
10833 byte c[2 * SymmCipher::KEYLENGTH];
10834
10835 memcpy(c, key.key, sizeof key.key);
10836 rng.genblock(c + SymmCipher::KEYLENGTH, SymmCipher::KEYLENGTH / 4);
10837 memset(c + SymmCipher::KEYLENGTH + SymmCipher::KEYLENGTH / 4, 0, SymmCipher::KEYLENGTH / 2);
10838 rng.genblock(c + 2 * SymmCipher::KEYLENGTH - SymmCipher::KEYLENGTH / 4, SymmCipher::KEYLENGTH / 4);
10839
10840 pwcipher.ecb_encrypt(c, c, sizeof c);
10841
10842 reqs.add(new CommandSendSignupLink(this, email, name, c));
10843 }
10844
sendsignuplink2(const char * email,const char * password,const char * name)10845 string MegaClient::sendsignuplink2(const char *email, const char *password, const char* name)
10846 {
10847 byte clientrandomvalue[SymmCipher::KEYLENGTH];
10848 rng.genblock(clientrandomvalue, sizeof(clientrandomvalue));
10849
10850 string salt;
10851 HashSHA256 hasher;
10852 string buffer = "mega.nz";
10853 buffer.resize(200, 'P');
10854 buffer.append((char *)clientrandomvalue, sizeof(clientrandomvalue));
10855 hasher.add((const byte*)buffer.data(), unsigned(buffer.size()));
10856 hasher.get(&salt);
10857
10858 byte derivedKey[2 * SymmCipher::KEYLENGTH];
10859 CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA512> pbkdf2;
10860 pbkdf2.DeriveKey(derivedKey, sizeof(derivedKey), 0, (byte *)password, strlen(password),
10861 (const byte *)salt.data(), salt.size(), 100000);
10862
10863 byte encmasterkey[SymmCipher::KEYLENGTH];
10864 SymmCipher cipher;
10865 cipher.setkey(derivedKey);
10866 cipher.ecb_encrypt(key.key, encmasterkey);
10867
10868 string hashedauthkey;
10869 byte *authkey = derivedKey + SymmCipher::KEYLENGTH;
10870 hasher.add(authkey, SymmCipher::KEYLENGTH);
10871 hasher.get(&hashedauthkey);
10872 hashedauthkey.resize(SymmCipher::KEYLENGTH);
10873
10874 accountversion = 2;
10875 accountsalt = salt;
10876 reqs.add(new CommandSendSignupLink2(this, email, name, clientrandomvalue, encmasterkey, (byte*)hashedauthkey.data()));
10877 return string((const char*)derivedKey, 2 * SymmCipher::KEYLENGTH);
10878 }
10879
resendsignuplink2(const char * email,const char * name)10880 void MegaClient::resendsignuplink2(const char *email, const char *name)
10881 {
10882 reqs.add(new CommandSendSignupLink2(this, email, name));
10883 }
10884
10885 // if query is 0, actually confirm account; just decode/query signup link
10886 // details otherwise
querysignuplink(const byte * code,unsigned len)10887 void MegaClient::querysignuplink(const byte* code, unsigned len)
10888 {
10889 reqs.add(new CommandQuerySignupLink(this, code, len));
10890 }
10891
confirmsignuplink(const byte * code,unsigned len,uint64_t emailhash)10892 void MegaClient::confirmsignuplink(const byte* code, unsigned len, uint64_t emailhash)
10893 {
10894 reqs.add(new CommandConfirmSignupLink(this, code, len, emailhash));
10895 }
10896
confirmsignuplink2(const byte * code,unsigned len)10897 void MegaClient::confirmsignuplink2(const byte *code, unsigned len)
10898 {
10899 reqs.add(new CommandConfirmSignupLink2(this, code, len));
10900 }
10901
10902 // generate and configure encrypted private key, plaintext public key
setkeypair()10903 void MegaClient::setkeypair()
10904 {
10905 CryptoPP::Integer pubk[AsymmCipher::PUBKEY];
10906
10907 string privks, pubks;
10908
10909 asymkey.genkeypair(rng, asymkey.key, pubk, 2048);
10910
10911 AsymmCipher::serializeintarray(pubk, AsymmCipher::PUBKEY, &pubks);
10912 AsymmCipher::serializeintarray(asymkey.key, AsymmCipher::PRIVKEY, &privks);
10913
10914 // add random padding and ECB-encrypt with master key
10915 unsigned t = unsigned(privks.size());
10916
10917 privks.resize((t + SymmCipher::BLOCKSIZE - 1) & - SymmCipher::BLOCKSIZE);
10918 rng.genblock((byte*)(privks.data() + t), privks.size() - t);
10919
10920 key.ecb_encrypt((byte*)privks.data(), (byte*)privks.data(), privks.size());
10921
10922 reqs.add(new CommandSetKeyPair(this,
10923 (const byte*)privks.data(),
10924 unsigned(privks.size()),
10925 (const byte*)pubks.data(),
10926 unsigned(pubks.size())));
10927 }
10928
fetchsc(DbTable * sctable)10929 bool MegaClient::fetchsc(DbTable* sctable)
10930 {
10931 uint32_t id;
10932 string data;
10933 Node* n;
10934 User* u;
10935 PendingContactRequest* pcr;
10936 node_vector dp;
10937
10938 LOG_info << "Loading session from local cache";
10939
10940 sctable->rewind();
10941
10942 bool hasNext = sctable->next(&id, &data, &key);
10943 WAIT_CLASS::bumpds();
10944 fnstats.timeToFirstByte = Waiter::ds - fnstats.startTime;
10945
10946 while (hasNext)
10947 {
10948 switch (id & 15)
10949 {
10950 case CACHEDSCSN:
10951 if (data.size() != sizeof cachedscsn)
10952 {
10953 return false;
10954 }
10955 break;
10956
10957 case CACHEDNODE:
10958 if ((n = Node::unserialize(this, &data, &dp)))
10959 {
10960 n->dbid = id;
10961 }
10962 else
10963 {
10964 LOG_err << "Failed - node record read error";
10965 return false;
10966 }
10967 break;
10968
10969 case CACHEDPCR:
10970 if ((pcr = PendingContactRequest::unserialize(&data)))
10971 {
10972 mappcr(pcr->id, pcr);
10973 pcr->dbid = id;
10974 }
10975 else
10976 {
10977 LOG_err << "Failed - pcr record read error";
10978 return false;
10979 }
10980 break;
10981
10982 case CACHEDUSER:
10983 if ((u = User::unserialize(this, &data)))
10984 {
10985 u->dbid = id;
10986 }
10987 else
10988 {
10989 LOG_err << "Failed - user record read error";
10990 return false;
10991 }
10992 break;
10993
10994 case CACHEDCHAT:
10995 #ifdef ENABLE_CHAT
10996 {
10997 TextChat *chat;
10998 if ((chat = TextChat::unserialize(this, &data)))
10999 {
11000 chat->dbid = id;
11001 }
11002 else
11003 {
11004 LOG_err << "Failed - chat record read error";
11005 return false;
11006 }
11007 }
11008 #endif
11009 break;
11010 }
11011 hasNext = sctable->next(&id, &data, &key);
11012 }
11013
11014 WAIT_CLASS::bumpds();
11015 fnstats.timeToLastByte = Waiter::ds - fnstats.startTime;
11016
11017 // any child nodes arrived before their parents?
11018 for (size_t i = dp.size(); i--; )
11019 {
11020 if ((n = nodebyhandle(dp[i]->parenthandle)))
11021 {
11022 dp[i]->setparent(n);
11023 }
11024 }
11025
11026 mergenewshares(0);
11027
11028 return true;
11029 }
11030
purgeOrphanTransfers(bool remove)11031 void MegaClient::purgeOrphanTransfers(bool remove)
11032 {
11033 bool purgeOrphanTransfers = statecurrent;
11034
11035 #ifdef ENABLE_SYNC
11036 if (purgeOrphanTransfers && !remove)
11037 {
11038 if (!syncsup)
11039 {
11040 purgeOrphanTransfers = false;
11041 }
11042 else
11043 {
11044 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
11045 {
11046 if ((*it)->state != SYNC_ACTIVE)
11047 {
11048 purgeOrphanTransfers = false;
11049 break;
11050 }
11051 }
11052 }
11053 }
11054 #endif
11055
11056 for (int d = GET; d == GET || d == PUT; d += PUT - GET)
11057 {
11058 DBTableTransactionCommitter committer(tctable);
11059 while (cachedtransfers[d].size())
11060 {
11061 transfer_map::iterator it = cachedtransfers[d].begin();
11062 Transfer *transfer = it->second;
11063 if (remove || (purgeOrphanTransfers && (m_time() - transfer->lastaccesstime) >= 172500))
11064 {
11065 LOG_warn << "Purging orphan transfer";
11066 transfer->finished = true;
11067 }
11068
11069 app->transfer_removed(transfer);
11070 delete transfer;
11071 cachedtransfers[d].erase(it);
11072 }
11073 }
11074 }
11075
closetc(bool remove)11076 void MegaClient::closetc(bool remove)
11077 {
11078 pendingtcids.clear();
11079 cachedfiles.clear();
11080 cachedfilesdbids.clear();
11081
11082 if (remove && tctable)
11083 {
11084 tctable->remove();
11085 }
11086 delete tctable;
11087 tctable = NULL;
11088 }
11089
enabletransferresumption(const char * loggedoutid)11090 void MegaClient::enabletransferresumption(const char *loggedoutid)
11091 {
11092 if (!dbaccess || tctable)
11093 {
11094 return;
11095 }
11096
11097 string dbname;
11098 if (sid.size() >= SIDLEN)
11099 {
11100 dbname.resize((SIDLEN - sizeof key.key) * 4 / 3 + 3);
11101 dbname.resize(Base64::btoa((const byte*)sid.data() + sizeof key.key, SIDLEN - sizeof key.key, (char*)dbname.c_str()));
11102 tckey = key;
11103 }
11104 else if (loggedinfolderlink())
11105 {
11106 dbname.resize(NODEHANDLE * 4 / 3 + 3);
11107 dbname.resize(Base64::btoa((const byte*)&publichandle, NODEHANDLE, (char*)dbname.c_str()));
11108 tckey = key;
11109 }
11110 else
11111 {
11112 dbname = loggedoutid ? loggedoutid : "default";
11113
11114 string lok;
11115 Hash hash;
11116 hash.add((const byte *)dbname.c_str(), unsigned(dbname.size() + 1));
11117 hash.get(&lok);
11118 tckey.setkey((const byte*)lok.data());
11119 }
11120
11121 dbname.insert(0, "transfers_");
11122
11123 tctable = dbaccess->open(rng, fsaccess, &dbname, true, true);
11124 if (!tctable)
11125 {
11126 return;
11127 }
11128
11129 uint32_t id;
11130 string data;
11131 Transfer* t;
11132
11133 LOG_info << "Loading transfers from local cache";
11134 tctable->rewind();
11135 while (tctable->next(&id, &data, &tckey))
11136 {
11137 switch (id & 15)
11138 {
11139 case CACHEDTRANSFER:
11140 if ((t = Transfer::unserialize(this, &data, cachedtransfers)))
11141 {
11142 t->dbid = id;
11143 if (t->priority > transferlist.currentpriority)
11144 {
11145 transferlist.currentpriority = t->priority;
11146 }
11147 LOG_debug << "Cached transfer loaded";
11148 }
11149 else
11150 {
11151 tctable->del(id);
11152 LOG_err << "Failed - transfer record read error";
11153 }
11154 break;
11155 case CACHEDFILE:
11156 cachedfiles.push_back(data);
11157 cachedfilesdbids.push_back(id);
11158 LOG_debug << "Cached file loaded";
11159 break;
11160 }
11161 }
11162
11163 // if we are logged in but the filesystem is not current yet
11164 // postpone the resumption until the filesystem is updated
11165 if ((!sid.size() && !loggedinfolderlink()) || statecurrent)
11166 {
11167 DBTableTransactionCommitter committer(tctable);
11168 for (unsigned int i = 0; i < cachedfiles.size(); i++)
11169 {
11170 direction_t type = NONE;
11171 File *file = app->file_resume(&cachedfiles.at(i), &type);
11172 if (!file || (type != GET && type != PUT))
11173 {
11174 tctable->del(cachedfilesdbids.at(i));
11175 continue;
11176 }
11177 nextreqtag();
11178 file->dbid = cachedfilesdbids.at(i);
11179 if (!startxfer(type, file, committer))
11180 {
11181 tctable->del(cachedfilesdbids.at(i));
11182 continue;
11183 }
11184 }
11185 cachedfiles.clear();
11186 cachedfilesdbids.clear();
11187 }
11188 }
11189
disabletransferresumption(const char * loggedoutid)11190 void MegaClient::disabletransferresumption(const char *loggedoutid)
11191 {
11192 if (!dbaccess)
11193 {
11194 return;
11195 }
11196 purgeOrphanTransfers(true);
11197 closetc(true);
11198
11199 string dbname;
11200 if (sid.size() >= SIDLEN)
11201 {
11202 dbname.resize((SIDLEN - sizeof key.key) * 4 / 3 + 3);
11203 dbname.resize(Base64::btoa((const byte*)sid.data() + sizeof key.key, SIDLEN - sizeof key.key, (char*)dbname.c_str()));
11204
11205 }
11206 else if (loggedinfolderlink())
11207 {
11208 dbname.resize(NODEHANDLE * 4 / 3 + 3);
11209 dbname.resize(Base64::btoa((const byte*)&publichandle, NODEHANDLE, (char*)dbname.c_str()));
11210 }
11211 else
11212 {
11213 dbname = loggedoutid ? loggedoutid : "default";
11214 }
11215 dbname.insert(0, "transfers_");
11216
11217 tctable = dbaccess->open(rng, fsaccess, &dbname, true, true);
11218 if (!tctable)
11219 {
11220 return;
11221 }
11222
11223 purgeOrphanTransfers(true);
11224 closetc(true);
11225 }
11226
fetchnodes(bool nocache)11227 void MegaClient::fetchnodes(bool nocache)
11228 {
11229 if (fetchingnodes)
11230 {
11231 return;
11232 }
11233
11234 WAIT_CLASS::bumpds();
11235 fnstats.init();
11236 if (sid.size() >= SIDLEN)
11237 {
11238 fnstats.type = FetchNodesStats::TYPE_ACCOUNT;
11239 }
11240 else if (loggedinfolderlink())
11241 {
11242 fnstats.type = FetchNodesStats::TYPE_FOLDER;
11243 }
11244
11245 opensctable();
11246
11247 if (sctable && cachedscsn == UNDEF)
11248 {
11249 sctable->truncate();
11250 }
11251
11252 // only initial load from local cache
11253 if (loggedin() == FULLACCOUNT && !nodes.size() && sctable && !ISUNDEF(cachedscsn) && fetchsc(sctable))
11254 {
11255 WAIT_CLASS::bumpds();
11256 fnstats.mode = FetchNodesStats::MODE_DB;
11257 fnstats.cache = FetchNodesStats::API_NO_CACHE;
11258 fnstats.nodesCached = nodes.size();
11259 fnstats.timeToCached = Waiter::ds - fnstats.startTime;
11260 fnstats.timeToResult = fnstats.timeToCached;
11261
11262 restag = reqtag;
11263 statecurrent = false;
11264
11265 sctable->begin();
11266 pendingsccommit = false;
11267
11268 // allow sc requests to start
11269 scsn.setScsn(cachedscsn);
11270 LOG_info << "Session loaded from local cache. SCSN: " << scsn.text();
11271
11272 #ifdef ENABLE_SYNC
11273 resumeResumableSyncs();
11274 #endif
11275 app->fetchnodes_result(API_OK);
11276
11277 loadAuthrings();
11278
11279 WAIT_CLASS::bumpds();
11280 fnstats.timeToSyncsResumed = Waiter::ds - fnstats.startTime;
11281 }
11282 else if (!fetchingnodes)
11283 {
11284 fnstats.mode = FetchNodesStats::MODE_API;
11285 fnstats.cache = nocache ? FetchNodesStats::API_NO_CACHE : FetchNodesStats::API_CACHE;
11286 fetchingnodes = true;
11287 pendingsccommit = false;
11288
11289 // prevent the processing of previous sc requests
11290 pendingsc.reset();
11291 pendingscUserAlerts.reset();
11292 jsonsc.pos = NULL;
11293 scnotifyurl.clear();
11294 insca = false;
11295 insca_notlast = false;
11296 btsc.reset();
11297
11298 // don't allow to start new sc requests yet
11299 scsn.clear();
11300
11301 #ifdef ENABLE_SYNC
11302 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
11303 {
11304 (*it)->changestate(SYNC_CANCELED);
11305 }
11306 #endif
11307
11308 if (!loggedinfolderlink())
11309 {
11310 getuserdata();
11311
11312 if (loggedin() == FULLACCOUNT)
11313 {
11314 fetchkeys();
11315 loadAuthrings();
11316 }
11317
11318 fetchtimezone();
11319 }
11320
11321 reqs.add(new CommandFetchNodes(this, nocache));
11322 }
11323 }
11324
fetchkeys()11325 void MegaClient::fetchkeys()
11326 {
11327 fetchingkeys = true;
11328
11329 resetKeyring();
11330 discarduser(me);
11331 User *u = finduser(me, 1);
11332
11333 // RSA public key is retrieved by getuserdata
11334
11335 getua(u, ATTR_KEYRING, 0); // private Cu25519 & private Ed25519
11336 getua(u, ATTR_ED25519_PUBK, 0);
11337 getua(u, ATTR_CU25519_PUBK, 0);
11338 getua(u, ATTR_SIG_CU255_PUBK, 0);
11339 getua(u, ATTR_SIG_RSA_PUBK, 0); // it triggers MegaClient::initializekeys() --> must be the latest
11340 }
11341
initializekeys()11342 void MegaClient::initializekeys()
11343 {
11344 User *u = finduser(me);
11345
11346 // Initialize private keys
11347 const string *av = (u->isattrvalid(ATTR_KEYRING)) ? u->getattr(ATTR_KEYRING) : NULL;
11348 if (av)
11349 {
11350 TLVstore *tlvRecords = TLVstore::containerToTLVrecords(av, &key);
11351 if (tlvRecords)
11352 {
11353
11354 if (tlvRecords->find(EdDSA::TLV_KEY))
11355 {
11356 string prEd255 = tlvRecords->get(EdDSA::TLV_KEY);
11357 if (prEd255.size() == EdDSA::SEED_KEY_LENGTH)
11358 {
11359 signkey = new EdDSA(rng, (unsigned char *) prEd255.data());
11360 if (!signkey->initializationOK)
11361 {
11362 delete signkey;
11363 signkey = NULL;
11364 clearKeys();
11365 return;
11366 }
11367 }
11368 }
11369
11370 if (tlvRecords->find(ECDH::TLV_KEY))
11371 {
11372 string prCu255 = tlvRecords->get(ECDH::TLV_KEY);
11373 if (prCu255.size() == ECDH::PRIVATE_KEY_LENGTH)
11374 {
11375 chatkey = new ECDH((unsigned char *) prCu255.data());
11376 if (!chatkey->initializationOK)
11377 {
11378 delete chatkey;
11379 chatkey = NULL;
11380 clearKeys();
11381 return;
11382 }
11383 }
11384 }
11385 delete tlvRecords;
11386 }
11387 else
11388 {
11389 LOG_warn << "Failed to decrypt keyring while initialization";
11390 }
11391 }
11392
11393 string puEd255 = (u->isattrvalid(ATTR_ED25519_PUBK)) ? *u->getattr(ATTR_ED25519_PUBK) : "";
11394 string puCu255 = (u->isattrvalid(ATTR_CU25519_PUBK)) ? *u->getattr(ATTR_CU25519_PUBK) : "";
11395 string sigCu255 = (u->isattrvalid(ATTR_SIG_CU255_PUBK)) ? *u->getattr(ATTR_SIG_CU255_PUBK) : "";
11396 string sigPubk = (u->isattrvalid(ATTR_SIG_RSA_PUBK)) ? *u->getattr(ATTR_SIG_RSA_PUBK) : "";
11397
11398 if (chatkey && signkey) // THERE ARE KEYS
11399 {
11400 // Check Ed25519 public key against derived version
11401 if ((puEd255.size() != EdDSA::PUBLIC_KEY_LENGTH) || memcmp(puEd255.data(), signkey->pubKey, EdDSA::PUBLIC_KEY_LENGTH))
11402 {
11403 LOG_warn << "Public key for Ed25519 mismatch.";
11404
11405 sendevent(99417, "Ed25519 public key mismatch", 0);
11406
11407 clearKeys();
11408 resetKeyring();
11409 return;
11410 }
11411
11412 // Check Cu25519 public key against derive version
11413 if ((puCu255.size() != ECDH::PUBLIC_KEY_LENGTH) || memcmp(puCu255.data(), chatkey->pubKey, ECDH::PUBLIC_KEY_LENGTH))
11414 {
11415 LOG_warn << "Public key for Cu25519 mismatch.";
11416
11417 sendevent(99412, "Cu25519 public key mismatch", 0);
11418
11419 clearKeys();
11420 resetKeyring();
11421 return;
11422 }
11423
11424 // Verify signatures for Cu25519
11425 if (!sigCu255.size() ||
11426 !EdDSA::verifyKey((unsigned char*) puCu255.data(),
11427 puCu255.size(),
11428 &sigCu255,
11429 (unsigned char*) puEd255.data()))
11430 {
11431 LOG_warn << "Signature of public key for Cu25519 not found or mismatch";
11432
11433 sendevent(99413, "Signature of Cu25519 public key mismatch", 0);
11434
11435 clearKeys();
11436 resetKeyring();
11437 return;
11438 }
11439
11440 // Verify signature for RSA public key
11441 string sigPubk = (u->isattrvalid(ATTR_SIG_RSA_PUBK)) ? *u->getattr(ATTR_SIG_RSA_PUBK) : "";
11442 string pubkstr;
11443 if (pubk.isvalid())
11444 {
11445 pubk.serializekeyforjs(pubkstr);
11446 }
11447 if (!pubkstr.size() || !sigPubk.size())
11448 {
11449 if (!pubkstr.size())
11450 {
11451 LOG_warn << "Error serializing RSA public key";
11452 sendevent(99421, "Error serializing RSA public key", 0);
11453 }
11454 if (!sigPubk.size())
11455 {
11456 LOG_warn << "Signature of public key for RSA not found";
11457 sendevent(99422, "Signature of public key for RSA not found", 0);
11458 }
11459
11460 clearKeys();
11461 resetKeyring();
11462 return;
11463 }
11464 if (!EdDSA::verifyKey((unsigned char*) pubkstr.data(),
11465 pubkstr.size(),
11466 &sigPubk,
11467 (unsigned char*) puEd255.data()))
11468 {
11469 LOG_warn << "Verification of signature of public key for RSA failed";
11470
11471 sendevent(99414, "Verification of signature of public key for RSA failed", 0);
11472
11473 clearKeys();
11474 resetKeyring();
11475 return;
11476 }
11477
11478 // if we reached this point, everything is OK
11479 LOG_info << "Keypairs and signatures loaded successfully";
11480 fetchingkeys = false;
11481 return;
11482 }
11483 else if (!signkey && !chatkey) // THERE ARE NO KEYS
11484 {
11485 // Check completeness of keypairs
11486 if (!pubk.isvalid() || puEd255.size() || puCu255.size() || sigCu255.size() || sigPubk.size())
11487 {
11488 LOG_warn << "Public keys and/or signatures found without their respective private key.";
11489
11490 sendevent(99415, "Incomplete keypair detected", 0);
11491
11492 clearKeys();
11493 return;
11494 }
11495 else // No keys were set --> generate keypairs and related attributes
11496 {
11497 // generate keypairs
11498 EdDSA *signkey = new EdDSA(rng);
11499 ECDH *chatkey = new ECDH();
11500
11501 if (!chatkey->initializationOK || !signkey->initializationOK)
11502 {
11503 LOG_err << "Initialization of keys Cu25519 and/or Ed25519 failed";
11504 clearKeys();
11505 delete signkey;
11506 delete chatkey;
11507 return;
11508 }
11509
11510 // prepare the TLV for private keys
11511 TLVstore tlvRecords;
11512 tlvRecords.set(EdDSA::TLV_KEY, string((const char*)signkey->keySeed, EdDSA::SEED_KEY_LENGTH));
11513 tlvRecords.set(ECDH::TLV_KEY, string((const char*)chatkey->privKey, ECDH::PRIVATE_KEY_LENGTH));
11514 string *tlvContainer = tlvRecords.tlvRecordsToContainer(rng, &key);
11515
11516 // prepare signatures
11517 string pubkStr;
11518 pubk.serializekeyforjs(pubkStr);
11519 signkey->signKey((unsigned char*)pubkStr.data(), pubkStr.size(), &sigPubk);
11520 signkey->signKey(chatkey->pubKey, ECDH::PUBLIC_KEY_LENGTH, &sigCu255);
11521
11522 // store keys into user attributes (skipping the procresult() <-- reqtag=0)
11523 userattr_map attrs;
11524 string buf;
11525
11526 buf.assign(tlvContainer->data(), tlvContainer->size());
11527 attrs[ATTR_KEYRING] = buf;
11528
11529 buf.assign((const char *) signkey->pubKey, EdDSA::PUBLIC_KEY_LENGTH);
11530 attrs[ATTR_ED25519_PUBK] = buf;
11531
11532 buf.assign((const char *) chatkey->pubKey, ECDH::PUBLIC_KEY_LENGTH);
11533 attrs[ATTR_CU25519_PUBK] = buf;
11534
11535 buf.assign(sigPubk.data(), sigPubk.size());
11536 attrs[ATTR_SIG_RSA_PUBK] = buf;
11537
11538 buf.assign(sigCu255.data(), sigCu255.size());
11539 attrs[ATTR_SIG_CU255_PUBK] = buf;
11540
11541 putua(&attrs, 0);
11542
11543 delete tlvContainer;
11544 delete chatkey;
11545 delete signkey; // MegaClient::signkey & chatkey are created on putua::procresult()
11546
11547 LOG_info << "Creating new keypairs and signatures";
11548 fetchingkeys = false;
11549 return;
11550 }
11551 }
11552 else // there is chatkey but no signing key, or viceversa
11553 {
11554 LOG_warn << "Keyring exists, but it's incomplete.";
11555
11556 if (!chatkey)
11557 {
11558 sendevent(99416, "Incomplete keyring detected: private key for Cu25519 not found.", 0);
11559 }
11560 else // !signkey
11561 {
11562 sendevent(99423, "Incomplete keyring detected: private key for Ed25519 not found.", 0);
11563 }
11564
11565 resetKeyring();
11566 clearKeys();
11567 return;
11568 }
11569 }
11570
loadAuthrings()11571 void MegaClient::loadAuthrings()
11572 {
11573 mFetchingAuthrings = true;
11574
11575 std::set<attr_t> attrs { ATTR_AUTHRING, ATTR_AUTHCU255, ATTR_AUTHRSA };
11576 for (auto at : attrs)
11577 {
11578 User *ownUser = finduser(me);
11579 const string *av = ownUser->getattr(at);
11580 if (av)
11581 {
11582 if (ownUser->isattrvalid(at))
11583 {
11584 std::unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(av, &key));
11585 if (tlvRecords)
11586 {
11587 mAuthRings.emplace(at, AuthRing(at, *tlvRecords));
11588 LOG_info << "Authring succesfully loaded from cache: " << User::attr2string(at);
11589 }
11590 else
11591 {
11592 LOG_err << "Failed to decrypt " << User::attr2string(at) << " from cached attribute";
11593 }
11594
11595 continue;
11596 }
11597 else
11598 {
11599 LOG_warn << User::attr2string(at) << " found in cache, but out of date. Fetching...";
11600 }
11601 }
11602 else
11603 {
11604 LOG_warn << User::attr2string(at) << " not found in cache. Fetching...";
11605 }
11606
11607 getua(ownUser, at, 0);
11608 }
11609
11610 // if all authrings were loaded from cache...
11611 if (mAuthRings.size() == attrs.size())
11612 {
11613 mFetchingAuthrings = false;
11614 fetchContactsKeys();
11615 }
11616 }
11617
fetchContactsKeys()11618 void MegaClient::fetchContactsKeys()
11619 {
11620 assert(mAuthRings.size() == 3);
11621 mAuthRingsTemp = mAuthRings;
11622
11623 for (auto &it : users)
11624 {
11625 User *user = &it.second;
11626 if (user->userhandle != me)
11627 {
11628 fetchContactKeys(user);
11629 }
11630 }
11631 }
11632
fetchContactKeys(User * user)11633 void MegaClient::fetchContactKeys(User *user)
11634 {
11635 getua(user, ATTR_ED25519_PUBK, 0);
11636 getua(user, ATTR_CU25519_PUBK, 0);
11637
11638 int creqtag = reqtag;
11639 reqtag = 0;
11640 getpubkey(user->uid.c_str());
11641 reqtag = creqtag;
11642 }
11643
trackKey(attr_t keyType,handle uh,const std::string & pubKey)11644 error MegaClient::trackKey(attr_t keyType, handle uh, const std::string &pubKey)
11645 {
11646 User *user = finduser(uh);
11647 if (!user)
11648 {
11649 LOG_err << "Attempt to track a key for an unknown user " << Base64Str<MegaClient::USERHANDLE>(uh) << ": " << User::attr2string(keyType);
11650 assert(false);
11651 return API_EARGS;
11652 }
11653 const char *uid = user->uid.c_str();
11654 attr_t authringType = AuthRing::keyTypeToAuthringType(keyType);
11655 if (authringType == ATTR_UNKNOWN)
11656 {
11657 LOG_err << "Attempt to track an unknown type of key for user " << uid << ": " << User::attr2string(keyType);
11658 assert(false);
11659 return API_EARGS;
11660 }
11661
11662 // If checking authrings for all contacts (new session), accumulate updates for all contacts first
11663 // in temporal authrings to put them all at once. Otherwise, update authring immediately
11664 AuthRing *authring = nullptr;
11665 unique_ptr<AuthRing> aux;
11666 auto it = mAuthRingsTemp.find(authringType);
11667 bool temporalAuthring = it != mAuthRingsTemp.end();
11668 if (temporalAuthring)
11669 {
11670 authring = &it->second; // modify the temporal authring directly
11671 }
11672 else
11673 {
11674 it = mAuthRings.find(authringType);
11675 if (it == mAuthRings.end())
11676 {
11677 LOG_warn << "Failed to track public key in " << User::attr2string(authringType) << " for user " << uid << ": authring not available";
11678 assert(false);
11679 return API_ETEMPUNAVAIL;
11680 }
11681 aux = make_unique<AuthRing>(it->second); // make a copy, once saved in API, it is updated
11682 authring = aux.get();
11683 }
11684
11685 // compute key's fingerprint
11686 string keyFingerprint = AuthRing::fingerprint(pubKey);
11687 bool fingerprintMatch = false;
11688
11689 // check if user's key is already being tracked in the authring
11690 bool keyTracked = authring->isTracked(uh);
11691 if (keyTracked)
11692 {
11693 fingerprintMatch = (keyFingerprint == authring->getFingerprint(uh));
11694 if (!fingerprintMatch)
11695 {
11696 if (!authring->isSignedKey())
11697 {
11698 LOG_err << "Failed to track public key in " << User::attr2string(authringType) << " for user " << uid << ": fingerprint mismatch";
11699
11700 app->key_modified(uh, keyType);
11701 sendevent(99451, "Key modification detected");
11702
11703 return API_EKEY;
11704 }
11705 //else --> verify signature, despite fingerprint does not match (it will be checked again later)
11706 }
11707 else
11708 {
11709 LOG_debug << "Authentication of public key in " << User::attr2string(authringType) << " for user " << uid << " was successful. Auth method: " << AuthRing::authMethodToStr(authring->getAuthMethod(uh));
11710 }
11711 }
11712
11713 if (authring->isSignedKey())
11714 {
11715 if (authring->getAuthMethod(uh) != AUTH_METHOD_SIGNATURE || !fingerprintMatch)
11716 {
11717 // load public signing key and key signature
11718 getua(user, ATTR_ED25519_PUBK, 0);
11719
11720 attr_t attrType = AuthRing::authringTypeToSignatureType(authringType);
11721 getua(user, attrType, 0); // in getua_result(), we check signature actually matches
11722 }
11723 }
11724 else if (!keyTracked)
11725 {
11726 LOG_debug << "Adding public key to " << User::attr2string(authringType) << " as seen for user " << uid;
11727
11728 // tracking has changed --> persist authring
11729 authring->add(uh, keyFingerprint, AUTH_METHOD_SEEN);
11730
11731 // if checking authrings for all contacts, accumulate updates for all contacts first
11732 bool finished = true;
11733 if (temporalAuthring)
11734 {
11735 for (auto &it : users)
11736 {
11737 User *user = &it.second;
11738 if (user->userhandle != me && !authring->isTracked(user->userhandle))
11739 {
11740 // if only a current user is not tracked yet, update temporal authring
11741 finished = false;
11742 break;
11743 }
11744 }
11745 }
11746 if (finished)
11747 {
11748 std::unique_ptr<string> newAuthring(authring->serialize(rng, key));
11749 putua(authringType, reinterpret_cast<const byte *>(newAuthring->data()), static_cast<unsigned>(newAuthring->size()), 0);
11750 mAuthRingsTemp.erase(authringType); // if(temporalAuthring) --> do nothing
11751 }
11752 }
11753
11754 return API_OK;
11755 }
11756
trackSignature(attr_t signatureType,handle uh,const std::string & signature)11757 error MegaClient::trackSignature(attr_t signatureType, handle uh, const std::string &signature)
11758 {
11759 User *user = finduser(uh);
11760 if (!user)
11761 {
11762 LOG_err << "Attempt to track a key for an unknown user " << Base64Str<MegaClient::USERHANDLE>(uh) << ": " << User::attr2string(signatureType);
11763 assert(false);
11764 return API_EARGS;
11765 }
11766 const char *uid = user->uid.c_str();
11767 attr_t authringType = AuthRing::signatureTypeToAuthringType(signatureType);
11768 if (authringType == ATTR_UNKNOWN)
11769 {
11770 LOG_err << "Attempt to track an unknown type of signature for user " << uid << ": " << User::attr2string(signatureType);
11771 assert(false);
11772 return API_EARGS;
11773 }
11774
11775 // If checking authrings for all contacts (new session), accumulate updates for all contacts first
11776 // in temporal authrings to put them all at once. Otherwise, send the update immediately
11777 AuthRing *authring = nullptr;
11778 unique_ptr<AuthRing> aux;
11779 auto it = mAuthRingsTemp.find(authringType);
11780 bool temporalAuthring = it != mAuthRingsTemp.end();
11781 if (temporalAuthring)
11782 {
11783 authring = &it->second; // modify the temporal authring directly
11784 }
11785 else
11786 {
11787 it = mAuthRings.find(authringType);
11788 if (it == mAuthRings.end())
11789 {
11790 LOG_warn << "Failed to track signature of public key in " << User::attr2string(authringType) << " for user " << uid << ": authring not available";
11791 assert(false);
11792 return API_ETEMPUNAVAIL;
11793 }
11794 aux = make_unique<AuthRing>(it->second); // make a copy, once saved in API, it is updated
11795 authring = aux.get();
11796 }
11797
11798 const string *pubKey;
11799 string pubKeyBuf; // for RSA, need to serialize the key
11800 if (signatureType == ATTR_SIG_CU255_PUBK)
11801 {
11802 // retrieve public key whose signature wants to be verified, from cache
11803 if (!user || !user->isattrvalid(ATTR_CU25519_PUBK))
11804 {
11805 LOG_warn << "Failed to verify signature " << User::attr2string(signatureType) << " for user " << uid << ": CU25519 public key is not available";
11806 assert(false);
11807 return API_EINTERNAL;
11808 }
11809 pubKey = user->getattr(ATTR_CU25519_PUBK);
11810 }
11811 else if (signatureType == ATTR_SIG_RSA_PUBK)
11812 {
11813 if (!user->pubk.isvalid())
11814 {
11815 LOG_warn << "Failed to verify signature " << User::attr2string(signatureType) << " for user " << uid << ": RSA public key is not available";
11816 assert(false);
11817 return API_EINTERNAL;
11818 }
11819 user->pubk.serializekeyforjs(pubKeyBuf);
11820 pubKey = &pubKeyBuf;
11821 }
11822 else
11823 {
11824 LOG_err << "Attempt to track an unknown type of signature: " << User::attr2string(signatureType);
11825 assert(false);
11826 return API_EINTERNAL;
11827 }
11828
11829 // retrieve signing key from cache
11830 if (!user->isattrvalid(ATTR_ED25519_PUBK))
11831 {
11832 LOG_warn << "Failed to verify signature " << User::attr2string(signatureType) << " for user " << uid << ": signing public key is not available";
11833 assert(false);
11834 return API_ETEMPUNAVAIL;
11835 }
11836 const string *signingPubKey = user->getattr(ATTR_ED25519_PUBK);
11837
11838 // compute key's fingerprint
11839 string keyFingerprint = AuthRing::fingerprint(*pubKey);
11840 bool fingerprintMatch = false;
11841 bool keyTracked = authring->isTracked(uh);
11842
11843 // check signature for the public key
11844 bool signatureVerified = EdDSA::verifyKey((unsigned char*) pubKey->data(), pubKey->size(), (string*)&signature, (unsigned char*) signingPubKey->data());
11845 if (signatureVerified)
11846 {
11847 LOG_debug << "Signature " << User::attr2string(signatureType) << " succesfully verified for user " << user->uid;
11848
11849 // check if user's key is already being tracked in the authring
11850 if (keyTracked)
11851 {
11852 fingerprintMatch = (keyFingerprint == authring->getFingerprint(uh));
11853 if (!fingerprintMatch)
11854 {
11855 LOG_err << "Failed to track signature of public key in " << User::attr2string(authringType) << " for user " << uid << ": fingerprint mismatch";
11856
11857 if (authring->isSignedKey()) // for unsigned keys, already notified in trackKey()
11858 {
11859 app->key_modified(uh, signatureType == ATTR_SIG_CU255_PUBK ? ATTR_CU25519_PUBK : ATTR_UNKNOWN);
11860 sendevent(99451, "Key modification detected");
11861 }
11862
11863 return API_EKEY;
11864 }
11865 else
11866 {
11867 assert(authring->getAuthMethod(uh) != AUTH_METHOD_SIGNATURE);
11868 LOG_warn << "Updating authentication method for user " << uid << " to signature verified, currently authenticated as seen";
11869
11870 authring->update(uh, AUTH_METHOD_SIGNATURE);
11871 }
11872 }
11873 else
11874 {
11875 LOG_debug << "Adding public key to " << User::attr2string(authringType) << " as signature verified for user " << uid;
11876
11877 authring->add(uh, keyFingerprint, AUTH_METHOD_SIGNATURE);
11878 }
11879
11880 // if checking authrings for all contacts, accumulate updates for all contacts first
11881 bool finished = true;
11882 if (temporalAuthring)
11883 {
11884 for (auto &it : users)
11885 {
11886 User *user = &it.second;
11887 if (user->userhandle != me && !authring->isTracked(user->userhandle))
11888 {
11889 // if only a current user is not tracked yet, update temporal authring
11890 finished = false;
11891 break;
11892 }
11893 }
11894 }
11895 if (finished)
11896 {
11897 std::unique_ptr<string> newAuthring(authring->serialize(rng, key));
11898 putua(authringType, reinterpret_cast<const byte *>(newAuthring->data()), static_cast<unsigned>(newAuthring->size()), 0);
11899 mAuthRingsTemp.erase(authringType);
11900 }
11901 }
11902 else
11903 {
11904 LOG_err << "Failed to verify signature of public key in " << User::attr2string(authringType) << " for user " << uid << ": signature mismatch";
11905
11906 app->key_modified(uh, signatureType);
11907 sendevent(99452, "Signature mismatch for public key");
11908
11909 return API_EKEY;
11910 }
11911
11912 return API_OK;
11913 }
11914
verifyCredentials(handle uh)11915 error MegaClient::verifyCredentials(handle uh)
11916 {
11917 Base64Str<MegaClient::USERHANDLE> uid(uh);
11918 auto it = mAuthRings.find(ATTR_AUTHRING);
11919 if (it == mAuthRings.end())
11920 {
11921 LOG_warn << "Failed to track public key for user " << uid << ": authring not available";
11922 assert(false);
11923 return API_ETEMPUNAVAIL;
11924 }
11925
11926 AuthRing authring = it->second; // copy, do not modify yet the cached authring
11927 AuthMethod authMethod = authring.getAuthMethod(uh);
11928 switch (authMethod)
11929 {
11930 case AUTH_METHOD_SEEN:
11931 LOG_debug << "Updating authentication method of Ed25519 public key for user " << uid << " from seen to signature verified";
11932 authring.update(uh, AUTH_METHOD_FINGERPRINT);
11933 break;
11934
11935 case AUTH_METHOD_FINGERPRINT:
11936 LOG_err << "Failed to verify credentials for user " << uid << ": already verified";
11937 return API_EEXIST;
11938
11939 case AUTH_METHOD_SIGNATURE:
11940 LOG_err << "Failed to verify credentials for user " << uid << ": invalid authentication method";
11941 return API_EINTERNAL;
11942
11943 case AUTH_METHOD_UNKNOWN:
11944 {
11945 User *user = finduser(uh);
11946 const string *pubKey = user ? user->getattr(ATTR_ED25519_PUBK) : nullptr;
11947 if (pubKey)
11948 {
11949 string keyFingerprint = AuthRing::fingerprint(*pubKey);
11950 LOG_warn << "Adding authentication method of Ed25519 public key for user " << uid << ": key is not tracked yet";
11951 authring.add(uh, keyFingerprint, AUTH_METHOD_FINGERPRINT);
11952 }
11953 else
11954 {
11955 LOG_err << "Failed to verify credentials for user " << uid << ": key not tracked and not available";
11956 return API_ETEMPUNAVAIL;
11957 }
11958 break;
11959 }
11960 }
11961
11962 std::unique_ptr<string> newAuthring(authring.serialize(rng, key));
11963 putua(ATTR_AUTHRING, reinterpret_cast<const byte *>(newAuthring->data()), static_cast<unsigned>(newAuthring->size()));
11964
11965 return API_OK;
11966 }
11967
resetCredentials(handle uh)11968 error MegaClient::resetCredentials(handle uh)
11969 {
11970 Base64Str<MegaClient::USERHANDLE> uid(uh);
11971 if (mAuthRings.size() != 3)
11972 {
11973 LOG_warn << "Failed to reset credentials for user " << uid << ": authring/s not available";
11974 // TODO: after testing, if not hit, remove assertion below
11975 assert(false);
11976 return API_ETEMPUNAVAIL;
11977 }
11978
11979 // store all required changes into user attributes
11980 userattr_map attrs;
11981 for (auto &it : mAuthRings)
11982 {
11983 AuthRing authring = it.second; // copy, do not update cached authring yet
11984 if (authring.remove(uh))
11985 {
11986 attrs[it.first] = *authring.serialize(rng, key);
11987 }
11988 }
11989
11990 if (attrs.size())
11991 {
11992 LOG_debug << "Removing credentials for user " << uid << "...";
11993 putua(&attrs);
11994 }
11995 else
11996 {
11997 LOG_warn << "Failed to reset credentials for user " << uid << ": keys not tracked yet";
11998 return API_ENOENT;
11999 }
12000
12001 return API_OK;
12002 }
12003
areCredentialsVerified(handle uh)12004 bool MegaClient::areCredentialsVerified(handle uh)
12005 {
12006 AuthRingsMap::const_iterator it = mAuthRings.find(ATTR_AUTHRING);
12007 if (it != mAuthRings.end())
12008 {
12009 return it->second.areCredentialsVerified(uh);
12010 }
12011 return false;
12012 }
12013
purgenodesusersabortsc(bool keepOwnUser)12014 void MegaClient::purgenodesusersabortsc(bool keepOwnUser)
12015 {
12016 app->clearing();
12017
12018 while (!hdrns.empty())
12019 {
12020 delete hdrns.begin()->second;
12021 }
12022
12023 #ifdef ENABLE_SYNC
12024 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); )
12025 {
12026 (*it)->changestate(SYNC_CANCELED);
12027 delete *(it++);
12028 }
12029
12030 syncs.clear();
12031 #endif
12032
12033 mOptimizePurgeNodes = true;
12034 mFingerprints.clear();
12035 mNodeCounters.clear();
12036 for (node_map::iterator it = nodes.begin(); it != nodes.end(); it++)
12037 {
12038 delete it->second;
12039 }
12040 nodes.clear();
12041 mOptimizePurgeNodes = false;
12042
12043 #ifdef ENABLE_SYNC
12044 todebris.clear();
12045 tounlink.clear();
12046 mFingerprints.clear();
12047 #endif
12048
12049 for (fafc_map::iterator cit = fafcs.begin(); cit != fafcs.end(); cit++)
12050 {
12051 for (int i = 2; i--; )
12052 {
12053 for (faf_map::iterator it = cit->second->fafs[i].begin(); it != cit->second->fafs[i].end(); it++)
12054 {
12055 delete it->second;
12056 }
12057
12058 cit->second->fafs[i].clear();
12059 }
12060 }
12061
12062 for (newshare_list::iterator it = newshares.begin(); it != newshares.end(); it++)
12063 {
12064 delete *it;
12065 }
12066
12067 newshares.clear();
12068 nodenotify.clear();
12069 usernotify.clear();
12070 pcrnotify.clear();
12071 useralerts.clear();
12072
12073 #ifdef ENABLE_CHAT
12074 for (textchat_map::iterator it = chats.begin(); it != chats.end();)
12075 {
12076 delete it->second;
12077 chats.erase(it++);
12078 }
12079 chatnotify.clear();
12080 #endif
12081
12082 for (user_map::iterator it = users.begin(); it != users.end(); )
12083 {
12084 User *u = &(it->second);
12085 if ((!keepOwnUser || u->userhandle != me) || u->userhandle == UNDEF)
12086 {
12087 umindex.erase(u->email);
12088 uhindex.erase(u->userhandle);
12089 users.erase(it++);
12090 }
12091 else
12092 {
12093 // if there are changes to notify, restore the notification in the queue
12094 if (u->notified)
12095 {
12096 usernotify.push_back(u);
12097 }
12098
12099 u->dbid = 0;
12100 it++;
12101 }
12102 }
12103 assert(users.size() <= 1 && uhindex.size() <= 1 && umindex.size() <= 1);
12104
12105 for (handlepcr_map::iterator it = pcrindex.begin(); it != pcrindex.end(); it++)
12106 {
12107 delete it->second;
12108 }
12109
12110 pcrindex.clear();
12111
12112 scsn.clear();
12113
12114 if (pendingsc)
12115 {
12116 app->request_response_progress(-1, -1);
12117 pendingsc->disconnect();
12118 }
12119
12120 if (pendingscUserAlerts)
12121 {
12122 pendingscUserAlerts->disconnect();
12123 }
12124
12125 init();
12126 }
12127
12128 // request direct read by node pointer
pread(Node * n,m_off_t count,m_off_t offset,void * appdata)12129 void MegaClient::pread(Node* n, m_off_t count, m_off_t offset, void* appdata)
12130 {
12131 queueread(n->nodehandle, true, n->nodecipher(), MemAccess::get<int64_t>((const char*)n->nodekey().data() + SymmCipher::KEYLENGTH), count, offset, appdata);
12132 }
12133
12134 // request direct read by exported handle / key
pread(handle ph,SymmCipher * key,int64_t ctriv,m_off_t count,m_off_t offset,void * appdata,bool isforeign,const char * privauth,const char * pubauth,const char * cauth)12135 void MegaClient::pread(handle ph, SymmCipher* key, int64_t ctriv, m_off_t count, m_off_t offset, void* appdata, bool isforeign, const char *privauth, const char *pubauth, const char *cauth)
12136 {
12137 queueread(ph, isforeign, key, ctriv, count, offset, appdata, privauth, pubauth, cauth);
12138 }
12139
12140 // since only the first six bytes of a handle are in use, we use the seventh to encode its type
encodehandletype(handle * hp,bool p)12141 void MegaClient::encodehandletype(handle* hp, bool p)
12142 {
12143 if (p)
12144 {
12145 ((char*)hp)[NODEHANDLE] = 1;
12146 }
12147 }
12148
isprivatehandle(handle * hp)12149 bool MegaClient::isprivatehandle(handle* hp)
12150 {
12151 return ((char*)hp)[NODEHANDLE] != 0;
12152 }
12153
queueread(handle h,bool p,SymmCipher * key,int64_t ctriv,m_off_t offset,m_off_t count,void * appdata,const char * privauth,const char * pubauth,const char * cauth)12154 void MegaClient::queueread(handle h, bool p, SymmCipher* key, int64_t ctriv, m_off_t offset, m_off_t count, void* appdata, const char* privauth, const char *pubauth, const char *cauth)
12155 {
12156 handledrn_map::iterator it;
12157
12158 encodehandletype(&h, p);
12159
12160 it = hdrns.find(h);
12161
12162 if (it == hdrns.end())
12163 {
12164 // this handle is not being accessed yet: insert
12165 it = hdrns.insert(hdrns.end(), pair<handle, DirectReadNode*>(h, new DirectReadNode(this, h, p, key, ctriv, privauth, pubauth, cauth)));
12166 it->second->hdrn_it = it;
12167 it->second->enqueue(offset, count, reqtag, appdata);
12168
12169 if (overquotauntil && overquotauntil > Waiter::ds)
12170 {
12171 dstime timeleft = dstime(overquotauntil - Waiter::ds);
12172 app->pread_failure(API_EOVERQUOTA, 0, appdata, timeleft);
12173 it->second->schedule(timeleft);
12174 }
12175 else
12176 {
12177 it->second->dispatch();
12178 }
12179 }
12180 else
12181 {
12182 it->second->enqueue(offset, count, reqtag, appdata);
12183 if (overquotauntil && overquotauntil > Waiter::ds)
12184 {
12185 dstime timeleft = dstime(overquotauntil - Waiter::ds);
12186 app->pread_failure(API_EOVERQUOTA, 0, appdata, timeleft);
12187 it->second->schedule(timeleft);
12188 }
12189 }
12190 }
12191
12192 // cancel direct read by node pointer / count / count
preadabort(Node * n,m_off_t offset,m_off_t count)12193 void MegaClient::preadabort(Node* n, m_off_t offset, m_off_t count)
12194 {
12195 abortreads(n->nodehandle, true, offset, count);
12196 }
12197
12198 // cancel direct read by exported handle / offset / count
preadabort(handle ph,m_off_t offset,m_off_t count)12199 void MegaClient::preadabort(handle ph, m_off_t offset, m_off_t count)
12200 {
12201 abortreads(ph, false, offset, count);
12202 }
12203
abortreads(handle h,bool p,m_off_t offset,m_off_t count)12204 void MegaClient::abortreads(handle h, bool p, m_off_t offset, m_off_t count)
12205 {
12206 handledrn_map::iterator it;
12207 DirectReadNode* drn;
12208
12209 encodehandletype(&h, p);
12210
12211 if ((it = hdrns.find(h)) != hdrns.end())
12212 {
12213 drn = it->second;
12214
12215 for (dr_list::iterator it = drn->reads.begin(); it != drn->reads.end(); )
12216 {
12217 if ((offset < 0 || offset == (*it)->offset) && (count < 0 || count == (*it)->count))
12218 {
12219 app->pread_failure(API_EINCOMPLETE, (*it)->drn->retries, (*it)->appdata, 0);
12220
12221 delete *(it++);
12222 }
12223 else it++;
12224 }
12225 }
12226 }
12227
12228 // execute pending directreads
execdirectreads()12229 bool MegaClient::execdirectreads()
12230 {
12231 CodeCounter::ScopeTimer ccst(performanceStats.execdirectreads);
12232
12233 bool r = false;
12234 DirectReadSlot* drs;
12235
12236 if (drq.size() < MAXDRSLOTS)
12237 {
12238 // fill slots
12239 for (dr_list::iterator it = drq.begin(); it != drq.end(); it++)
12240 {
12241 if (!(*it)->drs)
12242 {
12243 drs = new DirectReadSlot(*it);
12244 (*it)->drs = drs;
12245 r = true;
12246
12247 if (drq.size() >= MAXDRSLOTS) break;
12248 }
12249 }
12250 }
12251
12252 // perform slot I/O
12253 for (drs_list::iterator it = drss.begin(); it != drss.end(); )
12254 {
12255 if ((*(it++))->doio())
12256 {
12257 r = true;
12258 break;
12259 }
12260 }
12261
12262 while (!dsdrns.empty() && dsdrns.begin()->first <= Waiter::ds)
12263 {
12264 if (dsdrns.begin()->second->reads.size() && (dsdrns.begin()->second->tempurls.size() || dsdrns.begin()->second->pendingcmd))
12265 {
12266 LOG_warn << "DirectRead scheduled retry";
12267 dsdrns.begin()->second->retry(API_EAGAIN);
12268 }
12269 else
12270 {
12271 LOG_debug << "Dispatching scheduled streaming";
12272 dsdrns.begin()->second->dispatch();
12273 }
12274 }
12275
12276 return r;
12277 }
12278
12279 // recreate filenames of active PUT transfers
updateputs()12280 void MegaClient::updateputs()
12281 {
12282 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); it++)
12283 {
12284 if ((*it)->transfer->type == PUT && (*it)->transfer->files.size())
12285 {
12286 (*it)->transfer->files.front()->prepare();
12287 }
12288 }
12289 }
12290
isnodesyncable(Node * remotenode,bool * isinshare)12291 error MegaClient::isnodesyncable(Node *remotenode, bool *isinshare)
12292 {
12293 #ifdef ENABLE_SYNC
12294 // cannot sync files, rubbish bins or inboxes
12295 if (remotenode->type != FOLDERNODE && remotenode->type != ROOTNODE)
12296 {
12297 return API_EACCESS;
12298 }
12299
12300 Node* n;
12301 bool inshare;
12302
12303 // any active syncs below?
12304 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
12305 {
12306 if ((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN)
12307 {
12308 n = (*it)->localroot->node;
12309
12310 do {
12311 if (n == remotenode)
12312 {
12313 return API_EEXIST;
12314 }
12315 } while ((n = n->parent));
12316 }
12317 }
12318
12319 // any active syncs above?
12320 n = remotenode;
12321 inshare = false;
12322
12323 do {
12324 for (sync_list::iterator it = syncs.begin(); it != syncs.end(); it++)
12325 {
12326 if (((*it)->state == SYNC_ACTIVE || (*it)->state == SYNC_INITIALSCAN)
12327 && n == (*it)->localroot->node)
12328 {
12329 return API_EEXIST;
12330 }
12331 }
12332
12333 if (n->inshare && !inshare)
12334 {
12335 // we need FULL access to sync
12336 // FIXME: allow downsyncing from RDONLY and limited syncing to RDWR shares
12337 if (n->inshare->access != FULL) return API_EACCESS;
12338
12339 inshare = true;
12340 }
12341 } while ((n = n->parent));
12342
12343 if (inshare)
12344 {
12345 // this sync is located in an inbound share - make sure that there
12346 // are no access restrictions in place anywhere in the sync's tree
12347 for (user_map::iterator uit = users.begin(); uit != users.end(); uit++)
12348 {
12349 User* u = &uit->second;
12350
12351 if (u->sharing.size())
12352 {
12353 for (handle_set::iterator sit = u->sharing.begin(); sit != u->sharing.end(); sit++)
12354 {
12355 if ((n = nodebyhandle(*sit)) && n->inshare && n->inshare->access != FULL)
12356 {
12357 do {
12358 if (n == remotenode)
12359 {
12360 return API_EACCESS;
12361 }
12362 } while ((n = n->parent));
12363 }
12364 }
12365 }
12366 }
12367 }
12368
12369 if (isinshare)
12370 {
12371 *isinshare = inshare;
12372 }
12373 return API_OK;
12374 #else
12375 return API_EINCOMPLETE;
12376 #endif
12377 }
12378
addtimer(TimerWithBackoff * twb)12379 error MegaClient::addtimer(TimerWithBackoff *twb)
12380 {
12381 bttimers.push_back(twb);
12382 return API_OK;
12383 }
12384
12385 // check sync path, add sync if folder
12386 // disallow nested syncs (there is only one LocalNode pointer per node)
12387 // (FIXME: perform the same check for local paths!)
addsync(SyncConfig syncConfig,const char * debris,string * localdebris,int tag,void * appData)12388 error MegaClient::addsync(SyncConfig syncConfig, const char* debris, string* localdebris, int tag, void *appData)
12389 {
12390 #ifdef ENABLE_SYNC
12391 Node* remotenode = nodebyhandle(syncConfig.getRemoteNode());
12392 bool inshare = false;
12393 error e = isnodesyncable(remotenode, &inshare);
12394 if (e)
12395 {
12396 return e;
12397 }
12398
12399 string localPath = syncConfig.getLocalPath();
12400 auto rootpath = LocalPath::fromPath(localPath, *fsaccess);
12401 rootpath.trimNonDriveTrailingSeparator(*fsaccess);
12402
12403 bool isnetwork = false;
12404 if (!fsaccess->issyncsupported(rootpath, &isnetwork))
12405 {
12406 LOG_warn << "Unsupported filesystem";
12407 return API_EFAILED;
12408 }
12409
12410 auto fa = fsaccess->newfileaccess();
12411 if (fa->fopen(rootpath, true, false, nullptr, true))
12412 {
12413 if (fa->type == FOLDERNODE)
12414 {
12415 LOG_debug << "Adding sync: " << syncConfig.getLocalPath() << " vs " << remotenode->displaypath();
12416
12417 Sync* sync = new Sync(this, std::move(syncConfig), debris, localdebris, remotenode, inshare, tag, appData);
12418 sync->isnetwork = isnetwork;
12419
12420 if (!sync->fsstableids)
12421 {
12422 if (sync->assignfsids())
12423 {
12424 LOG_info << "Successfully assigned fs IDs for filesystem with unstable IDs";
12425 }
12426 else
12427 {
12428 LOG_warn << "Failed to assign some fs IDs for filesystem with unstable IDs";
12429 }
12430 }
12431
12432 if (sync->scan(&rootpath, fa.get()))
12433 {
12434 syncsup = false;
12435 e = API_OK;
12436 sync->initializing = false;
12437 LOG_debug << "Initial scan finished. New / modified files: " << sync->dirnotify->notifyq[DirNotify::DIREVENTS].size();
12438 }
12439 else
12440 {
12441 LOG_err << "Initial scan failed";
12442 sync->changestate(SYNC_FAILED);
12443 delete sync;
12444 e = API_EFAILED;
12445 }
12446
12447 syncactivity = true;
12448 }
12449 else
12450 {
12451 e = API_EACCESS; // cannot sync individual files
12452 }
12453 }
12454 else
12455 {
12456 e = fa->retry ? API_ETEMPUNAVAIL : API_ENOENT;
12457 }
12458
12459 return e;
12460 #else
12461 return API_EINCOMPLETE;
12462 #endif
12463 }
12464
12465 #ifdef ENABLE_SYNC
12466 // syncids are usable to indicate putnodes()-local parent linkage
nextsyncid()12467 handle MegaClient::nextsyncid()
12468 {
12469 byte* ptr = (byte*)&currsyncid;
12470
12471 while (!++*ptr && ptr < (byte*)&currsyncid + NODEHANDLE)
12472 {
12473 ptr++;
12474 }
12475
12476 return currsyncid;
12477 }
12478
12479 // recursively stop all transfers
stopxfers(LocalNode * l,DBTableTransactionCommitter & committer)12480 void MegaClient::stopxfers(LocalNode* l, DBTableTransactionCommitter& committer)
12481 {
12482 if (l->type != FILENODE)
12483 {
12484 for (localnode_map::iterator it = l->children.begin(); it != l->children.end(); it++)
12485 {
12486 stopxfers(it->second, committer);
12487 }
12488 }
12489
12490 stopxfer(l, &committer);
12491 }
12492
12493 // add child to nchildren hash (deterministically prefer newer/larger versions
12494 // of identical names to avoid flapping)
12495 // apply standard unescaping, if necessary (use *strings as ephemeral storage
12496 // space)
addchild(remotenode_map * nchildren,string * name,Node * n,list<string> * strings,FileSystemType fsType) const12497 void MegaClient::addchild(remotenode_map* nchildren, string* name, Node* n, list<string>* strings, FileSystemType fsType) const
12498 {
12499 Node** npp;
12500
12501 if (name->find('%') + 1)
12502 {
12503 string tmplocalname;
12504
12505 // perform one round of unescaping to ensure that the resulting local
12506 // filename matches
12507 fsaccess->path2local(name, &tmplocalname);
12508 fsaccess->local2name(&tmplocalname, fsType);
12509
12510 strings->push_back(tmplocalname);
12511 name = &strings->back();
12512 }
12513
12514 npp = &(*nchildren)[name];
12515
12516 if (!*npp
12517 || n->mtime > (*npp)->mtime
12518 || (n->mtime == (*npp)->mtime && n->size > (*npp)->size)
12519 || (n->mtime == (*npp)->mtime && n->size == (*npp)->size && memcmp(n->crc.data(), (*npp)->crc.data(), sizeof n->crc) > 0))
12520 {
12521 *npp = n;
12522 }
12523 }
12524
12525 // downward sync - recursively scan for tree differences and execute them locally
12526 // this is first called after the local node tree is complete
12527 // actions taken:
12528 // * create missing local folders
12529 // * initiate GET transfers to missing local files (but only if the target
12530 // folder was created successfully)
12531 // * attempt to execute renames, moves and deletions (deletions require the
12532 // rubbish flag to be set)
12533 // returns false if any local fs op failed transiently
syncdown(LocalNode * l,LocalPath & localpath,bool rubbish)12534 bool MegaClient::syncdown(LocalNode* l, LocalPath& localpath, bool rubbish)
12535 {
12536 // only use for LocalNodes with a corresponding and properly linked Node
12537 if (l->type != FOLDERNODE || !l->node || (l->parent && l->node->parent->localnode != l->parent))
12538 {
12539 return true;
12540 }
12541
12542 list<string> strings;
12543 remotenode_map nchildren;
12544 remotenode_map::iterator rit;
12545
12546 bool success = true;
12547
12548 // build array of sync-relevant (in case of clashes, the newest alias wins)
12549 // remote children by name
12550 string localname;
12551
12552 // build child hash - nameclash resolution: use newest/largest version
12553 for (node_list::iterator it = l->node->children.begin(); it != l->node->children.end(); it++)
12554 {
12555 attr_map::iterator ait;
12556
12557 // node must be syncable, alive, decrypted and have its name defined to
12558 // be considered - also, prevent clashes with the local debris folder
12559 if (((*it)->syncdeleted == SYNCDEL_NONE
12560 && !(*it)->attrstring
12561 && (ait = (*it)->attrs.map.find('n')) != (*it)->attrs.map.end()
12562 && ait->second.size())
12563 && (l->parent || l->sync->debris != ait->second))
12564 {
12565 ScopedLengthRestore restoreLen(localpath);
12566 localpath.appendWithSeparator(LocalPath::fromName(ait->second, *fsaccess, l->sync->mFilesystemType), true, fsaccess->localseparator);
12567
12568 if (app->sync_syncable(l->sync, ait->second.c_str(), localpath, *it))
12569 {
12570 addchild(&nchildren, &ait->second, *it, &strings, l->sync->mFilesystemType);
12571 }
12572 else
12573 {
12574 LOG_debug << "Node excluded " << LOG_NODEHANDLE((*it)->nodehandle) << " Name: " << (*it)->displayname();
12575 }
12576 }
12577 else
12578 {
12579 LOG_debug << "Node skipped " << LOG_NODEHANDLE((*it)->nodehandle) << " Name: " << (*it)->displayname();
12580 }
12581 }
12582
12583 // remove remote items that exist locally from hash, recurse into existing folders
12584 for (localnode_map::iterator lit = l->children.begin(); lit != l->children.end(); )
12585 {
12586 LocalNode* ll = lit->second;
12587
12588 rit = nchildren.find(&ll->name);
12589
12590 ScopedLengthRestore restoreLen(localpath);
12591 localpath.appendWithSeparator(ll->localname, true, fsaccess->localseparator);
12592
12593 // do we have a corresponding remote child?
12594 if (rit != nchildren.end())
12595 {
12596 // corresponding remote node exists
12597 // local: folder, remote: file - ignore
12598 // local: file, remote: folder - ignore
12599 // local: folder, remote: folder - recurse
12600 // local: file, remote: file - overwrite if newer
12601 if (ll->type != rit->second->type)
12602 {
12603 // folder/file clash: do nothing (rather than attempting to
12604 // second-guess the user)
12605 LOG_warn << "Type changed: " << ll->name << " LNtype: " << ll->type << " Ntype: " << rit->second->type;
12606 nchildren.erase(rit);
12607 }
12608 else if (ll->type == FILENODE)
12609 {
12610 if (ll->node != rit->second)
12611 {
12612 ll->sync->statecacheadd(ll);
12613 }
12614
12615 ll->setnode(rit->second);
12616
12617 if (*ll == *(FileFingerprint*)rit->second)
12618 {
12619 // both files are identical
12620 nchildren.erase(rit);
12621 }
12622 // file exists on both sides - do not overwrite if local version newer or same
12623 else if (ll->mtime > rit->second->mtime)
12624 {
12625 // local version is newer
12626 LOG_debug << "LocalNode is newer: " << ll->name << " LNmtime: " << ll->mtime << " Nmtime: " << rit->second->mtime;
12627 nchildren.erase(rit);
12628 }
12629 else if (ll->mtime == rit->second->mtime
12630 && (ll->size > rit->second->size
12631 || (ll->size == rit->second->size && memcmp(ll->crc.data(), rit->second->crc.data(), sizeof ll->crc) > 0)))
12632
12633 {
12634 if (ll->size < rit->second->size)
12635 {
12636 LOG_warn << "Syncdown. Same mtime but lower size: " << ll->name
12637 << " mtime: " << ll->mtime << " LNsize: " << ll->size << " Nsize: " << rit->second->size
12638 << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle);
12639 }
12640 else
12641 {
12642 LOG_warn << "Syncdown. Same mtime and size, but bigger CRC: " << ll->name
12643 << " mtime: " << ll->mtime << " size: " << ll->size << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle);
12644 }
12645
12646 nchildren.erase(rit);
12647 }
12648 else
12649 {
12650 // means that the localnode is going to be overwritten
12651 if (rit->second->localnode && rit->second->localnode->transfer)
12652 {
12653 LOG_debug << "Stopping an unneeded upload";
12654 DBTableTransactionCommitter committer(tctable);
12655 stopxfer(rit->second->localnode, &committer); // TODO: can we have one transaction for recursing through syncdown() ?
12656 }
12657
12658 rit->second->localnode = (LocalNode*)~0;
12659 }
12660 }
12661 else
12662 {
12663 if (ll->node != rit->second)
12664 {
12665 ll->setnode(rit->second);
12666 ll->sync->statecacheadd(ll);
12667 }
12668
12669 // recurse into directories of equal name
12670 if (!syncdown(ll, localpath, rubbish) && success)
12671 {
12672 success = false;
12673 }
12674
12675 nchildren.erase(rit);
12676 }
12677
12678 lit++;
12679 }
12680 else if (rubbish && ll->deleted) // no corresponding remote node: delete local item
12681 {
12682 if (ll->type == FILENODE)
12683 {
12684 // only delete the file if it is unchanged
12685 LocalPath tmplocalpath = ll->getLocalPath();
12686
12687 auto fa = fsaccess->newfileaccess(false);
12688 if (fa->fopen(tmplocalpath, true, false))
12689 {
12690 FileFingerprint fp;
12691 fp.genfingerprint(fa.get());
12692
12693 if (!(fp == *(FileFingerprint*)ll))
12694 {
12695 ll->deleted = false;
12696 }
12697 }
12698 }
12699
12700 if (ll->deleted)
12701 {
12702 // attempt deletion and re-queue for retry in case of a transient failure
12703 ll->treestate(TREESTATE_SYNCING);
12704
12705 if (l->sync->movetolocaldebris(localpath) || !fsaccess->transient_error)
12706 {
12707 DBTableTransactionCommitter committer(tctable);
12708 delete lit++->second;
12709 }
12710 else
12711 {
12712 blockedfile = localpath;
12713 LOG_warn << "Transient error deleting " << blockedfile.toPath(*fsaccess);
12714 success = false;
12715 lit++;
12716 }
12717 }
12718 }
12719 else
12720 {
12721 lit++;
12722 }
12723 }
12724
12725 // create/move missing local folders / FolderNodes, initiate downloads of
12726 // missing local files
12727 for (rit = nchildren.begin(); rit != nchildren.end(); rit++)
12728 {
12729
12730 localname = rit->second->attrs.map.find('n')->second;
12731
12732 ScopedLengthRestore restoreLen(localpath);
12733 localpath.appendWithSeparator(LocalPath::fromName(localname, *fsaccess, l->sync->mFilesystemType), true, fsaccess->localseparator);
12734
12735 LOG_debug << "Unsynced remote node in syncdown: " << localpath.toPath(*fsaccess) << " Nsize: " << rit->second->size
12736 << " Nmtime: " << rit->second->mtime << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle);
12737
12738 // does this node already have a corresponding LocalNode under
12739 // a different name or elsewhere in the filesystem?
12740 if (rit->second->localnode && rit->second->localnode != (LocalNode*)~0)
12741 {
12742 LOG_debug << "has a previous localnode: " << rit->second->localnode->name;
12743 if (rit->second->localnode->parent)
12744 {
12745 LOG_debug << "with a previous parent: " << rit->second->localnode->parent->name;
12746
12747 LocalPath curpath = rit->second->localnode->getLocalPath();
12748 rit->second->localnode->treestate(TREESTATE_SYNCING);
12749
12750 LOG_debug << "Renaming/moving from the previous location to the new one";
12751 if (fsaccess->renamelocal(curpath, localpath))
12752 {
12753 app->syncupdate_local_move(rit->second->localnode->sync,
12754 rit->second->localnode, localpath.toPath(*fsaccess).c_str());
12755
12756 // update LocalNode tree to reflect the move/rename
12757 rit->second->localnode->setnameparent(l, &localpath, fsaccess->fsShortname(localpath));
12758
12759 rit->second->localnode->sync->statecacheadd(rit->second->localnode);
12760
12761 // update filenames so that PUT transfers can continue seamlessly
12762 updateputs();
12763 syncactivity = true;
12764
12765 rit->second->localnode->treestate(TREESTATE_SYNCED);
12766 }
12767 else if (success && fsaccess->transient_error)
12768 {
12769 // schedule retry
12770 blockedfile = curpath;
12771 LOG_debug << "Transient error moving localnode " << blockedfile.toPath(*fsaccess);
12772 success = false;
12773 }
12774 }
12775 else
12776 {
12777 LOG_debug << "without a previous parent. Skipping";
12778 }
12779 }
12780 else
12781 {
12782 LOG_debug << "doesn't have a previous localnode";
12783 // missing node is not associated with an existing LocalNode
12784 if (rit->second->type == FILENODE)
12785 {
12786 if (!rit->second->syncget)
12787 {
12788 bool download = true;
12789 auto f = fsaccess->newfileaccess(false);
12790 if (rit->second->localnode != (LocalNode*)~0
12791 && (f->fopen(localpath) || f->type == FOLDERNODE))
12792 {
12793 if (f->mIsSymLink && l->sync->movetolocaldebris(localpath))
12794 {
12795 LOG_debug << "Found a link in localpath " << localpath.toPath(*fsaccess);
12796 }
12797 else
12798 {
12799 LOG_debug << "Skipping download over an unscanned file/folder, or the file/folder is not to be synced (special attributes)";
12800 download = false;
12801 }
12802 }
12803 f.reset();
12804 rit->second->localnode = NULL;
12805
12806 // start fetching this node, unless fetch is already in progress
12807 // FIXME: to cover renames that occur during the
12808 // download, reconstruct localname in complete()
12809 if (download)
12810 {
12811 LOG_debug << "Start fetching file node";
12812 app->syncupdate_get(l->sync, rit->second, localpath.toPath(*fsaccess).c_str());
12813
12814 rit->second->syncget = new SyncFileGet(l->sync, rit->second, localpath);
12815 nextreqtag();
12816 DBTableTransactionCommitter committer(tctable); // TODO: use one committer for all files in the loop, without calling syncdown() recursively
12817 startxfer(GET, rit->second->syncget, committer);
12818 syncactivity = true;
12819 }
12820 }
12821 }
12822 else
12823 {
12824 LOG_debug << "Creating local folder";
12825 auto f = fsaccess->newfileaccess(false);
12826 if (f->fopen(localpath) || f->type == FOLDERNODE)
12827 {
12828 LOG_debug << "Skipping folder creation over an unscanned file/folder, or the file/folder is not to be synced (special attributes)";
12829 }
12830 // create local path, add to LocalNodes and recurse
12831 else if (fsaccess->mkdirlocal(localpath))
12832 {
12833 LocalNode* ll = l->sync->checkpath(l, &localpath, &localname, NULL, true, nullptr);
12834
12835 if (ll && ll != (LocalNode*)~0)
12836 {
12837 LOG_debug << "Local folder created, continuing syncdown";
12838
12839 ll->setnode(rit->second);
12840 ll->sync->statecacheadd(ll);
12841
12842 if (!syncdown(ll, localpath, rubbish) && success)
12843 {
12844 LOG_debug << "Syncdown not finished";
12845 success = false;
12846 }
12847 }
12848 else
12849 {
12850 LOG_debug << "Checkpath() failed " << (ll == NULL);
12851 }
12852 }
12853 else if (success && fsaccess->transient_error)
12854 {
12855 blockedfile = localpath;
12856 LOG_debug << "Transient error creating folder " << blockedfile.toPath(*fsaccess);
12857 success = false;
12858 }
12859 else if (!fsaccess->transient_error)
12860 {
12861 LOG_debug << "Non transient error creating folder";
12862 }
12863 }
12864 }
12865 }
12866
12867 return success;
12868 }
12869
12870 // recursively traverse tree of LocalNodes and match with remote Nodes
12871 // mark nodes to be rubbished in deleted. with their nodehandle
12872 // mark additional nodes to to rubbished (those overwritten) by accumulating
12873 // their nodehandles in rubbish.
12874 // nodes to be added are stored in synccreate. - with nodehandle set to parent
12875 // if attached to an existing node
12876 // l and n are assumed to be folders and existing on both sides or scheduled
12877 // for creation
syncup(LocalNode * l,dstime * nds)12878 bool MegaClient::syncup(LocalNode* l, dstime* nds)
12879 {
12880 bool insync = true;
12881
12882 list<string> strings;
12883 remotenode_map nchildren;
12884 remotenode_map::iterator rit;
12885
12886 // build array of sync-relevant (newest alias wins) remote children by name
12887 attr_map::iterator ait;
12888
12889 if (l->node)
12890 {
12891 // corresponding remote node present: build child hash - nameclash
12892 // resolution: use newest version
12893 for (node_list::iterator it = l->node->children.begin(); it != l->node->children.end(); it++)
12894 {
12895 // node must be alive
12896 if ((*it)->syncdeleted == SYNCDEL_NONE)
12897 {
12898 // check if there is a crypto key missing...
12899 if ((*it)->attrstring)
12900 {
12901 if (!l->reported)
12902 {
12903 char* buf = new char[(*it)->nodekey().size() * 4 / 3 + 4];
12904 Base64::btoa((byte *)(*it)->nodekey().data(), int((*it)->nodekey().size()), buf);
12905
12906 LOG_warn << "Sync: Undecryptable child node. " << buf;
12907
12908 l->reported = true;
12909
12910 char report[256];
12911
12912 Base64::btoa((const byte *)&(*it)->nodehandle, MegaClient::NODEHANDLE, report);
12913
12914 sprintf(report + 8, " %d %.200s", (*it)->type, buf);
12915
12916 // report an "undecrypted child" event
12917 reportevent("CU", report, 0);
12918
12919 delete [] buf;
12920 }
12921
12922 continue;
12923 }
12924
12925 // ...or a node name attribute missing
12926 if ((ait = (*it)->attrs.map.find('n')) == (*it)->attrs.map.end())
12927 {
12928 LOG_warn << "Node name missing, not syncing subtree: " << l->name.c_str();
12929
12930 if (!l->reported)
12931 {
12932 l->reported = true;
12933
12934 // report a "no-name child" event
12935 reportevent("CN", NULL, 0);
12936 }
12937
12938 continue;
12939 }
12940
12941 addchild(&nchildren, &ait->second, *it, &strings, l->sync->mFilesystemType);
12942 }
12943 }
12944 }
12945
12946 // check for elements that need to be created, deleted or updated on the
12947 // remote side
12948 for (localnode_map::iterator lit = l->children.begin(); lit != l->children.end(); lit++)
12949 {
12950 LocalNode* ll = lit->second;
12951
12952 if (ll->deleted)
12953 {
12954 LOG_debug << "LocalNode deleted " << ll->name;
12955 continue;
12956 }
12957
12958 // UTF-8 converted local name
12959 string localname = ll->localname.toName(*fsaccess, l->sync->mFilesystemType);
12960 if (!localname.size() || !ll->name.size())
12961 {
12962 if (!ll->reported)
12963 {
12964 ll->reported = true;
12965
12966 char report[256];
12967 sprintf(report, "%d %d %d %d", (int)lit->first->editStringDirect()->size(), (int)localname.size(), (int)ll->name.size(), (int)ll->type);
12968
12969 // report a "no-name localnode" event
12970 reportevent("LN", report, 0);
12971 }
12972 continue;
12973 }
12974
12975 rit = nchildren.find(&localname);
12976
12977 bool isSymLink = false;
12978 #ifndef WIN32
12979 if (PosixFileAccess::mFoundASymlink)
12980 {
12981 unique_ptr<FileAccess> fa(fsaccess->newfileaccess(false));
12982 LocalPath localpath = ll->getLocalPath();
12983
12984 fa->fopen(localpath);
12985 isSymLink = fa->mIsSymLink;
12986 }
12987 #endif
12988 // do we have a corresponding remote child?
12989 if (rit != nchildren.end())
12990 {
12991 // corresponding remote node exists
12992 // local: folder, remote: file - overwrite
12993 // local: file, remote: folder - overwrite
12994 // local: folder, remote: folder - recurse
12995 // local: file, remote: file - overwrite if newer
12996 if (ll->type != rit->second->type || isSymLink)
12997 {
12998 insync = false;
12999 LOG_warn << "Type changed: " << localname << " LNtype: " << ll->type << " Ntype: " << rit->second->type << " isSymLink = " << isSymLink;
13000 movetosyncdebris(rit->second, l->sync->inshare);
13001 }
13002 else
13003 {
13004 // file on both sides - do not overwrite if local version older or identical
13005 if (ll->type == FILENODE)
13006 {
13007 if (ll->node != rit->second)
13008 {
13009 ll->sync->statecacheadd(ll);
13010 }
13011 ll->setnode(rit->second);
13012
13013 // check if file is likely to be identical
13014 if (*ll == *(FileFingerprint*)rit->second)
13015 {
13016 // files have the same size and the same mtime (or the
13017 // same fingerprint, if available): no action needed
13018 if (!ll->checked)
13019 {
13020 if (!gfxdisabled && gfx && gfx->isgfx(ll->localname.editStringDirect()))
13021 {
13022 int missingattr = 0;
13023
13024 // check for missing imagery
13025 if (!ll->node->hasfileattribute(GfxProc::THUMBNAIL))
13026 {
13027 missingattr |= 1 << GfxProc::THUMBNAIL;
13028 }
13029
13030 if (!ll->node->hasfileattribute(GfxProc::PREVIEW))
13031 {
13032 missingattr |= 1 << GfxProc::PREVIEW;
13033 }
13034
13035 if (missingattr && checkaccess(ll->node, OWNER)
13036 && !gfx->isvideo(ll->localname.editStringDirect()))
13037 {
13038 char me64[12];
13039 Base64::btoa((const byte*)&me, MegaClient::USERHANDLE, me64);
13040 if (ll->node->attrs.map.find('f') == ll->node->attrs.map.end() || ll->node->attrs.map['f'] != me64)
13041 {
13042 LOG_debug << "Restoring missing attributes: " << ll->name;
13043 SymmCipher *symmcipher = ll->node->nodecipher();
13044 auto llpath = ll->getLocalPath();
13045 gfx->gendimensionsputfa(NULL, llpath.editStringDirect(), ll->node->nodehandle, symmcipher, missingattr);
13046 }
13047 }
13048 }
13049
13050 ll->checked = true;
13051 }
13052
13053 // if this node is being fetched, but it's already synced
13054 if (rit->second->syncget)
13055 {
13056 LOG_debug << "Stopping unneeded download";
13057 delete rit->second->syncget;
13058 rit->second->syncget = NULL;
13059 }
13060
13061 // if this localnode is being uploaded, but it's already synced
13062 if (ll->transfer)
13063 {
13064 LOG_debug << "Stopping unneeded upload";
13065 DBTableTransactionCommitter committer(tctable);
13066 stopxfer(ll, &committer); // todo: can we use just one commiter for all of the recursive syncup() calls?
13067 }
13068
13069 ll->treestate(TREESTATE_SYNCED);
13070 continue;
13071 }
13072
13073 // skip if remote file is newer
13074 if (ll->mtime < rit->second->mtime)
13075 {
13076 LOG_debug << "LocalNode is older: " << ll->name << " LNmtime: " << ll->mtime << " Nmtime: " << rit->second->mtime;
13077 continue;
13078 }
13079
13080 if (ll->mtime == rit->second->mtime)
13081 {
13082 if (ll->size < rit->second->size)
13083 {
13084 LOG_warn << "Syncup. Same mtime but lower size: " << ll->name
13085 << " LNmtime: " << ll->mtime << " LNsize: " << ll->size << " Nsize: " << rit->second->size
13086 << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle) ;
13087
13088 continue;
13089 }
13090
13091 if (ll->size == rit->second->size && memcmp(ll->crc.data(), rit->second->crc.data(), sizeof ll->crc) < 0)
13092 {
13093 LOG_warn << "Syncup. Same mtime and size, but lower CRC: " << ll->name
13094 << " mtime: " << ll->mtime << " size: " << ll->size << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle);
13095
13096 continue;
13097 }
13098 }
13099
13100 LOG_debug << "LocalNode change detected on syncupload: " << ll->name << " LNsize: " << ll->size << " LNmtime: " << ll->mtime
13101 << " NSize: " << rit->second->size << " Nmtime: " << rit->second->mtime << " Nhandle: " << LOG_NODEHANDLE(rit->second->nodehandle);
13102
13103 #ifdef WIN32
13104 if(ll->size == ll->node->size && !memcmp(ll->crc.data(), ll->node->crc.data(), sizeof(ll->crc)))
13105 {
13106 LOG_debug << "Modification time changed only";
13107 auto f = fsaccess->newfileaccess();
13108 auto lpath = ll->getLocalPath(true);
13109 LocalPath stream = lpath;
13110 stream.append(LocalPath::fromLocalname(string((const char *)(const wchar_t*)L":$CmdTcID:$DATA", 30)));
13111 if (f->fopen(stream))
13112 {
13113 LOG_warn << "COMODO detected";
13114 HKEY hKey;
13115 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
13116 L"SYSTEM\\CurrentControlSet\\Services\\CmdAgent\\CisConfigs\\0\\HIPS\\SBSettings",
13117 0,
13118 KEY_QUERY_VALUE,
13119 &hKey ) == ERROR_SUCCESS)
13120 {
13121 DWORD value = 0;
13122 DWORD size = sizeof(value);
13123 if (RegQueryValueEx(hKey, L"EnableSourceTracking", NULL, NULL, (LPBYTE)&value, &size) == ERROR_SUCCESS)
13124 {
13125 if (value == 1 && fsaccess->setmtimelocal(lpath, ll->node->mtime))
13126 {
13127 LOG_warn << "Fixed modification time probably changed by COMODO";
13128 ll->mtime = ll->node->mtime;
13129 ll->treestate(TREESTATE_SYNCED);
13130 RegCloseKey(hKey);
13131 continue;
13132 }
13133 }
13134 RegCloseKey(hKey);
13135 }
13136 }
13137
13138 lpath.append(LocalPath::fromLocalname(string((const char *)(const wchar_t*)L":OECustomProperty", 34)));
13139 if (f->fopen(lpath))
13140 {
13141 LOG_warn << "Windows Search detected";
13142 continue;
13143 }
13144 }
13145 #endif
13146
13147 // if this node is being fetched, but has to be upsynced
13148 if (rit->second->syncget)
13149 {
13150 LOG_debug << "Stopping unneeded download";
13151 delete rit->second->syncget;
13152 rit->second->syncget = NULL;
13153 }
13154 }
13155 else
13156 {
13157 insync = false;
13158
13159 if (ll->node != rit->second)
13160 {
13161 ll->setnode(rit->second);
13162 ll->sync->statecacheadd(ll);
13163 }
13164
13165 // recurse into directories of equal name
13166 if (!syncup(ll, nds))
13167 {
13168 return false;
13169 }
13170 continue;
13171 }
13172 }
13173 }
13174
13175 if (isSymLink)
13176 {
13177 continue; //Do nothing for the moment
13178 }
13179 else if (ll->type == FILENODE)
13180 {
13181 // do not begin transfer until the file size / mtime has stabilized
13182 insync = false;
13183
13184 if (ll->transfer)
13185 {
13186 continue;
13187 }
13188
13189 LOG_verbose << "Unsynced LocalNode (file): " << ll->name << " " << ll << " " << (ll->transfer != 0);
13190 ll->treestate(TREESTATE_PENDING);
13191
13192 if (Waiter::ds < ll->nagleds)
13193 {
13194 LOG_debug << "Waiting for the upload delay: " << ll->name << " " << ll->nagleds;
13195 if (ll->nagleds < *nds)
13196 {
13197 *nds = ll->nagleds;
13198 }
13199
13200 continue;
13201 }
13202 else
13203 {
13204 Node *currentVersion = ll->node;
13205 if (currentVersion)
13206 {
13207 m_time_t delay = 0;
13208 m_time_t currentTime = m_time();
13209 if (currentVersion->ctime > currentTime + 30)
13210 {
13211 // with more than 30 seconds of detected clock drift,
13212 // we don't apply any version rate control for now
13213 LOG_err << "Incorrect local time detected";
13214 }
13215 else
13216 {
13217 int recentVersions = 0;
13218 m_time_t startInterval = currentTime - Sync::RECENT_VERSION_INTERVAL_SECS;
13219 Node *version = currentVersion;
13220 while (true)
13221 {
13222 if (version->ctime < startInterval)
13223 {
13224 break;
13225 }
13226
13227 recentVersions++;
13228 if (!version->children.size())
13229 {
13230 break;
13231 }
13232
13233 version = version->children.back();
13234 }
13235
13236 if (recentVersions > 10)
13237 {
13238 // version rate control starts with more than 10 recent versions
13239 delay = 7 * (recentVersions / 10) * (recentVersions - 10);
13240 }
13241
13242 LOG_debug << "Number of recent versions: " << recentVersions << " delay: " << delay
13243 << " prev: " << currentVersion->ctime << " current: " << currentTime;
13244 }
13245
13246 if (delay)
13247 {
13248 m_time_t next = currentVersion->ctime + delay;
13249 if (next > currentTime)
13250 {
13251 dstime backoffds = dstime((next - currentTime) * 10);
13252 ll->nagleds = waiter->ds + backoffds;
13253 LOG_debug << "Waiting for the version rate limit delay during " << backoffds << " ds";
13254
13255 if (ll->nagleds < *nds)
13256 {
13257 *nds = ll->nagleds;
13258 }
13259 continue;
13260 }
13261 else
13262 {
13263 LOG_debug << "Version rate limit delay already expired";
13264 }
13265 }
13266 }
13267
13268 LocalPath localpath = ll->getLocalPath();
13269 bool t;
13270 auto fa = fsaccess->newfileaccess(false);
13271
13272 if (!(t = fa->fopen(localpath, true, false))
13273 || fa->size != ll->size
13274 || fa->mtime != ll->mtime)
13275 {
13276 if (t)
13277 {
13278 ll->sync->localbytes -= ll->size;
13279 ll->genfingerprint(fa.get());
13280 ll->sync->localbytes += ll->size;
13281
13282 ll->sync->statecacheadd(ll);
13283 }
13284
13285 ll->bumpnagleds();
13286
13287 LOG_debug << "Localnode not stable yet: " << ll->name << " " << t << " " << fa->size << " " << ll->size
13288 << " " << fa->mtime << " " << ll->mtime << " " << ll->nagleds;
13289
13290 if (ll->nagleds < *nds)
13291 {
13292 *nds = ll->nagleds;
13293 }
13294
13295 continue;
13296 }
13297
13298 ll->created = false;
13299 }
13300 }
13301 else
13302 {
13303 LOG_verbose << "Unsynced LocalNode (folder): " << ll->name;
13304 }
13305
13306 if (ll->created)
13307 {
13308 if (!ll->reported)
13309 {
13310 ll->reported = true;
13311
13312 // FIXME: remove created flag and associated safeguards after
13313 // positively verifying the absence of a related repetitive node creation bug
13314 LOG_err << "Internal error: Duplicate node creation: " << ll->name.c_str();
13315
13316 char report[256];
13317
13318 // always report LocalNode's type, name length, mtime, file size
13319 sprintf(report, "[%u %u %d %d %d] %d %d %d %d %d %" PRIi64,
13320 (int)nchildren.size(),
13321 (int)l->children.size(),
13322 l->node ? (int)l->node->children.size() : -1,
13323 (int)synccreate.size(),
13324 syncadding,
13325 ll->type,
13326 (int)ll->name.size(),
13327 (int)ll->mtime,
13328 (int)ll->sync->state,
13329 (int)ll->sync->inshare,
13330 ll->size);
13331
13332 if (ll->node)
13333 {
13334 int namelen;
13335
13336 if ((ait = ll->node->attrs.map.find('n')) != ll->node->attrs.map.end())
13337 {
13338 namelen = int(ait->second.size());
13339 }
13340 else
13341 {
13342 namelen = -1;
13343 }
13344
13345 // additionally, report corresponding Node's type, name length, mtime, file size and handle
13346 sprintf(strchr(report, 0), " %d %d %d %" PRIi64 " %d ", ll->node->type, namelen, (int)ll->node->mtime, ll->node->size, ll->node->syncdeleted);
13347 Base64::btoa((const byte *)&ll->node->nodehandle, MegaClient::NODEHANDLE, strchr(report, 0));
13348 }
13349
13350 // report a "dupe" event
13351 reportevent("D2", report, 0);
13352 }
13353 else
13354 {
13355 LOG_err << "LocalNode created and reported " << ll->name;
13356 }
13357 }
13358 else
13359 {
13360 ll->created = true;
13361
13362 assert (!isSymLink);
13363 // create remote folder or send file
13364 LOG_debug << "Adding local file to synccreate: " << ll->name << " " << synccreate.size();
13365 synccreate.push_back(ll);
13366 syncactivity = true;
13367
13368 if (synccreate.size() >= MAX_NEWNODES)
13369 {
13370 LOG_warn << "Stopping syncup due to MAX_NEWNODES";
13371 return false;
13372 }
13373 }
13374
13375 if (ll->type == FOLDERNODE)
13376 {
13377 if (!syncup(ll, nds))
13378 {
13379 return false;
13380 }
13381 }
13382 }
13383
13384 if (insync && l->node)
13385 {
13386 l->treestate(TREESTATE_SYNCED);
13387 }
13388
13389 return true;
13390 }
13391
13392 // execute updates stored in synccreate[]
13393 // must not be invoked while the previous creation operation is still in progress
syncupdate()13394 void MegaClient::syncupdate()
13395 {
13396 // split synccreate[] in separate subtrees and send off to putnodes() for
13397 // creation on the server
13398 unsigned i, start, end;
13399 SymmCipher tkey;
13400 string tattrstring;
13401 AttrMap tattrs;
13402 Node* n;
13403 NewNode* nn;
13404 NewNode* nnp;
13405 LocalNode* l;
13406
13407 for (start = 0; start < synccreate.size(); start = end)
13408 {
13409 // determine length of distinct subtree beneath existing node
13410 for (end = start; end < synccreate.size(); end++)
13411 {
13412 if ((end > start) && synccreate[end]->parent->node)
13413 {
13414 break;
13415 }
13416 }
13417
13418 // add nodes that can be created immediately: folders & existing files;
13419 // start uploads of new files
13420 nn = nnp = new NewNode[end - start];
13421
13422 DBTableTransactionCommitter committer(tctable);
13423 for (i = start; i < end; i++)
13424 {
13425 n = NULL;
13426 l = synccreate[i];
13427
13428 if (l->type == FILENODE && l->parent->node)
13429 {
13430 l->h = l->parent->node->nodehandle;
13431 }
13432
13433 if (l->type == FOLDERNODE || (n = nodebyfingerprint(l)))
13434 {
13435 // create remote folder or copy file if it already exists
13436 nnp->source = NEW_NODE;
13437 nnp->type = l->type;
13438 nnp->syncid = l->syncid;
13439 nnp->localnode.crossref(l, nnp); // also sets l->newnode to nnp
13440 nnp->nodehandle = n ? n->nodehandle : l->syncid;
13441 nnp->parenthandle = i > start ? l->parent->syncid : UNDEF;
13442
13443 if (n)
13444 {
13445 // overwriting an existing remote node? tag it as the previous version or move to SyncDebris
13446 if (l->node && l->node->parent && l->node->parent->localnode)
13447 {
13448 if (versions_disabled)
13449 {
13450 movetosyncdebris(l->node, l->sync->inshare);
13451 }
13452 else
13453 {
13454 nnp->ovhandle = l->node->nodehandle;
13455 }
13456 }
13457
13458 // this is a file - copy, use original key & attributes
13459 // FIXME: move instead of creating a copy if it is in
13460 // rubbish to reduce node creation load
13461 nnp->nodekey = n->nodekey();
13462 tattrs.map = n->attrs.map;
13463
13464 nameid rrname = AttrMap::string2nameid("rr");
13465 attr_map::iterator it = tattrs.map.find(rrname);
13466 if (it != tattrs.map.end())
13467 {
13468 LOG_debug << "Removing rr attribute";
13469 tattrs.map.erase(it);
13470 }
13471
13472 app->syncupdate_remote_copy(l->sync, l->name.c_str());
13473 }
13474 else
13475 {
13476 // this is a folder - create, use fresh key & attributes
13477 nnp->nodekey.resize(FOLDERNODEKEYLENGTH);
13478 rng.genblock((byte*)nnp->nodekey.data(), FOLDERNODEKEYLENGTH);
13479 tattrs.map.clear();
13480 }
13481
13482 // set new name, encrypt and attach attributes
13483 tattrs.map['n'] = l->name;
13484 tattrs.getjson(&tattrstring);
13485 tkey.setkey((const byte*)nnp->nodekey.data(), nnp->type);
13486 nnp->attrstring.reset(new string);
13487 makeattr(&tkey, nnp->attrstring, tattrstring.c_str());
13488
13489 l->treestate(TREESTATE_SYNCING);
13490 nnp++;
13491 }
13492 else if (l->type == FILENODE)
13493 {
13494 l->treestate(TREESTATE_PENDING);
13495
13496 // the overwrite will happen upon PUT completion
13497 string tmppath;
13498
13499 nextreqtag();
13500 startxfer(PUT, l, committer);
13501
13502 tmppath = l->getLocalPath(true).toPath(*fsaccess);
13503 app->syncupdate_put(l->sync, l, tmppath.c_str());
13504 }
13505 }
13506
13507 if (nnp == nn)
13508 {
13509 delete[] nn;
13510 }
13511 else
13512 {
13513 // add nodes unless parent node has been deleted
13514 LocalNode *localNode = synccreate[start];
13515 if (localNode->parent->node)
13516 {
13517 syncadding++;
13518
13519 // this assert fails for the case of two different files uploaded to the same path, and both putnodes occurring in the same exec()
13520 assert(localNode->type == FOLDERNODE
13521 || localNode->h == localNode->parent->node->nodehandle); // if it's a file, it should match
13522 reqs.add(new CommandPutNodes(this,
13523 localNode->parent->node->nodehandle,
13524 NULL, nn, int(nnp - nn),
13525 localNode->sync->tag,
13526 PUTNODES_SYNC));
13527
13528 syncactivity = true;
13529 }
13530 }
13531 }
13532
13533 synccreate.clear();
13534 }
13535
putnodes_sync_result(error e,NewNode * nn,int nni)13536 void MegaClient::putnodes_sync_result(error e, NewNode* nn, int nni)
13537 {
13538 // check for file nodes that failed to copy and remove them from fingerprints
13539 // FIXME: retrigger sync decision upload them immediately
13540 while (nni--)
13541 {
13542 Node* n;
13543 if (nn[nni].type == FILENODE && !nn[nni].added)
13544 {
13545 if ((n = nodebyhandle(nn[nni].nodehandle)))
13546 {
13547 mFingerprints.remove(n);
13548 }
13549 }
13550 else if (nn[nni].localnode && (n = nn[nni].localnode->node))
13551 {
13552 if (n->type == FOLDERNODE)
13553 {
13554 app->syncupdate_remote_folder_addition(nn[nni].localnode->sync, n);
13555 }
13556 else
13557 {
13558 app->syncupdate_remote_file_addition(nn[nni].localnode->sync, n);
13559 }
13560 }
13561
13562 if (e && e != API_EEXPIRED && nn[nni].localnode && nn[nni].localnode->sync)
13563 {
13564 nn[nni].localnode->sync->errorcode = e;
13565 nn[nni].localnode->sync->changestate(SYNC_FAILED);
13566 }
13567 }
13568
13569 delete[] nn;
13570
13571 syncadding--;
13572 syncactivity = true;
13573 }
13574
13575 // move node to //bin, then on to the SyncDebris folder of the day (to prevent
13576 // dupes)
movetosyncdebris(Node * dn,bool unlink)13577 void MegaClient::movetosyncdebris(Node* dn, bool unlink)
13578 {
13579 dn->syncdeleted = SYNCDEL_DELETED;
13580
13581 // detach node from LocalNode
13582 if (dn->localnode)
13583 {
13584 dn->tag = dn->localnode->sync->tag;
13585 dn->localnode->node = NULL;
13586 dn->localnode = NULL;
13587 }
13588
13589 Node* n = dn;
13590
13591 // at least one parent node already on the way to SyncDebris?
13592 while ((n = n->parent) && n->syncdeleted == SYNCDEL_NONE);
13593
13594 // no: enqueue this one
13595 if (!n)
13596 {
13597 if (unlink)
13598 {
13599 dn->tounlink_it = tounlink.insert(dn).first;
13600 }
13601 else
13602 {
13603 dn->todebris_it = todebris.insert(dn).first;
13604 }
13605 }
13606 }
13607
execsyncdeletions()13608 void MegaClient::execsyncdeletions()
13609 {
13610 if (todebris.size())
13611 {
13612 execmovetosyncdebris();
13613 }
13614
13615 if (tounlink.size())
13616 {
13617 execsyncunlink();
13618 }
13619 }
13620
proclocaltree(LocalNode * n,LocalTreeProc * tp)13621 void MegaClient::proclocaltree(LocalNode* n, LocalTreeProc* tp)
13622 {
13623 if (n->type != FILENODE)
13624 {
13625 for (localnode_map::iterator it = n->children.begin(); it != n->children.end(); )
13626 {
13627 LocalNode *child = it->second;
13628 it++;
13629 proclocaltree(child, tp);
13630 }
13631 }
13632
13633 tp->proc(this, n);
13634 }
13635
unlinkifexists(LocalNode * l,FileAccess * fa,LocalPath & reuseBuffer)13636 void MegaClient::unlinkifexists(LocalNode *l, FileAccess *fa, LocalPath& reuseBuffer)
13637 {
13638 // sdisable = true for this call. In the case where we are doing a full scan due to fs notifications failing,
13639 // and a file was renamed but retains the same shortname, we would check the presence of the wrong file.
13640 // Also shortnames are slowly being deprecated by Microsoft, so using full names is now the normal case anyway.
13641 l->getlocalpath(reuseBuffer, true);
13642 if (fa->fopen(reuseBuffer) || fa->type == FOLDERNODE)
13643 {
13644 LOG_warn << "Deletion of existing file avoided";
13645 static bool reported99446 = false;
13646 if (!reported99446)
13647 {
13648 sendevent(99446, "Deletion of existing file avoided", 0);
13649 reported99446 = true;
13650 }
13651
13652 // The local file or folder seems to be still there, but invisible
13653 // for the sync engine, so we just stop syncing it
13654 LocalTreeProcUnlinkNodes tpunlink;
13655 proclocaltree(l, &tpunlink);
13656 }
13657 #ifdef _WIN32
13658 else if (fa->errorcode != ERROR_FILE_NOT_FOUND && fa->errorcode != ERROR_PATH_NOT_FOUND)
13659 {
13660 LOG_warn << "Unexpected error code for deleted file: " << fa->errorcode;
13661 static bool reported99447 = false;
13662 if (!reported99447)
13663 {
13664 ostringstream oss;
13665 oss << fa->errorcode;
13666 string message = oss.str();
13667 sendevent(99447, message.c_str(), 0);
13668 reported99447 = true;
13669 }
13670 }
13671 #endif
13672 }
13673
execsyncunlink()13674 void MegaClient::execsyncunlink()
13675 {
13676 Node* n;
13677 Node* tn;
13678
13679 // delete tounlink nodes
13680 do {
13681 n = tn = *tounlink.begin();
13682
13683 while ((n = n->parent) && n->syncdeleted == SYNCDEL_NONE);
13684
13685 if (!n)
13686 {
13687 int creqtag = reqtag;
13688 reqtag = tn->tag;
13689 unlink(tn);
13690 reqtag = creqtag;
13691 }
13692
13693 tn->tounlink_it = tounlink.end();
13694 tounlink.erase(tounlink.begin());
13695 } while (tounlink.size());
13696 }
13697
13698 // immediately moves pending todebris items to //bin
13699 // also deletes tounlink items directly
execmovetosyncdebris()13700 void MegaClient::execmovetosyncdebris()
13701 {
13702 Node* n;
13703 Node* tn;
13704 node_set::iterator it;
13705
13706 m_time_t ts;
13707 struct tm tms;
13708 char buf[32];
13709 syncdel_t target;
13710
13711 // attempt to move the nodes in node_set todebris to the following
13712 // locations (in falling order):
13713 // - //bin/SyncDebris/yyyy-mm-dd
13714 // - //bin/SyncDebris
13715 // - //bin
13716
13717 // (if no rubbish bin is found, we should probably reload...)
13718 if (!(tn = nodebyhandle(rootnodes[RUBBISHNODE - ROOTNODE])))
13719 {
13720 return;
13721 }
13722
13723 target = SYNCDEL_BIN;
13724
13725 ts = m_time();
13726 struct tm* ptm = m_localtime(ts, &tms);
13727 sprintf(buf, "%04d-%02d-%02d", ptm->tm_year + 1900, ptm->tm_mon + 1, ptm->tm_mday);
13728 m_time_t currentminute = ts / 60;
13729
13730 // locate //bin/SyncDebris
13731 if ((n = childnodebyname(tn, SYNCDEBRISFOLDERNAME)) && n->type == FOLDERNODE)
13732 {
13733 tn = n;
13734 target = SYNCDEL_DEBRIS;
13735
13736 // locate //bin/SyncDebris/yyyy-mm-dd
13737 if ((n = childnodebyname(tn, buf)) && n->type == FOLDERNODE)
13738 {
13739 tn = n;
13740 target = SYNCDEL_DEBRISDAY;
13741 }
13742 }
13743
13744 // in order to reduce the API load, we move
13745 // - SYNCDEL_DELETED nodes to any available target
13746 // - SYNCDEL_BIN/SYNCDEL_DEBRIS nodes to SYNCDEL_DEBRISDAY
13747 // (move top-level nodes only)
13748 for (it = todebris.begin(); it != todebris.end(); )
13749 {
13750 n = *it;
13751
13752 if (n->syncdeleted == SYNCDEL_DELETED
13753 || n->syncdeleted == SYNCDEL_BIN
13754 || n->syncdeleted == SYNCDEL_DEBRIS)
13755 {
13756 while ((n = n->parent) && n->syncdeleted == SYNCDEL_NONE);
13757
13758 if (!n)
13759 {
13760 n = *it;
13761
13762 if (n->syncdeleted == SYNCDEL_DELETED
13763 || ((n->syncdeleted == SYNCDEL_BIN
13764 || n->syncdeleted == SYNCDEL_DEBRIS)
13765 && target == SYNCDEL_DEBRISDAY))
13766 {
13767 n->syncdeleted = SYNCDEL_INFLIGHT;
13768 int creqtag = reqtag;
13769 reqtag = n->tag;
13770 LOG_debug << "Moving to Syncdebris: " << n->displayname() << " in " << tn->displayname() << " Nhandle: " << LOG_NODEHANDLE(n->nodehandle);
13771 rename(n, tn, target, n->parent ? n->parent->nodehandle : UNDEF);
13772 reqtag = creqtag;
13773 it++;
13774 }
13775 else
13776 {
13777 LOG_debug << "SyncDebris daily folder not created. Final target: " << n->syncdeleted;
13778 n->syncdeleted = SYNCDEL_NONE;
13779 n->todebris_it = todebris.end();
13780 todebris.erase(it++);
13781 }
13782 }
13783 else
13784 {
13785 it++;
13786 }
13787 }
13788 else if (n->syncdeleted == SYNCDEL_DEBRISDAY
13789 || n->syncdeleted == SYNCDEL_FAILED)
13790 {
13791 LOG_debug << "Move to SyncDebris finished. Final target: " << n->syncdeleted;
13792 n->syncdeleted = SYNCDEL_NONE;
13793 n->todebris_it = todebris.end();
13794 todebris.erase(it++);
13795 }
13796 else
13797 {
13798 it++;
13799 }
13800 }
13801
13802 if (target != SYNCDEL_DEBRISDAY && todebris.size() && !syncdebrisadding
13803 && (target == SYNCDEL_BIN || syncdebrisminute != currentminute))
13804 {
13805 syncdebrisadding = true;
13806 syncdebrisminute = currentminute;
13807 LOG_debug << "Creating daily SyncDebris folder: " << buf << " Target: " << target;
13808
13809 // create missing component(s) of the sync debris folder of the day
13810 NewNode* nn;
13811 SymmCipher tkey;
13812 string tattrstring;
13813 AttrMap tattrs;
13814 int i = (target == SYNCDEL_DEBRIS) ? 1 : 2;
13815
13816 nn = new NewNode[i] + i;
13817
13818 while (i--)
13819 {
13820 nn--;
13821
13822 nn->source = NEW_NODE;
13823 nn->type = FOLDERNODE;
13824 nn->nodehandle = i;
13825 nn->parenthandle = i ? 0 : UNDEF;
13826
13827 nn->nodekey.resize(FOLDERNODEKEYLENGTH);
13828 rng.genblock((byte*)nn->nodekey.data(), FOLDERNODEKEYLENGTH);
13829
13830 // set new name, encrypt and attach attributes
13831 tattrs.map['n'] = (i || target == SYNCDEL_DEBRIS) ? buf : SYNCDEBRISFOLDERNAME;
13832 tattrs.getjson(&tattrstring);
13833 tkey.setkey((const byte*)nn->nodekey.data(), FOLDERNODE);
13834 nn->attrstring.reset(new string);
13835 makeattr(&tkey, nn->attrstring, tattrstring.c_str());
13836 }
13837
13838 reqs.add(new CommandPutNodes(this, tn->nodehandle, NULL, nn,
13839 (target == SYNCDEL_DEBRIS) ? 1 : 2, -reqtag,
13840 PUTNODES_SYNCDEBRIS));
13841 }
13842 }
13843
13844 // we cannot delete the Sync object directly, as it might have pending
13845 // operations on it
delsync(Sync * sync,bool deletecache)13846 void MegaClient::delsync(Sync* sync, bool deletecache)
13847 {
13848 sync->changestate(SYNC_CANCELED);
13849
13850 sync->setResumable(false);
13851
13852 if (deletecache && sync->statecachetable)
13853 {
13854 sync->statecachetable->remove();
13855 delete sync->statecachetable;
13856 sync->statecachetable = NULL;
13857 }
13858
13859 syncactivity = true;
13860 }
13861
putnodes_syncdebris_result(error,NewNode * nn)13862 void MegaClient::putnodes_syncdebris_result(error, NewNode* nn)
13863 {
13864 delete[] nn;
13865
13866 syncdebrisadding = false;
13867 }
13868 #endif
13869
13870 // inject file into transfer subsystem
13871 // if file's fingerprint is not valid, it will be obtained from the local file
13872 // (PUT) or the file's key (GET)
startxfer(direction_t d,File * f,DBTableTransactionCommitter & committer,bool skipdupes,bool startfirst,bool donotpersist)13873 bool MegaClient::startxfer(direction_t d, File* f, DBTableTransactionCommitter& committer, bool skipdupes, bool startfirst, bool donotpersist)
13874 {
13875 if (!f->transfer)
13876 {
13877 if (d == PUT)
13878 {
13879 if (!f->isvalid) // (sync LocalNodes always have this set)
13880 {
13881 // missing FileFingerprint for local file - generate
13882 auto fa = fsaccess->newfileaccess();
13883
13884 if (fa->fopen(f->localname, d == PUT, d == GET))
13885 {
13886 f->genfingerprint(fa.get());
13887 }
13888 }
13889
13890 // if we are unable to obtain a valid file FileFingerprint, don't proceed
13891 if (!f->isvalid)
13892 {
13893 LOG_err << "Unable to get a fingerprint " << f->name;
13894 return false;
13895 }
13896
13897 #ifdef USE_MEDIAINFO
13898 mediaFileInfo.requestCodecMappingsOneTime(this, &f->localname);
13899 #endif
13900 }
13901 else
13902 {
13903 if (!f->isvalid)
13904 {
13905 // no valid fingerprint: use filekey as its replacement
13906 memcpy(f->crc.data(), f->filekey, sizeof f->crc);
13907 }
13908 }
13909
13910 Transfer* t = NULL;
13911 transfer_map::iterator it = transfers[d].find(f);
13912
13913 if (it != transfers[d].end())
13914 {
13915 t = it->second;
13916 if (skipdupes)
13917 {
13918 for (file_list::iterator fi = t->files.begin(); fi != t->files.end(); fi++)
13919 {
13920 if ((d == GET && f->localname == (*fi)->localname)
13921 || (d == PUT && f->h != UNDEF
13922 && f->h == (*fi)->h
13923 && !f->targetuser.size()
13924 && !(*fi)->targetuser.size()
13925 && f->name == (*fi)->name))
13926 {
13927 LOG_warn << "Skipping duplicated transfer";
13928 return false;
13929 }
13930 }
13931 }
13932 f->file_it = t->files.insert(t->files.end(), f);
13933 f->transfer = t;
13934 f->tag = reqtag;
13935 if (!f->dbid && !donotpersist)
13936 {
13937 filecacheadd(f, committer);
13938 }
13939 app->file_added(f);
13940
13941 if (startfirst)
13942 {
13943 transferlist.movetofirst(t, committer);
13944 }
13945
13946 if (overquotauntil && overquotauntil > Waiter::ds && d != PUT)
13947 {
13948 dstime timeleft = dstime(overquotauntil - Waiter::ds);
13949 t->failed(API_EOVERQUOTA, committer, timeleft);
13950 }
13951 else if (d == PUT && ststatus == STORAGE_RED)
13952 {
13953 t->failed(API_EOVERQUOTA, committer);
13954 }
13955 else if (ststatus == STORAGE_PAYWALL)
13956 {
13957 t->failed(API_EPAYWALL, committer);
13958 }
13959 }
13960 else
13961 {
13962 it = cachedtransfers[d].find(f);
13963 if (it != cachedtransfers[d].end())
13964 {
13965 LOG_debug << "Resumable transfer detected";
13966 t = it->second;
13967 bool hadAnyData = t->pos > 0;
13968 if ((d == GET && !t->pos) || ((m_time() - t->lastaccesstime) >= 172500))
13969 {
13970 LOG_warn << "Discarding temporary URL (" << t->pos << ", " << t->lastaccesstime << ")";
13971 t->tempurls.clear();
13972
13973 if (d == PUT)
13974 {
13975 t->chunkmacs.clear();
13976 t->progresscompleted = 0;
13977 delete [] t->ultoken;
13978 t->ultoken = NULL;
13979 t->pos = 0;
13980 }
13981 }
13982
13983 auto fa = fsaccess->newfileaccess();
13984 if (!fa->fopen(t->localfilename))
13985 {
13986 if (d == PUT)
13987 {
13988 LOG_warn << "Local file not found";
13989 // the transfer will be retried to ensure that the file
13990 // is not just just temporarily blocked
13991 }
13992 else
13993 {
13994 if (hadAnyData)
13995 {
13996 LOG_warn << "Temporary file not found";
13997 }
13998 t->localfilename.clear();
13999 t->chunkmacs.clear();
14000 t->progresscompleted = 0;
14001 t->pos = 0;
14002 }
14003 }
14004 else
14005 {
14006 if (d == PUT)
14007 {
14008 if (f->genfingerprint(fa.get()))
14009 {
14010 LOG_warn << "The local file has been modified";
14011 t->tempurls.clear();
14012 t->chunkmacs.clear();
14013 t->progresscompleted = 0;
14014 delete [] t->ultoken;
14015 t->ultoken = NULL;
14016 t->pos = 0;
14017 }
14018 }
14019 else
14020 {
14021 if (t->progresscompleted > fa->size)
14022 {
14023 LOG_warn << "Truncated temporary file";
14024 t->chunkmacs.clear();
14025 t->progresscompleted = 0;
14026 t->pos = 0;
14027 }
14028 }
14029 }
14030 cachedtransfers[d].erase(it);
14031 LOG_debug << "Transfer resumed";
14032 }
14033
14034 if (!t)
14035 {
14036 t = new Transfer(this, d);
14037 *(FileFingerprint*)t = *(FileFingerprint*)f;
14038 }
14039
14040 t->skipserialization = donotpersist;
14041
14042 t->lastaccesstime = m_time();
14043 t->tag = reqtag;
14044 f->tag = reqtag;
14045 t->transfers_it = transfers[d].insert(pair<FileFingerprint*, Transfer*>((FileFingerprint*)t, t)).first;
14046
14047 f->file_it = t->files.insert(t->files.end(), f);
14048 f->transfer = t;
14049 if (!f->dbid && !donotpersist)
14050 {
14051 filecacheadd(f, committer);
14052 }
14053
14054 transferlist.addtransfer(t, committer, startfirst);
14055 app->transfer_added(t);
14056 app->file_added(f);
14057 looprequested = true;
14058
14059 if (overquotauntil && overquotauntil > Waiter::ds && d != PUT)
14060 {
14061 dstime timeleft = dstime(overquotauntil - Waiter::ds);
14062 t->failed(API_EOVERQUOTA, committer, timeleft);
14063 }
14064 else if (d == PUT && ststatus == STORAGE_RED)
14065 {
14066 t->failed(API_EOVERQUOTA, committer);
14067 }
14068 else if (ststatus == STORAGE_PAYWALL)
14069 {
14070 t->failed(API_EPAYWALL, committer);
14071 }
14072 }
14073
14074 assert( (ISUNDEF(f->h) && f->targetuser.size() && (f->targetuser.size() == 11 || f->targetuser.find("@")!=string::npos) ) // <- uploading to inbox
14075 || (!ISUNDEF(f->h) && (nodebyhandle(f->h) || d == GET) )); // target handle for the upload should be known at this time (except for inbox uploads)
14076 }
14077
14078 return true;
14079 }
14080
14081 // remove file from transfer subsystem
stopxfer(File * f,DBTableTransactionCommitter * committer)14082 void MegaClient::stopxfer(File* f, DBTableTransactionCommitter* committer)
14083 {
14084 if (f->transfer)
14085 {
14086 LOG_debug << "Stopping transfer: " << f->name;
14087
14088 Transfer *transfer = f->transfer;
14089 transfer->removeTransferFile(API_EINCOMPLETE, f, committer);
14090
14091 // last file for this transfer removed? shut down transfer.
14092 if (!transfer->files.size())
14093 {
14094 looprequested = true;
14095 transfer->finished = true;
14096 transfer->state = TRANSFERSTATE_CANCELLED;
14097 app->transfer_removed(transfer);
14098 delete transfer;
14099 }
14100 else
14101 {
14102 if (transfer->type == PUT && !transfer->localfilename.empty())
14103 {
14104 LOG_debug << "Updating transfer path";
14105 transfer->files.front()->prepare();
14106 }
14107 }
14108 }
14109 }
14110
14111 // pause/unpause transfers
pausexfers(direction_t d,bool pause,bool hard,DBTableTransactionCommitter & committer)14112 void MegaClient::pausexfers(direction_t d, bool pause, bool hard, DBTableTransactionCommitter& committer)
14113 {
14114 xferpaused[d] = pause;
14115
14116 if (!pause || hard)
14117 {
14118 WAIT_CLASS::bumpds();
14119
14120 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); )
14121 {
14122 if ((*it)->transfer->type == d)
14123 {
14124 if (pause)
14125 {
14126 if (hard)
14127 {
14128 (*it++)->disconnect();
14129 }
14130 }
14131 else
14132 {
14133 (*it)->lastdata = Waiter::ds;
14134 (*it++)->doio(this, committer);
14135 }
14136 }
14137 else
14138 {
14139 it++;
14140 }
14141 }
14142 }
14143 }
14144
setmaxconnections(direction_t d,int num)14145 void MegaClient::setmaxconnections(direction_t d, int num)
14146 {
14147 if (num > 0)
14148 {
14149 if ((unsigned int) num > MegaClient::MAX_NUM_CONNECTIONS)
14150 {
14151 num = MegaClient::MAX_NUM_CONNECTIONS;
14152 }
14153
14154 if (connections[d] != num)
14155 {
14156 connections[d] = (unsigned char)num;
14157 for (transferslot_list::iterator it = tslots.begin(); it != tslots.end(); )
14158 {
14159 TransferSlot *slot = *it++;
14160 if (slot->transfer->type == d)
14161 {
14162 slot->transfer->state = TRANSFERSTATE_QUEUED;
14163 if (slot->transfer->client->ststatus != STORAGE_RED || slot->transfer->type == GET)
14164 {
14165 slot->transfer->bt.arm();
14166 }
14167 delete slot;
14168 }
14169 }
14170 }
14171 }
14172 }
14173
nodebyfingerprint(FileFingerprint * fingerprint)14174 Node* MegaClient::nodebyfingerprint(FileFingerprint* fingerprint)
14175 {
14176 return mFingerprints.nodebyfingerprint(fingerprint);
14177 }
14178
14179 #ifdef ENABLE_SYNC
nodebyfingerprint(LocalNode * localNode)14180 Node* MegaClient::nodebyfingerprint(LocalNode* localNode)
14181 {
14182 std::unique_ptr<const node_vector>
14183 remoteNodes(mFingerprints.nodesbyfingerprint(localNode));
14184
14185 if (remoteNodes->empty())
14186 return nullptr;
14187
14188 std::string localName = localNode->localname.toName(*fsaccess);
14189
14190
14191 // Only compare metamac if the node doesn't already exist.
14192 node_vector::const_iterator remoteNode =
14193 std::find_if(remoteNodes->begin(),
14194 remoteNodes->end(),
14195 [&](const Node *remoteNode) -> bool
14196 {
14197 return localName == remoteNode->displayname();
14198 });
14199
14200 if (remoteNode != remoteNodes->end())
14201 return *remoteNode;
14202
14203 remoteNode = remoteNodes->begin();
14204
14205 // Compare the local file's metamac against a random candidate.
14206 //
14207 // If we're unable to generate the metamac, fail in such a way that
14208 // guarantees safe behavior.
14209 //
14210 // That is, treat both nodes as distinct until we're absolutely certain
14211 // they are identical.
14212 auto ifAccess = fsaccess->newfileaccess();
14213
14214 auto localPath = localNode->getLocalPath(true);
14215
14216 if (!ifAccess->fopen(localPath, true, false))
14217 return nullptr;
14218
14219 std::string remoteKey = (*remoteNode)->nodekey();
14220 const char *iva = &remoteKey[SymmCipher::KEYLENGTH];
14221
14222 SymmCipher cipher;
14223 cipher.setkey((byte*)&remoteKey[0], (*remoteNode)->type);
14224
14225 int64_t remoteIv = MemAccess::get<int64_t>(iva);
14226 int64_t remoteMac = MemAccess::get<int64_t>(iva + sizeof(int64_t));
14227
14228 auto result = generateMetaMac(cipher, *ifAccess, remoteIv);
14229 if (!result.first || result.second != remoteMac)
14230 return nullptr;
14231
14232 return *remoteNode;
14233 }
14234 #endif /* ENABLE_SYNC */
14235
nodesbyfingerprint(FileFingerprint * fingerprint)14236 node_vector *MegaClient::nodesbyfingerprint(FileFingerprint* fingerprint)
14237 {
14238 return mFingerprints.nodesbyfingerprint(fingerprint);
14239 }
14240
nodes_ctime_less(const Node * a,const Node * b)14241 static bool nodes_ctime_less(const Node* a, const Node* b)
14242 {
14243 // heaps return the largest element
14244 return a->ctime < b->ctime;
14245 }
14246
nodes_ctime_greater(const Node * a,const Node * b)14247 static bool nodes_ctime_greater(const Node* a, const Node* b)
14248 {
14249 return a->ctime > b->ctime;
14250 }
14251
getRecentNodes(unsigned maxcount,m_time_t since,bool includerubbishbin)14252 node_vector MegaClient::getRecentNodes(unsigned maxcount, m_time_t since, bool includerubbishbin)
14253 {
14254 // 1. Get nodes added/modified not older than `since`
14255 node_vector v;
14256 v.reserve(nodes.size());
14257 for (node_map::iterator i = nodes.begin(); i != nodes.end(); ++i)
14258 {
14259 if (i->second->type == FILENODE && i->second->ctime >= since && // recent files only
14260 (!i->second->parent || i->second->parent->type != FILENODE)) // excluding versions
14261 {
14262 v.push_back(i->second);
14263 }
14264 }
14265
14266 // heaps use a 'less' function, and pop_heap returns the largest item stored.
14267 std::make_heap(v.begin(), v.end(), nodes_ctime_less);
14268
14269 // 2. Order them chronologically and restrict them to a maximum of `maxcount`
14270 node_vector v2;
14271 unsigned maxItems = std::min(maxcount, unsigned(v.size()));
14272 v2.reserve(maxItems);
14273 while (v2.size() < maxItems && !v.empty())
14274 {
14275 std::pop_heap(v.begin(), v.end(), nodes_ctime_less);
14276 Node* n = v.back();
14277 v.pop_back();
14278 if (includerubbishbin || n->firstancestor()->type != RUBBISHNODE)
14279 {
14280 v2.push_back(n);
14281 }
14282 }
14283 return v2;
14284 }
14285
14286
14287 namespace action_bucket_compare
14288 {
14289 // these lists of file extensions (and the logic to use them) all come from the webclient - if updating here, please make sure the webclient is updated too, preferably webclient first.
14290 const static string webclient_is_image_def = ".jpg.jpeg.gif.bmp.png.";
14291 const static string webclient_is_image_raw = ".3fr.arw.cr2.crw.ciff.cs1.dcr.dng.erf.iiq.k25.kdc.mef.mos.mrw.nef.nrw.orf.pef.raf.raw.rw2.rwl.sr2.srf.srw.x3f.";
14292 const static string webclient_is_image_thumb = "psd.svg.tif.tiff.webp"; // leaving out .pdf
14293 const static string webclient_mime_photo_extensions = ".3ds.bmp.btif.cgm.cmx.djv.djvu.dwg.dxf.fbs.fh.fh4.fh5.fh7.fhc.fpx.fst.g3.gif.heic.heif.ico.ief.jpe.jpeg.jpg.ktx.mdi.mmr.npx.pbm.pct.pcx.pgm.pic.png.pnm.ppm.psd.ras.rgb.rlc.sgi.sid.svg.svgz.tga.tif.tiff.uvg.uvi.uvvg.uvvi.wbmp.wdp.webp.xbm.xif.xpm.xwd.";
14294 const static string webclient_mime_video_extensions = ".3g2.3gp.asf.asx.avi.dvb.f4v.fli.flv.fvt.h261.h263.h264.jpgm.jpgv.jpm.m1v.m2v.m4u.m4v.mj2.mjp2.mk3d.mks.mkv.mng.mov.movie.mp4.mp4v.mpe.mpeg.mpg.mpg4.mxu.ogv.pyv.qt.smv.uvh.uvm.uvp.uvs.uvu.uvv.uvvh.uvvm.uvvp.uvvs.uvvu.uvvv.viv.vob.webm.wm.wmv.wmx.wvx.";
14295
nodeIsVideo(const Node * n,char ext[12],const MegaClient & mc)14296 bool nodeIsVideo(const Node* n, char ext[12], const MegaClient& mc)
14297 {
14298 if (n->hasfileattribute(fa_media) && n->nodekey().size() == FILENODEKEYLENGTH)
14299 {
14300 #ifdef USE_MEDIAINFO
14301 if (mc.mediaFileInfo.mediaCodecsReceived)
14302 {
14303 MediaProperties mp = MediaProperties::decodeMediaPropertiesAttributes(n->fileattrstring, (uint32_t*)(n->nodekey().data() + FILENODEKEYLENGTH / 2));
14304 unsigned videocodec = mp.videocodecid;
14305 if (!videocodec && mp.shortformat)
14306 {
14307 auto& v = mc.mediaFileInfo.mediaCodecs.shortformats;
14308 if (mp.shortformat < v.size())
14309 {
14310 videocodec = v[mp.shortformat].videocodecid;
14311 }
14312 }
14313 // approximation: the webclient has a lot of logic to determine if a particular codec is playable in that browser. We'll just base our decision on the presence of a video codec.
14314 if (!videocodec)
14315 {
14316 return false; // otherwise double-check by extension
14317 }
14318 }
14319 #endif
14320 }
14321 return action_bucket_compare::webclient_mime_video_extensions.find(ext) != string::npos;
14322 }
14323
nodeIsPhoto(const Node * n,char ext[12])14324 bool nodeIsPhoto(const Node* n, char ext[12])
14325 {
14326 // evaluate according to the webclient rules, so that we get exactly the same bucketing.
14327 return action_bucket_compare::webclient_is_image_def.find(ext) != string::npos ||
14328 action_bucket_compare::webclient_is_image_raw.find(ext) != string::npos ||
14329 (action_bucket_compare::webclient_mime_photo_extensions.find(ext) != string::npos && n->hasfileattribute(GfxProc::PREVIEW));
14330 }
14331
compare(const Node * a,const Node * b,MegaClient * mc)14332 static bool compare(const Node* a, const Node* b, MegaClient* mc)
14333 {
14334 if (a->owner != b->owner) return a->owner > b->owner;
14335 if (a->parent != b->parent) return a->parent > b->parent;
14336
14337 // added/updated - distinguish by versioning
14338 if (a->children.size() != b->children.size()) return a->children.size() > b->children.size();
14339
14340 // media/nonmedia
14341 bool a_media = mc->nodeIsMedia(a, nullptr, nullptr);
14342 bool b_media = mc->nodeIsMedia(b, nullptr, nullptr);
14343 if (a_media != b_media) return a_media && !b_media;
14344
14345 return false;
14346 }
14347
comparetime(const recentaction & a,const recentaction & b)14348 static bool comparetime(const recentaction& a, const recentaction& b)
14349 {
14350 return a.time > b.time;
14351 }
14352
getExtensionDotted(const Node * n,char ext[12],const MegaClient & mc)14353 bool getExtensionDotted(const Node* n, char ext[12], const MegaClient& mc)
14354 {
14355 auto localname = LocalPath::fromPath(n->displayname(), *mc.fsaccess);
14356 if (mc.fsaccess->getextension(localname, ext, 8)) // plenty of buffer space left to append a '.'
14357 {
14358 strcat(ext, ".");
14359 return true;
14360 }
14361 return false;
14362 }
14363
14364 } // end namespace action_bucket_compare
14365
14366
nodeIsMedia(const Node * n,bool * isphoto,bool * isvideo) const14367 bool MegaClient::nodeIsMedia(const Node* n, bool* isphoto, bool* isvideo) const
14368 {
14369 char ext[12];
14370 if (n->type == FILENODE && action_bucket_compare::getExtensionDotted(n, ext, *this))
14371 {
14372 bool a = action_bucket_compare::nodeIsPhoto(n, ext);
14373 if (isphoto)
14374 {
14375 *isphoto = a;
14376 }
14377 if (a && !isvideo)
14378 {
14379 return true;
14380 }
14381 bool b = action_bucket_compare::nodeIsVideo(n, ext, *this);
14382 if (isvideo)
14383 {
14384 *isvideo = b;
14385 }
14386 return a || b;
14387 }
14388 return false;
14389 }
14390
getRecentActions(unsigned maxcount,m_time_t since)14391 recentactions_vector MegaClient::getRecentActions(unsigned maxcount, m_time_t since)
14392 {
14393 recentactions_vector rav;
14394 node_vector v = getRecentNodes(maxcount, since, false);
14395
14396 for (node_vector::iterator i = v.begin(); i != v.end(); )
14397 {
14398 // find the oldest node, maximum 6h
14399 node_vector::iterator bucketend = i + 1;
14400 while (bucketend != v.end() && (*bucketend)->ctime > (*i)->ctime - 6 * 3600)
14401 {
14402 ++bucketend;
14403 }
14404
14405 // sort the defined bucket by owner, parent folder, added/updated and ismedia
14406 std::sort(i, bucketend, [this](const Node* n1, const Node* n2) { return action_bucket_compare::compare(n1, n2, this); });
14407
14408 // split the 6h-bucket in different buckets according to their content
14409 for (node_vector::iterator j = i; j != bucketend; ++j)
14410 {
14411 if (i == j || action_bucket_compare::compare(*i, *j, this))
14412 {
14413 // add a new bucket
14414 recentaction ra;
14415 ra.time = (*j)->ctime;
14416 ra.user = (*j)->owner;
14417 ra.parent = (*j)->parent ? (*j)->parent->nodehandle : UNDEF;
14418 ra.updated = !(*j)->children.empty(); // children of files represent previous versions
14419 ra.media = nodeIsMedia(*j, nullptr, nullptr);
14420 rav.push_back(ra);
14421 }
14422 // add the node to the bucket
14423 rav.back().nodes.push_back(*j);
14424 i = j;
14425 }
14426 i = bucketend;
14427 }
14428 // sort nodes inside each bucket
14429 for (recentactions_vector::iterator i = rav.begin(); i != rav.end(); ++i)
14430 {
14431 // for the bucket vector, most recent (larger ctime) first
14432 std::sort(i->nodes.begin(), i->nodes.end(), nodes_ctime_greater);
14433 i->time = i->nodes.front()->ctime;
14434 }
14435 // sort buckets in the vector
14436 std::sort(rav.begin(), rav.end(), action_bucket_compare::comparetime);
14437 return rav;
14438 }
14439
14440
nodesbyoriginalfingerprint(const char * originalfingerprint,Node * parent,node_vector * nv)14441 void MegaClient::nodesbyoriginalfingerprint(const char* originalfingerprint, Node* parent, node_vector *nv)
14442 {
14443 if (parent)
14444 {
14445 for (node_list::iterator i = parent->children.begin(); i != parent->children.end(); ++i)
14446 {
14447 if ((*i)->type == FILENODE)
14448 {
14449 attr_map::const_iterator a = (*i)->attrs.map.find(MAKENAMEID2('c', '0'));
14450 if (a != (*i)->attrs.map.end() && !a->second.compare(originalfingerprint))
14451 {
14452 nv->push_back(*i);
14453 }
14454 }
14455 else
14456 {
14457 nodesbyoriginalfingerprint(originalfingerprint, *i, nv);
14458 }
14459 }
14460 }
14461 else
14462 {
14463 for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
14464 {
14465 if (i->second->type == FILENODE)
14466 {
14467 attr_map::const_iterator a = i->second->attrs.map.find(MAKENAMEID2('c', '0'));
14468 if (a != i->second->attrs.map.end() && !a->second.compare(originalfingerprint))
14469 {
14470 nv->push_back(i->second);
14471 }
14472 }
14473 }
14474 }
14475 }
14476
14477 // a chunk transfer request failed: record failed protocol & host
setchunkfailed(string * url)14478 void MegaClient::setchunkfailed(string* url)
14479 {
14480 if (!chunkfailed && url->size() > 19)
14481 {
14482 LOG_debug << "Adding badhost report for URL " << *url;
14483 chunkfailed = true;
14484 httpio->success = false;
14485
14486 // record protocol and hostname
14487 if (badhosts.size())
14488 {
14489 badhosts.append(",");
14490 }
14491
14492 const char* ptr = url->c_str()+4;
14493
14494 if (*ptr == 's')
14495 {
14496 badhosts.append("S");
14497 ptr++;
14498 }
14499
14500 badhosts.append(ptr+6,7);
14501 btbadhost.reset();
14502 }
14503 }
14504
toggledebug()14505 bool MegaClient::toggledebug()
14506 {
14507 SimpleLogger::setLogLevel((SimpleLogger::logCurrentLevel >= logDebug) ? logWarning : logDebug);
14508 return debugstate();
14509 }
14510
debugstate()14511 bool MegaClient::debugstate()
14512 {
14513 return SimpleLogger::logCurrentLevel >= logDebug;
14514 }
14515
reportevent(const char * event,const char * details)14516 void MegaClient::reportevent(const char* event, const char* details)
14517 {
14518 LOG_err << "SERVER REPORT: " << event << " DETAILS: " << details;
14519 reqs.add(new CommandReportEvent(this, event, details));
14520 }
14521
reportevent(const char * event,const char * details,int tag)14522 void MegaClient::reportevent(const char* event, const char* details, int tag)
14523 {
14524 int creqtag = reqtag;
14525 reqtag = tag;
14526 reportevent(event, details);
14527 reqtag = creqtag;
14528 }
14529
setmaxdownloadspeed(m_off_t bpslimit)14530 bool MegaClient::setmaxdownloadspeed(m_off_t bpslimit)
14531 {
14532 return httpio->setmaxdownloadspeed(bpslimit >= 0 ? bpslimit : 0);
14533 }
14534
setmaxuploadspeed(m_off_t bpslimit)14535 bool MegaClient::setmaxuploadspeed(m_off_t bpslimit)
14536 {
14537 return httpio->setmaxuploadspeed(bpslimit >= 0 ? bpslimit : 0);
14538 }
14539
getmaxdownloadspeed()14540 m_off_t MegaClient::getmaxdownloadspeed()
14541 {
14542 return httpio->getmaxdownloadspeed();
14543 }
14544
getmaxuploadspeed()14545 m_off_t MegaClient::getmaxuploadspeed()
14546 {
14547 return httpio->getmaxuploadspeed();
14548 }
14549
getovhandle(Node * parent,string * name)14550 handle MegaClient::getovhandle(Node *parent, string *name)
14551 {
14552 handle ovhandle = UNDEF;
14553 if (parent && name)
14554 {
14555 Node *ovn = childnodebyname(parent, name->c_str(), true);
14556 if (ovn)
14557 {
14558 ovhandle = ovn->nodehandle;
14559 }
14560 }
14561 return ovhandle;
14562 }
14563
userfeedbackstore(const char * message)14564 void MegaClient::userfeedbackstore(const char *message)
14565 {
14566 string type = "feedback.";
14567 type.append(&(appkey[4]));
14568 type.append(".");
14569
14570 string base64userAgent;
14571 base64userAgent.resize(useragent.size() * 4 / 3 + 4);
14572 Base64::btoa((byte *)useragent.data(), int(useragent.size()), (char *)base64userAgent.data());
14573 type.append(base64userAgent);
14574
14575 reqs.add(new CommandUserFeedbackStore(this, type.c_str(), message, NULL));
14576 }
14577
sendevent(int event,const char * desc)14578 void MegaClient::sendevent(int event, const char *desc)
14579 {
14580 LOG_warn << clientname << "Event " << event << ": " << desc;
14581 reqs.add(new CommandSendEvent(this, event, desc));
14582 }
14583
sendevent(int event,const char * message,int tag)14584 void MegaClient::sendevent(int event, const char *message, int tag)
14585 {
14586 int creqtag = reqtag;
14587 reqtag = tag;
14588 sendevent(event, message);
14589 reqtag = creqtag;
14590 }
14591
supportticket(const char * message,int type)14592 void MegaClient::supportticket(const char *message, int type)
14593 {
14594 reqs.add(new CommandSupportTicket(this, message, type));
14595 }
14596
cleanrubbishbin()14597 void MegaClient::cleanrubbishbin()
14598 {
14599 reqs.add(new CommandCleanRubbishBin(this));
14600 }
14601
14602 #ifdef ENABLE_CHAT
createChat(bool group,bool publicchat,const userpriv_vector * userpriv,const string_map * userkeymap,const char * title)14603 void MegaClient::createChat(bool group, bool publicchat, const userpriv_vector *userpriv, const string_map *userkeymap, const char *title)
14604 {
14605 reqs.add(new CommandChatCreate(this, group, publicchat, userpriv, userkeymap, title));
14606 }
14607
inviteToChat(handle chatid,handle uh,int priv,const char * unifiedkey,const char * title)14608 void MegaClient::inviteToChat(handle chatid, handle uh, int priv, const char *unifiedkey, const char *title)
14609 {
14610 reqs.add(new CommandChatInvite(this, chatid, uh, (privilege_t) priv, unifiedkey, title));
14611 }
14612
removeFromChat(handle chatid,handle uh)14613 void MegaClient::removeFromChat(handle chatid, handle uh)
14614 {
14615 reqs.add(new CommandChatRemove(this, chatid, uh));
14616 }
14617
getUrlChat(handle chatid)14618 void MegaClient::getUrlChat(handle chatid)
14619 {
14620 reqs.add(new CommandChatURL(this, chatid));
14621 }
14622
readuserpriv(JSON * j)14623 userpriv_vector *MegaClient::readuserpriv(JSON *j)
14624 {
14625 userpriv_vector *userpriv = NULL;
14626
14627 if (j->enterarray())
14628 {
14629 while(j->enterobject())
14630 {
14631 handle uh = UNDEF;
14632 privilege_t priv = PRIV_UNKNOWN;
14633
14634 bool readingUsers = true;
14635 while(readingUsers)
14636 {
14637 switch (j->getnameid())
14638 {
14639 case 'u':
14640 uh = j->gethandle(MegaClient::USERHANDLE);
14641 break;
14642
14643 case 'p':
14644 priv = (privilege_t) j->getint();
14645 break;
14646
14647 case EOO:
14648 if(uh == UNDEF || priv == PRIV_UNKNOWN)
14649 {
14650 delete userpriv;
14651 return NULL;
14652 }
14653
14654 if (!userpriv)
14655 {
14656 userpriv = new userpriv_vector;
14657 }
14658
14659 userpriv->push_back(userpriv_pair(uh, priv));
14660 readingUsers = false;
14661 break;
14662
14663 default:
14664 if (!j->storeobject())
14665 {
14666 delete userpriv;
14667 return NULL;
14668 }
14669 break;
14670 }
14671 }
14672 j->leaveobject();
14673 }
14674 j->leavearray();
14675 }
14676
14677 return userpriv;
14678 }
14679
grantAccessInChat(handle chatid,handle h,const char * uid)14680 void MegaClient::grantAccessInChat(handle chatid, handle h, const char *uid)
14681 {
14682 reqs.add(new CommandChatGrantAccess(this, chatid, h, uid));
14683 }
14684
removeAccessInChat(handle chatid,handle h,const char * uid)14685 void MegaClient::removeAccessInChat(handle chatid, handle h, const char *uid)
14686 {
14687 reqs.add(new CommandChatRemoveAccess(this, chatid, h, uid));
14688 }
14689
updateChatPermissions(handle chatid,handle uh,int priv)14690 void MegaClient::updateChatPermissions(handle chatid, handle uh, int priv)
14691 {
14692 reqs.add(new CommandChatUpdatePermissions(this, chatid, uh, (privilege_t) priv));
14693 }
14694
truncateChat(handle chatid,handle messageid)14695 void MegaClient::truncateChat(handle chatid, handle messageid)
14696 {
14697 reqs.add(new CommandChatTruncate(this, chatid, messageid));
14698 }
14699
setChatTitle(handle chatid,const char * title)14700 void MegaClient::setChatTitle(handle chatid, const char *title)
14701 {
14702 reqs.add(new CommandChatSetTitle(this, chatid, title));
14703 }
14704
getChatPresenceUrl()14705 void MegaClient::getChatPresenceUrl()
14706 {
14707 reqs.add(new CommandChatPresenceURL(this));
14708 }
14709
registerPushNotification(int deviceType,const char * token)14710 void MegaClient::registerPushNotification(int deviceType, const char *token)
14711 {
14712 reqs.add(new CommandRegisterPushNotification(this, deviceType, token));
14713 }
14714
archiveChat(handle chatid,bool archived)14715 void MegaClient::archiveChat(handle chatid, bool archived)
14716 {
14717 reqs.add(new CommandArchiveChat(this, chatid, archived));
14718 }
14719
richlinkrequest(const char * url)14720 void MegaClient::richlinkrequest(const char *url)
14721 {
14722 reqs.add(new CommandRichLink(this, url));
14723 }
14724
chatlink(handle chatid,bool del,bool createifmissing)14725 void MegaClient::chatlink(handle chatid, bool del, bool createifmissing)
14726 {
14727 reqs.add(new CommandChatLink(this, chatid, del, createifmissing));
14728 }
14729
chatlinkurl(handle publichandle)14730 void MegaClient::chatlinkurl(handle publichandle)
14731 {
14732 reqs.add(new CommandChatLinkURL(this, publichandle));
14733 }
14734
chatlinkclose(handle chatid,const char * title)14735 void MegaClient::chatlinkclose(handle chatid, const char *title)
14736 {
14737 reqs.add(new CommandChatLinkClose(this, chatid, title));
14738 }
14739
chatlinkjoin(handle publichandle,const char * unifiedkey)14740 void MegaClient::chatlinkjoin(handle publichandle, const char *unifiedkey)
14741 {
14742 reqs.add(new CommandChatLinkJoin(this, publichandle, unifiedkey));
14743 }
14744
setchatretentiontime(handle chatid,int period)14745 void MegaClient::setchatretentiontime(handle chatid, int period)
14746 {
14747 reqs.add(new CommandSetChatRetentionTime(this, chatid, period));
14748 }
14749 #endif
14750
getaccountachievements(AchievementsDetails * details)14751 void MegaClient::getaccountachievements(AchievementsDetails *details)
14752 {
14753 reqs.add(new CommandGetMegaAchievements(this, details));
14754 }
14755
getmegaachievements(AchievementsDetails * details)14756 void MegaClient::getmegaachievements(AchievementsDetails *details)
14757 {
14758 reqs.add(new CommandGetMegaAchievements(this, details, false));
14759 }
14760
getwelcomepdf()14761 void MegaClient::getwelcomepdf()
14762 {
14763 reqs.add(new CommandGetWelcomePDF(this));
14764 }
14765
14766 #ifdef MEGA_MEASURE_CODE
report(bool reset,HttpIO * httpio,Waiter * waiter,const RequestDispatcher & reqs)14767 std::string MegaClient::PerformanceStats::report(bool reset, HttpIO* httpio, Waiter* waiter, const RequestDispatcher& reqs)
14768 {
14769 std::ostringstream s;
14770 s << prepareWait.report(reset) << "\n"
14771 << doWait.report(reset) << "\n"
14772 << checkEvents.report(reset) << "\n"
14773 << execFunction.report(reset) << "\n"
14774 << transferslotDoio.report(reset) << "\n"
14775 << execdirectreads.report(reset) << "\n"
14776 << transferComplete.report(reset) << "\n"
14777 << dispatchTransfers.report(reset) << "\n"
14778 << applyKeys.report(reset) << "\n"
14779 << scProcessingTime.report(reset) << "\n"
14780 << csResponseProcessingTime.report(reset) << "\n"
14781 << " cs Request waiting time: " << csRequestWaitTime.report(reset) << "\n"
14782 << " cs requests sent/received: " << reqs.csRequestsSent << "/" << reqs.csRequestsCompleted << " batches: " << reqs.csBatchesSent << "/" << reqs.csBatchesReceived << "\n"
14783 << " transfers active time: " << transfersActiveTime.report(reset) << "\n"
14784 << " transfer starts/finishes: " << transferStarts << " " << transferFinishes << "\n"
14785 << " transfer temperror/fails: " << transferTempErrors << " " << transferFails << "\n"
14786 << " nowait reason: immedate: " << prepwaitImmediate << " zero: " << prepwaitZero << " httpio: " << prepwaitHttpio << " fsaccess: " << prepwaitFsaccess << " nonzero waits: " << nonzeroWait << "\n";
14787 #ifdef USE_CURL
14788 if (auto curlhttpio = dynamic_cast<CurlHttpIO*>(httpio))
14789 {
14790 s << curlhttpio->countCurlHttpIOAddevents.report(reset) << "\n"
14791 << curlhttpio->countAddAresEventsCode.report(reset) << "\n"
14792 << curlhttpio->countAddCurlEventsCode.report(reset) << "\n"
14793 << curlhttpio->countProcessAresEventsCode.report(reset) << "\n"
14794 << curlhttpio->countProcessCurlEventsCode.report(reset) << "\n";
14795 }
14796 #endif
14797 #ifdef WIN32
14798 s << " waiter nonzero timeout: " << static_cast<WinWaiter*>(waiter)->performanceStats.waitTimedoutNonzero
14799 << " zero timeout: " << static_cast<WinWaiter*>(waiter)->performanceStats.waitTimedoutZero
14800 << " io trigger: " << static_cast<WinWaiter*>(waiter)->performanceStats.waitIOCompleted
14801 << " event trigger: " << static_cast<WinWaiter*>(waiter)->performanceStats.waitSignalled << "\n";
14802 #endif
14803 if (reset)
14804 {
14805 transferStarts = transferFinishes = transferTempErrors = transferFails = 0;
14806 prepwaitImmediate = prepwaitZero = prepwaitHttpio = prepwaitFsaccess = nonzeroWait = 0;
14807 }
14808 return s.str();
14809 }
14810 #endif
14811
FetchNodesStats()14812 FetchNodesStats::FetchNodesStats()
14813 {
14814 init();
14815 }
14816
init()14817 void FetchNodesStats::init()
14818 {
14819 mode = MODE_NONE;
14820 type = TYPE_NONE;
14821 cache = API_NONE;
14822 nodesCached = 0;
14823 nodesCurrent = 0;
14824 actionPackets = 0;
14825
14826 eAgainCount = 0;
14827 e500Count = 0;
14828 eOthersCount = 0;
14829
14830 startTime = Waiter::ds;
14831 timeToFirstByte = NEVER;
14832 timeToLastByte = NEVER;
14833 timeToCached = NEVER;
14834 timeToResult = NEVER;
14835 timeToSyncsResumed = NEVER;
14836 timeToCurrent = NEVER;
14837 timeToTransfersResumed = NEVER;
14838 }
14839
toJsonArray(string * json)14840 void FetchNodesStats::toJsonArray(string *json)
14841 {
14842 if (!json)
14843 {
14844 return;
14845 }
14846
14847 ostringstream oss;
14848 oss << "[" << mode << "," << type << ","
14849 << nodesCached << "," << nodesCurrent << "," << actionPackets << ","
14850 << eAgainCount << "," << e500Count << "," << eOthersCount << ","
14851 << timeToFirstByte << "," << timeToLastByte << ","
14852 << timeToCached << "," << timeToResult << ","
14853 << timeToSyncsResumed << "," << timeToCurrent << ","
14854 << timeToTransfersResumed << "," << cache << "]";
14855 json->append(oss.str());
14856 }
14857
14858 } // namespace
14859