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(&lt, 0, sizeof(struct tm));
912         memset(&ut, 0, sizeof(struct tm));
913         memset(&it, 0, sizeof(struct tm));
914         m_localtime(rawtime, &lt);
915         m_gmtime(rawtime, &ut);
916         if (memcmp(&ut, &it, sizeof(struct tm)) && memcmp(&lt, &it, sizeof(struct tm)))
917         {
918             m_time_t local_time = m_mktime(&lt);
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