1 /**
2  * @file commands.cpp
3  * @brief Implementation of various commands
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/types.h"
23 #include "mega/command.h"
24 #include "mega/megaapp.h"
25 #include "mega/fileattributefetch.h"
26 #include "mega/base64.h"
27 #include "mega/transferslot.h"
28 #include "mega/transfer.h"
29 #include "mega/utils.h"
30 #include "mega/user.h"
31 #include "mega.h"
32 #include "mega/mediafileattribute.h"
33 
34 namespace mega {
HttpReqCommandPutFA(MegaClient * client,handle cth,fatype ctype,std::unique_ptr<string> cdata,bool checkAccess)35 HttpReqCommandPutFA::HttpReqCommandPutFA(MegaClient* client, handle cth, fatype ctype, std::unique_ptr<string> cdata, bool checkAccess)
36     : data(move(cdata))
37 {
38     cmd("ufa");
39     arg("s", data->size());
40 
41     if (checkAccess)
42     {
43         arg("h", (byte*)&cth, MegaClient::NODEHANDLE);
44     }
45 
46     progressreported = 0;
47     persistent = true;  // object will be recycled either for retry or for
48                         // posting to the file attribute server
49 
50     if (client->usehttps)
51     {
52         arg("ssl", 2);
53     }
54 
55     th = cth;
56     type = ctype;
57 
58     binary = true;
59 
60     tag = client->reqtag;
61 }
62 
procresult()63 void HttpReqCommandPutFA::procresult()
64 {
65     client->looprequested = true;
66 
67     Error e;
68     if (checkError(e, client->json))
69     {
70         if (e == API_EAGAIN || e == API_ERATELIMIT)
71         {
72             status = REQ_FAILURE;
73         }
74         else
75         {
76             if (e == API_EACCESS)
77             {
78                 // create a custom attribute indicating thumbnail can't be restored from this account
79                 Node *n = client->nodebyhandle(th);
80 
81                 char me64[12];
82                 Base64::btoa((const byte*)&client->me, MegaClient::USERHANDLE, me64);
83 
84                 if (n && client->checkaccess(n, FULL) &&
85                         (n->attrs.map.find('f') == n->attrs.map.end() || n->attrs.map['f'] != me64) )
86                 {
87                     LOG_debug << "Restoration of file attributes is not allowed for current user (" << me64 << ").";
88                     n->attrs.map['f'] = me64;
89 
90                     int creqtag = client->reqtag;
91                     client->reqtag = 0;
92                     client->setattr(n);
93                     client->reqtag = creqtag;
94                 }
95             }
96 
97             status = REQ_SUCCESS;
98             return client->app->putfa_result(th, type, e);
99         }
100     }
101     else
102     {
103         const char* p = NULL;
104 
105         for (;;)
106         {
107             switch (client->json.getnameid())
108             {
109                 case 'p':
110                     p = client->json.getvalue();
111                     break;
112 
113                 case EOO:
114                     if (!p)
115                     {
116                         status = REQ_FAILURE;
117                     }
118                     else
119                     {
120                         LOG_debug << "Sending file attribute data";
121                         Node::copystring(&posturl, p);
122                         progressreported = 0;
123                         HttpReq::type = REQ_BINARY;
124                         post(client, data->data(), unsigned(data->size()));
125                     }
126                     return;
127 
128                 default:
129                     if (!client->json.storeobject())
130                     {
131                         status = REQ_SUCCESS;
132                         return client->app->putfa_result(th, type, API_EINTERNAL);
133                     }
134             }
135         }
136     }
137 }
138 
transferred(MegaClient * client)139 m_off_t HttpReqCommandPutFA::transferred(MegaClient *client)
140 {
141     if (httpiohandle)
142     {
143         return client->httpio->postpos(httpiohandle);
144     }
145 
146     return 0;
147 }
148 
CommandGetFA(MegaClient * client,int p,handle fahref)149 CommandGetFA::CommandGetFA(MegaClient *client, int p, handle fahref)
150 {
151     part = p;
152 
153     cmd("ufa");
154     arg("fah", (byte*)&fahref, sizeof fahref);
155 
156     if (client->usehttps)
157     {
158         arg("ssl", 2);
159     }
160 
161     arg("r", 1);
162 }
163 
procresult()164 void CommandGetFA::procresult()
165 {
166     fafc_map::iterator it = client->fafcs.find(part);
167     client->looprequested = true;
168 
169     Error e;
170     if (checkError(e, client->json))
171     {
172         if (it != client->fafcs.end())
173         {
174             faf_map::iterator fafsit;
175             for (fafsit = it->second->fafs[0].begin(); fafsit != it->second->fafs[0].end(); )
176             {
177                 // move from fresh to pending
178                 it->second->fafs[1][fafsit->first] = fafsit->second;
179                 it->second->fafs[0].erase(fafsit++);
180             }
181 
182             it->second->e = e;
183             it->second->req.status = REQ_FAILURE;
184         }
185 
186         return;
187     }
188 
189     const char* p = NULL;
190 
191     for (;;)
192     {
193         switch (client->json.getnameid())
194         {
195             case 'p':
196                 p = client->json.getvalue();
197                 break;
198 
199             case EOO:
200                 if (it != client->fafcs.end())
201                 {
202                     if (p)
203                     {
204                         Node::copystring(&it->second->posturl, p);
205                         it->second->urltime = Waiter::ds;
206                         it->second->dispatch();
207                     }
208                     else
209                     {
210                         faf_map::iterator fafsit;
211                         for (fafsit = it->second->fafs[0].begin(); fafsit != it->second->fafs[0].end(); )
212                         {
213                             // move from fresh to pending
214                             it->second->fafs[1][fafsit->first] = fafsit->second;
215                             it->second->fafs[0].erase(fafsit++);
216                         }
217 
218                         it->second->e = API_EINTERNAL;
219                         it->second->req.status = REQ_FAILURE;
220                     }
221                 }
222 
223                 return;
224 
225             default:
226                 if (!client->json.storeobject())
227                 {
228                     faf_map::iterator fafsit;
229                     for (fafsit = it->second->fafs[0].begin(); fafsit != it->second->fafs[0].end(); )
230                     {
231                         // move from fresh to pending
232                         it->second->fafs[1][fafsit->first] = fafsit->second;
233                         it->second->fafs[0].erase(fafsit++);
234                     }
235 
236                     it->second->e = API_EINTERNAL;
237                     it->second->req.status = REQ_FAILURE;
238                     return;
239                 }
240         }
241     }
242 }
243 
CommandAttachFA(MegaClient * client,handle nh,fatype t,handle ah,int ctag)244 CommandAttachFA::CommandAttachFA(MegaClient *client, handle nh, fatype t, handle ah, int ctag)
245 {
246     cmd("pfa");
247     notself(client);
248 
249     arg("n", (byte*)&nh, MegaClient::NODEHANDLE);
250 
251     char buf[64];
252 
253     sprintf(buf, "%u*", t);
254     Base64::btoa((byte*)&ah, sizeof(ah), strchr(buf + 2, 0));
255     arg("fa", buf);
256 
257     h = nh;
258     type = t;
259     tag = ctag;
260 }
261 
CommandAttachFA(MegaClient * client,handle nh,fatype t,const std::string & encryptedAttributes,int ctag)262 CommandAttachFA::CommandAttachFA(MegaClient *client, handle nh, fatype t, const std::string& encryptedAttributes, int ctag)
263 {
264     cmd("pfa");
265     notself(client);
266 
267     arg("n", (byte*)&nh, MegaClient::NODEHANDLE);
268 
269     arg("fa", encryptedAttributes.c_str());
270 
271     h = nh;
272     type = t;
273     tag = ctag;
274 }
275 
procresult()276 void CommandAttachFA::procresult()
277 {
278     Error e;
279     if (!checkError(e, client->json))
280     {
281          string fa;
282          if (client->json.storeobject(&fa))
283          {
284              Node* n = client->nodebyhandle(h);
285              if (n)
286              {
287                 n->fileattrstring = fa;
288                 n->changed.fileattrstring = true;
289                 client->notifynode(n);
290              }
291              return client->app->putfa_result(h, type, fa.c_str());
292          }
293 
294          e = API_EINTERNAL;
295     }
296 
297     client->app->putfa_result(h, type, e);
298 }
299 
300 // request upload target URL
CommandPutFile(MegaClient * client,TransferSlot * ctslot,int ms)301 CommandPutFile::CommandPutFile(MegaClient* client, TransferSlot* ctslot, int ms)
302 {
303     tslot = ctslot;
304 
305     cmd("u");
306 
307     if (client->usehttps)
308     {
309         arg("ssl", 2);
310     }
311 
312     arg("v", 2);
313     arg("s", tslot->fa->size);
314     arg("ms", ms);
315 
316     // send minimum set of different tree's roots for API to check overquota
317     set<handle> targetRoots;
318     bool begun = false;
319     for (auto &file : tslot->transfer->files)
320     {
321         if (!ISUNDEF(file->h))
322         {
323             Node *node = client->nodebyhandle(file->h);
324             if (node)
325             {
326                 handle rootnode = client->getrootnode(node)->nodehandle;
327                 if (targetRoots.find(rootnode) != targetRoots.end())
328                 {
329                     continue;
330                 }
331 
332                 targetRoots.insert(rootnode);
333             }
334             if (!begun)
335             {
336                 beginarray("t");
337                 begun = true;
338             }
339 
340             element((byte*)&file->h, MegaClient::NODEHANDLE);
341         }
342     }
343 
344     if (begun)
345     {
346         endarray();
347     }
348     else
349     {
350         // Target user goes alone, not inside an array. Note: we are skipping this if a)more than two b)the array had been created for node handles
351         for (auto &file : tslot->transfer->files)
352         {
353             if (ISUNDEF(file->h) && file->targetuser.size())
354             {
355                 arg("t", file->targetuser.c_str());
356                 break;
357             }
358         }
359     }
360 }
361 
cancel()362 void CommandPutFile::cancel()
363 {
364     Command::cancel();
365     tslot = NULL;
366 }
367 
368 // set up file transfer with returned target URL
procresult()369 void CommandPutFile::procresult()
370 {
371     if (tslot)
372     {
373         tslot->pendingcmd = NULL;
374     }
375     else
376     {
377         canceled = true;
378     }
379 
380     Error e;
381     if (checkError(e, client->json))
382     {
383         if (!canceled)
384         {
385             tslot->transfer->failed(e, *client->mTctableRequestCommitter);
386         }
387 
388         return;
389     }
390 
391     std::vector<std::string> tempurls;
392     for (;;)
393     {
394         switch (client->json.getnameid())
395         {
396             case 'p':
397                 tempurls.push_back("");
398                 client->json.storeobject(canceled ? NULL : &tempurls.back());
399                 break;
400 
401             case EOO:
402                 if (canceled) return;
403 
404                 if (tempurls.size() == 1)
405                 {
406                     tslot->transfer->tempurls = tempurls;
407                     tslot->transferbuf.setIsRaid(tslot->transfer, tempurls, tslot->transfer->pos, tslot->maxRequestSize);
408                     tslot->starttime = tslot->lastdata = client->waiter->ds;
409                     return tslot->progress();
410                 }
411                 else
412                 {
413                     return tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
414                 }
415 
416             default:
417                 if (!client->json.storeobject())
418                 {
419                     if (!canceled)
420                     {
421                         tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
422                     }
423 
424                     return;
425                 }
426         }
427     }
428 }
429 
430 // request upload target URL for application to upload photo to using eg. iOS background upload feature
CommandPutFileBackgroundURL(m_off_t size,int putmbpscap,int ctag)431 CommandPutFileBackgroundURL::CommandPutFileBackgroundURL(m_off_t size, int putmbpscap, int ctag)
432 {
433     cmd("u");
434     arg("ssl", 2);   // always SSL for background uploads
435     arg("v", 2);
436     arg("s", size);
437     arg("ms", putmbpscap);
438 
439     tag = ctag;
440 }
441 
442 // set up file transfer with returned target URL
procresult()443 void CommandPutFileBackgroundURL::procresult()
444 {
445     string url;
446     Error e;
447     if (checkError(e, client->json))
448     {
449         if (!canceled)
450         {
451             client->app->backgrounduploadurl_result(e, NULL);
452         }
453         return;
454     }
455 
456     for (;;)
457     {
458         switch (client->json.getnameid())
459         {
460             case 'p':
461                 client->json.storeobject(canceled ? NULL : &url);
462                 break;
463 
464             case EOO:
465                 if (canceled) return;
466 
467                 client->app->backgrounduploadurl_result(API_OK, &url);
468                 return;
469 
470             default:
471                 if (!client->json.storeobject())
472                 {
473                     if (!canceled)
474                     {
475                         client->app->backgrounduploadurl_result(API_EINTERNAL, NULL);
476                     }
477                     return;
478                 }
479         }
480     }
481 }
482 
483 // request temporary source URL for DirectRead
CommandDirectRead(MegaClient * client,DirectReadNode * cdrn)484 CommandDirectRead::CommandDirectRead(MegaClient *client, DirectReadNode* cdrn)
485 {
486     drn = cdrn;
487 
488     cmd("g");
489     arg(drn->p ? "n" : "p", (byte*)&drn->h, MegaClient::NODEHANDLE);
490     arg("g", 1);
491     arg("v", 2);  // version 2: server can supply details for cloudraid files
492 
493     if (drn->privateauth.size())
494     {
495         arg("esid", drn->privateauth.c_str());
496     }
497 
498     if (drn->publicauth.size())
499     {
500         arg("en", drn->publicauth.c_str());
501     }
502 
503     if (drn->chatauth.size())
504     {
505         arg("cauth", drn->chatauth.c_str());
506     }
507 
508     if (client->usehttps)
509     {
510         arg("ssl", 2);
511     }
512 }
513 
cancel()514 void CommandDirectRead::cancel()
515 {
516     Command::cancel();
517     drn = NULL;
518 }
519 
procresult()520 void CommandDirectRead::procresult()
521 {
522     if (drn)
523     {
524         drn->pendingcmd = NULL;
525     }
526 
527     Error e;
528     if (checkError(e, client->json))
529     {
530         if (!canceled && drn)
531         {
532             return drn->cmdresult(e);
533         }
534     }
535     else
536     {
537         dstime tl = 0;
538         std::vector<std::string> tempurls;
539 
540         for (;;)
541         {
542             switch (client->json.getnameid())
543             {
544                 case 'g':
545                     if (client->json.enterarray())   // now that we are requesting v2, the reply will be an array of 6 URLs for a raid download, or a single URL for the original direct download
546                     {
547                         for (;;)
548                         {
549                             std::string tu;
550                             if (!client->json.storeobject(&tu))
551                             {
552                                 break;
553                             }
554                             tempurls.push_back(tu);
555                         }
556                         client->json.leavearray();
557                     }
558                     else
559                     {
560                         std::string tu;
561                         if (client->json.storeobject(&tu))
562                         {
563                             tempurls.push_back(tu);
564                         }
565                     }
566                     if (tempurls.size() == 1 || tempurls.size() == RAIDPARTS)
567                     {
568                         drn->tempurls.swap(tempurls);
569                         e = API_OK;
570                     }
571                     else
572                     {
573                         e = API_EINCOMPLETE;
574                     }
575                     break;
576 
577                 case 's':
578                     if (drn)
579                     {
580                         drn->size = client->json.getint();
581                     }
582                     break;
583 
584                 case 'd':
585                     e = API_EBLOCKED;
586                     break;
587 
588                 case 'e':
589                     e = (error)client->json.getint();
590                     break;
591 
592                 case MAKENAMEID2('t', 'l'):
593                     tl = dstime(client->json.getint());
594                     break;
595 
596                 case EOO:
597                     if (!canceled && drn)
598                     {
599                         if (e == API_EOVERQUOTA && !tl)
600                         {
601                             // default retry interval
602                             tl = MegaClient::DEFAULT_BW_OVERQUOTA_BACKOFF_SECS;
603                         }
604 
605                         drn->cmdresult(e, e == API_EOVERQUOTA ? tl * 10 : 0);
606                     }
607 
608                     return;
609 
610                 default:
611                     if (!client->json.storeobject())
612                     {
613                         if (!canceled && drn)
614                         {
615                             drn->cmdresult(e);
616                         }
617 
618                         return;
619                     }
620             }
621         }
622     }
623 }
624 
625 // request temporary source URL for full-file access (p == private node)
CommandGetFile(MegaClient * client,TransferSlot * ctslot,const byte * key,handle h,bool p,const char * privateauth,const char * publicauth,const char * chatauth)626 CommandGetFile::CommandGetFile(MegaClient *client, TransferSlot* ctslot, const byte* key, handle h, bool p, const char *privateauth, const char *publicauth, const char *chatauth)
627 {
628     cmd("g");
629     arg(p ? "n" : "p", (byte*)&h, MegaClient::NODEHANDLE);
630     arg("g", 1);
631     arg("v", 2);  // version 2: server can supply details for cloudraid files
632 
633     if (client->usehttps)
634     {
635         arg("ssl", 2);
636     }
637 
638     if (privateauth)
639     {
640         arg("esid", privateauth);
641     }
642 
643     if (publicauth)
644     {
645         arg("en", publicauth);
646     }
647 
648     if (chatauth)
649     {
650         arg("cauth", chatauth);
651     }
652 
653     tslot = ctslot;
654     priv = p;
655     ph = h;
656 
657     if (!tslot)
658     {
659         memcpy(filekey, key, FILENODEKEYLENGTH);
660     }
661 }
662 
cancel()663 void CommandGetFile::cancel()
664 {
665     Command::cancel();
666     tslot = NULL;
667 }
668 
669 // process file credentials
procresult()670 void CommandGetFile::procresult()
671 {
672     if (tslot)
673     {
674         tslot->pendingcmd = NULL;
675     }
676 
677     Error e;
678     if (checkError(e, client->json))
679     {
680         if (canceled)
681         {
682             return;
683         }
684 
685         if (tslot)
686         {
687             return tslot->transfer->failed(e, *client->mTctableRequestCommitter);
688         }
689 
690         return client->app->checkfile_result(ph, e);
691     }
692 
693     const char* at = NULL;
694     m_off_t s = -1;
695     dstime tl = 0;
696     int d = 0;
697     byte* buf;
698     m_time_t ts = 0, tm = 0;
699 
700     // credentials relevant to a non-TransferSlot scenario (node query)
701     string fileattrstring;
702     string filenamestring;
703     string filefingerprint;
704     std::vector<string> tempurls;
705 
706     for (;;)
707     {
708         switch (client->json.getnameid())
709         {
710             case 'g':
711                 if (client->json.enterarray())   // now that we are requesting v2, the reply will be an array of 6 URLs for a raid download, or a single URL for the original direct download
712                 {
713                     for (;;)
714                     {
715                         std::string tu;
716                         if (!client->json.storeobject(&tu))
717                         {
718                             break;
719                         }
720                         tempurls.push_back(tu);
721                     }
722                     client->json.leavearray();
723                 }
724                 else
725                 {
726                     std::string tu;
727                     if (client->json.storeobject(&tu))
728                     {
729                         tempurls.push_back(tu);
730                     }
731                 }
732                 e = API_OK;
733                 break;
734 
735             case 's':
736                 s = client->json.getint();
737                 break;
738 
739             case 'd':
740                 d = 1;
741                 break;
742 
743             case MAKENAMEID2('t', 's'):
744                 ts = client->json.getint();
745                 break;
746 
747             case MAKENAMEID3('t', 'm', 'd'):
748                 tm = ts + client->json.getint();
749                 break;
750 
751             case MAKENAMEID2('a', 't'):
752                 at = client->json.getvalue();
753                 break;
754 
755             case MAKENAMEID2('f', 'a'):
756                 if (tslot)
757                 {
758                     client->json.storeobject(&tslot->fileattrstring);
759                 }
760                 else
761                 {
762                     client->json.storeobject(&fileattrstring);
763                 }
764                 break;
765 
766             case MAKENAMEID3('p', 'f', 'a'):
767                 if (tslot)
768                 {
769                     tslot->fileattrsmutable = (int)client->json.getint();
770                 }
771                 break;
772 
773             case 'e':
774                 e = (error)client->json.getint();
775                 break;
776 
777             case MAKENAMEID2('t', 'l'):
778                 tl = dstime(client->json.getint());
779                 break;
780 
781             case EOO:
782                 if (d || !at)
783                 {
784                     e = at ? API_EBLOCKED : API_EINTERNAL;
785 
786                     if (canceled)
787                     {
788                         return;
789                     }
790 
791                     if (tslot)
792                     {
793                         return tslot->transfer->failed(e, *client->mTctableRequestCommitter);
794                     }
795 
796                     return client->app->checkfile_result(ph, e);
797                 }
798                 else
799                 {
800                     // decrypt at and set filename
801                     SymmCipher key;
802                     const char* eos = strchr(at, '"');
803 
804                     key.setkey(filekey, FILENODE);
805 
806                     if ((buf = Node::decryptattr(tslot ? tslot->transfer->transfercipher() : &key,
807                                                  at, eos ? eos - at : strlen(at))))
808                     {
809                         JSON json;
810 
811                         json.begin((char*)buf + 5);
812 
813                         for (;;)
814                         {
815                             switch (json.getnameid())
816                             {
817                                 case 'c':
818                                     if (!json.storeobject(&filefingerprint))
819                                     {
820                                         delete[] buf;
821 
822                                         if (tslot)
823                                         {
824                                             return tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
825                                         }
826 
827                                         return client->app->checkfile_result(ph, API_EINTERNAL);
828                                     }
829                                     break;
830 
831                                 case 'n':
832                                     if (!json.storeobject(&filenamestring))
833                                     {
834                                         delete[] buf;
835 
836                                         if (tslot)
837                                         {
838                                             return tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
839                                         }
840 
841                                         return client->app->checkfile_result(ph, API_EINTERNAL);
842                                     }
843                                     break;
844 
845                                 case EOO:
846                                     delete[] buf;
847 
848                                     if (tslot)
849                                     {
850                                         if (s >= 0 && s != tslot->transfer->size)
851                                         {
852                                             tslot->transfer->size = s;
853                                             for (file_list::iterator it = tslot->transfer->files.begin(); it != tslot->transfer->files.end(); it++)
854                                             {
855                                                 (*it)->size = s;
856                                             }
857 
858                                             if (priv)
859                                             {
860                                                 Node *n = client->nodebyhandle(ph);
861                                                 if (n)
862                                                 {
863                                                     n->size = s;
864                                                     client->notifynode(n);
865                                                 }
866                                             }
867 
868                                             int creqtag = client->reqtag;
869                                             client->reqtag = 0;
870                                             client->sendevent(99411, "Node size mismatch");
871                                             client->reqtag = creqtag;
872                                         }
873 
874                                         tslot->starttime = tslot->lastdata = client->waiter->ds;
875 
876                                         if ((tempurls.size() == 1 || tempurls.size() == RAIDPARTS) && s >= 0)
877                                         {
878                                             tslot->transfer->tempurls = tempurls;
879                                             tslot->transferbuf.setIsRaid(tslot->transfer, tempurls, tslot->transfer->pos, tslot->maxRequestSize);
880                                             return tslot->progress();
881                                         }
882 
883                                         if (e == API_EOVERQUOTA && tl <= 0)
884                                         {
885                                             // default retry interval
886                                             tl = MegaClient::DEFAULT_BW_OVERQUOTA_BACKOFF_SECS;
887                                         }
888 
889                                         return tslot->transfer->failed(e, *client->mTctableRequestCommitter, e == API_EOVERQUOTA ? tl * 10 : 0);
890                                     }
891                                     else
892                                     {
893                                         return client->app->checkfile_result(ph, e, filekey, s, ts, tm,
894                                                                              &filenamestring,
895                                                                              &filefingerprint,
896                                                                              &fileattrstring);
897                                     }
898 
899                                 default:
900                                     if (!json.storeobject())
901                                     {
902                                         delete[] buf;
903 
904                                         if (tslot)
905                                         {
906                                             return tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
907                                         }
908                                         else
909                                         {
910                                             return client->app->checkfile_result(ph, API_EINTERNAL);
911                                         }
912                                     }
913                             }
914                         }
915                     }
916 
917                     if (canceled)
918                     {
919                         return;
920                     }
921 
922                     if (tslot)
923                     {
924                         return tslot->transfer->failed(API_EKEY, *client->mTctableRequestCommitter);
925                     }
926                     else
927                     {
928                         return client->app->checkfile_result(ph, API_EKEY);
929                     }
930                 }
931 
932             default:
933                 if (!client->json.storeobject())
934                 {
935                     if (tslot)
936                     {
937                         return tslot->transfer->failed(API_EINTERNAL, *client->mTctableRequestCommitter);
938                     }
939                     else
940                     {
941                         return client->app->checkfile_result(ph, API_EINTERNAL);
942                     }
943                 }
944         }
945     }
946 }
947 
CommandSetAttr(MegaClient * client,Node * n,SymmCipher * cipher,const char * prevattr)948 CommandSetAttr::CommandSetAttr(MegaClient* client, Node* n, SymmCipher* cipher, const char* prevattr)
949 {
950     cmd("a");
951     notself(client);
952 
953     string at;
954 
955     n->attrs.getjson(&at);
956     client->makeattr(cipher, &at, at.c_str(), int(at.size()));
957 
958     arg("n", (byte*)&n->nodehandle, MegaClient::NODEHANDLE);
959     arg("at", (byte*)at.c_str(), int(at.size()));
960 
961     h = n->nodehandle;
962     tag = client->reqtag;
963     syncop = prevattr;
964 
965     if(prevattr)
966     {
967         pa = prevattr;
968     }
969 }
970 
procresult()971 void CommandSetAttr::procresult()
972 {
973     Error e;
974     if (checkError(e, client->json))
975     {
976 #ifdef ENABLE_SYNC
977         if(!e && syncop)
978         {
979             Node* node = client->nodebyhandle(h);
980             if(node)
981             {
982                 Sync* sync = NULL;
983                 for (sync_list::iterator it = client->syncs.begin(); it != client->syncs.end(); it++)
984                 {
985                     if((*it)->tag == tag)
986                     {
987                         sync = (*it);
988                         break;
989                     }
990                 }
991 
992                 if(sync)
993                 {
994                     client->app->syncupdate_remote_rename(sync, node, pa.c_str());
995                 }
996             }
997         }
998 #endif
999         client->app->setattr_result(h, e);
1000     }
1001     else
1002     {
1003         client->json.storeobject();
1004         client->app->setattr_result(h, API_EINTERNAL);
1005     }
1006 }
1007 
1008 // (the result is not processed directly - we rely on the server-client
1009 // response)
CommandPutNodes(MegaClient * client,handle th,const char * userhandle,NewNode * newnodes,int numnodes,int ctag,putsource_t csource,const char * cauth)1010 CommandPutNodes::CommandPutNodes(MegaClient* client, handle th,
1011                                  const char* userhandle, NewNode* newnodes,
1012                                  int numnodes, int ctag, putsource_t csource, const char *cauth)
1013 {
1014     byte key[FILENODEKEYLENGTH];
1015     int i;
1016 
1017     nn = newnodes;
1018     nnsize = numnodes;
1019     type = userhandle ? USER_HANDLE : NODE_HANDLE;
1020     source = csource;
1021 
1022     cmd("p");
1023     notself(client);
1024 
1025     if (userhandle)
1026     {
1027         arg("t", userhandle);
1028         targethandle = UNDEF;
1029     }
1030     else
1031     {
1032         arg("t", (byte*)&th, MegaClient::NODEHANDLE);
1033         targethandle = th;
1034     }
1035 
1036     arg("sm",1);
1037 
1038     if (cauth)
1039     {
1040         arg("cauth", cauth);
1041     }
1042 
1043     beginarray("n");
1044 
1045     for (i = 0; i < numnodes; i++)
1046     {
1047         beginobject();
1048 
1049         NewNode* nni = &nn[i];
1050         switch (nni->source)
1051         {
1052             case NEW_NODE:
1053                 arg("h", (byte*)&nni->nodehandle, MegaClient::NODEHANDLE);
1054                 break;
1055 
1056             case NEW_PUBLIC:
1057                 arg("ph", (byte*)&nni->nodehandle, MegaClient::NODEHANDLE);
1058                 break;
1059 
1060             case NEW_UPLOAD:
1061                 arg("h", nni->uploadtoken, sizeof nn->uploadtoken);
1062 
1063                 // include pending file attributes for this upload
1064                 string s;
1065 
1066                 if (nni->fileattributes)
1067                 {
1068                     // if attributes are set on the newnode then the app is not using the pendingattr mechanism
1069                     s.swap(*nni->fileattributes);
1070                     nni->fileattributes.reset();
1071                 }
1072                 else
1073                 {
1074                     client->pendingattrstring(nn[i].uploadhandle, &s);
1075 
1076 #ifdef USE_MEDIAINFO
1077                     client->mediaFileInfo.addUploadMediaFileAttributes(nn[i].uploadhandle, &s);
1078 #endif
1079                 }
1080 
1081                 if (s.size())
1082                 {
1083                     arg("fa", s.c_str(), 1);
1084                 }
1085         }
1086 
1087         if (!ISUNDEF(nn[i].parenthandle))
1088         {
1089             arg("p", (byte*)&nn[i].parenthandle, MegaClient::NODEHANDLE);
1090         }
1091 
1092         if (nn[i].type == FILENODE && !ISUNDEF(nn[i].ovhandle))
1093         {
1094             arg("ov", (byte*)&nn[i].ovhandle, MegaClient::NODEHANDLE);
1095         }
1096 
1097         arg("t", nn[i].type);
1098         arg("a", (byte*)nn[i].attrstring->data(), int(nn[i].attrstring->size()));
1099 
1100         if (nn[i].nodekey.size() <= sizeof key)
1101         {
1102             client->key.ecb_encrypt((byte*)nn[i].nodekey.data(), key, nn[i].nodekey.size());
1103             arg("k", key, int(nn[i].nodekey.size()));
1104         }
1105         else
1106         {
1107             arg("k", (const byte*)nn[i].nodekey.data(), int(nn[i].nodekey.size()));
1108         }
1109 
1110         endobject();
1111     }
1112 
1113     endarray();
1114 
1115     // add cr element for new nodes, if applicable
1116     if (type == NODE_HANDLE)
1117     {
1118         Node* tn;
1119 
1120         if ((tn = client->nodebyhandle(th)))
1121         {
1122             ShareNodeKeys snk;
1123 
1124             for (i = 0; i < numnodes; i++)
1125             {
1126                 switch (nn[i].source)
1127                 {
1128                     case NEW_PUBLIC:
1129                     case NEW_NODE:
1130                         snk.add(nn[i].nodekey, nn[i].nodehandle, tn, 0);
1131                         break;
1132 
1133                     case NEW_UPLOAD:
1134                         snk.add(nn[i].nodekey, nn[i].nodehandle, tn, 0, nn[i].uploadtoken, (int)sizeof nn->uploadtoken);
1135                         break;
1136                 }
1137             }
1138 
1139             snk.get(this, true);
1140         }
1141     }
1142 
1143     tag = ctag;
1144 }
1145 
1146 // add new nodes and handle->node handle mapping
procresult()1147 void CommandPutNodes::procresult()
1148 {
1149     pendingdbid_map::iterator it = client->pendingtcids.find(tag);
1150     if (it != client->pendingtcids.end())
1151     {
1152         if (client->tctable)
1153         {
1154             client->mTctableRequestCommitter->beginOnce();
1155             vector<uint32_t> &ids = it->second;
1156             for (unsigned int i = 0; i < ids.size(); i++)
1157             {
1158                 if (ids[i])
1159                 {
1160                     client->tctable->del(ids[i]);
1161                 }
1162             }
1163         }
1164         client->pendingtcids.erase(it);
1165     }
1166     pendingfiles_map::iterator pit = client->pendingfiles.find(tag);
1167     if (pit != client->pendingfiles.end())
1168     {
1169         vector<LocalPath> &pfs = pit->second;
1170         for (unsigned int i = 0; i < pfs.size(); i++)
1171         {
1172             client->fsaccess->unlinklocal(pfs[i]);
1173         }
1174         client->pendingfiles.erase(pit);
1175     }
1176 
1177     Error e;
1178     if (checkError(e, client->json))
1179     {
1180         LOG_debug << "Putnodes error " << e;
1181         if (e == API_EOVERQUOTA)
1182         {
1183             client->activateoverquota(0, false);
1184         }
1185 #ifdef ENABLE_SYNC
1186         if (source == PUTNODES_SYNC)
1187         {
1188             if (e == API_EACCESS)
1189             {
1190                 int creqtag = client->reqtag;
1191                 client->reqtag = 0;
1192                 client->sendevent(99402, "API_EACCESS putting node in sync transfer");
1193                 client->reqtag = creqtag;
1194             }
1195 
1196             client->app->putnodes_result(e, type, NULL);
1197 
1198             for (int i=0; i < nnsize; i++)
1199             {
1200                 nn[i].localnode.reset();
1201             }
1202 
1203             return client->putnodes_sync_result(e, nn, nnsize);
1204         }
1205         else
1206         {
1207 #endif
1208             if (source == PUTNODES_APP)
1209             {
1210                 return client->app->putnodes_result(e, type, nn);
1211             }
1212 #ifdef ENABLE_SYNC
1213             else
1214             {
1215                 return client->putnodes_syncdebris_result(e, nn);
1216             }
1217         }
1218 #endif
1219     }
1220 
1221     bool noexit = true;
1222     bool empty = false;
1223     while (noexit)
1224     {
1225         switch (client->json.getnameid())
1226         {
1227             case 'f':
1228                 empty = !memcmp(client->json.pos, "[]", 2);
1229                 if (client->readnodes(&client->json, 1, source, nn, nnsize, tag, true))  // do apply keys to received nodes only as we go for command response, much much faster for many small responses
1230                 {
1231                     e = API_OK;
1232                 }
1233                 else
1234                 {
1235                     LOG_err << "Parse error (readnodes)";
1236                     e = API_EINTERNAL;
1237                     noexit = false;
1238                 }
1239                 break;
1240 
1241             case MAKENAMEID2('f', '2'):
1242                 if (!client->readnodes(&client->json, 1, PUTNODES_APP, NULL, 0, 0, true))  // do apply keys to received nodes only as we go for command response, much much faster for many small responses
1243                 {
1244                     LOG_err << "Parse error (readversions)";
1245                     e = API_EINTERNAL;
1246                     noexit = false;
1247                 }
1248                 break;
1249 
1250             default:
1251                 if (client->json.storeobject())
1252                 {
1253                     continue;
1254                 }
1255 
1256                 e = API_EINTERNAL;
1257                 LOG_err << "Parse error (PutNodes)";
1258 
1259                 // fall through
1260             case EOO:
1261                 noexit = false;
1262                 break;
1263         }
1264     }
1265 
1266     client->sendkeyrewrites();
1267 
1268 #ifdef ENABLE_SYNC
1269     if (source == PUTNODES_SYNC)
1270     {
1271         client->app->putnodes_result(e, type, NULL);
1272         client->putnodes_sync_result(e, nn, nnsize);
1273     }
1274     else
1275 #endif
1276     if (source == PUTNODES_APP)
1277     {
1278 #ifdef ENABLE_SYNC
1279         if (!ISUNDEF(targethandle))
1280         {
1281             Node *parent = client->nodebyhandle(targethandle);
1282             if (parent && parent->localnode)
1283             {
1284                 // A node has been added by a regular (non sync) putnodes
1285                 // inside a synced folder, so force a syncdown to detect
1286                 // and sync the changes.
1287                 client->syncdownrequired = true;
1288             }
1289         }
1290 #endif
1291         client->app->putnodes_result((!e && empty) ? API_ENOENT : static_cast<error>(e), type, nn);
1292     }
1293 #ifdef ENABLE_SYNC
1294     else
1295     {
1296         client->putnodes_syncdebris_result(e, nn);
1297     }
1298 #endif
1299 }
1300 
CommandMoveNode(MegaClient * client,Node * n,Node * t,syncdel_t csyncdel,handle prevparent)1301 CommandMoveNode::CommandMoveNode(MegaClient* client, Node* n, Node* t, syncdel_t csyncdel, handle prevparent)
1302 {
1303     h = n->nodehandle;
1304     syncdel = csyncdel;
1305     np = t->nodehandle;
1306     pp = prevparent;
1307     syncop = pp != UNDEF;
1308 
1309     cmd("m");
1310     notself(client);
1311     arg("n", (byte*)&h, MegaClient::NODEHANDLE);
1312     arg("t", (byte*)&t->nodehandle, MegaClient::NODEHANDLE);
1313 
1314     TreeProcShareKeys tpsk;
1315     client->proctree(n, &tpsk);
1316     tpsk.get(this);
1317 
1318     tag = client->reqtag;
1319 }
1320 
procresult()1321 void CommandMoveNode::procresult()
1322 {
1323     Error e;
1324     if (checkError(e, client->json))
1325     {
1326         if (e == API_EOVERQUOTA)
1327         {
1328             client->activateoverquota(0, false);
1329         }
1330 
1331 #ifdef ENABLE_SYNC
1332         if (syncdel != SYNCDEL_NONE)
1333         {
1334             Node* syncn = client->nodebyhandle(h);
1335 
1336             if (syncn)
1337             {
1338                 if (!e)
1339                 {
1340                     Node* n;
1341 
1342                     // update all todebris records in the subtree
1343                     for (node_set::iterator it = client->todebris.begin(); it != client->todebris.end(); it++)
1344                     {
1345                         n = *it;
1346 
1347                         do {
1348                             if (n == syncn)
1349                             {
1350                                 if (syncop)
1351                                 {
1352                                     Sync* sync = NULL;
1353                                     for (sync_list::iterator its = client->syncs.begin(); its != client->syncs.end(); its++)
1354                                     {
1355                                         if ((*its)->tag == tag)
1356                                         {
1357                                             sync = (*its);
1358                                             break;
1359                                         }
1360                                     }
1361 
1362                                     if (sync)
1363                                     {
1364                                         if ((*it)->type == FOLDERNODE)
1365                                         {
1366                                             sync->client->app->syncupdate_remote_folder_deletion(sync, (*it));
1367                                         }
1368                                         else
1369                                         {
1370                                             sync->client->app->syncupdate_remote_file_deletion(sync, (*it));
1371                                         }
1372                                     }
1373                                 }
1374 
1375                                 (*it)->syncdeleted = syncdel;
1376                                 break;
1377                             }
1378                         } while ((n = n->parent));
1379                     }
1380                 }
1381                 else
1382                 {
1383                     Node *tn = NULL;
1384                     if (syncdel == SYNCDEL_BIN || syncdel == SYNCDEL_FAILED
1385                             || !(tn = client->nodebyhandle(client->rootnodes[RUBBISHNODE - ROOTNODE])))
1386                     {
1387                         LOG_err << "Error moving node to the Rubbish Bin";
1388                         syncn->syncdeleted = SYNCDEL_NONE;
1389                         client->todebris.erase(syncn->todebris_it);
1390                         syncn->todebris_it = client->todebris.end();
1391                     }
1392                     else
1393                     {
1394                         int creqtag = client->reqtag;
1395                         client->reqtag = syncn->tag;
1396                         LOG_warn << "Move to Syncdebris failed. Moving to the Rubbish Bin instead.";
1397                         client->rename(syncn, tn, SYNCDEL_FAILED, pp);
1398                         client->reqtag = creqtag;
1399                     }
1400                 }
1401             }
1402         }
1403         else if(syncop)
1404         {
1405             Node *n = client->nodebyhandle(h);
1406             if(n)
1407             {
1408                 Sync *sync = NULL;
1409                 for (sync_list::iterator it = client->syncs.begin(); it != client->syncs.end(); it++)
1410                 {
1411                     if((*it)->tag == tag)
1412                     {
1413                         sync = (*it);
1414                         break;
1415                     }
1416                 }
1417 
1418                 if(sync)
1419                 {
1420                     client->app->syncupdate_remote_move(sync, n, client->nodebyhandle(pp));
1421                 }
1422             }
1423         }
1424 #endif
1425         // Movement of shares and pending shares into Rubbish should remove them
1426         if (e == API_OK)
1427         {
1428             Node *n = client->nodebyhandle(h);
1429             if (n && (n->pendingshares || n->outshares))
1430             {
1431                 Node *rootnode = client->nodebyhandle(np);
1432                 while (rootnode)
1433                 {
1434                     if (!rootnode->parent)
1435                     {
1436                         break;
1437                     }
1438                     rootnode = rootnode->parent;
1439                 }
1440                 if (rootnode && rootnode->type == RUBBISHNODE)
1441                 {
1442                     share_map::iterator it;
1443                     if (n->pendingshares)
1444                     {
1445                         for (it = n->pendingshares->begin(); it != n->pendingshares->end(); it++)
1446                         {
1447                             client->newshares.push_back(new NewShare(
1448                                                             n->nodehandle, 1, n->owner, ACCESS_UNKNOWN,
1449                                                             0, NULL, NULL, it->first, false));
1450                         }
1451                     }
1452 
1453                     if (n->outshares)
1454                     {
1455                         for (it = n->outshares->begin(); it != n->outshares->end(); it++)
1456                         {
1457                             client->newshares.push_back(new NewShare(
1458                                                             n->nodehandle, 1, it->first, ACCESS_UNKNOWN,
1459                                                             0, NULL, NULL, UNDEF, false));
1460                         }
1461                     }
1462 
1463                     client->mergenewshares(1);
1464                 }
1465             }
1466         }
1467         else if (syncdel == SYNCDEL_NONE)
1468         {
1469             int creqtag = client->reqtag;
1470             client->reqtag = 0;
1471             client->sendevent(99439, "Unexpected move error");
1472             client->reqtag = creqtag;
1473         }
1474 
1475         client->app->rename_result(h, e);
1476     }
1477     else
1478     {
1479         client->json.storeobject();
1480         client->app->rename_result(h, API_EINTERNAL);
1481     }
1482 }
1483 
CommandDelNode(MegaClient * client,handle th,bool keepversions)1484 CommandDelNode::CommandDelNode(MegaClient* client, handle th, bool keepversions)
1485 {
1486     cmd("d");
1487     notself(client);
1488 
1489     arg("n", (byte*)&th, MegaClient::NODEHANDLE);
1490 
1491     if (keepversions)
1492     {
1493         arg("v", 1);
1494     }
1495 
1496     h = th;
1497     tag = client->reqtag;
1498 }
1499 
procresult()1500 void CommandDelNode::procresult()
1501 {
1502     Error e;
1503     if (checkError(e, client->json))
1504     {
1505         client->app->unlink_result(h, e);
1506     }
1507     else
1508     {
1509         for (;;)
1510         {
1511             switch (client->json.getnameid())
1512             {
1513                 case 'r':
1514                     if (client->json.enterarray())
1515                     {
1516                         if(client->json.isnumeric())
1517                         {
1518                             e = (error)client->json.getint();
1519                         }
1520 
1521                         client->json.leavearray();
1522                     }
1523                     break;
1524 
1525                 case EOO:
1526                     client->app->unlink_result(h, e);
1527                     return;
1528 
1529                 default:
1530                     if (!client->json.storeobject())
1531                     {
1532                         client->app->unlink_result(h, API_EINTERNAL);
1533                         return;
1534                     }
1535             }
1536         }
1537     }
1538 }
1539 
CommandDelVersions(MegaClient * client)1540 CommandDelVersions::CommandDelVersions(MegaClient* client)
1541 {
1542     cmd("dv");
1543     tag = client->reqtag;
1544 }
1545 
procresult()1546 void CommandDelVersions::procresult()
1547 {
1548     Error e;
1549     checkError(e, client->json);
1550     client->app->unlinkversions_result(e);
1551 }
1552 
CommandKillSessions(MegaClient * client)1553 CommandKillSessions::CommandKillSessions(MegaClient* client)
1554 {
1555     cmd("usr");
1556     arg("ko", 1); // Request to kill all sessions except the current one
1557 
1558     h = UNDEF;
1559     tag = client->reqtag;
1560 }
1561 
CommandKillSessions(MegaClient * client,handle sessionid)1562 CommandKillSessions::CommandKillSessions(MegaClient* client, handle sessionid)
1563 {
1564     cmd("usr");
1565     beginarray("s");
1566     element(sessionid, MegaClient::USERHANDLE);
1567     endarray();
1568 
1569     h = sessionid;
1570     tag = client->reqtag;
1571 }
procresult()1572 void CommandKillSessions::procresult()
1573 {
1574     Error e;
1575     checkError(e, client->json);
1576 
1577     client->app->sessions_killed(h, e);
1578 }
1579 
CommandLogout(MegaClient * client)1580 CommandLogout::CommandLogout(MegaClient *client)
1581 {
1582     cmd("sml");
1583 
1584     batchSeparately = true;
1585 
1586     tag = client->reqtag;
1587 }
1588 
procresult()1589 void CommandLogout::procresult()
1590 {
1591     Error e;
1592     checkError(e, client->json);
1593     MegaApp *app = client->app;
1594     if (client->loggingout > 0)
1595     {
1596         client->loggingout--;
1597     }
1598     if(!e)
1599     {
1600         // notify client after cache removal, as before
1601         client->loggedout = true;
1602     }
1603     else
1604     {
1605         app->logout_result(e);
1606     }
1607 }
1608 
CommandPrelogin(MegaClient * client,const char * email)1609 CommandPrelogin::CommandPrelogin(MegaClient* client, const char* email)
1610 {
1611     cmd("us0");
1612     arg("user", email);
1613     batchSeparately = true;  // in case the account is blocked (we need to get a sid so we can issue whyamiblocked)
1614 
1615     this->email = email;
1616     tag = client->reqtag;
1617 }
1618 
procresult()1619 void CommandPrelogin::procresult()
1620 {
1621     Error e;
1622     if (checkError(e, client->json))
1623     {
1624         return client->app->prelogin_result(0, NULL, NULL, e);
1625     }
1626 
1627     int v = 0;
1628     string salt;
1629     for (;;)
1630     {
1631         switch (client->json.getnameid())
1632         {
1633             case 'v':
1634                 v = int(client->json.getint());
1635                 break;
1636             case 's':
1637                 client->json.storeobject(&salt);
1638                 break;
1639             case EOO:
1640                 if (v == 0)
1641                 {
1642                     LOG_err << "No version returned";
1643                     return client->app->prelogin_result(0, NULL, NULL, API_EINTERNAL);
1644                 }
1645                 else if (v > 2)
1646                 {
1647                     LOG_err << "Version of account not supported";
1648                     return client->app->prelogin_result(0, NULL, NULL, API_EINTERNAL);
1649                 }
1650                 else if (v == 2 && !salt.size())
1651                 {
1652                     LOG_err << "No salt returned";
1653                     return client->app->prelogin_result(0, NULL, NULL, API_EINTERNAL);
1654                 }
1655                 client->accountversion = v;
1656                 Base64::atob(salt, client->accountsalt);
1657                 client->app->prelogin_result(v, &email, &salt, API_OK);
1658                 return;
1659             default:
1660                 if (!client->json.storeobject())
1661                 {
1662                     return client->app->prelogin_result(0, NULL, NULL, API_EINTERNAL);
1663                 }
1664         }
1665     }
1666 }
1667 
1668 // login request with user e-mail address and user hash
CommandLogin(MegaClient * client,const char * email,const byte * emailhash,int emailhashsize,const byte * sessionkey,int csessionversion,const char * pin)1669 CommandLogin::CommandLogin(MegaClient* client, const char* email, const byte *emailhash, int emailhashsize, const byte *sessionkey, int csessionversion, const char *pin)
1670 {
1671     cmd("us");
1672     batchSeparately = true;  // in case the account is blocked (we need to get a sid so we can issue whyamiblocked)
1673 
1674     // are we just performing a session validation?
1675     checksession = !email;
1676     sessionversion = csessionversion;
1677 
1678     if (!checksession)
1679     {
1680         arg("user", email);
1681         arg("uh", emailhash, emailhashsize);
1682         if (pin)
1683         {
1684             arg("mfa", pin);
1685         }
1686     }
1687     else
1688     {
1689         if (client->sctable && client->dbaccess->currentDbVersion == DbAccess::LEGACY_DB_VERSION)
1690         {
1691             LOG_debug << "Requesting a local cache upgrade";
1692             arg("fa", 1);
1693         }
1694     }
1695 
1696     if (sessionkey)
1697     {
1698         arg("sek", sessionkey, SymmCipher::KEYLENGTH);
1699     }
1700 
1701     if (client->cachedscsn != UNDEF)
1702     {
1703         arg("sn", (byte*)&client->cachedscsn, sizeof client->cachedscsn);
1704     }
1705 
1706     string id = client->getDeviceid();
1707 
1708     if (id.size())
1709     {
1710         string hash;
1711         HashSHA256 hasher;
1712         hasher.add((const byte*)id.data(), unsigned(id.size()));
1713         hasher.get(&hash);
1714         arg("si", (const byte*)hash.data(), int(hash.size()));
1715     }
1716 
1717     tag = client->reqtag;
1718 }
1719 
1720 // process login result
procresult()1721 void CommandLogin::procresult()
1722 {
1723     Error e;
1724     if (checkError(e, client->json))
1725     {
1726         return client->app->login_result(e);
1727     }
1728 
1729     byte hash[SymmCipher::KEYLENGTH];
1730     byte sidbuf[AsymmCipher::MAXKEYLENGTH];
1731     byte privkbuf[AsymmCipher::MAXKEYLENGTH * 2];
1732     byte sek[SymmCipher::KEYLENGTH];
1733     int len_k = 0, len_privk = 0, len_csid = 0, len_tsid = 0, len_sek = 0;
1734     handle me = UNDEF;
1735     bool fa = false;
1736     bool ach = false;
1737 
1738     for (;;)
1739     {
1740         switch (client->json.getnameid())
1741         {
1742             case 'k':
1743                 len_k = client->json.storebinary(hash, sizeof hash);
1744                 break;
1745 
1746             case 'u':
1747                 me = client->json.gethandle(MegaClient::USERHANDLE);
1748                 break;
1749 
1750             case MAKENAMEID3('s', 'e', 'k'):
1751                 len_sek = client->json.storebinary(sek, sizeof sek);
1752                 break;
1753 
1754             case MAKENAMEID4('t', 's', 'i', 'd'):
1755                 len_tsid = client->json.storebinary(sidbuf, sizeof sidbuf);
1756                 break;
1757 
1758             case MAKENAMEID4('c', 's', 'i', 'd'):
1759                 len_csid = client->json.storebinary(sidbuf, sizeof sidbuf);
1760                 break;
1761 
1762             case MAKENAMEID5('p', 'r', 'i', 'v', 'k'):
1763                 len_privk = client->json.storebinary(privkbuf, sizeof privkbuf);
1764                 break;
1765 
1766             case MAKENAMEID2('f', 'a'):
1767                 fa = client->json.getint();
1768                 break;
1769 
1770             case MAKENAMEID3('a', 'c', 'h'):
1771                 ach = client->json.getint();
1772                 break;
1773 
1774             case MAKENAMEID2('s', 'n'):
1775                 if (!client->json.getint())
1776                 {
1777                     // local state cache continuity rejected: read state from
1778                     // server instead
1779                     client->cachedscsn = UNDEF;
1780                 }
1781                 break;
1782 
1783             case EOO:
1784                 if (!checksession)
1785                 {
1786                     if (ISUNDEF(me) || len_k != sizeof hash)
1787                     {
1788                         return client->app->login_result(API_EINTERNAL);
1789                     }
1790 
1791                     // decrypt and set master key
1792                     client->key.ecb_decrypt(hash);
1793                     client->key.setkey(hash);
1794                 }
1795                 else
1796                 {
1797                     if (fa && client->sctable)
1798                     {
1799                         client->sctable->remove();
1800                         delete client->sctable;
1801                         client->sctable = NULL;
1802                         client->pendingsccommit = false;
1803                         client->cachedscsn = UNDEF;
1804                         client->dbaccess->currentDbVersion = DbAccess::DB_VERSION;
1805 
1806                         int creqtag = client->reqtag;
1807                         client->reqtag = 0;
1808                         client->sendevent(99404, "Local DB upgrade granted");
1809                         client->reqtag = creqtag;
1810                     }
1811                 }
1812 
1813                 if (len_sek)
1814                 {
1815                     if (len_sek != SymmCipher::KEYLENGTH)
1816                     {
1817                         return client->app->login_result(API_EINTERNAL);
1818                     }
1819 
1820                     if (checksession && sessionversion)
1821                     {
1822                         byte k[SymmCipher::KEYLENGTH];
1823                         memcpy(k, client->key.key, sizeof(k));
1824 
1825                         client->key.setkey(sek);
1826                         client->key.ecb_decrypt(k);
1827                         client->key.setkey(k);
1828                     }
1829                 }
1830 
1831                 if (len_tsid)
1832                 {
1833                     client->setsid(sidbuf, MegaClient::SIDLEN);
1834 
1835                     // account does not have an RSA keypair set: verify
1836                     // password using symmetric challenge
1837                     if (!client->checktsid(sidbuf, len_tsid))
1838                     {
1839                         LOG_warn << "Error checking tsid";
1840                         return client->app->login_result(API_ENOENT);
1841                     }
1842 
1843                     // add missing RSA keypair
1844                     LOG_info << "Generating and adding missing RSA keypair";
1845                     client->setkeypair();
1846                 }
1847                 else
1848                 {
1849                     // account has RSA keypair: decrypt server-provided session ID
1850                     if (len_privk < 256)
1851                     {
1852                         if (!checksession)
1853                         {
1854                             return client->app->login_result(API_EINTERNAL);
1855                         }
1856                         else
1857                         {
1858                             // logging in with tsid to an account without a RSA keypair
1859                             LOG_info << "Generating and adding missing RSA keypair";
1860                             client->setkeypair();
1861                         }
1862                     }
1863                     else
1864                     {
1865                         // decrypt and set private key
1866                         client->key.ecb_decrypt(privkbuf, len_privk);
1867                         client->mPrivKey.resize(AsymmCipher::MAXKEYLENGTH * 2);
1868                         client->mPrivKey.resize(Base64::btoa(privkbuf, len_privk, (char *)client->mPrivKey.data()));
1869 
1870                         if (!client->asymkey.setkey(AsymmCipher::PRIVKEY, privkbuf, len_privk))
1871                         {
1872                             LOG_warn << "Error checking private key";
1873                             return client->app->login_result(API_ENOENT);
1874                         }
1875                     }
1876 
1877                     if (!checksession)
1878                     {
1879                         if (len_csid < 32)
1880                         {
1881                             return client->app->login_result(API_EINTERNAL);
1882                         }
1883 
1884                         // decrypt and set session ID for subsequent API communication
1885                         if (!client->asymkey.decrypt(sidbuf, len_csid, sidbuf, MegaClient::SIDLEN))
1886                         {
1887                             return client->app->login_result(API_EINTERNAL);
1888                         }
1889 
1890                         client->setsid(sidbuf, MegaClient::SIDLEN);
1891                     }
1892                 }
1893 
1894                 client->me = me;
1895                 client->uid = Base64Str<MegaClient::USERHANDLE>(client->me);
1896                 client->achievements_enabled = ach;
1897                 // Force to create own user
1898                 client->finduser(me, 1);
1899 
1900                 if (len_sek)
1901                 {
1902                     client->sessionkey.assign((const char *)sek, sizeof(sek));
1903                 }
1904 
1905 #ifdef ENABLE_SYNC
1906                 client->resetSyncConfigs();
1907 #endif
1908 
1909                 return client->app->login_result(API_OK);
1910 
1911             default:
1912                 if (!client->json.storeobject())
1913                 {
1914                     return client->app->login_result(API_EINTERNAL);
1915                 }
1916         }
1917     }
1918 }
1919 
CommandShareKeyUpdate(MegaClient *,handle sh,const char * uid,const byte * key,int len)1920 CommandShareKeyUpdate::CommandShareKeyUpdate(MegaClient*, handle sh, const char* uid, const byte* key, int len)
1921 {
1922     cmd("k");
1923     beginarray("sr");
1924 
1925     element(sh, MegaClient::NODEHANDLE);
1926     element(uid);
1927     element(key, len);
1928 
1929     endarray();
1930 }
1931 
CommandShareKeyUpdate(MegaClient * client,handle_vector * v)1932 CommandShareKeyUpdate::CommandShareKeyUpdate(MegaClient* client, handle_vector* v)
1933 {
1934     Node* n;
1935     byte sharekey[SymmCipher::KEYLENGTH];
1936 
1937     cmd("k");
1938     beginarray("sr");
1939 
1940     for (size_t i = v->size(); i--;)
1941     {
1942         handle h = (*v)[i];
1943 
1944         if ((n = client->nodebyhandle(h)) && n->sharekey)
1945         {
1946             client->key.ecb_encrypt(n->sharekey->key, sharekey, SymmCipher::KEYLENGTH);
1947 
1948             element(h, MegaClient::NODEHANDLE);
1949             element(client->me, MegaClient::USERHANDLE);
1950             element(sharekey, SymmCipher::KEYLENGTH);
1951         }
1952     }
1953 
1954     endarray();
1955 }
1956 
1957 // add/remove share; include node share keys if new share
CommandSetShare(MegaClient * client,Node * n,User * u,accesslevel_t a,int newshare,const char * msg,const char * personal_representation)1958 CommandSetShare::CommandSetShare(MegaClient* client, Node* n, User* u, accesslevel_t a, int newshare, const char* msg, const char* personal_representation)
1959 {
1960     byte auth[SymmCipher::BLOCKSIZE];
1961     byte key[SymmCipher::KEYLENGTH];
1962     byte asymmkey[AsymmCipher::MAXKEYLENGTH];
1963     int t = 0;
1964 
1965     tag = client->restag;
1966 
1967     sh = n->nodehandle;
1968     user = u;
1969     access = a;
1970 
1971     cmd("s2");
1972     arg("n", (byte*)&sh, MegaClient::NODEHANDLE);
1973 
1974     // Only for inviting non-contacts
1975     if (personal_representation && personal_representation[0])
1976     {
1977         this->personal_representation = personal_representation;
1978         arg("e", personal_representation);
1979     }
1980 
1981     if (msg && msg[0])
1982     {
1983         this->msg = msg;
1984         arg("msg", msg);
1985     }
1986 
1987     if (a != ACCESS_UNKNOWN)
1988     {
1989         // securely store/transmit share key
1990         // by creating a symmetrically (for the sharer) and an asymmetrically
1991         // (for the sharee) encrypted version
1992         memcpy(key, n->sharekey->key, sizeof key);
1993         memcpy(asymmkey, key, sizeof key);
1994 
1995         client->key.ecb_encrypt(key);
1996         arg("ok", key, sizeof key);
1997 
1998         if (u && u->pubk.isvalid())
1999         {
2000             t = u->pubk.encrypt(client->rng, asymmkey, SymmCipher::KEYLENGTH, asymmkey, sizeof asymmkey);
2001         }
2002 
2003         // outgoing handle authentication
2004         client->handleauth(sh, auth);
2005         arg("ha", auth, sizeof auth);
2006     }
2007 
2008     beginarray("s");
2009     beginobject();
2010 
2011     arg("u", u ? ((u->show == VISIBLE) ? u->uid.c_str() : u->email.c_str()) : MegaClient::EXPORTEDLINK);
2012     // if the email is registered, the pubk request has returned the userhandle -->
2013     // sending the userhandle instead of the email makes the API to assume the user is already a contact
2014 
2015     if (a != ACCESS_UNKNOWN)
2016     {
2017         arg("r", a);
2018 
2019         if (u && u->pubk.isvalid() && t)
2020         {
2021             arg("k", asymmkey, t);
2022         }
2023     }
2024 
2025     endobject();
2026     endarray();
2027 
2028     // only for a fresh share: add cr element with all node keys encrypted to
2029     // the share key
2030     if (newshare)
2031     {
2032         // the new share's nodekeys for this user: generate node list
2033         TreeProcShareKeys tpsk(n);
2034         client->proctree(n, &tpsk);
2035         tpsk.get(this);
2036     }
2037 }
2038 
2039 // process user element (email/handle pairs)
procuserresult(MegaClient * client)2040 bool CommandSetShare::procuserresult(MegaClient* client)
2041 {
2042     while (client->json.enterobject())
2043     {
2044         handle uh = UNDEF;
2045         const char* m = NULL;
2046 
2047         for (;;)
2048         {
2049             switch (client->json.getnameid())
2050             {
2051                 case 'u':
2052                     uh = client->json.gethandle(MegaClient::USERHANDLE);
2053                     break;
2054 
2055                 case 'm':
2056                     m = client->json.getvalue();
2057                     break;
2058 
2059                 case EOO:
2060                     if (!ISUNDEF(uh) && m)
2061                     {
2062                         client->mapuser(uh, m);
2063                     }
2064                     return true;
2065 
2066                 default:
2067                     if (!client->json.storeobject())
2068                     {
2069                         return false;
2070                     }
2071             }
2072         }
2073     }
2074 
2075     return false;
2076 }
2077 
2078 // process result of share addition/modification
procresult()2079 void CommandSetShare::procresult()
2080 {
2081     Error e;
2082     if (checkError(e, client->json))
2083     {
2084         return client->app->share_result(e);
2085     }
2086 
2087     for (;;)
2088     {
2089         switch (client->json.getnameid())
2090         {
2091             case MAKENAMEID2('o', 'k'):  // an owner key response will only
2092                                          // occur if the same share was created
2093                                          // concurrently with a different key
2094             {
2095                 byte key[SymmCipher::KEYLENGTH + 1];
2096                 if (client->json.storebinary(key, sizeof key + 1) == SymmCipher::KEYLENGTH)
2097                 {
2098                     Node* n;
2099 
2100                     if ((n = client->nodebyhandle(sh)) && n->sharekey)
2101                     {
2102                         client->key.ecb_decrypt(key);
2103                         n->sharekey->setkey(key);
2104 
2105                         // repeat attempt with corrected share key
2106                         client->restag = tag;
2107                         client->reqs.add(new CommandSetShare(client, n, user, access, 0, msg.c_str(), personal_representation.c_str()));
2108                         return;
2109                     }
2110                 }
2111                 break;
2112             }
2113 
2114             case 'u':   // user/handle confirmation
2115                 if (client->json.enterarray())
2116                 {
2117                     while (procuserresult(client))
2118                     {}
2119                     client->json.leavearray();
2120                 }
2121                 break;
2122 
2123             case 'r':
2124                 if (client->json.enterarray())
2125                 {
2126                     int i = 0;
2127 
2128                     while (client->json.isnumeric())
2129                     {
2130                         client->app->share_result(i++, (error)client->json.getint());
2131                     }
2132 
2133                     client->json.leavearray();
2134                 }
2135                 break;
2136 
2137             case MAKENAMEID3('s', 'n', 'k'):
2138                 client->procsnk(&client->json);
2139                 break;
2140 
2141             case MAKENAMEID3('s', 'u', 'k'):
2142                 client->procsuk(&client->json);
2143                 break;
2144 
2145             case MAKENAMEID2('c', 'r'):
2146                 client->proccr(&client->json);
2147                 break;
2148 
2149             case EOO:
2150                 client->app->share_result(API_OK);
2151                 return;
2152 
2153             default:
2154                 if (!client->json.storeobject())
2155                 {
2156                     return;
2157                 }
2158         }
2159     }
2160 }
2161 
2162 
CommandSetPendingContact(MegaClient * client,const char * temail,opcactions_t action,const char * msg,const char * oemail,handle contactLink)2163 CommandSetPendingContact::CommandSetPendingContact(MegaClient* client, const char* temail, opcactions_t action, const char* msg, const char* oemail, handle contactLink)
2164 {
2165     cmd("upc");
2166 
2167     if (oemail != NULL)
2168     {
2169         arg("e", oemail);
2170     }
2171 
2172     arg("u", temail);
2173     switch (action)
2174     {
2175         case OPCA_DELETE:
2176             arg("aa", "d");
2177             break;
2178         case OPCA_REMIND:
2179             arg("aa", "r");
2180             break;
2181         case OPCA_ADD:
2182             arg("aa", "a");
2183             if (!ISUNDEF(contactLink))
2184             {
2185                 arg("cl", (byte*)&contactLink, MegaClient::CONTACTLINKHANDLE);
2186             }
2187             break;
2188     }
2189 
2190     if (msg != NULL)
2191     {
2192         arg("msg", msg);
2193     }
2194 
2195     if (action != OPCA_REMIND)  // for reminders, need the actionpacket to update `uts`
2196     {
2197         notself(client);
2198     }
2199 
2200     tag = client->reqtag;
2201     this->action = action;
2202     this->temail = temail;
2203 }
2204 
procresult()2205 void CommandSetPendingContact::procresult()
2206 {
2207     Error e;
2208     if (checkError(e, client->json))
2209     {
2210         handle pcrhandle = UNDEF;
2211         if (e == API_OK) // response for delete & remind actions is always numeric
2212         {
2213             // find the PCR by email
2214             PendingContactRequest *pcr = NULL;
2215             for (handlepcr_map::iterator it = client->pcrindex.begin();
2216                  it != client->pcrindex.end(); it++)
2217             {
2218                 if (it->second->targetemail == temail)
2219                 {
2220                     pcr = it->second;
2221                     pcrhandle = pcr->id;
2222                     break;
2223                 }
2224             }
2225 
2226             if (!pcr)
2227             {
2228                 LOG_err << "Reminded/deleted PCR not found";
2229             }
2230             else if (action == OPCA_DELETE)
2231             {
2232                 pcr->changed.deleted = true;
2233                 client->notifypcr(pcr);
2234 
2235                 // remove pending shares related to the deleted PCR
2236                 Node *n;
2237                 for (node_map::iterator it = client->nodes.begin(); it != client->nodes.end(); it++)
2238                 {
2239                     n = it->second;
2240                     if (n->pendingshares && n->pendingshares->find(pcr->id) != n->pendingshares->end())
2241                     {
2242                         client->newshares.push_back(
2243                                     new NewShare(n->nodehandle, 1, n->owner, ACCESS_UNKNOWN,
2244                                                  0, NULL, NULL, pcr->id, false));
2245                     }
2246                 }
2247 
2248                 client->mergenewshares(1);
2249             }
2250         }
2251 
2252         return client->app->setpcr_result(pcrhandle, e, this->action);
2253     }
2254 
2255     // if the PCR has been added, the response contains full details
2256     handle p = UNDEF;
2257     m_time_t ts = 0;
2258     m_time_t uts = 0;
2259     const char *eValue = NULL;
2260     const char *m = NULL;
2261     const char *msg = NULL;
2262     PendingContactRequest *pcr = NULL;
2263     for (;;)
2264     {
2265         switch (client->json.getnameid())
2266         {
2267             case 'p':
2268                 p = client->json.gethandle(MegaClient::PCRHANDLE);
2269                 break;
2270             case 'm':
2271                 m = client->json.getvalue();
2272                 break;
2273             case 'e':
2274                 eValue = client->json.getvalue();
2275                 break;
2276             case MAKENAMEID3('m', 's', 'g'):
2277                 msg = client->json.getvalue();
2278                 break;
2279             case MAKENAMEID2('t', 's'):
2280                 ts = client->json.getint();
2281                 break;
2282             case MAKENAMEID3('u', 't', 's'):
2283                 uts = client->json.getint();
2284                 break;
2285             case EOO:
2286                 if (ISUNDEF(p))
2287                 {
2288                     LOG_err << "Error in CommandSetPendingContact. Undefined handle";
2289                     client->app->setpcr_result(UNDEF, API_EINTERNAL, this->action);
2290                     return;
2291                 }
2292 
2293                 if (action != OPCA_ADD || !eValue || !m || ts == 0 || uts == 0)
2294                 {
2295                     LOG_err << "Error in CommandSetPendingContact. Wrong parameters";
2296                     client->app->setpcr_result(UNDEF, API_EINTERNAL, this->action);
2297                     return;
2298                 }
2299 
2300                 pcr = new PendingContactRequest(p, eValue, m, ts, uts, msg, true);
2301                 client->mappcr(p, pcr);
2302 
2303                 client->notifypcr(pcr);
2304                 client->app->setpcr_result(p, API_OK, this->action);
2305                 return;
2306 
2307             default:
2308                 if (!client->json.storeobject())
2309                 {
2310                     LOG_err << "Error in CommandSetPendingContact. Parse error";
2311                     client->app->setpcr_result(UNDEF, API_EINTERNAL, this->action);
2312                     return;
2313                 }
2314         }
2315     }
2316 }
2317 
CommandUpdatePendingContact(MegaClient * client,handle p,ipcactions_t action)2318 CommandUpdatePendingContact::CommandUpdatePendingContact(MegaClient* client, handle p, ipcactions_t action)
2319 {
2320     cmd("upca");
2321 
2322     arg("p", (byte*)&p, MegaClient::PCRHANDLE);
2323     switch (action)
2324     {
2325         case IPCA_ACCEPT:
2326             arg("aa", "a");
2327             break;
2328         case IPCA_DENY:
2329             arg("aa", "d");
2330             break;
2331         case IPCA_IGNORE:
2332         default:
2333             arg("aa", "i");
2334             break;
2335     }
2336 
2337     tag = client->reqtag;
2338     this->action = action;
2339 }
2340 
procresult()2341 void CommandUpdatePendingContact::procresult()
2342 {
2343     Error e;
2344     if (checkError(e, client->json))
2345     {
2346         return client->app->updatepcr_result(e, this->action);
2347     }
2348 
2349     LOG_err << "Unexpected response for CommandUpdatePendingContact";
2350     client->app->updatepcr_result(API_EINTERNAL, this->action);
2351     return;
2352 }
2353 
2354 
CommandEnumerateQuotaItems(MegaClient * client)2355 CommandEnumerateQuotaItems::CommandEnumerateQuotaItems(MegaClient* client)
2356 {
2357     cmd("utqa");
2358     arg("nf", 1);
2359     arg("b", 1);
2360     tag = client->reqtag;
2361 }
2362 
procresult()2363 void CommandEnumerateQuotaItems::procresult()
2364 {
2365     Error e;
2366     if (checkError(e, client->json))
2367     {
2368         return client->app->enumeratequotaitems_result(e);
2369     }
2370 
2371     while (client->json.enterobject())
2372     {
2373         handle product = UNDEF;
2374         int prolevel = -1, gbstorage = -1, gbtransfer = -1, months = -1, type = -1;
2375         unsigned amount = 0, amountMonth = 0;
2376         const char* amountStr = nullptr;
2377         const char* amountMonthStr = nullptr;
2378         const char* curr = nullptr;
2379         const char* desc = nullptr;
2380         const char* ios = nullptr;
2381         const char* android = nullptr;
2382         string currency;
2383         string description;
2384         string ios_id;
2385         string android_id;
2386         bool finished = false;
2387         while (!finished)
2388         {
2389             switch (client->json.getnameid())
2390             {
2391                 case MAKENAMEID2('i', 't'):
2392                     type = static_cast<int>(client->json.getint());
2393                     break;
2394                 case MAKENAMEID2('i', 'd'):
2395                     product = client->json.gethandle(8);
2396                     break;
2397                 case MAKENAMEID2('a', 'l'):
2398                     prolevel = static_cast<int>(client->json.getint());
2399                     break;
2400                 case 's':
2401                     gbstorage = static_cast<int>(client->json.getint());
2402                     break;
2403                 case 't':
2404                     gbtransfer = static_cast<int>(client->json.getint());
2405                     break;
2406                 case 'm':
2407                     months = static_cast<int>(client->json.getint());
2408                     break;
2409                 case 'p':
2410                     amountStr = client->json.getvalue();
2411                     break;
2412                 case 'c':
2413                     curr = client->json.getvalue();
2414                     break;
2415                 case 'd':
2416                     desc = client->json.getvalue();
2417                     break;
2418                 case MAKENAMEID3('i', 'o', 's'):
2419                     ios = client->json.getvalue();
2420                     break;
2421                 case MAKENAMEID6('g', 'o', 'o', 'g', 'l', 'e'):
2422                     android = client->json.getvalue();
2423                     break;
2424                 case MAKENAMEID3('m', 'b', 'p'):
2425                     amountMonthStr = client->json.getvalue();
2426                     break;
2427                 case EOO:
2428                     if (type < 0
2429                             || ISUNDEF(product)
2430                             || (prolevel < 0)
2431                             || (!type && gbstorage < 0)
2432                             || (!type && gbtransfer < 0)
2433                             || (months < 0)
2434                             || !amountStr
2435                             || !curr
2436                             || !desc
2437                             || !amountMonthStr
2438                             || (!type && !ios)
2439                             || (!type && !android))
2440                     {
2441                         return client->app->enumeratequotaitems_result(API_EINTERNAL);
2442                     }
2443 
2444                     finished = true;
2445                     break;
2446                 default:
2447                     return client->app->enumeratequotaitems_result(API_EINTERNAL);
2448             }
2449         }
2450 
2451         client->json.leaveobject();
2452         Node::copystring(&currency, curr);
2453         Node::copystring(&description, desc);
2454         Node::copystring(&ios_id, ios);
2455         Node::copystring(&android_id, android);
2456 
2457         amount = atoi(amountStr) * 100;
2458         if ((curr = strchr(amountStr, '.')))
2459         {
2460             curr++;
2461             if ((*curr >= '0') && (*curr <= '9'))
2462             {
2463                 amount += (*curr - '0') * 10;
2464             }
2465             curr++;
2466             if ((*curr >= '0') && (*curr <= '9'))
2467             {
2468                 amount += *curr - '0';
2469             }
2470         }
2471 
2472         amountMonth = atoi(amountMonthStr) * 100;
2473         if ((curr = strchr(amountMonthStr, '.')))
2474         {
2475             curr++;
2476             if ((*curr >= '0') && (*curr <= '9'))
2477             {
2478                 amountMonth += (*curr - '0') * 10;
2479             }
2480             curr++;
2481             if ((*curr >= '0') && (*curr <= '9'))
2482             {
2483                 amountMonth += *curr - '0';
2484             }
2485         }
2486 
2487         client->app->enumeratequotaitems_result(type, product, prolevel, gbstorage,
2488                                                 gbtransfer, months, amount, amountMonth,
2489                                                 currency.c_str(), description.c_str(),
2490                                                 ios_id.c_str(), android_id.c_str());
2491     }
2492 
2493     client->app->enumeratequotaitems_result(API_OK);
2494 }
2495 
CommandPurchaseAddItem(MegaClient * client,int itemclass,handle item,unsigned price,const char * currency,unsigned,const char *,handle lph,int phtype,int64_t ts)2496 CommandPurchaseAddItem::CommandPurchaseAddItem(MegaClient* client, int itemclass,
2497                                                handle item, unsigned price,
2498                                                const char* currency, unsigned /*tax*/,
2499                                                const char* /*country*/, handle lph,
2500                                                int phtype, int64_t ts)
2501 {
2502     string sprice;
2503     sprice.resize(128);
2504     sprintf((char *)sprice.data(), "%.2f", price/100.0);
2505     replace( sprice.begin(), sprice.end(), ',', '.');
2506     cmd("uts");
2507     arg("it", itemclass);
2508     arg("si", (byte*)&item, 8);
2509     arg("p", sprice.c_str());
2510     arg("c", currency);
2511     if (!ISUNDEF(lph))
2512     {
2513         if (phtype == 0) // legacy mode
2514         {
2515             arg("aff", (byte*)&lph, MegaClient::NODEHANDLE);
2516         }
2517         else
2518         {
2519             beginobject("aff");
2520             arg("id", (byte*)&lph, MegaClient::NODEHANDLE);
2521             arg("ts", ts);
2522             arg("t", phtype);   // 1=affiliate id, 2=file/folder link, 3=chat link, 4=contact link
2523             endobject();
2524         }
2525     }
2526 
2527     tag = client->reqtag;
2528 
2529     //TODO: Complete this (tax? country?)
2530 }
2531 
procresult()2532 void CommandPurchaseAddItem::procresult()
2533 {
2534     Error e;
2535     if (checkError(e, client->json))
2536     {
2537         return client->app->additem_result(e);
2538     }
2539 
2540     handle item = client->json.gethandle(8);
2541     if (item != UNDEF)
2542     {
2543         client->purchase_basket.push_back(item);
2544         client->app->additem_result(API_OK);
2545     }
2546     else
2547     {
2548         client->json.storeobject();
2549         client->app->additem_result(API_EINTERNAL);
2550     }
2551 }
2552 
CommandPurchaseCheckout(MegaClient * client,int gateway)2553 CommandPurchaseCheckout::CommandPurchaseCheckout(MegaClient* client, int gateway)
2554 {
2555     cmd("utc");
2556 
2557     beginarray("s");
2558     for (handle_vector::iterator it = client->purchase_basket.begin(); it != client->purchase_basket.end(); it++)
2559     {
2560         element((byte*)&*it, sizeof(handle));
2561     }
2562 
2563     endarray();
2564 
2565     arg("m", gateway);
2566 
2567     // empty basket
2568     client->purchase_begin();
2569 
2570     tag = client->reqtag;
2571 }
2572 
procresult()2573 void CommandPurchaseCheckout::procresult()
2574 {
2575     Error e;
2576     if (checkError(e, client->json))
2577     {
2578         return client->app->checkout_result(NULL, e);
2579     }
2580 
2581     //Expected response: "EUR":{"res":X,"code":Y}}
2582     client->json.getnameid();
2583     if (!client->json.enterobject())
2584     {
2585         LOG_err << "Parse error (CommandPurchaseCheckout)";
2586         client->app->checkout_result(NULL, API_EINTERNAL);
2587         return;
2588     }
2589 
2590     string errortype;
2591     for (;;)
2592     {
2593         switch (client->json.getnameid())
2594         {
2595             case MAKENAMEID3('r', 'e', 's'):
2596                 if (client->json.isnumeric())
2597                 {
2598                     e = (error)client->json.getint();
2599                 }
2600                 else
2601                 {
2602                     client->json.storeobject(&errortype);
2603                     if (errortype == "S")
2604                     {
2605                         errortype.clear();
2606                         e = API_OK;
2607                     }
2608                 }
2609                 break;
2610 
2611             case MAKENAMEID4('c', 'o', 'd', 'e'):
2612                 if (client->json.isnumeric())
2613                 {
2614                     e = (error)client->json.getint();
2615                 }
2616                 else
2617                 {
2618                     LOG_err << "Parse error in CommandPurchaseCheckout (code)";
2619                 }
2620                 break;
2621             case EOO:
2622                 client->json.leaveobject();
2623                 if (!errortype.size() || errortype == "FI" || e == API_OK)
2624                 {
2625                     client->app->checkout_result(NULL, e);
2626                 }
2627                 else
2628                 {
2629                     client->app->checkout_result(errortype.c_str(), e);
2630                 }
2631                 return;
2632             default:
2633                 if (!client->json.storeobject())
2634                 {
2635                     client->app->checkout_result(NULL, API_EINTERNAL);
2636                     return;
2637                 }
2638         }
2639     }
2640 }
2641 
CommandRemoveContact(MegaClient * client,const char * m,visibility_t show)2642 CommandRemoveContact::CommandRemoveContact(MegaClient* client, const char* m, visibility_t show)
2643 {
2644     this->email = m ? m : "";
2645     this->v = show;
2646 
2647     cmd("ur2");
2648     arg("u", m);
2649     arg("l", (int)show);
2650 
2651     tag = client->reqtag;
2652 }
2653 
procresult()2654 void CommandRemoveContact::procresult()
2655 {
2656     Error e;
2657     if (!checkError(e, client->json))
2658     {
2659         client->json.storeobject();
2660         e = API_OK;
2661 
2662         User *u = client->finduser(email.c_str());
2663         if (u)
2664         {
2665             u->show = v;
2666         }
2667     }
2668 
2669     client->app->removecontact_result(e);
2670 }
2671 
2672 
CommandPutMultipleUAVer(MegaClient * client,const userattr_map * attrs,int ctag)2673 CommandPutMultipleUAVer::CommandPutMultipleUAVer(MegaClient *client, const userattr_map *attrs, int ctag)
2674 {
2675     this->attrs = *attrs;
2676 
2677     cmd("upv");
2678 
2679     for (userattr_map::const_iterator it = attrs->begin(); it != attrs->end(); it++)
2680     {
2681         attr_t type = it->first;
2682 
2683         beginarray(User::attr2string(type).c_str());
2684 
2685         element((const byte *) it->second.data(), int(it->second.size()));
2686 
2687         const string *attrv = client->ownuser()->getattrversion(type);
2688         if (attrv)
2689         {
2690             element(attrv->c_str());
2691         }
2692 
2693         endarray();
2694     }
2695 
2696     tag = ctag;
2697 }
2698 
procresult()2699 void CommandPutMultipleUAVer::procresult()
2700 {
2701     Error e;
2702     if (checkError(e, client->json))
2703     {
2704         int creqtag = client->reqtag;
2705         client->reqtag = 0;
2706         client->sendevent(99419, "Error attaching keys");
2707         client->reqtag = creqtag;
2708 
2709         return client->app->putua_result(e);
2710     }
2711 
2712     User *u = client->ownuser();
2713     for(;;)   // while there are more attrs to read...
2714     {
2715         const char* ptr;
2716         const char* end;
2717 
2718         if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
2719         {
2720             break;
2721         }
2722         attr_t type = User::string2attr(string(ptr, (end-ptr)).c_str());
2723 
2724         if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
2725         {
2726             return client->app->putua_result(API_EINTERNAL);
2727         }
2728         string version = string(ptr, (end-ptr));
2729 
2730         userattr_map::iterator it = this->attrs.find(type);
2731         if (type == ATTR_UNKNOWN || version.empty() || (it == this->attrs.end()))
2732         {
2733             LOG_err << "Error in CommandPutUA. Undefined attribute or version";
2734             return client->app->putua_result(API_EINTERNAL);
2735         }
2736         else
2737         {
2738             u->setattr(type, &it->second, &version);
2739             u->setTag(tag ? tag : -1);
2740 
2741             if (type == ATTR_KEYRING)
2742             {
2743                 TLVstore *tlvRecords = TLVstore::containerToTLVrecords(&attrs[type], &client->key);
2744                 if (tlvRecords)
2745                 {
2746                     if (tlvRecords->find(EdDSA::TLV_KEY))
2747                     {
2748                         string prEd255 = tlvRecords->get(EdDSA::TLV_KEY);
2749                         if (prEd255.size() == EdDSA::SEED_KEY_LENGTH)
2750                         {
2751                             client->signkey = new EdDSA(client->rng, (unsigned char *) prEd255.data());
2752                         }
2753                     }
2754 
2755                     if (tlvRecords->find(ECDH::TLV_KEY))
2756                     {
2757                         string prCu255 = tlvRecords->get(ECDH::TLV_KEY);
2758                         if (prCu255.size() == ECDH::PRIVATE_KEY_LENGTH)
2759                         {
2760                             client->chatkey = new ECDH((unsigned char *) prCu255.data());
2761                         }
2762                     }
2763 
2764                     if (!client->chatkey || !client->chatkey->initializationOK ||
2765                             !client->signkey || !client->signkey->initializationOK)
2766                     {
2767                         client->resetKeyring();
2768 
2769                         int creqtag = client->reqtag;
2770                         client->reqtag = 0;
2771                         client->sendevent(99418, "Failed to load attached keys");
2772                         client->reqtag = creqtag;
2773                     }
2774                     else
2775                     {
2776                         int creqtag = client->reqtag;
2777                         client->reqtag = 0;
2778                         client->sendevent(99420, "Signing and chat keys attached OK");
2779                         client->reqtag = creqtag;
2780                     }
2781 
2782                     delete tlvRecords;
2783                 }
2784                 else
2785                 {
2786                     LOG_warn << "Failed to decrypt keyring after putua";
2787                 }
2788             }
2789             else if (User::isAuthring(type))
2790             {
2791                 client->mAuthRings.erase(type);
2792                 const std::unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&attrs[type], &client->key));
2793                 if (tlvRecords)
2794                 {
2795                     client->mAuthRings.emplace(type, AuthRing(type, *tlvRecords));
2796                 }
2797                 else
2798                 {
2799                     LOG_err << "Failed to decrypt keyring after putua";
2800                 }
2801             }
2802         }
2803     }
2804 
2805     client->notifyuser(u);
2806     client->app->putua_result(API_OK);
2807 }
2808 
CommandPutUAVer(MegaClient * client,attr_t at,const byte * av,unsigned avl,int ctag)2809 CommandPutUAVer::CommandPutUAVer(MegaClient* client, attr_t at, const byte* av, unsigned avl, int ctag)
2810 {
2811     this->at = at;
2812     this->av.assign((const char*)av, avl);
2813 
2814     cmd("upv");
2815 
2816     beginarray(User::attr2string(at).c_str());
2817 
2818     // if removing avatar, do not Base64 encode the attribute value
2819     if (at == ATTR_AVATAR && !strcmp((const char *)av, "none"))
2820     {
2821         element((const char*)av);
2822     }
2823     else
2824     {
2825         element(av, avl);
2826     }
2827 
2828     const string *attrv = client->ownuser()->getattrversion(at);
2829     if (client->ownuser()->isattrvalid(at) && attrv)
2830     {
2831         element(attrv->c_str());
2832     }
2833 
2834     endarray();
2835 
2836     tag = ctag;
2837 }
2838 
procresult()2839 void CommandPutUAVer::procresult()
2840 {
2841     Error e;
2842     if (checkError(e, client->json))
2843     {
2844         if (e == API_EEXPIRED)
2845         {
2846             User *u = client->ownuser();
2847             u->invalidateattr(at);
2848         }
2849 
2850         client->app->putua_result(e);
2851     }
2852     else
2853     {
2854         const char* ptr;
2855         const char* end;
2856 
2857         if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
2858         {
2859             client->app->putua_result(API_EINTERNAL);
2860             return;
2861         }
2862         attr_t at = User::string2attr(string(ptr, (end-ptr)).c_str());
2863 
2864         if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
2865         {
2866             client->app->putua_result(API_EINTERNAL);
2867             return;
2868         }
2869         string v = string(ptr, (end-ptr));
2870 
2871         if (at == ATTR_UNKNOWN || v.empty() || (this->at != at))
2872         {
2873             LOG_err << "Error in CommandPutUA. Undefined attribute or version";
2874             client->app->putua_result(API_EINTERNAL);
2875         }
2876         else
2877         {
2878             User *u = client->ownuser();
2879             u->setattr(at, &av, &v);
2880             u->setTag(tag ? tag : -1);
2881 
2882             if (User::isAuthring(at))
2883             {
2884                 client->mAuthRings.erase(at);
2885                 const std::unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&av, &client->key));
2886                 if (tlvRecords)
2887                 {
2888                     client->mAuthRings.emplace(at, AuthRing(at, *tlvRecords));
2889                 }
2890                 else
2891                 {
2892                     LOG_err << "Failed to decrypt " << User::attr2string(at) << " after putua";
2893                 }
2894             }
2895 
2896             client->notifyuser(u);
2897             client->app->putua_result(API_OK);
2898         }
2899     }
2900 }
2901 
CommandPutUA(MegaClient *,attr_t at,const byte * av,unsigned avl,int ctag,handle lph,int phtype,int64_t ts)2902 CommandPutUA::CommandPutUA(MegaClient* /*client*/, attr_t at, const byte* av, unsigned avl, int ctag, handle lph, int phtype, int64_t ts)
2903 {
2904     this->at = at;
2905     this->av.assign((const char*)av, avl);
2906 
2907     cmd("up");
2908 
2909     string an = User::attr2string(at);
2910 
2911     // if removing avatar, do not Base64 encode the attribute value
2912     if (at == ATTR_AVATAR && !strcmp((const char *)av, "none"))
2913     {
2914         arg(an.c_str(),(const char *)av, avl);
2915     }
2916     else
2917     {
2918         arg(an.c_str(), av, avl);
2919     }
2920 
2921     if (!ISUNDEF(lph))
2922     {
2923         beginobject("aff");
2924         arg("id", (byte*)&lph, MegaClient::NODEHANDLE);
2925         arg("ts", ts);
2926         arg("t", phtype);   // 1=affiliate id, 2=file/folder link, 3=chat link, 4=contact link
2927         endobject();
2928     }
2929 
2930     tag = ctag;
2931 }
2932 
procresult()2933 void CommandPutUA::procresult()
2934 {
2935     Error e;
2936     if (!checkError(e, client->json))
2937     {
2938         client->json.storeobject(); // [<uh>]
2939         e = API_OK;
2940 
2941         User *u = client->ownuser();
2942         assert(u);
2943         if (!u)
2944         {
2945             LOG_err << "Own user not found when attempting to set user attributes";
2946             client->app->putua_result(API_EACCESS);
2947             return;
2948         }
2949         u->setattr(at, &av, NULL);
2950         u->setTag(tag ? tag : -1);
2951         client->notifyuser(u);
2952 
2953         if (at == ATTR_DISABLE_VERSIONS)
2954         {
2955             client->versions_disabled = (av == "1");
2956             if (client->versions_disabled)
2957             {
2958                 LOG_info << "File versioning is disabled";
2959             }
2960             else
2961             {
2962                 LOG_info << "File versioning is enabled";
2963             }
2964         }
2965         else if (at == ATTR_UNSHAREABLE_KEY)
2966         {
2967             LOG_info << "Unshareable key successfully created";
2968             client->unshareablekey.swap(av);
2969         }
2970     }
2971 
2972     client->app->putua_result(e);
2973 }
2974 
CommandGetUA(MegaClient *,const char * uid,attr_t at,const char * ph,int ctag)2975 CommandGetUA::CommandGetUA(MegaClient* /*client*/, const char* uid, attr_t at, const char* ph, int ctag)
2976 {
2977     this->uid = uid;
2978     this->at = at;
2979     this->ph = ph ? string(ph) : "";
2980 
2981     if (ph && ph[0])
2982     {
2983         cmd("mcuga");
2984         arg("ph", ph);
2985     }
2986     else
2987     {
2988         cmd("uga");
2989     }
2990 
2991     arg("u", uid);
2992     arg("ua", User::attr2string(at).c_str());
2993     arg("v", 1);
2994     tag = ctag;
2995 }
2996 
procresult()2997 void CommandGetUA::procresult()
2998 {
2999     User *u = client->finduser(uid.c_str());
3000 
3001     Error e;
3002     if (checkError(e, client->json))
3003     {
3004         if (e == API_ENOENT && u)
3005         {
3006             u->removeattr(at);
3007         }
3008 
3009         client->app->getua_result(e);
3010 
3011         if (isFromChatPreview())    // if `mcuga` was sent, no need to do anything else
3012         {
3013             return;
3014         }
3015 
3016         if (u && u->userhandle == client->me && e != API_EBLOCKED)
3017         {
3018             if (client->fetchingkeys && at == ATTR_SIG_RSA_PUBK)
3019             {
3020                 client->initializekeys(); // we have now all the required data
3021             }
3022 
3023             if (e == API_ENOENT && User::isAuthring(at))
3024             {
3025                 // authring not created yet, will do it upon retrieval of public keys
3026                 client->mAuthRings.erase(at);
3027                 client->mAuthRings.emplace(at, AuthRing(at, TLVstore()));
3028 
3029                 if (client->mFetchingAuthrings && client->mAuthRings.size() == 3)
3030                 {
3031                     client->mFetchingAuthrings = false;
3032                     client->fetchContactsKeys();
3033                 }
3034             }
3035         }
3036 
3037         // if the attr does not exist, initialize it
3038         if (at == ATTR_DISABLE_VERSIONS && e == API_ENOENT)
3039         {
3040             LOG_info << "File versioning is enabled";
3041             client->versions_disabled = false;
3042         }
3043     }
3044     else
3045     {
3046         const char* ptr;
3047         const char* end;
3048         string value, version, buf;
3049 
3050         //If we are in preview mode, we only can retrieve atributes with mcuga and the response format is different
3051         if (isFromChatPreview())
3052         {
3053             ptr = client->json.getvalue();
3054             if (!ptr || !(end = strchr(ptr, '"')))
3055             {
3056                 client->app->getua_result(API_EINTERNAL);
3057             }
3058             else
3059             {
3060                 // convert from ASCII to binary the received data
3061                 buf.assign(ptr, (end-ptr));
3062                 value.resize(buf.size() / 4 * 3 + 3);
3063                 value.resize(Base64::atob(buf.data(), (byte *)value.data(), int(value.size())));
3064                 client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3065             }
3066             return;
3067         }
3068 
3069         for (;;)
3070         {
3071             switch (client->json.getnameid())
3072             {
3073                 case MAKENAMEID2('a','v'):
3074                 {
3075                     if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
3076                     {
3077                         client->app->getua_result(API_EINTERNAL);
3078                         if (client->fetchingkeys && at == ATTR_SIG_RSA_PUBK && u && u->userhandle == client->me)
3079                         {
3080                             client->initializekeys(); // we have now all the required data
3081                         }
3082                         return;
3083                     }
3084                     buf.assign(ptr, (end-ptr));
3085                     break;
3086                 }
3087                 case 'v':
3088                 {
3089                     if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
3090                     {
3091                         client->app->getua_result(API_EINTERNAL);
3092                         if (client->fetchingkeys && at == ATTR_SIG_RSA_PUBK && u && u->userhandle == client->me)
3093                         {
3094                             client->initializekeys(); // we have now all the required data
3095                         }
3096                         return;
3097                     }
3098                     version.assign(ptr, (end-ptr));
3099                     break;
3100                 }
3101                 case EOO:
3102                 {
3103                     // if there's no avatar, the value is "none" (not Base64 encoded)
3104                     if (u && at == ATTR_AVATAR && buf == "none")
3105                     {
3106                         u->setattr(at, NULL, &version);
3107                         u->setTag(tag ? tag : -1);
3108                         client->app->getua_result(API_ENOENT);
3109                         client->notifyuser(u);
3110                         return;
3111                     }
3112 
3113                     // convert from ASCII to binary the received data
3114                     value.resize(buf.size() / 4 * 3 + 3);
3115                     value.resize(Base64::atob(buf.data(), (byte *)value.data(), int(value.size())));
3116 
3117                     // Some attributes don't keep historic records, ie. *!authring or *!lstint
3118                     // (none of those attributes are used by the SDK yet)
3119                     // bool nonHistoric = (attributename.at(1) == '!');
3120 
3121                     // handle the attribute data depending on the scope
3122                     char scope = User::scope(at);
3123 
3124                     if (!u) // retrieval of attributes without contact-relationship
3125                     {
3126                         if (at == ATTR_AVATAR && buf == "none")
3127                         {
3128                             client->app->getua_result(API_ENOENT);
3129                         }
3130                         else
3131                         {
3132                             client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3133                         }
3134                         return;
3135                     }
3136 
3137                     switch (scope)
3138                     {
3139                         case '*':   // private, encrypted
3140                         {
3141                             // decrypt the data and build the TLV records
3142                             TLVstore *tlvRecords = TLVstore::containerToTLVrecords(&value, &client->key);
3143                             if (!tlvRecords)
3144                             {
3145                                 LOG_err << "Cannot extract TLV records for private attribute " << User::attr2string(at);
3146                                 client->app->getua_result(API_EINTERNAL);
3147                                 return;
3148                             }
3149 
3150                             // store the value for private user attributes (decrypted version of serialized TLV)
3151                             string *tlvString = tlvRecords->tlvRecordsToContainer(client->rng, &client->key);
3152                             u->setattr(at, tlvString, &version);
3153                             delete tlvString;
3154                             client->app->getua_result(tlvRecords, at);
3155 
3156                             if (User::isAuthring(at))
3157                             {
3158                                 client->mAuthRings.erase(at);
3159                                 client->mAuthRings.emplace(at, AuthRing(at, *tlvRecords));
3160 
3161                                 if (client->mFetchingAuthrings && client->mAuthRings.size() == 3)
3162                                 {
3163                                     client->mFetchingAuthrings = false;
3164                                     client->fetchContactsKeys();
3165                                 }
3166                             }
3167 
3168                             delete tlvRecords;
3169                             break;
3170                         }
3171                         case '+':   // public
3172                         {
3173                             u->setattr(at, &value, &version);
3174                             client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3175 
3176                             if (client->fetchingkeys && at == ATTR_SIG_RSA_PUBK && u && u->userhandle == client->me)
3177                             {
3178                                 client->initializekeys(); // we have now all the required data
3179                             }
3180 
3181                             if (!u->isTemporary && u->userhandle != client->me)
3182                             {
3183                                 if (at == ATTR_ED25519_PUBK || at == ATTR_CU25519_PUBK)
3184                                 {
3185                                     client->trackKey(at, u->userhandle, value);
3186                                 }
3187                                 else if (at == ATTR_SIG_CU255_PUBK || at == ATTR_SIG_RSA_PUBK)
3188                                 {
3189                                     client->trackSignature(at, u->userhandle, value);
3190                                 }
3191                             }
3192                             break;
3193                         }
3194                         case '#':   // protected
3195                         {
3196                             u->setattr(at, &value, &version);
3197                             client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3198                             break;
3199                         }
3200                         case '^': // private, non-encrypted
3201                         {
3202                             // store the value in cache in binary format
3203                             u->setattr(at, &value, &version);
3204                             client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3205 
3206                             if (at == ATTR_DISABLE_VERSIONS)
3207                             {
3208                                 client->versions_disabled = !strcmp(value.data(), "1");
3209                                 if (client->versions_disabled)
3210                                 {
3211                                     LOG_info << "File versioning is disabled";
3212                                 }
3213                                 else
3214                                 {
3215                                     LOG_info << "File versioning is enabled";
3216                                 }
3217                             }
3218                             break;
3219                         }
3220                         default:    // legacy attributes or unknown attribute
3221                         {
3222                             if (at != ATTR_FIRSTNAME &&           // protected
3223                                     at != ATTR_LASTNAME &&        // protected
3224                                     at != ATTR_COUNTRY  &&        // private
3225                                     at != ATTR_BIRTHDAY &&        // private
3226                                     at != ATTR_BIRTHMONTH &&      // private
3227                                     at != ATTR_BIRTHYEAR)     // private
3228                             {
3229                                 LOG_err << "Unknown received attribute: " << User::attr2string(at);
3230                                 client->app->getua_result(API_EINTERNAL);
3231                                 return;
3232                             }
3233 
3234                             u->setattr(at, &value, &version);
3235                             client->app->getua_result((byte*) value.data(), unsigned(value.size()), at);
3236                             break;
3237                         }
3238 
3239                     }   // switch (scope)
3240 
3241                     u->setTag(tag ? tag : -1);
3242                     client->notifyuser(u);
3243                     return;
3244                 }
3245                 default:
3246                 {
3247                     if (!client->json.storeobject())
3248                     {
3249                         LOG_err << "Error in CommandGetUA. Parse error";
3250                         client->app->getua_result(API_EINTERNAL);
3251                         if (client->fetchingkeys && at == ATTR_SIG_RSA_PUBK && u && u->userhandle == client->me)
3252                         {
3253                             client->initializekeys(); // we have now all the required data
3254                         }
3255                         return;
3256                     }
3257                 }
3258 
3259             }   // switch (nameid)
3260         }
3261     }
3262 }
3263 
3264 #ifdef DEBUG
CommandDelUA(MegaClient * client,const char * an)3265 CommandDelUA::CommandDelUA(MegaClient *client, const char *an)
3266 {
3267     this->an = an;
3268 
3269     cmd("upr");
3270     arg("ua", an);
3271 
3272     arg("v", 1);    // returns the new version for the (removed) null value
3273 
3274     tag = client->reqtag;
3275 }
3276 
procresult()3277 void CommandDelUA::procresult()
3278 {
3279     Error e;
3280     if (checkError(e, client->json))
3281     {
3282         client->app->delua_result(e);
3283     }
3284     else
3285     {
3286         const char* ptr;
3287         const char* end;
3288         if (!(ptr = client->json.getvalue()) || !(end = strchr(ptr, '"')))
3289         {
3290             client->app->delua_result(API_EINTERNAL);
3291             return;
3292         }
3293 
3294         User *u = client->ownuser();
3295         attr_t at = User::string2attr(an.c_str());
3296         string version(ptr, (end-ptr));
3297 
3298         u->removeattr(at, &version); // store version to filter corresponding AP in order to avoid double onUsersUpdate()
3299 
3300         if (at == ATTR_KEYRING)
3301         {
3302             client->resetKeyring();
3303         }
3304         else if (User::isAuthring(at))
3305         {
3306             client->mAuthRings.emplace(at, AuthRing(at, TLVstore()));
3307             client->getua(u, at, 0);
3308         }
3309 
3310         client->notifyuser(u);
3311         client->app->delua_result(API_OK);
3312     }
3313 }
3314 
CommandSendDevCommand(MegaClient * client,const char * command,const char * email)3315 CommandSendDevCommand::CommandSendDevCommand(MegaClient *client, const char *command, const char *email)
3316 {
3317     cmd("dev");
3318 
3319     arg("aa", command);
3320     if (email)
3321     {
3322         arg("t", email);
3323     }
3324 
3325     tag = client->reqtag;
3326 }
3327 
procresult()3328 void CommandSendDevCommand::procresult()
3329 {
3330     if (client->json.isnumeric())
3331     {
3332         client->app->senddevcommand_result(static_cast<int>(client->json.getint()));
3333     }
3334     else
3335     {
3336         client->app->senddevcommand_result(API_EINTERNAL);
3337     }
3338 
3339 }
3340 
3341 #endif  // #ifdef DEBUG
3342 
CommandGetUserEmail(MegaClient * client,const char * uid)3343 CommandGetUserEmail::CommandGetUserEmail(MegaClient *client, const char *uid)
3344 {
3345     cmd("uge");
3346     arg("u", uid);
3347 
3348     tag = client->reqtag;
3349 }
3350 
procresult()3351 void CommandGetUserEmail::procresult()
3352 {
3353     Error e;
3354     if (checkError(e, client->json))
3355     {
3356         return client->app->getuseremail_result(NULL, e);
3357     }
3358 
3359     string email;
3360     if (!client->json.storeobject(&email))
3361     {
3362         return client->app->getuseremail_result(NULL, API_EINTERNAL);
3363     }
3364     else
3365     {
3366         return client->app->getuseremail_result(&email, API_OK);
3367     }
3368 }
3369 
3370 // set node keys (e.g. to convert asymmetric keys to symmetric ones)
CommandNodeKeyUpdate(MegaClient * client,handle_vector * v)3371 CommandNodeKeyUpdate::CommandNodeKeyUpdate(MegaClient* client, handle_vector* v)
3372 {
3373     byte nodekey[FILENODEKEYLENGTH];
3374 
3375     cmd("k");
3376     beginarray("nk");
3377 
3378     for (size_t i = v->size(); i--;)
3379     {
3380         handle h = (*v)[i];
3381 
3382         Node* n;
3383 
3384         if ((n = client->nodebyhandle(h)))
3385         {
3386             client->key.ecb_encrypt((byte*)n->nodekey().data(), nodekey, n->nodekey().size());
3387 
3388             element(h, MegaClient::NODEHANDLE);
3389             element(nodekey, int(n->nodekey().size()));
3390         }
3391     }
3392 
3393     endarray();
3394 }
3395 
CommandSingleKeyCR(handle sh,handle nh,const byte * key,size_t keylen)3396 CommandSingleKeyCR::CommandSingleKeyCR(handle sh, handle nh, const byte* key, size_t keylen)
3397 {
3398     cmd("k");
3399     beginarray("cr");
3400 
3401     beginarray();
3402     element(sh, MegaClient::NODEHANDLE);
3403     endarray();
3404 
3405     beginarray();
3406     element(nh, MegaClient::NODEHANDLE);
3407     endarray();
3408 
3409     beginarray();
3410     element(0);
3411     element(0);
3412     element(key, static_cast<int>(keylen));
3413     endarray();
3414 
3415     endarray();
3416 }
3417 
CommandKeyCR(MegaClient *,node_vector * rshares,node_vector * rnodes,const char * keys)3418 CommandKeyCR::CommandKeyCR(MegaClient* /*client*/, node_vector* rshares, node_vector* rnodes, const char* keys)
3419 {
3420     cmd("k");
3421     beginarray("cr");
3422 
3423     beginarray();
3424     for (int i = 0; i < (int)rshares->size(); i++)
3425     {
3426         element((*rshares)[i]->nodehandle, MegaClient::NODEHANDLE);
3427     }
3428 
3429     endarray();
3430 
3431     beginarray();
3432     for (int i = 0; i < (int)rnodes->size(); i++)
3433     {
3434         element((*rnodes)[i]->nodehandle, MegaClient::NODEHANDLE);
3435     }
3436 
3437     endarray();
3438 
3439     beginarray();
3440     appendraw(keys);
3441     endarray();
3442 
3443     endarray();
3444 }
3445 
3446 // a == ACCESS_UNKNOWN: request public key for user handle and respond with
3447 // share key for sn
3448 // otherwise: request public key for user handle and continue share creation
3449 // for node sn to user u with access a
CommandPubKeyRequest(MegaClient * client,User * user)3450 CommandPubKeyRequest::CommandPubKeyRequest(MegaClient* client, User* user)
3451 {
3452     cmd("uk");
3453     arg("u", user->uid.c_str());
3454 
3455     u = user;
3456     tag = client->reqtag;
3457 }
3458 
procresult()3459 void CommandPubKeyRequest::procresult()
3460 {
3461     byte pubkbuf[AsymmCipher::MAXKEYLENGTH];
3462     int len_pubk = 0;
3463     handle uh = UNDEF;
3464 
3465     Error e;
3466     if (checkError(e, client->json))
3467     {
3468         if(e != API_ENOENT) //API_ENOENT = unregistered users or accounts without a public key yet
3469         {
3470             LOG_err << "Unexpected error in CommandPubKeyRequest: " << e;
3471         }
3472     }
3473     else
3474     {
3475         bool finished = false;
3476         while (!finished)
3477         {
3478             switch (client->json.getnameid())
3479             {
3480                 case 'u':
3481                     uh = client->json.gethandle(MegaClient::USERHANDLE);
3482                     break;
3483 
3484                 case MAKENAMEID4('p', 'u', 'b', 'k'):
3485                     len_pubk = client->json.storebinary(pubkbuf, sizeof pubkbuf);
3486                     break;
3487 
3488                 case EOO:
3489                     if (!u) // user has cancelled the account
3490                     {
3491                         return;
3492                     }
3493 
3494                     if (!ISUNDEF(uh))
3495                     {
3496                         client->mapuser(uh, u->email.c_str());
3497                         if (u->isTemporary && u->uid == u->email) //update uid with the received USERHANDLE (will be used as target for putnodes)
3498                         {
3499                             u->uid = Base64Str<MegaClient::USERHANDLE>(uh);
3500                         }
3501                     }
3502 
3503                     if (client->fetchingkeys && u->userhandle == client->me && len_pubk)
3504                     {
3505                         client->pubk.setkey(AsymmCipher::PUBKEY, pubkbuf, len_pubk);
3506                         return;
3507                     }
3508 
3509                     if (len_pubk && !u->pubk.setkey(AsymmCipher::PUBKEY, pubkbuf, len_pubk))
3510                     {
3511                         len_pubk = 0;
3512                     }
3513 
3514                     if (!u->isTemporary && u->userhandle != client->me && len_pubk && u->pubk.isvalid())
3515                     {
3516                         string pubkstr;
3517                         u->pubk.serializekeyforjs(pubkstr);
3518                         client->trackKey(ATTR_UNKNOWN, u->userhandle, pubkstr);
3519                     }
3520                     finished = true;
3521                     break;
3522 
3523                 default:
3524                     if (client->json.storeobject())
3525                     {
3526                         continue;
3527                     }
3528                     len_pubk = 0;
3529                     finished = true;
3530                     break;
3531             }
3532         }
3533     }
3534 
3535     // satisfy all pending PubKeyAction requests for this user
3536     while (u->pkrs.size())
3537     {
3538         client->restag = tag;
3539         u->pkrs[0]->proc(client, u);
3540         u->pkrs.pop_front();
3541     }
3542 
3543     if (len_pubk && !u->isTemporary)
3544     {
3545         client->notifyuser(u);
3546     }
3547 
3548     if (u->isTemporary)
3549     {
3550         delete u;
3551         u = NULL;
3552     }
3553 
3554     return;
3555 }
3556 
invalidateUser()3557 void CommandPubKeyRequest::invalidateUser()
3558 {
3559     u = NULL;
3560 }
3561 
CommandGetUserData(MegaClient * client)3562 CommandGetUserData::CommandGetUserData(MegaClient *client)
3563 {
3564     cmd("ug");
3565     arg("v", 1);
3566 
3567     tag = client->reqtag;
3568 }
3569 
procresult()3570 void CommandGetUserData::procresult()
3571 {
3572     string name;
3573     string pubk;
3574     string privk;
3575     string k;
3576     byte privkbuf[AsymmCipher::MAXKEYLENGTH * 2];
3577     int len_privk = 0;
3578     byte pubkbuf[AsymmCipher::MAXKEYLENGTH];
3579     int len_pubk = 0;
3580     m_time_t since = 0;
3581     int v = 0;
3582     string salt;
3583     string smsv;
3584     string lastname;
3585     string versionLastname;
3586     string firstname;
3587     string versionFirstname;
3588     string language;
3589     string versionLanguage;
3590     string pwdReminderDialog;
3591     string versionPwdReminderDialog;
3592     string pushSetting;
3593     string versionPushSetting;
3594     string contactLinkVerification;
3595     string versionContactLinkVerification;
3596     handle me = UNDEF;
3597     string chatFolder;
3598     string versionChatFolder;
3599     string cameraUploadFolder;
3600     string versionCameraUploadFolder;
3601     string aliases;
3602     string versionAliases;
3603     string disableVersions;
3604     string versionDisableVersions;
3605     string country;
3606     string versionCountry;
3607     string birthday;
3608     string versionBirthday;
3609     string birthmonth;
3610     string versionBirthmonth;
3611     string birthyear;
3612     string versionBirthyear;
3613     string email;
3614     string unshareableKey;
3615     string versionUnshareableKey;
3616     string deviceNames;
3617     string versionDeviceNames;
3618 
3619     bool uspw = false;
3620     vector<m_time_t> warningTs;
3621     m_time_t deadlineTs = -1;
3622 
3623     bool b = false;
3624     BizMode m = BIZ_MODE_UNKNOWN;
3625     BizStatus s = BIZ_STATUS_UNKNOWN;
3626     std::set<handle> masters;
3627     std::vector<std::pair<BizStatus, m_time_t>> sts;
3628 
3629     Error e;
3630     if (checkError(e, client->json))
3631     {
3632         if (!e)
3633         {
3634             e = API_ENOENT;
3635         }
3636         return client->app->userdata_result(NULL, NULL, NULL, e);
3637     }
3638 
3639     for (;;)
3640     {
3641         string attributeName = client->json.getnameWithoutAdvance();
3642         switch (client->json.getnameid())
3643         {
3644         case MAKENAMEID3('a', 'a', 'v'):    // account authentication version
3645             v = (int)client->json.getint();
3646             break;
3647 
3648         case MAKENAMEID3('a', 'a', 's'):    // account authentication salt
3649             client->json.storeobject(&salt);
3650             break;
3651 
3652         case MAKENAMEID4('n', 'a', 'm', 'e'):
3653             client->json.storeobject(&name);
3654             break;
3655 
3656         case 'k':   // master key
3657             k.resize(SymmCipher::KEYLENGTH);
3658             client->json.storebinary((byte *)k.data(), int(k.size()));
3659             break;
3660 
3661         case MAKENAMEID5('s', 'i', 'n', 'c', 'e'):
3662             since = client->json.getint();
3663             break;
3664 
3665         case MAKENAMEID4('p', 'u', 'b', 'k'):   // RSA public key
3666             client->json.storeobject(&pubk);
3667             len_pubk = Base64::atob(pubk.c_str(), pubkbuf, sizeof pubkbuf);
3668             break;
3669 
3670         case MAKENAMEID5('p', 'r', 'i', 'v', 'k'):  // RSA private key (encrypted to MK)
3671             len_privk = client->json.storebinary(privkbuf, sizeof privkbuf);
3672             break;
3673 
3674         case MAKENAMEID5('f', 'l', 'a', 'g', 's'):
3675             if (client->json.enterobject())
3676             {
3677                 if (client->readmiscflags(&client->json) != API_OK)
3678                 {
3679                     return client->app->userdata_result(NULL, NULL, NULL, API_EINTERNAL);
3680                 }
3681                 client->json.leaveobject();
3682             }
3683             break;
3684 
3685         case 'u':
3686             me = client->json.gethandle(MegaClient::USERHANDLE);
3687             break;
3688 
3689         case MAKENAMEID8('l', 'a', 's', 't', 'n', 'a', 'm', 'e'):
3690             parseUserAttribute(lastname, versionLastname);
3691             break;
3692 
3693         case MAKENAMEID6('^', '!', 'l', 'a', 'n', 'g'):
3694             parseUserAttribute(language, versionLanguage);
3695             break;
3696 
3697         case MAKENAMEID8('b', 'i', 'r', 't', 'h', 'd', 'a', 'y'):
3698             parseUserAttribute(birthday, versionBirthday);
3699             break;
3700 
3701         case MAKENAMEID7('c', 'o', 'u', 'n', 't', 'r', 'y'):
3702             parseUserAttribute(country, versionCountry);
3703             break;
3704 
3705         case MAKENAMEID4('^', '!', 'p', 's'):
3706             parseUserAttribute(pushSetting, versionPushSetting);
3707             break;
3708 
3709         case MAKENAMEID5('^', '!', 'p', 'r', 'd'):
3710             parseUserAttribute(pwdReminderDialog, versionPwdReminderDialog);
3711             break;
3712 
3713         case MAKENAMEID4('^', 'c', 'l', 'v'):
3714             parseUserAttribute(contactLinkVerification, versionContactLinkVerification);
3715             break;
3716 
3717         case MAKENAMEID4('^', '!', 'd', 'v'):
3718             parseUserAttribute(disableVersions, versionDisableVersions);
3719             break;
3720 
3721         case MAKENAMEID4('*', '!', 'c', 'f'):
3722             parseUserAttribute(chatFolder, versionChatFolder);
3723             break;
3724 
3725         case MAKENAMEID5('*', '!', 'c', 'a', 'm'):
3726             parseUserAttribute(cameraUploadFolder, versionCameraUploadFolder);
3727             break;
3728 
3729         case MAKENAMEID8('*', '!', '>', 'a', 'l', 'i', 'a', 's'):
3730             parseUserAttribute(aliases, versionAliases);
3731             break;
3732 
3733         case MAKENAMEID5('e', 'm', 'a', 'i', 'l'):
3734             client->json.storeobject(&email);
3735             break;
3736 
3737         case MAKENAMEID5('*', '~', 'u', 's', 'k'):
3738             parseUserAttribute(unshareableKey, versionUnshareableKey, false);
3739             break;
3740 
3741         case MAKENAMEID4('*', '!', 'd', 'n'):
3742             parseUserAttribute(deviceNames, versionDeviceNames);
3743             break;
3744 
3745         case 'b':   // business account's info
3746             assert(!b);
3747             b = true;
3748             if (client->json.enterobject())
3749             {
3750                 bool endobject = false;
3751                 while (!endobject)
3752                 {
3753                     switch (client->json.getnameid())
3754                     {
3755                         case 's':   // status
3756                             // -1: expired, 1: active, 2: grace-period
3757                             s = BizStatus(client->json.getint32());
3758                             break;
3759 
3760                         case 'm':   // mode
3761                             m = BizMode(client->json.getint32());
3762                             break;
3763 
3764                         case MAKENAMEID2('m', 'u'):
3765                             if (client->json.enterarray())
3766                             {
3767                                 for (;;)
3768                                 {
3769                                     handle uh = client->json.gethandle(MegaClient::USERHANDLE);
3770                                     if (!ISUNDEF(uh))
3771                                     {
3772                                         masters.emplace(uh);
3773                                     }
3774                                     else
3775                                     {
3776                                         break;
3777                                     }
3778                                 }
3779                                 client->json.leavearray();
3780                             }
3781                             break;
3782 
3783                         case MAKENAMEID3('s', 't', 's'):    // status timestamps
3784                             // ie. "sts":[{"s":-1,"ts":1566182227},{"s":1,"ts":1563590227}]
3785                             client->json.enterarray();
3786                             while (client->json.enterobject())
3787                             {
3788                                 BizStatus status = BIZ_STATUS_UNKNOWN;
3789                                 m_time_t ts = 0;
3790 
3791                                 bool exit = false;
3792                                 while (!exit)
3793                                 {
3794                                     switch (client->json.getnameid())
3795                                     {
3796                                         case 's':
3797                                            status = BizStatus(client->json.getint());
3798                                            break;
3799 
3800                                         case MAKENAMEID2('t', 's'):
3801                                            ts = client->json.getint();
3802                                            break;
3803 
3804                                         case EOO:
3805                                             if (status != BIZ_STATUS_UNKNOWN && ts != 0)
3806                                             {
3807                                                 sts.push_back(std::make_pair(status, ts));
3808                                             }
3809                                             else
3810                                             {
3811                                                 LOG_warn << "Unpaired/missing business status-ts in b.sts";
3812                                             }
3813                                             exit = true;
3814                                             break;
3815 
3816                                         default:
3817                                             if (!client->json.storeobject())
3818                                             {
3819                                                 return client->app->userdata_result(NULL, NULL, NULL, API_EINTERNAL);
3820                                             }
3821                                     }
3822                                 }
3823                                 client->json.leaveobject();
3824                             }
3825                             client->json.leavearray();
3826                             break;
3827 
3828                         case EOO:
3829                             endobject = true;
3830                             break;
3831 
3832                         default:
3833                             if (!client->json.storeobject())
3834                             {
3835                                 return client->app->userdata_result(NULL, NULL, NULL, API_EINTERNAL);
3836                             }
3837                     }
3838                 }
3839                 client->json.leaveobject();
3840             }
3841             break;
3842 
3843         case MAKENAMEID4('s', 'm', 's', 'v'):   // SMS verified phone number
3844             if (!client->json.storeobject(&smsv))
3845             {
3846                 LOG_err << "Invalid verified phone number (smsv)";
3847                 assert(false);
3848             }
3849             break;
3850 
3851         case MAKENAMEID4('u', 's', 'p', 'w'):   // user paywall data
3852         {
3853             uspw = true;
3854 
3855             if (client->json.enterobject())
3856             {
3857                 bool endobject = false;
3858                 while (!endobject)
3859                 {
3860                     switch (client->json.getnameid())
3861                     {
3862                         case MAKENAMEID2('d', 'l'): // deadline timestamp
3863                             deadlineTs = client->json.getint();
3864                             break;
3865 
3866                         case MAKENAMEID3('w', 't', 's'):    // warning timestamps
3867                             // ie. "wts":[1591803600,1591813600,1591823600
3868 
3869                             if (client->json.enterarray())
3870                             {
3871                                 m_time_t ts;
3872                                 while (client->json.isnumeric() && (ts = client->json.getint()) != -1)
3873                                 {
3874                                     warningTs.push_back(ts);
3875                                 }
3876 
3877                                 client->json.leavearray();
3878                             }
3879                             break;
3880 
3881                         case EOO:
3882                             endobject = true;
3883                             break;
3884 
3885                         default:
3886                             if (!client->json.storeobject())
3887                             {
3888                                 return client->app->userdata_result(NULL, NULL, NULL, API_EINTERNAL);
3889                             }
3890                     }
3891                 }
3892                 client->json.leaveobject();
3893             }
3894             break;
3895         }
3896 
3897         case EOO:
3898         {
3899             assert(me == client->me);
3900 
3901             if (len_privk)
3902             {
3903                 client->key.ecb_decrypt(privkbuf, len_privk);
3904                 privk.resize(AsymmCipher::MAXKEYLENGTH * 2);
3905                 privk.resize(Base64::btoa(privkbuf, len_privk, (char *)privk.data()));
3906 
3907                 // RSA private key should be already assigned at login
3908                 assert(privk == client->mPrivKey);
3909                 if (client->mPrivKey.empty())
3910                 {
3911                     LOG_warn << "Private key not set by login, setting at `ug` response...";
3912                     if (!client->asymkey.setkey(AsymmCipher::PRIVKEY, privkbuf, len_privk))
3913                     {
3914                         LOG_warn << "Error checking private key at `ug` response";
3915                     }
3916                 }
3917             }
3918 
3919             if (len_pubk)
3920             {
3921                 client->pubk.setkey(AsymmCipher::PUBKEY, pubkbuf, len_pubk);
3922             }
3923 
3924             if (v)
3925             {
3926                 client->accountversion = v;
3927             }
3928 
3929             if (salt.size())
3930             {
3931                 Base64::atob(salt, client->accountsalt);
3932             }
3933 
3934             client->accountsince = since;
3935             client->mSmsVerifiedPhone = smsv;
3936 
3937             client->k = k;
3938 
3939             client->btugexpiration.backoff(MegaClient::USER_DATA_EXPIRATION_BACKOFF_SECS * 10);
3940             client->cachedug = true;
3941 
3942             // pre-load received user attributes into cache
3943             User* u = client->ownuser();
3944             if (u)
3945             {
3946                 int changes = 0;
3947                 if (u->email.empty())
3948                 {
3949                     u->email = email;
3950                 }
3951 
3952                 if (firstname.size())
3953                 {
3954                     changes += u->updateattr(ATTR_FIRSTNAME, &firstname, &versionFirstname);
3955                 }
3956 
3957                 if (lastname.size())
3958                 {
3959                     changes += u->updateattr(ATTR_LASTNAME, &lastname, &versionLastname);
3960                 }
3961 
3962                 if (language.size())
3963                 {
3964                     changes += u->updateattr(ATTR_LANGUAGE, &language, &versionLanguage);
3965                 }
3966 
3967                 if (birthday.size())
3968                 {
3969                     changes += u->updateattr(ATTR_BIRTHDAY, &birthday, &versionBirthday);
3970                 }
3971 
3972                 if (birthmonth.size())
3973                 {
3974                     changes += u->updateattr(ATTR_BIRTHMONTH, &birthmonth, &versionBirthmonth);
3975                 }
3976 
3977                 if (birthyear.size())
3978                 {
3979                     changes += u->updateattr(ATTR_BIRTHYEAR, &birthyear, &versionBirthyear);
3980                 }
3981 
3982                 if (country.size())
3983                 {
3984                     changes += u->updateattr(ATTR_COUNTRY, &country, &versionCountry);
3985                 }
3986 
3987                 if (pwdReminderDialog.size())
3988                 {
3989                     changes += u->updateattr(ATTR_PWD_REMINDER, &pwdReminderDialog, &versionPwdReminderDialog);
3990                 }
3991 
3992                 if (pushSetting.size())
3993                 {
3994                     changes += u->updateattr(ATTR_PUSH_SETTINGS, &pushSetting, &versionPushSetting);
3995 
3996                     // initialize the settings for the intermediate layer by simulating there was a getua()
3997                     client->app->getua_result((byte*) pushSetting.data(), (unsigned) pushSetting.size(), ATTR_PUSH_SETTINGS);
3998                 }
3999 
4000                 if (contactLinkVerification.size())
4001                 {
4002                     changes += u->updateattr(ATTR_CONTACT_LINK_VERIFICATION, &contactLinkVerification, &versionContactLinkVerification);
4003                 }
4004 
4005                 if (disableVersions.size())
4006                 {
4007                     changes += u->updateattr(ATTR_DISABLE_VERSIONS, &disableVersions, &versionDisableVersions);
4008 
4009                     // initialize the status of file-versioning for the client
4010                     client->versions_disabled = (disableVersions == "1");
4011                     if (client->versions_disabled)
4012                     {
4013                         LOG_info << "File versioning is disabled";
4014                     }
4015                     else
4016                     {
4017                         LOG_info << "File versioning is enabled";
4018                     }
4019                 }
4020                 else    // attribute does not exists
4021                 {
4022                     LOG_info << "File versioning is enabled";
4023                     client->versions_disabled = false;
4024                 }
4025 
4026                 if (chatFolder.size())
4027                 {
4028                     unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&chatFolder, &client->key));
4029                     if (tlvRecords)
4030                     {
4031                         // store the value for private user attributes (decrypted version of serialized TLV)
4032                         unique_ptr<string> tlvString(tlvRecords->tlvRecordsToContainer(client->rng, &client->key));
4033                         changes += u->updateattr(ATTR_MY_CHAT_FILES_FOLDER, tlvString.get(), &versionChatFolder);
4034                     }
4035                     else
4036                     {
4037                         LOG_err << "Cannot extract TLV records for ATTR_MY_CHAT_FILES_FOLDER";
4038                     }
4039                 }
4040 
4041                 if (cameraUploadFolder.size())
4042                 {
4043                     unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&cameraUploadFolder, &client->key));
4044                     if (tlvRecords)
4045                     {
4046                         // store the value for private user attributes (decrypted version of serialized TLV)
4047                         unique_ptr<string> tlvString(tlvRecords->tlvRecordsToContainer(client->rng, &client->key));
4048                         changes += u->updateattr(ATTR_CAMERA_UPLOADS_FOLDER, tlvString.get(), &versionCameraUploadFolder);
4049                     }
4050                     else
4051                     {
4052                         LOG_err << "Cannot extract TLV records for ATTR_CAMERA_UPLOADS_FOLDER";
4053                     }
4054                 }
4055 
4056                 if (aliases.size())
4057                 {
4058                     unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&aliases, &client->key));
4059                     if (tlvRecords)
4060                     {
4061                         // store the value for private user attributes (decrypted version of serialized TLV)
4062                         unique_ptr<string> tlvString(tlvRecords->tlvRecordsToContainer(client->rng, &client->key));
4063                         changes += u->updateattr(ATTR_ALIAS, tlvString.get(), &versionAliases);
4064                     }
4065                     else
4066                     {
4067                         LOG_err << "Cannot extract TLV records for ATTR_ALIAS";
4068                     }
4069                 }
4070 
4071                 if (unshareableKey.size() == Base64Str<SymmCipher::BLOCKSIZE>::STRLEN)
4072                 {
4073                     changes += u->updateattr(ATTR_UNSHAREABLE_KEY, &unshareableKey, &versionUnshareableKey);
4074                     client->unshareablekey.swap(unshareableKey);
4075                 }
4076                 else if (unshareableKey.empty())    // it has not been created yet
4077                 {
4078                     LOG_info << "Creating unshareable key...";
4079                     byte newunshareablekey[SymmCipher::BLOCKSIZE];
4080                     client->rng.genblock(newunshareablekey, sizeof(newunshareablekey));
4081                     client->putua(ATTR_UNSHAREABLE_KEY, newunshareablekey, sizeof(newunshareablekey), 0);
4082                 }
4083                 else
4084                 {
4085                     LOG_err << "Unshareable key wrong length";
4086                 }
4087 
4088                 if (deviceNames.size())
4089                 {
4090                     unique_ptr<TLVstore> tlvRecords(TLVstore::containerToTLVrecords(&deviceNames, &client->key));
4091                     if (tlvRecords)
4092                     {
4093                         // store the value for private user attributes (decrypted version of serialized TLV)
4094                         unique_ptr<string> tlvString(tlvRecords->tlvRecordsToContainer(client->rng, &client->key));
4095                         changes += u->updateattr(ATTR_DEVICE_NAMES, tlvString.get(), &versionDeviceNames);
4096                     }
4097                     else
4098                     {
4099                         LOG_err << "Cannot extract TLV records for ATTR_DEVICE_NAMES";
4100                     }
4101                 }
4102 
4103                 if (changes > 0)
4104                 {
4105                     u->setTag(tag ? tag : -1);
4106                     client->notifyuser(u);
4107                 }
4108             }
4109 
4110             if (b)  // business account
4111             {
4112                 // integrity checks
4113                 if ((s < BIZ_STATUS_EXPIRED || s > BIZ_STATUS_GRACE_PERIOD)  // status not received or invalid
4114                         || (m == BIZ_MODE_UNKNOWN))  // master flag not received or invalid
4115                 {
4116                     std::string err = "GetUserData: invalid business status / account mode";
4117                     LOG_err << err;
4118                     client->sendevent(99450, err.c_str());
4119 
4120                     client->mBizStatus = BIZ_STATUS_EXPIRED;
4121                     client->mBizMode = BIZ_MODE_SUBUSER;
4122                     client->mBizExpirationTs = client->mBizGracePeriodTs = 0;
4123                     client->app->notify_business_status(client->mBizStatus);
4124                 }
4125                 else
4126                 {
4127                     for (auto it : sts)
4128                     {
4129                         BizStatus status = it.first;
4130                         m_time_t ts = it.second;
4131                         if (status == BIZ_STATUS_EXPIRED)
4132                         {
4133                             client->mBizExpirationTs = ts;
4134                         }
4135                         else if (status == BIZ_STATUS_GRACE_PERIOD)
4136                         {
4137                             client->mBizGracePeriodTs = ts;
4138                         }
4139                         else
4140                         {
4141                             LOG_warn << "Unexpected status in b.sts. Status: " << status << "ts: " << ts;
4142                         }
4143                     }
4144 
4145                     client->mBizMode = m;
4146                     // subusers must receive the list of master users
4147                     assert(m != BIZ_MODE_SUBUSER || !masters.empty());
4148                     client->mBizMasters = masters;
4149 
4150                     if (client->mBizStatus != s)
4151                     {
4152                         client->mBizStatus = s;
4153                         client->app->notify_business_status(s);
4154                     }
4155 
4156                     // if current business status will expire sooner than the scheduled `ug`, update the
4157                     // backoff to a shorter one in order to refresh the business status asap
4158                     m_time_t auxts = 0;
4159                     m_time_t now = m_time(nullptr);
4160                     if (client->mBizGracePeriodTs && client->mBizGracePeriodTs > now)
4161                     {
4162                         auxts = client->mBizGracePeriodTs;
4163                     }
4164                     else if (client->mBizExpirationTs && client->mBizExpirationTs > now)
4165                     {
4166                         auxts = client->mBizExpirationTs;
4167                     }
4168                     if (auxts)
4169                     {
4170                         dstime diff = static_cast<dstime>((now - auxts) * 10);
4171                         dstime current = client->btugexpiration.backoffdelta();
4172                         if (current > diff)
4173                         {
4174                             client->btugexpiration.backoff(diff);
4175                         }
4176                     }
4177                     // TODO: check if type of account has changed and notify with new event (not yet supported by API)
4178                 }
4179             }
4180             else
4181             {
4182                 BizStatus oldStatus = client->mBizStatus;
4183                 client->mBizStatus = BIZ_STATUS_INACTIVE;
4184                 client->mBizMode = BIZ_MODE_UNKNOWN;
4185                 client->mBizMasters.clear();
4186                 client->mBizExpirationTs = client->mBizGracePeriodTs = 0;
4187 
4188                 if (client->mBizStatus != oldStatus)
4189                 {
4190                     client->app->notify_business_status(client->mBizStatus);
4191                 }
4192             }
4193 
4194             if (uspw)
4195             {
4196                 if (deadlineTs == -1 || warningTs.empty())
4197                 {
4198                     LOG_err << "uspw received with missing timestamps";
4199                 }
4200                 else
4201                 {
4202                     client->mOverquotaWarningTs = std::move(warningTs);
4203                     client->mOverquotaDeadlineTs = deadlineTs;
4204                     client->activateoverquota(0, true);
4205                 }
4206 
4207             }
4208 
4209             client->app->userdata_result(&name, &pubk, &privk, API_OK);
4210             return;
4211         }
4212         default:
4213             switch (User::string2attr(attributeName.c_str()))
4214             {
4215                 case ATTR_FIRSTNAME:
4216                     parseUserAttribute(firstname, versionFirstname);
4217                     break;
4218 
4219                 case ATTR_BIRTHMONTH:
4220                     parseUserAttribute(birthmonth, versionBirthmonth);
4221                     break;
4222 
4223                 case ATTR_BIRTHYEAR:
4224                     parseUserAttribute(birthyear, versionBirthyear);
4225                     break;
4226 
4227                 default:
4228                     if (!client->json.storeobject())
4229                     {
4230                         return client->app->userdata_result(NULL, NULL, NULL, API_EINTERNAL);
4231                     }
4232                     break;
4233             }
4234 
4235             break;
4236         }
4237     }
4238 }
4239 
parseUserAttribute(std::string & value,std::string & version,bool asciiToBinary)4240 void CommandGetUserData::parseUserAttribute(std::string &value, std::string &version, bool asciiToBinary)
4241 {
4242     string info;
4243     if (!client->json.storeobject(&info))
4244     {
4245         LOG_err << "Failed to parse user attribute from the array";
4246         return;
4247     }
4248 
4249     string buf;
4250     JSON json;
4251     json.pos = info.c_str() + 1;
4252     for (;;)
4253     {
4254         switch (json.getnameid())
4255         {
4256             case MAKENAMEID2('a','v'):  // value
4257             {
4258                 json.storeobject(&buf);
4259                 break;
4260             }
4261             case 'v':   // version
4262             {
4263                 json.storeobject(&version);
4264                 break;
4265             }
4266             case EOO:
4267             {
4268                 value = asciiToBinary ? Base64::atob(buf) : buf;
4269                 return;
4270             }
4271             default:
4272             {
4273                 if (!json.storeobject())
4274                 {
4275                     version.clear();
4276                     LOG_err << "Failed to parse user attribute inside the array";
4277                     return;
4278                 }
4279             }
4280         }
4281     }
4282 }
4283 
CommandGetMiscFlags(MegaClient * client)4284 CommandGetMiscFlags::CommandGetMiscFlags(MegaClient *client)
4285 {
4286     cmd("gmf");
4287 
4288     // this one can get the smsve flag when the account is blocked (if it's in a batch by itself)
4289     batchSeparately = true;
4290     suppressSID = true;
4291 
4292     tag = client->reqtag;
4293 }
4294 
procresult()4295 void CommandGetMiscFlags::procresult()
4296 {
4297     Error e;
4298     if (checkError(e, client->json))
4299     {
4300         if (!e)
4301         {
4302             LOG_err << "Unexpected response for gmf: no flags, but no error";
4303             e = API_ENOENT;
4304         }
4305         LOG_err << "gmf failed: " << e;
4306     }
4307     else
4308     {
4309         e = client->readmiscflags(&client->json);
4310     }
4311 
4312     client->app->getmiscflags_result(e);
4313 }
4314 
CommandGetUserQuota(MegaClient * client,AccountDetails * ad,bool storage,bool transfer,bool pro,int source)4315 CommandGetUserQuota::CommandGetUserQuota(MegaClient* client, AccountDetails* ad, bool storage, bool transfer, bool pro, int source)
4316 {
4317     details = ad;
4318     mStorage = storage;
4319     mTransfer = transfer;
4320     mPro = pro;
4321 
4322     cmd("uq");
4323     if (storage)
4324     {
4325         arg("strg", "1", 0);
4326     }
4327     if (transfer)
4328     {
4329         arg("xfer", "1", 0);
4330     }
4331     if (pro)
4332     {
4333         arg("pro", "1", 0);
4334     }
4335 
4336     arg("src", source);
4337 
4338     arg("v", 1);
4339 
4340     tag = client->reqtag;
4341 }
4342 
procresult()4343 void CommandGetUserQuota::procresult()
4344 {
4345     m_off_t td;
4346     bool got_storage = false;
4347     bool got_storage_used = false;
4348     int uslw = -1;
4349 
4350     Error e;
4351     if (checkError(e, client->json))
4352     {
4353         return client->app->account_details(details, e);
4354     }
4355 
4356     details->pro_level = 0;
4357     details->subscription_type = 'O';
4358     details->subscription_renew = 0;
4359     details->subscription_method.clear();
4360     memset(details->subscription_cycle, 0, sizeof(details->subscription_cycle));
4361 
4362     details->pro_until = 0;
4363 
4364     details->storage_used = 0;
4365     details->storage_max = 0;
4366 
4367     details->transfer_max = 0;
4368     details->transfer_own_used = 0;
4369     details->transfer_srv_used = 0;
4370     details->srv_ratio = 0;
4371 
4372     details->transfer_hist_starttime = 0;
4373     details->transfer_hist_interval = 3600;
4374     details->transfer_hist.clear();
4375     details->transfer_hist_valid = true;
4376 
4377     details->transfer_reserved = 0;
4378     details->transfer_own_reserved = 0;
4379     details->transfer_srv_reserved = 0;
4380 
4381     for (;;)
4382     {
4383         switch (client->json.getnameid())
4384         {
4385             case MAKENAMEID2('b', 't'):
4386             // "Base time age", this is number of seconds since the start of the current quota buckets
4387                 // age of transfer
4388                 // window start
4389                 td = client->json.getint();
4390                 if (td != -1)
4391                 {
4392                     details->transfer_hist_starttime = m_time() - td;
4393                 }
4394                 break;
4395 
4396             case MAKENAMEID3('t', 'a', 'h'):
4397             // The free IP-based quota buckets, 6 entries for 6 hours
4398                 if (client->json.enterarray())
4399                 {
4400                     m_off_t t;
4401 
4402                     while (client->json.isnumeric() && (t = client->json.getint()) != -1)
4403                     {
4404                         details->transfer_hist.push_back(t);
4405                     }
4406 
4407                     client->json.leavearray();
4408                 }
4409                 break;
4410 
4411             case MAKENAMEID3('t', 'a', 'r'):
4412             // IP transfer reserved
4413                 details->transfer_reserved = client->json.getint();
4414                 break;
4415 
4416             case MAKENAMEID3('r', 'u', 'a'):
4417             // Actor reserved quota
4418                 details->transfer_own_reserved += client->json.getint();
4419                 break;
4420 
4421             case MAKENAMEID3('r', 'u', 'o'):
4422             // Owner reserved quota
4423                 details->transfer_srv_reserved += client->json.getint();
4424                 break;
4425 
4426             case MAKENAMEID5('c', 's', 't', 'r', 'g'):
4427             // Your total account storage usage
4428                 details->storage_used = client->json.getint();
4429                 got_storage_used = true;
4430                 break;
4431 
4432             case MAKENAMEID6('c', 's', 't', 'r', 'g', 'n'):
4433             // Storage breakdown of root nodes and shares for your account
4434             // [bytes, numFiles, numFolders, versionedBytes, numVersionedFiles]
4435                 if (client->json.enterobject())
4436                 {
4437                     handle h;
4438                     NodeStorage* ns;
4439 
4440                     while (!ISUNDEF(h = client->json.gethandle()) && client->json.enterarray())
4441                     {
4442                         ns = &details->storage[h];
4443 
4444                         ns->bytes = client->json.getint();
4445                         ns->files = uint32_t(client->json.getint());
4446                         ns->folders = uint32_t(client->json.getint());
4447                         ns->version_bytes = client->json.getint();
4448                         ns->version_files = client->json.getint32();
4449 
4450 #ifdef _DEBUG
4451                         // TODO: remove this debugging block once local count is confirmed to work correctly 100%
4452                         // verify the new local storage counters per root match server side (could fail if actionpackets are pending)
4453                         auto iter = client->mNodeCounters.find(h);
4454                         if (iter != client->mNodeCounters.end())
4455                         {
4456                             LOG_debug << client->nodebyhandle(h)->displaypath() << " " << iter->second.storage << " " << ns->bytes << " " << iter->second.files << " " << ns->files << " " << iter->second.folders << " " << ns->folders << " "
4457                                       << iter->second.versionStorage << " " << ns->version_bytes << " " << iter->second.versions << " " << ns->version_files
4458                                       << (iter->second.storage == ns->bytes && iter->second.files == ns->files && iter->second.folders == ns->folders && iter->second.versionStorage == ns->version_bytes && iter->second.versions == ns->version_files
4459                                           ? "" : " ******************************************* mismatch *******************************************");
4460                         }
4461 #endif
4462 
4463                         while(client->json.storeobject());
4464                         client->json.leavearray();
4465                     }
4466 
4467                     client->json.leaveobject();
4468                 }
4469                 break;
4470 
4471             case MAKENAMEID5('m', 's', 't', 'r', 'g'):
4472             // maximum storage allowance
4473                 details->storage_max = client->json.getint();
4474                 got_storage = true;
4475                 break;
4476 
4477             case MAKENAMEID6('c', 'a', 'x', 'f', 'e', 'r'):
4478             // PRO transfer quota consumed by yourself
4479                 details->transfer_own_used += client->json.getint();
4480                 break;
4481 
4482             case MAKENAMEID3('t', 'u', 'o'):
4483             // Transfer usage by the owner on quotad which hasn't yet been committed back to the API DB. Supplements caxfer
4484                 details->transfer_own_used += client->json.getint();
4485                 break;
4486 
4487             case MAKENAMEID6('c', 's', 'x', 'f', 'e', 'r'):
4488             // PRO transfer quota served to others
4489                 details->transfer_srv_used += client->json.getint();
4490                 break;
4491 
4492             case MAKENAMEID3('t', 'u', 'a'):
4493             // Transfer usage served to other users which hasn't yet been committed back to the API DB. Supplements csxfer
4494                 details->transfer_srv_used += client->json.getint();
4495                 break;
4496 
4497             case MAKENAMEID5('m', 'x', 'f', 'e', 'r'):
4498             // maximum transfer allowance
4499                 details->transfer_max = client->json.getint();
4500                 break;
4501 
4502             case MAKENAMEID8('s', 'r', 'v', 'r', 'a', 't', 'i', 'o'):
4503             // The ratio of your PRO transfer quota that is able to be served to others
4504                 details->srv_ratio = client->json.getfloat();
4505                 break;
4506 
4507             case MAKENAMEID5('u', 't', 'y', 'p', 'e'):
4508             // PRO type. 0 means Free; 4 is Pro Lite as it was added late; 100 indicates a business.
4509                 details->pro_level = (int)client->json.getint();
4510                 break;
4511 
4512             case MAKENAMEID5('s', 't', 'y', 'p', 'e'):
4513             // Flag indicating if this is a recurring subscription or one-off. "O" is one off, "R" is recurring.
4514                 const char* ptr;
4515                 if ((ptr = client->json.getvalue()))
4516                 {
4517                     details->subscription_type = *ptr;
4518                 }
4519                 break;
4520 
4521             case MAKENAMEID6('s', 'c', 'y', 'c', 'l', 'e'):
4522                 const char* scycle;
4523                 if ((scycle = client->json.getvalue()))
4524                 {
4525                     memcpy(details->subscription_cycle, scycle, 3);
4526                     details->subscription_cycle[3] = 0;
4527                 }
4528                 break;
4529 
4530             case MAKENAMEID6('s', 'r', 'e', 'n', 'e', 'w'):
4531             // Only provided for recurring subscriptions to indicate the best estimate of when the subscription will renew
4532                 if (client->json.enterarray())
4533                 {
4534                     details->subscription_renew = client->json.getint();
4535                     while(!client->json.leavearray())
4536                     {
4537                         client->json.storeobject();
4538                     }
4539                 }
4540                 break;
4541 
4542             case MAKENAMEID3('s', 'g', 'w'):
4543                 if (client->json.enterarray())
4544                 {
4545                     client->json.storeobject(&details->subscription_method);
4546                     while(!client->json.leavearray())
4547                     {
4548                         client->json.storeobject();
4549                     }
4550                 }
4551                 break;
4552 
4553             case MAKENAMEID3('r', 't', 't'):
4554                 details->transfer_hist_valid = !client->json.getint();
4555                 break;
4556 
4557             case MAKENAMEID6('s', 'u', 'n', 't', 'i', 'l'):
4558             // Time the last active PRO plan will expire (may be different from current one)
4559                 details->pro_until = client->json.getint();
4560                 break;
4561 
4562             case MAKENAMEID7('b', 'a', 'l', 'a', 'n', 'c', 'e'):
4563             // Balance of your account
4564                 if (client->json.enterarray())
4565                 {
4566                     const char* cur;
4567                     const char* amount;
4568 
4569                     while (client->json.enterarray())
4570                     {
4571                         if ((amount = client->json.getvalue()) && (cur = client->json.getvalue()))
4572                         {
4573                             size_t t = details->balances.size();
4574                             details->balances.resize(t + 1);
4575                             details->balances[t].amount = atof(amount);
4576                             memcpy(details->balances[t].currency, cur, 3);
4577                             details->balances[t].currency[3] = 0;
4578                         }
4579 
4580                         client->json.leavearray();
4581                     }
4582 
4583                     client->json.leavearray();
4584                 }
4585                 break;
4586 
4587             case MAKENAMEID4('u', 's', 'l', 'w'):
4588             // The percentage (in 1000s) indicating the limit at which you are 'nearly' over. Currently 98% for PRO, 90% for free.
4589                 uslw = int(client->json.getint());
4590                 break;
4591 
4592             case EOO:
4593                 assert(!mStorage || (got_storage && got_storage_used) || client->loggedinfolderlink());
4594 
4595                 if (mStorage)
4596                 {
4597                     if (uslw <= 0)
4598                     {
4599                         uslw = 9000;
4600                         LOG_warn << "Using default almost overstorage threshold";
4601                     }
4602 
4603                     if (details->storage_used >= details->storage_max)
4604                     {
4605                         LOG_debug << "Account full";
4606                         bool isPaywall = (client->ststatus == STORAGE_PAYWALL);
4607                         client->activateoverquota(0, isPaywall);
4608                     }
4609                     else if (details->storage_used >= (details->storage_max / 10000 * uslw))
4610                     {
4611                         LOG_debug << "Few storage space available";
4612                         client->setstoragestatus(STORAGE_ORANGE);
4613                     }
4614                     else
4615                     {
4616                         LOG_debug << "There are no storage problems";
4617                         client->setstoragestatus(STORAGE_GREEN);
4618                     }
4619                 }
4620 
4621                 client->app->account_details(details, mStorage, mTransfer, mPro, false, false, false);
4622                 return;
4623 
4624             default:
4625                 if (!client->json.storeobject())
4626                 {
4627                     return client->app->account_details(details, API_EINTERNAL);
4628                 }
4629         }
4630     }
4631 }
4632 
CommandQueryTransferQuota(MegaClient * client,m_off_t size)4633 CommandQueryTransferQuota::CommandQueryTransferQuota(MegaClient* client, m_off_t size)
4634 {
4635     cmd("qbq");
4636     arg("s", size);
4637 
4638     tag = client->reqtag;
4639 }
4640 
procresult()4641 void CommandQueryTransferQuota::procresult()
4642 {
4643     Error e;
4644     if (!checkError(e, client->json))
4645     {
4646         LOG_err << "Unexpected response: " << client->json.pos;
4647         client->json.storeobject();
4648 
4649         // Returns 0 to not alarm apps and don't show overquota pre-warnings
4650         // if something unexpected is received, following the same approach as
4651         // in the webclient
4652         return client->app->querytransferquota_result(0);
4653     }
4654 
4655     return client->app->querytransferquota_result(e);
4656 }
4657 
CommandGetUserTransactions(MegaClient * client,AccountDetails * ad)4658 CommandGetUserTransactions::CommandGetUserTransactions(MegaClient* client, AccountDetails* ad)
4659 {
4660     cmd("utt");
4661 
4662     details = ad;
4663     tag = client->reqtag;
4664 }
4665 
procresult()4666 void CommandGetUserTransactions::procresult()
4667 {
4668     details->transactions.clear();
4669 
4670     while (client->json.enterarray())
4671     {
4672         const char* handle = client->json.getvalue();
4673         m_time_t ts = client->json.getint();
4674         const char* delta = client->json.getvalue();
4675         const char* cur = client->json.getvalue();
4676 
4677         if (handle && (ts > 0) && delta && cur)
4678         {
4679             size_t t = details->transactions.size();
4680             details->transactions.resize(t + 1);
4681             memcpy(details->transactions[t].handle, handle, 11);
4682             details->transactions[t].handle[11] = 0;
4683             details->transactions[t].timestamp = ts;
4684             details->transactions[t].delta = atof(delta);
4685             memcpy(details->transactions[t].currency, cur, 3);
4686             details->transactions[t].currency[3] = 0;
4687         }
4688 
4689         client->json.leavearray();
4690     }
4691 
4692     client->app->account_details(details, false, false, false, false, true, false);
4693 }
4694 
CommandGetUserPurchases(MegaClient * client,AccountDetails * ad)4695 CommandGetUserPurchases::CommandGetUserPurchases(MegaClient* client, AccountDetails* ad)
4696 {
4697     cmd("utp");
4698 
4699     details = ad;
4700     tag = client->reqtag;
4701 }
4702 
procresult()4703 void CommandGetUserPurchases::procresult()
4704 {
4705     client->restag = tag;
4706 
4707     details->purchases.clear();
4708 
4709     while (client->json.enterarray())
4710     {
4711         const char* handle = client->json.getvalue();
4712         const m_time_t ts = client->json.getint();
4713         const char* amount = client->json.getvalue();
4714         const char* cur = client->json.getvalue();
4715         int method = (int)client->json.getint();
4716 
4717         if (handle && (ts > 0) && amount && cur && (method >= 0))
4718         {
4719             size_t t = details->purchases.size();
4720             details->purchases.resize(t + 1);
4721             memcpy(details->purchases[t].handle, handle, 11);
4722             details->purchases[t].handle[11] = 0;
4723             details->purchases[t].timestamp = ts;
4724             details->purchases[t].amount = atof(amount);
4725             memcpy(details->purchases[t].currency, cur, 3);
4726             details->purchases[t].currency[3] = 0;
4727             details->purchases[t].method = method;
4728         }
4729 
4730         client->json.leavearray();
4731     }
4732 
4733     client->app->account_details(details, false, false, false, true, false, false);
4734 }
4735 
CommandGetUserSessions(MegaClient * client,AccountDetails * ad)4736 CommandGetUserSessions::CommandGetUserSessions(MegaClient* client, AccountDetails* ad)
4737 {
4738     cmd("usl");
4739     arg("x", 1); // Request the additional id and alive information
4740 
4741     details = ad;
4742     tag = client->reqtag;
4743 }
4744 
procresult()4745 void CommandGetUserSessions::procresult()
4746 {
4747     details->sessions.clear();
4748 
4749     while (client->json.enterarray())
4750     {
4751         size_t t = details->sessions.size();
4752         details->sessions.resize(t + 1);
4753 
4754         details->sessions[t].timestamp = client->json.getint();
4755         details->sessions[t].mru = client->json.getint();
4756         client->json.storeobject(&details->sessions[t].useragent);
4757         client->json.storeobject(&details->sessions[t].ip);
4758 
4759         const char* country = client->json.getvalue();
4760         memcpy(details->sessions[t].country, country ? country : "\0\0", 2);
4761         details->sessions[t].country[2] = 0;
4762 
4763         details->sessions[t].current = (int)client->json.getint();
4764 
4765         details->sessions[t].id = client->json.gethandle(8);
4766         details->sessions[t].alive = (int)client->json.getint();
4767 
4768         client->json.leavearray();
4769     }
4770 
4771     client->app->account_details(details, false, false, false, false, false, true);
4772 }
4773 
CommandSetPH(MegaClient * client,Node * n,int del,m_time_t ets)4774 CommandSetPH::CommandSetPH(MegaClient* client, Node* n, int del, m_time_t ets)
4775 {
4776     cmd("l");
4777     arg("n", (byte*)&n->nodehandle, MegaClient::NODEHANDLE);
4778 
4779     if (del)
4780     {
4781         arg("d", 1);
4782     }
4783 
4784     if (ets)
4785     {
4786         arg("ets", ets);
4787     }
4788 
4789     this->h = n->nodehandle;
4790     this->ets = ets;
4791     this->tag = client->reqtag;
4792 }
4793 
procresult()4794 void CommandSetPH::procresult()
4795 {
4796     Error e;
4797     if (checkError(e, client->json))
4798     {
4799         return client->app->exportnode_result(e);
4800     }
4801 
4802     handle ph = client->json.gethandle();
4803 
4804     if (ISUNDEF(ph))
4805     {
4806         return client->app->exportnode_result(API_EINTERNAL);
4807     }
4808 
4809     Node *n = client->nodebyhandle(h);
4810     if (n)
4811     {
4812         n->setpubliclink(ph, time(nullptr), ets, false);
4813         n->changed.publiclink = true;
4814         client->notifynode(n);
4815     }
4816 
4817     client->app->exportnode_result(h, ph);
4818 }
4819 
CommandGetPH(MegaClient * client,handle cph,const byte * ckey,int cop)4820 CommandGetPH::CommandGetPH(MegaClient* client, handle cph, const byte* ckey, int cop)
4821 {
4822     cmd("g");
4823     arg("p", (byte*)&cph, MegaClient::NODEHANDLE);
4824 
4825     ph = cph;
4826     havekey = ckey ? true : false;
4827     if (havekey)
4828     {
4829         memcpy(key, ckey, sizeof key);
4830     }
4831     tag = client->reqtag;
4832     op = cop;
4833 }
4834 
procresult()4835 void CommandGetPH::procresult()
4836 {
4837     Error e;
4838     if (checkError(e, client->json))
4839     {
4840         return client->app->openfilelink_result(e);
4841     }
4842 
4843     m_off_t s = -1;
4844     string a, fa;
4845 
4846     for (;;)
4847     {
4848         switch (client->json.getnameid())
4849         {
4850             case 's':
4851                 s = client->json.getint();
4852                 break;
4853 
4854             case MAKENAMEID2('a', 't'):
4855                 client->json.storeobject(&a);
4856                 break;
4857 
4858             case MAKENAMEID2('f', 'a'):
4859                 client->json.storeobject(&fa);
4860                 break;
4861 
4862             case EOO:
4863                 // we want at least the attributes
4864                 if (s >= 0)
4865                 {
4866                     a.resize(Base64::atob(a.c_str(), (byte*)a.data(), int(a.size())));
4867                     if (havekey)
4868                     {
4869                         client->app->openfilelink_result(ph, key, s, &a, &fa, op);
4870                     }
4871                     else
4872                     {
4873                         client->app->openfilelink_result(ph, NULL, s, &a, &fa, op);
4874                     }
4875                 }
4876                 else
4877                 {
4878                     client->app->openfilelink_result(API_EINTERNAL);
4879                 }
4880                 return;
4881 
4882             default:
4883                 if (!client->json.storeobject())
4884                 {
4885                     client->app->openfilelink_result(API_EINTERNAL);
4886                 }
4887         }
4888     }
4889 }
4890 
CommandSetMasterKey(MegaClient * client,const byte * newkey,const byte * hash,int hashsize,const byte * clientrandomvalue,const char * pin,string * salt)4891 CommandSetMasterKey::CommandSetMasterKey(MegaClient* client, const byte* newkey, const byte *hash, int hashsize, const byte *clientrandomvalue, const char *pin, string *salt)
4892 {
4893     memcpy(this->newkey, newkey, SymmCipher::KEYLENGTH);
4894 
4895     cmd("up");
4896     arg("k", newkey, SymmCipher::KEYLENGTH);
4897     if (clientrandomvalue)
4898     {
4899         arg("crv", clientrandomvalue, SymmCipher::KEYLENGTH);
4900     }
4901     arg("uh", hash, hashsize);
4902     if (pin)
4903     {
4904         arg("mfa", pin);
4905     }
4906 
4907     if (salt)
4908     {
4909         this->salt = *salt;
4910     }
4911 
4912     tag = client->reqtag;
4913 }
4914 
procresult()4915 void CommandSetMasterKey::procresult()
4916 {
4917     Error e;
4918     if (checkError(e, client->json))
4919     {
4920         client->app->changepw_result(e);
4921     }
4922     else
4923     {
4924         // update encrypted MK and salt for further checkups
4925         client->k.assign((const char *) newkey, SymmCipher::KEYLENGTH);
4926         client->accountsalt = salt;
4927 
4928         client->json.storeobject();
4929         client->app->changepw_result(API_OK);
4930     }
4931 }
4932 
CommandCreateEphemeralSession(MegaClient * client,const byte * key,const byte * cpw,const byte * ssc)4933 CommandCreateEphemeralSession::CommandCreateEphemeralSession(MegaClient* client,
4934                                                              const byte* key,
4935                                                              const byte* cpw,
4936                                                              const byte* ssc)
4937 {
4938     memcpy(pw, cpw, sizeof pw);
4939 
4940     cmd("up");
4941     arg("k", key, SymmCipher::KEYLENGTH);
4942     arg("ts", ssc, 2 * SymmCipher::KEYLENGTH);
4943 
4944     tag = client->reqtag;
4945 }
4946 
procresult()4947 void CommandCreateEphemeralSession::procresult()
4948 {
4949     Error e;
4950     if (checkError(e, client->json))
4951     {
4952         client->ephemeralSession = false;
4953         client->app->ephemeral_result(e);
4954     }
4955     else
4956     {
4957         client->me = client->json.gethandle(MegaClient::USERHANDLE);
4958         client->uid = Base64Str<MegaClient::USERHANDLE>(client->me);
4959         client->resumeephemeral(client->me, pw, tag);
4960     }
4961 }
4962 
CommandResumeEphemeralSession(MegaClient *,handle cuh,const byte * cpw,int ctag)4963 CommandResumeEphemeralSession::CommandResumeEphemeralSession(MegaClient*, handle cuh, const byte* cpw, int ctag)
4964 {
4965     memcpy(pw, cpw, sizeof pw);
4966 
4967     uh = cuh;
4968 
4969     cmd("us");
4970     arg("user", (byte*)&uh, MegaClient::USERHANDLE);
4971 
4972     tag = ctag;
4973 }
4974 
procresult()4975 void CommandResumeEphemeralSession::procresult()
4976 {
4977     byte keybuf[SymmCipher::KEYLENGTH];
4978     byte sidbuf[MegaClient::SIDLEN];
4979     int havek = 0, havecsid = 0;
4980 
4981     Error e;
4982     if (checkError(e, client->json))
4983     {
4984         return client->app->ephemeral_result(e);
4985     }
4986 
4987     for (;;)
4988     {
4989         switch (client->json.getnameid())
4990         {
4991             case 'k':
4992                 havek = client->json.storebinary(keybuf, sizeof keybuf) == sizeof keybuf;
4993                 break;
4994 
4995             case MAKENAMEID4('t', 's', 'i', 'd'):
4996                 havecsid = client->json.storebinary(sidbuf, sizeof sidbuf) == sizeof sidbuf;
4997                 break;
4998 
4999             case EOO:
5000                 if (!havek || !havecsid)
5001                 {
5002                     return client->app->ephemeral_result(API_EINTERNAL);
5003                 }
5004 
5005                 client->setsid(sidbuf, sizeof sidbuf);
5006 
5007                 client->key.setkey(pw);
5008                 client->key.ecb_decrypt(keybuf);
5009 
5010                 client->key.setkey(keybuf);
5011 
5012                 if (!client->checktsid(sidbuf, sizeof sidbuf))
5013                 {
5014                     return client->app->ephemeral_result(API_EKEY);
5015                 }
5016 
5017                 client->me = uh;
5018                 client->uid = Base64Str<MegaClient::USERHANDLE>(client->me);
5019 
5020                 return client->app->ephemeral_result(uh, pw);
5021 
5022             default:
5023                 if (!client->json.storeobject())
5024                 {
5025                     return client->app->ephemeral_result(API_EINTERNAL);
5026                 }
5027         }
5028     }
5029 }
5030 
CommandCancelSignup(MegaClient * client)5031 CommandCancelSignup::CommandCancelSignup(MegaClient *client)
5032 {
5033     cmd("ucr");
5034 
5035     tag = client->reqtag;
5036 }
5037 
procresult()5038 void CommandCancelSignup::procresult()
5039 {
5040     Error e;
5041     if (checkError(e, client->json))
5042     {
5043         return client->app->cancelsignup_result(e);
5044     }
5045 
5046     client->json.storeobject();
5047 
5048     client->app->cancelsignup_result(API_EINTERNAL);
5049 }
5050 
5051 
CommandWhyAmIblocked(MegaClient * client)5052 CommandWhyAmIblocked::CommandWhyAmIblocked(MegaClient *client)
5053 {
5054     cmd("whyamiblocked");
5055     batchSeparately = true;  // don't let any other commands that might get batched with it cause the whole batch to fail
5056 
5057     tag = client->reqtag;
5058 }
5059 
procresult()5060 void CommandWhyAmIblocked::procresult()
5061 {
5062     Error e;
5063     if (checkError(e, client->json))
5064     {
5065         if (!e) //unblocked
5066         {
5067             client->unblock();
5068         }
5069 
5070         return client->app->whyamiblocked_result(e);
5071     }
5072     else if (client->json.isnumeric())
5073     {
5074          int response = int(client->json.getint());
5075          return client->app->whyamiblocked_result(response);
5076     }
5077 
5078     client->json.storeobject();
5079     client->app->whyamiblocked_result(API_EINTERNAL);
5080 }
5081 
CommandSendSignupLink(MegaClient * client,const char * email,const char * name,byte * c)5082 CommandSendSignupLink::CommandSendSignupLink(MegaClient* client, const char* email, const char* name, byte* c)
5083 {
5084     cmd("uc");
5085     arg("c", c, 2 * SymmCipher::KEYLENGTH);
5086     arg("n", (byte*)name, int(strlen(name)));
5087     arg("m", (byte*)email, int(strlen(email)));
5088 
5089     tag = client->reqtag;
5090 }
5091 
procresult()5092 void CommandSendSignupLink::procresult()
5093 {
5094     Error e;
5095     if (checkError(e, client->json))
5096     {
5097         return client->app->sendsignuplink_result(e);
5098     }
5099 
5100     client->json.storeobject();
5101 
5102     client->app->sendsignuplink_result(API_EINTERNAL);
5103 }
5104 
CommandSendSignupLink2(MegaClient * client,const char * email,const char * name)5105 CommandSendSignupLink2::CommandSendSignupLink2(MegaClient* client, const char* email, const char* name)
5106 {
5107     cmd("uc2");
5108     arg("n", (byte*)name, int(strlen(name)));
5109     arg("m", (byte*)email, int(strlen(email)));
5110     arg("v", 2);
5111     tag = client->reqtag;
5112 }
5113 
CommandSendSignupLink2(MegaClient * client,const char * email,const char * name,byte * clientrandomvalue,byte * encmasterkey,byte * hashedauthkey)5114 CommandSendSignupLink2::CommandSendSignupLink2(MegaClient* client, const char* email, const char* name, byte *clientrandomvalue, byte *encmasterkey, byte *hashedauthkey)
5115 {
5116     cmd("uc2");
5117     arg("n", (byte*)name, int(strlen(name)));
5118     arg("m", (byte*)email, int(strlen(email)));
5119     arg("crv", clientrandomvalue, SymmCipher::KEYLENGTH);
5120     arg("hak", hashedauthkey, SymmCipher::KEYLENGTH);
5121     arg("k", encmasterkey, SymmCipher::KEYLENGTH);
5122     arg("v", 2);
5123 
5124     tag = client->reqtag;
5125 }
5126 
procresult()5127 void CommandSendSignupLink2::procresult()
5128 {
5129     Error e;
5130     if (checkError(e, client->json))
5131     {
5132         return client->app->sendsignuplink_result(e);
5133     }
5134 
5135     client->json.storeobject();
5136 
5137     client->app->sendsignuplink_result(API_EINTERNAL);
5138 }
5139 
CommandQuerySignupLink(MegaClient * client,const byte * code,unsigned len)5140 CommandQuerySignupLink::CommandQuerySignupLink(MegaClient* client, const byte* code, unsigned len)
5141 {
5142     confirmcode.assign((char*)code, len);
5143 
5144     cmd("ud");
5145     arg("c", code, len);
5146 
5147     tag = client->reqtag;
5148 }
5149 
procresult()5150 void CommandQuerySignupLink::procresult()
5151 {
5152     string name;
5153     string email;
5154     handle uh;
5155     const char* kc;
5156     const char* pwcheck;
5157     string namebuf, emailbuf;
5158     byte pwcheckbuf[SymmCipher::KEYLENGTH];
5159     byte kcbuf[SymmCipher::KEYLENGTH];
5160 
5161     Error e;
5162     if (checkError(e, client->json))
5163     {
5164         return client->app->querysignuplink_result(e);
5165     }
5166 
5167     if (client->json.storebinary(&name) && client->json.storebinary(&email)
5168         && (uh = client->json.gethandle(MegaClient::USERHANDLE))
5169         && (kc = client->json.getvalue()) && (pwcheck = client->json.getvalue()))
5170     {
5171         if (!ISUNDEF(uh)
5172             && (Base64::atob(pwcheck, pwcheckbuf, sizeof pwcheckbuf) == sizeof pwcheckbuf)
5173             && (Base64::atob(kc, kcbuf, sizeof kcbuf) == sizeof kcbuf))
5174         {
5175             client->json.leavearray();
5176 
5177             return client->app->querysignuplink_result(uh, name.c_str(),
5178                                                        email.c_str(),
5179                                                        pwcheckbuf, kcbuf,
5180                                                        (const byte*)confirmcode.data(),
5181                                                        confirmcode.size());
5182         }
5183     }
5184 
5185     client->app->querysignuplink_result(API_EINTERNAL);
5186 }
5187 
CommandConfirmSignupLink2(MegaClient * client,const byte * code,unsigned len)5188 CommandConfirmSignupLink2::CommandConfirmSignupLink2(MegaClient* client,
5189                                                    const byte* code,
5190                                                    unsigned len)
5191 {
5192     cmd("ud2");
5193     arg("c", code, len);
5194 
5195     tag = client->reqtag;
5196 }
5197 
procresult()5198 void CommandConfirmSignupLink2::procresult()
5199 {
5200     string name;
5201     string email;
5202     handle uh = UNDEF;
5203     int version = 0;
5204 
5205     Error e;
5206     if (checkError(e, client->json))
5207     {
5208         client->app->confirmsignuplink2_result(UNDEF, NULL, NULL, e);
5209     }
5210 
5211     if (client->json.storebinary(&email) && client->json.storebinary(&name))
5212     {
5213         uh = client->json.gethandle(MegaClient::USERHANDLE);
5214         version = int(client->json.getint());
5215     }
5216     while (client->json.storeobject());
5217 
5218     if (!ISUNDEF(uh) && version == 2)
5219     {
5220         client->ephemeralSession = false;
5221         client->app->confirmsignuplink2_result(uh, name.c_str(), email.c_str(), API_OK);
5222     }
5223     else
5224     {
5225         client->app->confirmsignuplink2_result(UNDEF, NULL, NULL, API_EINTERNAL);
5226     }
5227 }
5228 
CommandConfirmSignupLink(MegaClient * client,const byte * code,unsigned len,uint64_t emailhash)5229 CommandConfirmSignupLink::CommandConfirmSignupLink(MegaClient* client,
5230                                                    const byte* code,
5231                                                    unsigned len,
5232                                                    uint64_t emailhash)
5233 {
5234     cmd("up");
5235     arg("c", code, len);
5236     arg("uh", (byte*)&emailhash, sizeof emailhash);
5237 
5238     notself(client);
5239 
5240     tag = client->reqtag;
5241 }
5242 
procresult()5243 void CommandConfirmSignupLink::procresult()
5244 {
5245     Error e;
5246     if (checkError(e, client->json))
5247     {
5248         return client->app->confirmsignuplink_result(e);
5249     }
5250 
5251     client->json.storeobject();
5252 
5253     client->ephemeralSession = false;
5254     client->app->confirmsignuplink_result(API_OK);
5255 }
5256 
CommandSetKeyPair(MegaClient * client,const byte * privk,unsigned privklen,const byte * pubk,unsigned pubklen)5257 CommandSetKeyPair::CommandSetKeyPair(MegaClient* client, const byte* privk,
5258                                      unsigned privklen, const byte* pubk,
5259                                      unsigned pubklen)
5260 {
5261     cmd("up");
5262     arg("privk", privk, privklen);
5263     arg("pubk", pubk, pubklen);
5264 
5265     tag = client->reqtag;
5266 
5267     len = privklen;
5268     privkBuffer.reset(new byte[privklen]);
5269     memcpy(privkBuffer.get(), privk, len);
5270 }
5271 
procresult()5272 void CommandSetKeyPair::procresult()
5273 {
5274     Error e;
5275     if (checkError(e, client->json))
5276     {
5277         return client->app->setkeypair_result(e);
5278     }
5279 
5280     client->json.storeobject();
5281 
5282     client->key.ecb_decrypt(privkBuffer.get(), len);
5283     client->mPrivKey.resize(AsymmCipher::MAXKEYLENGTH * 2);
5284     client->mPrivKey.resize(Base64::btoa(privkBuffer.get(), len, (char *)client->mPrivKey.data()));
5285 
5286     client->app->setkeypair_result(API_OK);
5287 }
5288 
5289 // fetch full node tree
CommandFetchNodes(MegaClient * client,bool nocache)5290 CommandFetchNodes::CommandFetchNodes(MegaClient* client, bool nocache)
5291 {
5292     cmd("f");
5293     arg("c", 1);
5294     arg("r", 1);
5295 
5296     if (!nocache)
5297     {
5298         arg("ca", 1);
5299     }
5300 
5301     // The servers are more efficient with this command when it's the only one in the batch
5302     batchSeparately = true;
5303 
5304     tag = client->reqtag;
5305 }
5306 
5307 // purge and rebuild node/user tree
procresult()5308 void CommandFetchNodes::procresult()
5309 {
5310     WAIT_CLASS::bumpds();
5311     client->fnstats.timeToLastByte = Waiter::ds - client->fnstats.startTime;
5312 
5313     client->purgenodesusersabortsc(true);
5314 
5315     Error e;
5316     if (checkError(e, client->json))
5317     {
5318         client->fetchingnodes = false;
5319         return client->app->fetchnodes_result(e);
5320     }
5321 
5322     for (;;)
5323     {
5324         switch (client->json.getnameid())
5325         {
5326             case 'f':
5327                 // nodes
5328                 if (!client->readnodes(&client->json, 0))
5329                 {
5330                     client->fetchingnodes = false;
5331                     return client->app->fetchnodes_result(API_EINTERNAL);
5332                 }
5333                 break;
5334 
5335             case MAKENAMEID2('f', '2'):
5336                 // old versions
5337                 if (!client->readnodes(&client->json, 0))
5338                 {
5339                     client->fetchingnodes = false;
5340                     return client->app->fetchnodes_result(API_EINTERNAL);
5341                 }
5342                 break;
5343 
5344             case MAKENAMEID2('o', 'k'):
5345                 // outgoing sharekeys
5346                 client->readok(&client->json);
5347                 break;
5348 
5349             case 's':
5350                 // Fall through
5351             case MAKENAMEID2('p', 's'):
5352                 // outgoing or pending shares
5353                 client->readoutshares(&client->json);
5354                 break;
5355 
5356             case 'u':
5357                 // users/contacts
5358                 if (!client->readusers(&client->json, false))
5359                 {
5360                     client->fetchingnodes = false;
5361                     return client->app->fetchnodes_result(API_EINTERNAL);
5362                 }
5363                 break;
5364 
5365             case MAKENAMEID2('c', 'r'):
5366                 // crypto key request
5367                 client->proccr(&client->json);
5368                 break;
5369 
5370             case MAKENAMEID2('s', 'r'):
5371                 // sharekey distribution request
5372                 client->procsr(&client->json);
5373                 break;
5374 
5375             case MAKENAMEID2('s', 'n'):
5376                 // sequence number
5377                 if (!client->scsn.setScsn(&client->json))
5378                 {
5379                     client->fetchingnodes = false;
5380                     return client->app->fetchnodes_result(API_EINTERNAL);
5381                 }
5382                 break;
5383 
5384             case MAKENAMEID3('i', 'p', 'c'):
5385                 // Incoming pending contact
5386                 client->readipc(&client->json);
5387                 break;
5388 
5389             case MAKENAMEID3('o', 'p', 'c'):
5390                 // Outgoing pending contact
5391                 client->readopc(&client->json);
5392                 break;
5393 
5394             case MAKENAMEID2('p', 'h'):
5395                 // Public links handles
5396                 client->procph(&client->json);
5397                 break;
5398 
5399 #ifdef ENABLE_CHAT
5400             case MAKENAMEID3('m', 'c', 'f'):
5401                 // List of chatrooms
5402                 client->procmcf(&client->json);
5403                 break;
5404 
5405             case MAKENAMEID5('m', 'c', 'p', 'n', 'a'):   // fall-through
5406             case MAKENAMEID4('m', 'c', 'n', 'a'):
5407                 // nodes shared in chatrooms
5408                 client->procmcna(&client->json);
5409                 break;
5410 #endif
5411             case EOO:
5412             {
5413                 if (!client->scsn.ready())
5414                 {
5415                     client->fetchingnodes = false;
5416                     return client->app->fetchnodes_result(API_EINTERNAL);
5417                 }
5418 
5419                 client->mergenewshares(0);
5420                 client->applykeys();
5421                 client->initsc();
5422                 client->pendingsccommit = false;
5423                 client->fetchnodestag = tag;
5424 
5425                 WAIT_CLASS::bumpds();
5426                 client->fnstats.timeToCached = Waiter::ds - client->fnstats.startTime;
5427                 client->fnstats.nodesCached = client->nodes.size();
5428                 return;
5429             }
5430             default:
5431                 if (!client->json.storeobject())
5432                 {
5433                     client->fetchingnodes = false;
5434                     return client->app->fetchnodes_result(API_EINTERNAL);
5435                 }
5436         }
5437     }
5438 }
5439 
5440 // report event to server logging facility
CommandReportEvent(MegaClient * client,const char * event,const char * details)5441 CommandReportEvent::CommandReportEvent(MegaClient *client, const char *event, const char *details)
5442 {
5443     cmd("cds");
5444     arg("c", event);
5445 
5446     if (details)
5447     {
5448         arg("v", details);
5449     }
5450 
5451     tag = client->reqtag;
5452 }
5453 
procresult()5454 void CommandReportEvent::procresult()
5455 {
5456     Error e;
5457     if (checkError(e, client->json))
5458     {
5459         client->app->reportevent_result(e);
5460     }
5461     else
5462     {
5463         client->json.storeobject();
5464         client->app->reportevent_result(API_EINTERNAL);
5465     }
5466 }
5467 
CommandSubmitPurchaseReceipt(MegaClient * client,int type,const char * receipt,handle lph,int phtype,int64_t ts)5468 CommandSubmitPurchaseReceipt::CommandSubmitPurchaseReceipt(MegaClient *client, int type, const char *receipt, handle lph, int phtype, int64_t ts)
5469 {
5470     cmd("vpay");
5471     arg("t", type);
5472 
5473     if(receipt)
5474     {
5475         arg("receipt", receipt);
5476     }
5477 
5478     if(type == 2 && client->loggedin() == FULLACCOUNT)
5479     {
5480         arg("user", client->finduser(client->me)->uid.c_str());
5481     }
5482 
5483     if (!ISUNDEF(lph))
5484     {
5485         if (phtype == 0) // legacy mode
5486         {
5487             arg("aff", (byte*)&lph, MegaClient::NODEHANDLE);
5488         }
5489         else
5490         {
5491             beginobject("aff");
5492             arg("id", (byte*)&lph, MegaClient::NODEHANDLE);
5493             arg("ts", ts);
5494             arg("t", phtype);   // 1=affiliate id, 2=file/folder link, 3=chat link, 4=contact link
5495             endobject();
5496         }
5497     }
5498 
5499     tag = client->reqtag;
5500 }
5501 
procresult()5502 void CommandSubmitPurchaseReceipt::procresult()
5503 {
5504     Error e;
5505     if (checkError(e, client->json))
5506     {
5507         client->app->submitpurchasereceipt_result(e);
5508     }
5509     else
5510     {
5511         client->json.storeobject();
5512         client->app->submitpurchasereceipt_result(API_EINTERNAL);
5513     }
5514 }
5515 
5516 // Credit Card Store
CommandCreditCardStore(MegaClient * client,const char * cc,const char * last4,const char * expm,const char * expy,const char * hash)5517 CommandCreditCardStore::CommandCreditCardStore(MegaClient* client, const char *cc, const char *last4, const char *expm, const char *expy, const char *hash)
5518 {
5519     cmd("ccs");
5520     arg("cc", cc);
5521     arg("last4", last4);
5522     arg("expm", expm);
5523     arg("expy", expy);
5524     arg("hash", hash);
5525 
5526     tag = client->reqtag;
5527 }
5528 
procresult()5529 void CommandCreditCardStore::procresult()
5530 {
5531     Error e;
5532     if (checkError(e, client->json))
5533     {
5534         client->app->creditcardstore_result(e);
5535     }
5536     else
5537     {
5538         client->json.storeobject();
5539         client->app->creditcardstore_result(API_EINTERNAL);
5540     }
5541 }
5542 
CommandCreditCardQuerySubscriptions(MegaClient * client)5543 CommandCreditCardQuerySubscriptions::CommandCreditCardQuerySubscriptions(MegaClient* client)
5544 {
5545     cmd("ccqns");
5546 
5547     tag = client->reqtag;
5548 }
5549 
procresult()5550 void CommandCreditCardQuerySubscriptions::procresult()
5551 {
5552     Error e;
5553     if (checkError(e, client->json))
5554     {
5555         client->app->creditcardquerysubscriptions_result(0, e);
5556     }
5557     else if (client->json.isnumeric())
5558     {
5559         int number = int(client->json.getint());
5560         client->app->creditcardquerysubscriptions_result(number, API_OK);
5561     }
5562     else
5563     {
5564         client->json.storeobject();
5565         client->app->creditcardquerysubscriptions_result(0, API_EINTERNAL);
5566     }
5567 }
5568 
CommandCreditCardCancelSubscriptions(MegaClient * client,const char * reason)5569 CommandCreditCardCancelSubscriptions::CommandCreditCardCancelSubscriptions(MegaClient* client, const char* reason)
5570 {
5571     cmd("cccs");
5572 
5573     if (reason)
5574     {
5575         arg("r", reason);
5576     }
5577 
5578     tag = client->reqtag;
5579 }
5580 
procresult()5581 void CommandCreditCardCancelSubscriptions::procresult()
5582 {
5583     Error e;
5584     if (checkError(e, client->json))
5585     {
5586         client->app->creditcardcancelsubscriptions_result(e);
5587     }
5588     else
5589     {
5590         client->json.storeobject();
5591         client->app->creditcardcancelsubscriptions_result(API_EINTERNAL);
5592     }
5593 }
5594 
CommandCopySession(MegaClient * client)5595 CommandCopySession::CommandCopySession(MegaClient *client)
5596 {
5597     cmd("us");
5598     arg("c", 1);
5599     batchSeparately = true;  // don't let any other commands that might get batched with it cause the whole batch to fail when blocked
5600     tag = client->reqtag;
5601 }
5602 
procresult()5603 void CommandCopySession::procresult()
5604 {
5605     string session;
5606     byte sidbuf[AsymmCipher::MAXKEYLENGTH];
5607     int len_csid = 0;
5608 
5609     Error e;
5610     if (checkError(e, client->json))
5611     {
5612         client->app->copysession_result(NULL, e);
5613         return;
5614     }
5615 
5616     for (;;)
5617     {
5618         switch (client->json.getnameid())
5619         {
5620             case MAKENAMEID4('c', 's', 'i', 'd'):
5621                 len_csid = client->json.storebinary(sidbuf, sizeof sidbuf);
5622                 break;
5623 
5624             case EOO:
5625                 if (len_csid < 32)
5626                 {
5627                     return client->app->copysession_result(NULL, API_EINTERNAL);
5628                 }
5629 
5630                 if (!client->asymkey.decrypt(sidbuf, len_csid, sidbuf, MegaClient::SIDLEN))
5631                 {
5632                     return client->app->copysession_result(NULL, API_EINTERNAL);
5633                 }
5634 
5635                 session.resize(MegaClient::SIDLEN * 4 / 3 + 4);
5636                 session.resize(Base64::btoa(sidbuf, MegaClient::SIDLEN, (char *)session.data()));
5637                 return client->app->copysession_result(&session, API_OK);
5638 
5639             default:
5640                 if (!client->json.storeobject())
5641                 {
5642                     return client->app->copysession_result(NULL, API_EINTERNAL);
5643                 }
5644         }
5645     }
5646 }
5647 
CommandGetPaymentMethods(MegaClient * client)5648 CommandGetPaymentMethods::CommandGetPaymentMethods(MegaClient *client)
5649 {
5650     cmd("ufpq");
5651     tag = client->reqtag;
5652 }
5653 
procresult()5654 void CommandGetPaymentMethods::procresult()
5655 {
5656     int methods = 0;
5657     int64_t value;
5658     Error e;
5659 
5660     if (checkError(e, client->json))
5661     {
5662         if (e < 0)
5663         {
5664             client->app->getpaymentmethods_result(methods, e);
5665 
5666             //Consume remaining values if they exist
5667             while(client->json.isnumeric())
5668             {
5669                 client->json.getint();
5670             }
5671             return;
5672         }
5673 
5674         value = static_cast<int64_t>(e);
5675     }
5676     else if (client->json.isnumeric())
5677     {
5678         value = client->json.getint();
5679     }
5680     else
5681     {
5682         LOG_err << "Parse error in ufpq";
5683         client->app->getpaymentmethods_result(methods, API_EINTERNAL);
5684         return;
5685     }
5686 
5687     methods |= 1 << value;
5688 
5689     while (client->json.isnumeric())
5690     {
5691         value = client->json.getint();
5692         if (value < 0)
5693         {
5694             client->app->getpaymentmethods_result(methods, static_cast<error>(value));
5695 
5696             //Consume remaining values if they exist
5697             while(client->json.isnumeric())
5698             {
5699                 client->json.getint();
5700             }
5701             return;
5702         }
5703 
5704         methods |= 1 << value;
5705     }
5706 
5707     client->app->getpaymentmethods_result(methods, API_OK);
5708 }
5709 
CommandUserFeedbackStore(MegaClient * client,const char * type,const char * blob,const char * uid)5710 CommandUserFeedbackStore::CommandUserFeedbackStore(MegaClient *client, const char *type, const char *blob, const char *uid)
5711 {
5712     cmd("clog");
5713 
5714     arg("t", type);
5715 
5716     if (blob)
5717     {
5718         arg("d", blob);
5719     }
5720 
5721     if (uid)
5722     {
5723         arg("id", uid);
5724     }
5725 
5726     tag = client->reqtag;
5727 }
5728 
procresult()5729 void CommandUserFeedbackStore::procresult()
5730 {
5731     Error e;
5732     if (checkError(e, client->json))
5733     {
5734         client->app->userfeedbackstore_result(e);
5735     }
5736     else
5737     {
5738         client->json.storeobject();
5739         client->app->userfeedbackstore_result(API_EINTERNAL);
5740     }
5741 }
5742 
CommandSendEvent(MegaClient * client,int type,const char * desc)5743 CommandSendEvent::CommandSendEvent(MegaClient *client, int type, const char *desc)
5744 {
5745     cmd("log");
5746     arg("e", type);
5747     arg("m", desc);
5748 
5749     tag = client->reqtag;
5750 }
5751 
procresult()5752 void CommandSendEvent::procresult()
5753 {
5754     Error e;
5755     if (checkError(e, client->json))
5756     {
5757         client->app->sendevent_result(e);
5758     }
5759     else
5760     {
5761         client->json.storeobject();
5762         client->app->sendevent_result(API_EINTERNAL);
5763     }
5764 }
5765 
CommandSupportTicket(MegaClient * client,const char * message,int type)5766 CommandSupportTicket::CommandSupportTicket(MegaClient *client, const char *message, int type)
5767 {
5768     cmd("sse");
5769     arg("t", type);
5770     arg("b", 1);    // base64 encoding for `msg`
5771     arg("m", (const byte*)message, int(strlen(message)));
5772 
5773     tag = client->reqtag;
5774 }
5775 
procresult()5776 void CommandSupportTicket::procresult()
5777 {
5778     Error e;
5779     if (checkError(e, client->json))
5780     {
5781         client->app->supportticket_result(e);
5782     }
5783     else
5784     {
5785         client->json.storeobject();
5786         client->app->supportticket_result(API_EINTERNAL);
5787     }
5788 }
5789 
CommandCleanRubbishBin(MegaClient * client)5790 CommandCleanRubbishBin::CommandCleanRubbishBin(MegaClient *client)
5791 {
5792     cmd("dr");
5793 
5794     tag = client->reqtag;
5795 }
5796 
procresult()5797 void CommandCleanRubbishBin::procresult()
5798 {
5799     Error e;
5800     if (checkError(e, client->json))
5801     {
5802         client->app->cleanrubbishbin_result(e);
5803     }
5804     else
5805     {
5806         client->json.storeobject();
5807         client->app->cleanrubbishbin_result(API_EINTERNAL);
5808     }
5809 }
5810 
CommandGetRecoveryLink(MegaClient * client,const char * email,int type,const char * pin)5811 CommandGetRecoveryLink::CommandGetRecoveryLink(MegaClient *client, const char *email, int type, const char *pin)
5812 {
5813     cmd("erm");
5814     arg("m", email);
5815     arg("t", type);
5816 
5817     if (type == CANCEL_ACCOUNT && pin)
5818     {
5819         arg("mfa", pin);
5820     }
5821 
5822     tag = client->reqtag;
5823 }
5824 
procresult()5825 void CommandGetRecoveryLink::procresult()
5826 {
5827     Error e;
5828     if (checkError(e, client->json))
5829     {
5830         client->app->getrecoverylink_result(e);
5831     }
5832     else    // error
5833     {
5834         client->json.storeobject();
5835         client->app->getrecoverylink_result(API_EINTERNAL);
5836     }
5837 }
5838 
CommandQueryRecoveryLink(MegaClient * client,const char * linkcode)5839 CommandQueryRecoveryLink::CommandQueryRecoveryLink(MegaClient *client, const char *linkcode)
5840 {
5841     cmd("erv");
5842     arg("c", linkcode);
5843 
5844     tag = client->reqtag;
5845 }
5846 
procresult()5847 void CommandQueryRecoveryLink::procresult()
5848 {
5849     // [<code>,"<email>","<ip_address>",<timestamp>,"<user_handle>",["<email>"]]
5850 
5851     client->json.enterarray();
5852 
5853     string email;
5854     string ip;
5855     m_time_t ts;
5856     handle uh;
5857 
5858     int type;
5859     Error e;
5860 
5861     if (checkError(e, client->json) && e < 0)
5862     {
5863         return client->app->queryrecoverylink_result(e);
5864     }
5865 
5866     type = static_cast<int>(e);
5867     if (type != API_OK)
5868     {
5869         if (!client->json.isnumeric())
5870         {
5871             return client->app->queryrecoverylink_result(API_EINTERNAL);
5872         }
5873 
5874         type = static_cast<int>(client->json.getint());
5875     }
5876 
5877     if ( !client->json.storeobject(&email)  ||
5878          !client->json.storeobject(&ip)     ||
5879          ((ts = client->json.getint()) == -1) ||
5880          !(uh = client->json.gethandle(MegaClient::USERHANDLE)) )
5881     {
5882         return client->app->queryrecoverylink_result(API_EINTERNAL);
5883     }
5884 
5885     string tmp;
5886     vector<string> emails;
5887 
5888     // read emails registered for this account
5889     client->json.enterarray();
5890     while (client->json.storeobject(&tmp))
5891     {
5892         emails.push_back(tmp);
5893         if (*client->json.pos == ']')
5894         {
5895             break;
5896         }
5897     }
5898     client->json.leavearray();  // emails array
5899     client->json.leavearray();  // response array
5900 
5901     if (!emails.size()) // there should be at least one email
5902     {
5903         return client->app->queryrecoverylink_result(API_EINTERNAL);
5904     }
5905 
5906     if (client->loggedin() == FULLACCOUNT && uh != client->me)
5907     {
5908         return client->app->queryrecoverylink_result(API_EACCESS);
5909     }
5910 
5911     return client->app->queryrecoverylink_result(type, email.c_str(), ip.c_str(), time_t(ts), uh, &emails);
5912 }
5913 
CommandGetPrivateKey(MegaClient * client,const char * code)5914 CommandGetPrivateKey::CommandGetPrivateKey(MegaClient *client, const char *code)
5915 {
5916     cmd("erx");
5917     arg("r", "gk");
5918     arg("c", code);
5919 
5920     tag = client->reqtag;
5921 }
5922 
procresult()5923 void CommandGetPrivateKey::procresult()
5924 {
5925     Error e;
5926     if (checkError(e, client->json))   // error
5927     {
5928         return client->app->getprivatekey_result(e);
5929     }
5930     else
5931     {
5932         byte privkbuf[AsymmCipher::MAXKEYLENGTH * 2];
5933         int len_privk = client->json.storebinary(privkbuf, sizeof privkbuf);
5934 
5935         // account has RSA keypair: decrypt server-provided session ID
5936         if (len_privk < 256)
5937         {
5938             return client->app->getprivatekey_result(API_EINTERNAL);
5939         }
5940         else
5941         {
5942             return client->app->getprivatekey_result((error)API_OK, privkbuf, len_privk);
5943         }
5944     }
5945 }
5946 
CommandConfirmRecoveryLink(MegaClient * client,const char * code,const byte * hash,int hashsize,const byte * clientrandomvalue,const byte * encMasterKey,const byte * initialSession)5947 CommandConfirmRecoveryLink::CommandConfirmRecoveryLink(MegaClient *client, const char *code, const byte *hash, int hashsize, const byte *clientrandomvalue, const byte *encMasterKey, const byte *initialSession)
5948 {
5949     cmd("erx");
5950 
5951     if (!initialSession)
5952     {
5953         arg("r", "sk");
5954     }
5955 
5956     arg("c", code);
5957 
5958     arg("x", encMasterKey, SymmCipher::KEYLENGTH);
5959     if (!clientrandomvalue)
5960     {
5961         arg("y", hash, hashsize);
5962     }
5963     else
5964     {
5965         beginobject("y");
5966         arg("crv", clientrandomvalue, SymmCipher::KEYLENGTH);
5967         arg("hak", hash, hashsize); //hashed authentication key
5968         endobject();
5969     }
5970 
5971     if (initialSession)
5972     {
5973         arg("z", initialSession, 2 * SymmCipher::KEYLENGTH);
5974     }
5975 
5976     tag = client->reqtag;
5977 }
5978 
procresult()5979 void CommandConfirmRecoveryLink::procresult()
5980 {
5981     Error e;
5982     if (checkError(e, client->json))
5983     {
5984         return client->app->confirmrecoverylink_result(e);
5985     }
5986     else   // error
5987     {
5988         client->json.storeobject();
5989         return client->app->confirmrecoverylink_result((error)API_EINTERNAL);
5990     }
5991 }
5992 
CommandConfirmCancelLink(MegaClient * client,const char * code)5993 CommandConfirmCancelLink::CommandConfirmCancelLink(MegaClient *client, const char *code)
5994 {
5995     cmd("erx");
5996     arg("c", code);
5997 
5998     tag = client->reqtag;
5999 }
6000 
procresult()6001 void CommandConfirmCancelLink::procresult()
6002 {
6003     Error e;
6004     if (checkError(e, client->json))
6005     {
6006         MegaApp *app = client->app;
6007         app->confirmcancellink_result(e);
6008         if (!e)
6009         {
6010             app->request_error(API_ESID);
6011         }
6012         return;
6013     }
6014     else   // error
6015     {
6016         client->json.storeobject();
6017         return client->app->confirmcancellink_result((error)API_EINTERNAL);
6018     }
6019 }
6020 
CommandResendVerificationEmail(MegaClient * client)6021 CommandResendVerificationEmail::CommandResendVerificationEmail(MegaClient *client)
6022 {
6023     cmd("era");
6024     batchSeparately = true;  // don't let any other commands that might get batched with it cause the whole batch to fail
6025 
6026     tag = client->reqtag;
6027 }
6028 
procresult()6029 void CommandResendVerificationEmail::procresult()
6030 {
6031     Error e;
6032     if (checkError(e, client->json))
6033     {
6034         client->app->resendverificationemail_result(e);
6035     }
6036     else
6037     {
6038         client->json.storeobject();
6039         client->app->resendverificationemail_result((error)API_EINTERNAL);
6040     }
6041 }
6042 
CommandResetSmsVerifiedPhoneNumber(MegaClient * client)6043 CommandResetSmsVerifiedPhoneNumber::CommandResetSmsVerifiedPhoneNumber(MegaClient *client)
6044 {
6045     cmd("smsr");
6046     tag = client->reqtag;
6047 }
6048 
procresult()6049 void CommandResetSmsVerifiedPhoneNumber::procresult()
6050 {
6051     Error e;
6052     if (checkError(e, client->json))
6053     {
6054         if (e == API_OK)
6055         {
6056             client->mSmsVerifiedPhone.clear();
6057         }
6058         client->app->resetSmsVerifiedPhoneNumber_result(e);
6059     }
6060     else
6061     {
6062         client->json.storeobject();
6063         client->app->resetSmsVerifiedPhoneNumber_result((error)API_EINTERNAL);
6064     }
6065 }
6066 
CommandValidatePassword(MegaClient * client,const char * email,uint64_t emailhash)6067 CommandValidatePassword::CommandValidatePassword(MegaClient *client, const char *email, uint64_t emailhash)
6068 {
6069     cmd("us");
6070     arg("user", email);
6071     arg("uh", (byte*)&emailhash, sizeof emailhash);
6072 
6073     tag = client->reqtag;
6074 }
6075 
procresult()6076 void CommandValidatePassword::procresult()
6077 {
6078     Error e;
6079     if (checkError(e, client->json))
6080     {
6081         return client->app->validatepassword_result(e);
6082     }
6083     else
6084     {
6085         client->json.storeobject();
6086         return client->app->validatepassword_result((error)API_OK);
6087     }
6088 }
6089 
CommandGetEmailLink(MegaClient * client,const char * email,int add,const char * pin)6090 CommandGetEmailLink::CommandGetEmailLink(MegaClient *client, const char *email, int add, const char *pin)
6091 {
6092     cmd("se");
6093 
6094     if (add)
6095     {
6096         arg("aa", "a");     // add
6097     }
6098     else
6099     {
6100         arg("aa", "r");     // remove
6101     }
6102     arg("e", email);
6103     if (pin)
6104     {
6105         arg("mfa", pin);
6106     }
6107 
6108     notself(client);
6109 
6110     tag = client->reqtag;
6111 }
6112 
procresult()6113 void CommandGetEmailLink::procresult()
6114 {
6115     Error e;
6116     if (checkError(e, client->json))
6117     {
6118         return client->app->getemaillink_result(e);
6119     }
6120     else    // error
6121     {
6122         client->json.storeobject();
6123         return client->app->getemaillink_result((error)API_EINTERNAL);
6124     }
6125 }
6126 
CommandConfirmEmailLink(MegaClient * client,const char * code,const char * email,const byte * newLoginHash,bool replace)6127 CommandConfirmEmailLink::CommandConfirmEmailLink(MegaClient *client, const char *code, const char *email, const byte *newLoginHash, bool replace)
6128 {
6129     this->email = email;
6130     this->replace = replace;
6131 
6132     cmd("sec");
6133 
6134     arg("c", code);
6135     arg("e", email);
6136     if (newLoginHash)
6137     {
6138         arg("uh", newLoginHash, sizeof(uint64_t));
6139     }
6140     if (replace)
6141     {
6142         arg("r", 1);    // replace the current email address by this one
6143     }
6144     notself(client);
6145 
6146     tag = client->reqtag;
6147 }
6148 
procresult()6149 void CommandConfirmEmailLink::procresult()
6150 {
6151     Error e;
6152     if (checkError(e, client->json))
6153     {
6154         if (!e)
6155         {
6156             User *u = client->finduser(client->me);
6157 
6158             if (replace)
6159             {
6160                 LOG_debug << "Email changed from `" << u->email << "` to `" << email << "`";
6161 
6162                 client->mapuser(u->userhandle, email.c_str()); // update email used as index for user's map
6163                 u->changed.email = true;
6164                 client->notifyuser(u);
6165             }
6166             // TODO: once we manage multiple emails, add the new email to the list of emails
6167         }
6168 
6169         return client->app->confirmemaillink_result(e);
6170     }
6171     else   // error
6172     {
6173         client->json.storeobject();
6174         return client->app->confirmemaillink_result((error)API_EINTERNAL);
6175     }
6176 }
6177 
CommandGetVersion(MegaClient * client,const char * appKey)6178 CommandGetVersion::CommandGetVersion(MegaClient *client, const char *appKey)
6179 {
6180     this->client = client;
6181     cmd("lv");
6182     arg("a", appKey);
6183     tag = client->reqtag;
6184 }
6185 
procresult()6186 void CommandGetVersion::procresult()
6187 {
6188     int versioncode = 0;
6189     string versionstring;
6190 
6191     Error e;
6192     if (checkError(e, client->json))
6193     {
6194         client->app->getversion_result(0, NULL, e);
6195         return;
6196     }
6197 
6198     for (;;)
6199     {
6200         switch (client->json.getnameid())
6201         {
6202             case 'c':
6203                 versioncode = int(client->json.getint());
6204                 break;
6205 
6206             case 's':
6207                 client->json.storeobject(&versionstring);
6208                 break;
6209 
6210             case EOO:
6211                 return client->app->getversion_result(versioncode, versionstring.c_str(), API_OK);
6212 
6213             default:
6214                 if (!client->json.storeobject())
6215                 {
6216                     return client->app->getversion_result(0, NULL, API_EINTERNAL);
6217                 }
6218         }
6219     }
6220 }
6221 
CommandGetLocalSSLCertificate(MegaClient * client)6222 CommandGetLocalSSLCertificate::CommandGetLocalSSLCertificate(MegaClient *client)
6223 {
6224     this->client = client;
6225     cmd("lc");
6226     arg("v", 1);
6227 
6228     tag = client->reqtag;
6229 }
6230 
procresult()6231 void CommandGetLocalSSLCertificate::procresult()
6232 {
6233     Error e;
6234     if (checkError(e, client->json))
6235     {
6236         client->app->getlocalsslcertificate_result(0, NULL, e);
6237         return;
6238     }
6239 
6240     string certdata;
6241     m_time_t ts = 0;
6242     int numelements = 0;
6243 
6244     for (;;)
6245     {
6246         switch (client->json.getnameid())
6247         {
6248             case 't':
6249             {
6250                 ts = client->json.getint();
6251                 break;
6252             }
6253             case 'd':
6254             {
6255                 string data;
6256                 client->json.enterarray();
6257                 while (client->json.storeobject(&data))
6258                 {
6259                     if (numelements)
6260                     {
6261                         certdata.append(";");
6262                     }
6263                     numelements++;
6264                     certdata.append(data);
6265                 }
6266                 client->json.leavearray();
6267                 break;
6268             }
6269             case EOO:
6270             {
6271                 if (numelements < 2)
6272                 {
6273                     return client->app->getlocalsslcertificate_result(0, NULL, API_EINTERNAL);
6274                 }
6275                 return client->app->getlocalsslcertificate_result(ts, &certdata, API_OK);
6276             }
6277 
6278             default:
6279                 if (!client->json.storeobject())
6280                 {
6281                     return client->app->getlocalsslcertificate_result(0, NULL, API_EINTERNAL);
6282                 }
6283         }
6284     }
6285 }
6286 
6287 #ifdef ENABLE_CHAT
CommandChatCreate(MegaClient * client,bool group,bool publicchat,const userpriv_vector * upl,const string_map * ukm,const char * title)6288 CommandChatCreate::CommandChatCreate(MegaClient *client, bool group, bool publicchat, const userpriv_vector *upl, const string_map *ukm, const char *title)
6289 {
6290     this->client = client;
6291     this->chatPeers = new userpriv_vector(*upl);
6292     this->mPublicChat = publicchat;
6293     this->mTitle = title ? string(title) : "";
6294     this->mUnifiedKey = "";
6295 
6296     cmd("mcc");
6297     arg("g", (group) ? 1 : 0);
6298 
6299     if (group && title)
6300     {
6301         arg("ct", title);
6302     }
6303 
6304     if (publicchat)
6305     {
6306         arg("m", 1);
6307 
6308         char ownHandleB64[12];
6309         Base64::btoa((byte *)&client->me, MegaClient::USERHANDLE, ownHandleB64);
6310         ownHandleB64[11] = '\0';
6311 
6312         string_map::const_iterator it = ukm->find(ownHandleB64);
6313         if (it != ukm->end())
6314         {
6315             mUnifiedKey = it->second;
6316             arg("ck", mUnifiedKey.c_str());
6317         }
6318     }
6319 
6320     beginarray("u");
6321 
6322     userpriv_vector::iterator itupl;
6323     for (itupl = chatPeers->begin(); itupl != chatPeers->end(); itupl++)
6324     {
6325         beginobject();
6326 
6327         handle uh = itupl->first;
6328         privilege_t priv = itupl->second;
6329 
6330         arg("u", (byte *)&uh, MegaClient::USERHANDLE);
6331         arg("p", priv);
6332 
6333         if (publicchat)
6334         {
6335             char uid[12];
6336             Base64::btoa((byte*)&uh, MegaClient::USERHANDLE, uid);
6337             uid[11] = '\0';
6338 
6339             string_map::const_iterator ituk = ukm->find(uid);
6340             if(ituk != ukm->end())
6341             {
6342                 arg("ck", ituk->second.c_str());
6343             }
6344         }
6345         endobject();
6346     }
6347 
6348     endarray();
6349 
6350     arg("v", 1);
6351     notself(client);
6352 
6353     tag = client->reqtag;
6354 }
6355 
procresult()6356 void CommandChatCreate::procresult()
6357 {
6358     Error e;
6359     if (checkError(e, client->json))
6360     {
6361         client->app->chatcreate_result(NULL, e);
6362         delete chatPeers;
6363     }
6364     else
6365     {
6366         handle chatid = UNDEF;
6367         int shard = -1;
6368         bool group = false;
6369         m_time_t ts = -1;
6370 
6371         for (;;)
6372         {
6373             switch (client->json.getnameid())
6374             {
6375                 case MAKENAMEID2('i','d'):
6376                     chatid = client->json.gethandle(MegaClient::CHATHANDLE);
6377                     break;
6378 
6379                 case MAKENAMEID2('c','s'):
6380                     shard = int(client->json.getint());
6381                     break;
6382 
6383                 case 'g':
6384                     group = client->json.getint();
6385                     break;
6386 
6387                 case MAKENAMEID2('t', 's'):  // actual creation timestamp
6388                     ts = client->json.getint();
6389                     break;
6390 
6391                 case EOO:
6392                     if (chatid != UNDEF && shard != -1)
6393                     {
6394                         if (client->chats.find(chatid) == client->chats.end())
6395                         {
6396                             client->chats[chatid] = new TextChat();
6397                         }
6398 
6399                         TextChat *chat = client->chats[chatid];
6400                         chat->id = chatid;
6401                         chat->priv = PRIV_MODERATOR;
6402                         chat->shard = shard;
6403                         delete chat->userpriv;  // discard any existing `userpriv`
6404                         chat->userpriv = this->chatPeers;
6405                         chat->group = group;
6406                         chat->ts = (ts != -1) ? ts : 0;
6407                         chat->publicchat = mPublicChat;
6408                         chat->setTag(tag ? tag : -1);
6409                         if (chat->group && !mTitle.empty())
6410                         {
6411                             chat->title = mTitle;
6412                         }
6413                         if (mPublicChat)
6414                         {
6415                             chat->unifiedKey = mUnifiedKey;
6416                         }
6417 
6418                         client->notifychat(chat);
6419                         client->app->chatcreate_result(chat, API_OK);
6420                     }
6421                     else
6422                     {
6423                         client->app->chatcreate_result(NULL, API_EINTERNAL);
6424                         delete chatPeers;   // unused, but might be set at creation
6425                     }
6426                     return;
6427 
6428                 default:
6429                     if (!client->json.storeobject())
6430                     {
6431                         client->app->chatcreate_result(NULL, API_EINTERNAL);
6432                         delete chatPeers;   // unused, but might be set at creation
6433                         return;
6434                     }
6435             }
6436         }
6437     }
6438 }
6439 
CommandChatInvite(MegaClient * client,handle chatid,handle uh,privilege_t priv,const char * unifiedkey,const char * title)6440 CommandChatInvite::CommandChatInvite(MegaClient *client, handle chatid, handle uh, privilege_t priv, const char *unifiedkey, const char* title)
6441 {
6442     this->client = client;
6443     this->chatid = chatid;
6444     this->uh = uh;
6445     this->priv = priv;
6446     this->title = title ? string(title) : "";
6447 
6448     cmd("mci");
6449 
6450     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6451     arg("u", (byte *)&uh, MegaClient::USERHANDLE);
6452     arg("p", priv);
6453     arg("v", 1);
6454 
6455     if (title)
6456     {
6457         arg("ct", title);
6458     }
6459 
6460     if (unifiedkey)
6461     {
6462         arg("ck", unifiedkey);
6463     }
6464 
6465     notself(client);
6466 
6467     tag = client->reqtag;
6468 }
6469 
procresult()6470 void CommandChatInvite::procresult()
6471 {
6472     Error e;
6473     if (checkError(e, client->json))
6474     {
6475         if (e == API_OK)
6476         {
6477             if (client->chats.find(chatid) == client->chats.end())
6478             {
6479                 // the invitation succeed for a non-existing chatroom
6480                 client->app->chatinvite_result(API_EINTERNAL);
6481                 return;
6482             }
6483 
6484             TextChat *chat = client->chats[chatid];
6485             if (!chat->userpriv)
6486             {
6487                 chat->userpriv = new userpriv_vector();
6488             }
6489 
6490             chat->userpriv->push_back(userpriv_pair(uh, priv));
6491 
6492             if (!title.empty())  // only if title was set for this chatroom, update it
6493             {
6494                 chat->title = title;
6495             }
6496 
6497             chat->setTag(tag ? tag : -1);
6498             client->notifychat(chat);
6499         }
6500 
6501         client->app->chatinvite_result(e);
6502     }
6503     else
6504     {
6505         client->json.storeobject();
6506         client->app->chatinvite_result(API_EINTERNAL);
6507     }
6508 }
6509 
CommandChatRemove(MegaClient * client,handle chatid,handle uh)6510 CommandChatRemove::CommandChatRemove(MegaClient *client, handle chatid, handle uh)
6511 {
6512     this->client = client;
6513     this->chatid = chatid;
6514     this->uh = uh;
6515 
6516     cmd("mcr");
6517 
6518     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6519 
6520     if (uh != client->me)
6521     {
6522         arg("u", (byte *)&uh, MegaClient::USERHANDLE);
6523     }
6524     arg("v", 1);
6525     notself(client);
6526 
6527     tag = client->reqtag;
6528 }
6529 
procresult()6530 void CommandChatRemove::procresult()
6531 {
6532     Error e;
6533     if (checkError(e, client->json))
6534     {
6535         if (e == API_OK)
6536         {
6537             if (client->chats.find(chatid) == client->chats.end())
6538             {
6539                 // the invitation succeed for a non-existing chatroom
6540                 client->app->chatremove_result(API_EINTERNAL);
6541                 return;
6542             }
6543 
6544             TextChat *chat = client->chats[chatid];
6545             if (chat->userpriv)
6546             {
6547                 userpriv_vector::iterator upvit;
6548                 for (upvit = chat->userpriv->begin(); upvit != chat->userpriv->end(); upvit++)
6549                 {
6550                     if (upvit->first == uh)
6551                     {
6552                         chat->userpriv->erase(upvit);
6553                         if (chat->userpriv->empty())
6554                         {
6555                             delete chat->userpriv;
6556                             chat->userpriv = NULL;
6557                         }
6558                         break;
6559                     }
6560                 }
6561             }
6562             else
6563             {
6564                 if (uh != client->me)
6565                 {
6566                     // the removal succeed, but the list of peers is empty
6567                     client->app->chatremove_result(API_EINTERNAL);
6568                     return;
6569                 }
6570             }
6571 
6572             if (uh == client->me)
6573             {
6574                 chat->priv = PRIV_RM;
6575 
6576                 // clear the list of peers (if re-invited, peers will be re-added)
6577                 delete chat->userpriv;
6578                 chat->userpriv = NULL;
6579             }
6580 
6581             chat->setTag(tag ? tag : -1);
6582             client->notifychat(chat);
6583         }
6584 
6585         client->app->chatremove_result(e);
6586     }
6587     else
6588     {
6589         client->json.storeobject();
6590         client->app->chatremove_result(API_EINTERNAL);
6591     }
6592 }
6593 
CommandChatURL(MegaClient * client,handle chatid)6594 CommandChatURL::CommandChatURL(MegaClient *client, handle chatid)
6595 {
6596     this->client = client;
6597 
6598     cmd("mcurl");
6599 
6600     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6601     arg("v", 1);
6602     notself(client);
6603 
6604     tag = client->reqtag;
6605 }
6606 
procresult()6607 void CommandChatURL::procresult()
6608 {
6609     Error e;
6610     if (checkError(e, client->json))
6611     {
6612         client->app->chaturl_result(NULL, e);
6613     }
6614     else
6615     {
6616         string url;
6617         if (!client->json.storeobject(&url))
6618         {
6619             client->app->chaturl_result(NULL, API_EINTERNAL);
6620         }
6621         else
6622         {
6623             client->app->chaturl_result(&url, API_OK);
6624         }
6625     }
6626 }
6627 
CommandChatGrantAccess(MegaClient * client,handle chatid,handle h,const char * uid)6628 CommandChatGrantAccess::CommandChatGrantAccess(MegaClient *client, handle chatid, handle h, const char *uid)
6629 {
6630     this->client = client;
6631     this->chatid = chatid;
6632     this->h = h;
6633     Base64::atob(uid, (byte*)&uh, MegaClient::USERHANDLE);
6634 
6635     cmd("mcga");
6636 
6637     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6638     arg("n", (byte*)&h, MegaClient::NODEHANDLE);
6639     arg("u", uid);
6640     arg("v", 1);
6641     notself(client);
6642 
6643     tag = client->reqtag;
6644 }
6645 
procresult()6646 void CommandChatGrantAccess::procresult()
6647 {
6648     Error e;
6649     if (checkError(e, client->json))
6650     {
6651         if (e == API_OK)
6652         {
6653             if (client->chats.find(chatid) == client->chats.end())
6654             {
6655                 // the action succeed for a non-existing chatroom??
6656                 client->app->chatgrantaccess_result(API_EINTERNAL);
6657                 return;
6658             }
6659 
6660             TextChat *chat = client->chats[chatid];
6661             chat->setNodeUserAccess(h, uh);
6662 
6663             chat->setTag(tag ? tag : -1);
6664             client->notifychat(chat);
6665         }
6666 
6667         client->app->chatgrantaccess_result(e);
6668     }
6669     else
6670     {
6671         client->json.storeobject();
6672         client->app->chatgrantaccess_result(API_EINTERNAL);
6673     }
6674 }
6675 
CommandChatRemoveAccess(MegaClient * client,handle chatid,handle h,const char * uid)6676 CommandChatRemoveAccess::CommandChatRemoveAccess(MegaClient *client, handle chatid, handle h, const char *uid)
6677 {
6678     this->client = client;
6679     this->chatid = chatid;
6680     this->h = h;
6681     Base64::atob(uid, (byte*)&uh, MegaClient::USERHANDLE);
6682 
6683     cmd("mcra");
6684 
6685     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6686     arg("n", (byte*)&h, MegaClient::NODEHANDLE);
6687     arg("u", uid);
6688     arg("v", 1);
6689     notself(client);
6690 
6691     tag = client->reqtag;
6692 }
6693 
procresult()6694 void CommandChatRemoveAccess::procresult()
6695 {
6696     Error e;
6697     if (checkError(e, client->json))
6698     {
6699         if (e == API_OK)
6700         {
6701             if (client->chats.find(chatid) == client->chats.end())
6702             {
6703                 // the action succeed for a non-existing chatroom??
6704                 client->app->chatremoveaccess_result(API_EINTERNAL);
6705                 return;
6706             }
6707 
6708             TextChat *chat = client->chats[chatid];
6709             chat->setNodeUserAccess(h, uh, true);
6710 
6711             chat->setTag(tag ? tag : -1);
6712             client->notifychat(chat);
6713         }
6714 
6715         client->app->chatremoveaccess_result(e);
6716     }
6717     else
6718     {
6719         client->json.storeobject();
6720         client->app->chatremoveaccess_result(API_EINTERNAL);
6721     }
6722 }
6723 
CommandChatUpdatePermissions(MegaClient * client,handle chatid,handle uh,privilege_t priv)6724 CommandChatUpdatePermissions::CommandChatUpdatePermissions(MegaClient *client, handle chatid, handle uh, privilege_t priv)
6725 {
6726     this->client = client;
6727     this->chatid = chatid;
6728     this->uh = uh;
6729     this->priv = priv;
6730 
6731     cmd("mcup");
6732     arg("v", 1);
6733 
6734     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6735     arg("u", (byte *)&uh, MegaClient::USERHANDLE);
6736     arg("p", priv);
6737     notself(client);
6738 
6739     tag = client->reqtag;
6740 }
6741 
procresult()6742 void CommandChatUpdatePermissions::procresult()
6743 {
6744     Error e;
6745     if (checkError(e, client->json))
6746     {
6747         if (e == API_OK)
6748         {
6749             if (client->chats.find(chatid) == client->chats.end())
6750             {
6751                 // the invitation succeed for a non-existing chatroom
6752                 client->app->chatupdatepermissions_result(API_EINTERNAL);
6753                 return;
6754             }
6755 
6756             TextChat *chat = client->chats[chatid];
6757             if (uh != client->me)
6758             {
6759                 if (!chat->userpriv)
6760                 {
6761                     // the update succeed, but that peer is not included in the chatroom
6762                     client->app->chatupdatepermissions_result(API_EINTERNAL);
6763                     return;
6764                 }
6765 
6766                 bool found = false;
6767                 userpriv_vector::iterator upvit;
6768                 for (upvit = chat->userpriv->begin(); upvit != chat->userpriv->end(); upvit++)
6769                 {
6770                     if (upvit->first == uh)
6771                     {
6772                         chat->userpriv->erase(upvit);
6773                         chat->userpriv->push_back(userpriv_pair(uh, priv));
6774                         found = true;
6775                         break;
6776                     }
6777                 }
6778 
6779                 if (!found)
6780                 {
6781                     // the update succeed, but that peer is not included in the chatroom
6782                     client->app->chatupdatepermissions_result(API_EINTERNAL);
6783                     return;
6784                 }
6785             }
6786             else
6787             {
6788                 chat->priv = priv;
6789             }
6790 
6791             chat->setTag(tag ? tag : -1);
6792             client->notifychat(chat);
6793         }
6794 
6795         client->app->chatupdatepermissions_result(e);
6796     }
6797     else
6798     {
6799         client->json.storeobject();
6800         client->app->chatupdatepermissions_result(API_EINTERNAL);
6801     }
6802 }
6803 
6804 
CommandChatTruncate(MegaClient * client,handle chatid,handle messageid)6805 CommandChatTruncate::CommandChatTruncate(MegaClient *client, handle chatid, handle messageid)
6806 {
6807     this->client = client;
6808     this->chatid = chatid;
6809 
6810     cmd("mct");
6811     arg("v", 1);
6812 
6813     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6814     arg("m", (byte*)&messageid, MegaClient::CHATHANDLE);
6815     notself(client);
6816 
6817     tag = client->reqtag;
6818 }
6819 
procresult()6820 void CommandChatTruncate::procresult()
6821 {
6822     Error e;
6823     if (checkError(e, client->json))
6824     {
6825         if (e == API_OK)
6826         {
6827             if (client->chats.find(chatid) == client->chats.end())
6828             {
6829                 // the truncation succeed for a non-existing chatroom
6830                 client->app->chattruncate_result(API_EINTERNAL);
6831                 return;
6832             }
6833 
6834             TextChat *chat = client->chats[chatid];
6835             chat->setTag(tag ? tag : -1);
6836             client->notifychat(chat);
6837         }
6838 
6839         client->app->chattruncate_result(e);
6840     }
6841     else
6842     {
6843         client->json.storeobject();
6844         client->app->chattruncate_result(API_EINTERNAL);
6845     }
6846 }
6847 
CommandChatSetTitle(MegaClient * client,handle chatid,const char * title)6848 CommandChatSetTitle::CommandChatSetTitle(MegaClient *client, handle chatid, const char *title)
6849 {
6850     this->client = client;
6851     this->chatid = chatid;
6852     this->title = title ? string(title) : "";
6853 
6854     cmd("mcst");
6855     arg("v", 1);
6856 
6857     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6858     arg("ct", title);
6859     notself(client);
6860 
6861     tag = client->reqtag;
6862 }
6863 
procresult()6864 void CommandChatSetTitle::procresult()
6865 {
6866     Error e;
6867     if (checkError(e, client->json))
6868     {
6869         if (e == API_OK)
6870         {
6871             if (client->chats.find(chatid) == client->chats.end())
6872             {
6873                 // the invitation succeed for a non-existing chatroom
6874                 client->app->chatsettitle_result(API_EINTERNAL);
6875                 return;
6876             }
6877 
6878             TextChat *chat = client->chats[chatid];
6879             chat->title = title;
6880 
6881             chat->setTag(tag ? tag : -1);
6882             client->notifychat(chat);
6883         }
6884 
6885         client->app->chatsettitle_result(e);
6886     }
6887     else
6888     {
6889         client->json.storeobject();
6890         client->app->chatsettitle_result(API_EINTERNAL);
6891     }
6892 }
6893 
CommandChatPresenceURL(MegaClient * client)6894 CommandChatPresenceURL::CommandChatPresenceURL(MegaClient *client)
6895 {
6896     this->client = client;
6897     cmd("pu");
6898     notself(client);
6899     tag = client->reqtag;
6900 }
6901 
procresult()6902 void CommandChatPresenceURL::procresult()
6903 {
6904     Error e;
6905     if (checkError(e, client->json))
6906     {
6907         client->app->chatpresenceurl_result(NULL, e);
6908     }
6909     else
6910     {
6911         string url;
6912         if (!client->json.storeobject(&url))
6913         {
6914             client->app->chatpresenceurl_result(NULL, API_EINTERNAL);
6915         }
6916         else
6917         {
6918             client->app->chatpresenceurl_result(&url, API_OK);
6919         }
6920     }
6921 }
6922 
CommandRegisterPushNotification(MegaClient * client,int deviceType,const char * token)6923 CommandRegisterPushNotification::CommandRegisterPushNotification(MegaClient *client, int deviceType, const char *token)
6924 {
6925     this->client = client;
6926     cmd("spt");
6927     arg("p", deviceType);
6928     arg("t", token);
6929 
6930     tag = client->reqtag;
6931 }
6932 
procresult()6933 void CommandRegisterPushNotification::procresult()
6934 {
6935     Error e;
6936     if (checkError(e, client->json))
6937     {
6938         client->app->registerpushnotification_result(e);
6939     }
6940     else
6941     {
6942         client->json.storeobject();
6943         client->app->registerpushnotification_result(API_EINTERNAL);
6944     }
6945 }
6946 
CommandArchiveChat(MegaClient * client,handle chatid,bool archive)6947 CommandArchiveChat::CommandArchiveChat(MegaClient *client, handle chatid, bool archive)
6948 {
6949     this->mChatid = chatid;
6950     this->mArchive = archive;
6951 
6952     cmd("mcsf");
6953 
6954     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
6955     arg("m", 1);
6956     arg("f", archive);
6957 
6958     notself(client);
6959 
6960     tag = client->reqtag;
6961 }
6962 
procresult()6963 void CommandArchiveChat::procresult()
6964 {
6965     Error e;
6966     if (checkError(e, client->json))
6967     {
6968         if (e == API_OK)
6969         {
6970             textchat_map::iterator it = client->chats.find(mChatid);
6971             if (it == client->chats.end())
6972             {
6973                 LOG_err << "Archive chat succeeded for a non-existing chatroom";
6974                 client->app->archivechat_result(API_ENOENT);
6975                 return;
6976             }
6977 
6978             TextChat *chat = it->second;
6979             chat->setFlag(mArchive, TextChat::FLAG_OFFSET_ARCHIVE);
6980 
6981             chat->setTag(tag ? tag : -1);
6982             client->notifychat(chat);
6983         }
6984 
6985         client->app->archivechat_result(e);
6986     }
6987     else
6988     {
6989         client->json.storeobject();
6990         client->app->archivechat_result(API_EINTERNAL);
6991     }
6992 }
6993 
CommandSetChatRetentionTime(MegaClient * client,handle chatid,int period)6994 CommandSetChatRetentionTime::CommandSetChatRetentionTime(MegaClient *client, handle chatid, int period)
6995 {
6996     mChatid = chatid;
6997 
6998     cmd("mcsr");
6999     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
7000     arg("d", period);
7001     arg("ds", 1);
7002     tag = client->reqtag;
7003 }
7004 
procresult()7005 void CommandSetChatRetentionTime::procresult()
7006 {
7007     if (client->json.isnumeric())
7008     {
7009         client->app->setchatretentiontime_result(static_cast<error>(client->json.getint()));
7010     }
7011     else
7012     {
7013         client->json.storeobject();
7014         client->app->setchatretentiontime_result(API_EINTERNAL);
7015     }
7016 }
7017 
CommandRichLink(MegaClient * client,const char * url)7018 CommandRichLink::CommandRichLink(MegaClient *client, const char *url)
7019 {
7020     cmd("erlsd");
7021 
7022     arg("url", url);
7023 
7024     tag = client->reqtag;
7025 }
7026 
procresult()7027 void CommandRichLink::procresult()
7028 {
7029     // error format: [{"error":<code>}]
7030     // result format: [{"result":{
7031     //                      "url":"<url>",
7032     //                      "t":"<title>",
7033     //                      "d":"<description>",
7034     //                      "ic":"<format>:<icon_B64>",
7035     //                      "i":"<format>:<image>"}}]
7036 
7037     Error e;
7038     if (checkError(e, client->json))
7039     {
7040         return client->app->richlinkrequest_result(NULL, e);
7041     }
7042 
7043 
7044     string res;
7045     int errCode = 0;
7046     string metadata;
7047     for (;;)
7048     {
7049         switch (client->json.getnameid())
7050         {
7051             case MAKENAMEID5('e', 'r', 'r', 'o', 'r'):
7052                 errCode = int(client->json.getint());
7053                 break;
7054 
7055             case MAKENAMEID6('r', 'e', 's', 'u', 'l', 't'):
7056                 client->json.storeobject(&metadata);
7057                 break;
7058 
7059             case EOO:
7060             {
7061                 error e = API_EINTERNAL;
7062                 if (!metadata.empty())
7063                 {
7064                     return client->app->richlinkrequest_result(&metadata, API_OK);
7065                 }
7066                 else if (errCode)
7067                 {
7068                     switch(errCode)
7069                     {
7070                         case 403:
7071                             e = API_EACCESS;
7072                             break;
7073 
7074                         case 404:
7075                             e = API_ENOENT;
7076                             break;
7077 
7078                         default:
7079                             e = API_EINTERNAL;
7080                             break;
7081                     }
7082                 }
7083 
7084                 return client->app->richlinkrequest_result(NULL, e);
7085             }
7086 
7087             default:
7088                 if (!client->json.storeobject())
7089                 {
7090                     return client->app->richlinkrequest_result(NULL, API_EINTERNAL);
7091                 }
7092         }
7093     }
7094 }
7095 
CommandChatLink(MegaClient * client,handle chatid,bool del,bool createifmissing)7096 CommandChatLink::CommandChatLink(MegaClient *client, handle chatid, bool del, bool createifmissing)
7097 {
7098     mDelete = del;
7099 
7100     cmd("mcph");
7101     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
7102 
7103     if (del)
7104     {
7105         arg("d", 1);
7106     }
7107 
7108     if (!createifmissing)
7109     {
7110         arg("cim", (m_off_t)0);
7111     }
7112 
7113     notself(client);
7114     tag = client->reqtag;
7115 }
7116 
procresult()7117 void CommandChatLink::procresult()
7118 {
7119     Error e;
7120     if (checkError(e, client->json))
7121     {
7122         if (e == API_OK && !mDelete)
7123         {
7124             LOG_err << "Unexpected response for create/get chatlink";
7125             client->app->chatlink_result(UNDEF, API_EINTERNAL);
7126             return;
7127         }
7128 
7129         client->app->chatlink_result(UNDEF, e);
7130     }
7131     else
7132     {
7133         handle h = client->json.gethandle(MegaClient::CHATLINKHANDLE);
7134         if (ISUNDEF(h))
7135         {
7136             client->app->chatlink_result(UNDEF, API_EINTERNAL);
7137         }
7138         else
7139         {
7140             client->app->chatlink_result(h, API_OK);
7141         }
7142     }
7143 }
7144 
CommandChatLinkURL(MegaClient * client,handle publichandle)7145 CommandChatLinkURL::CommandChatLinkURL(MegaClient *client, handle publichandle)
7146 {
7147     cmd("mcphurl");
7148     arg("ph", (byte*)&publichandle, MegaClient::CHATLINKHANDLE);
7149 
7150     notself(client);
7151     tag = client->reqtag;
7152 }
7153 
procresult()7154 void CommandChatLinkURL::procresult()
7155 {
7156     Error e;
7157     if (checkError(e, client->json))
7158     {
7159         client->app->chatlinkurl_result(UNDEF, -1, NULL, NULL, -1, 0, e);
7160     }
7161     else
7162     {
7163         handle chatid = UNDEF;
7164         int shard = -1;
7165         int numPeers = -1;
7166         string url;
7167         string ct;
7168         m_time_t ts = 0;
7169 
7170         for (;;)
7171         {
7172             switch (client->json.getnameid())
7173             {
7174                 case MAKENAMEID2('i','d'):
7175                     chatid = client->json.gethandle(MegaClient::CHATHANDLE);
7176                     break;
7177 
7178                 case MAKENAMEID2('c','s'):
7179                     shard = int(client->json.getint());
7180                     break;
7181 
7182                 case MAKENAMEID2('c','t'):  // chat-title
7183                     client->json.storeobject(&ct);
7184                     break;
7185 
7186                 case MAKENAMEID3('u','r','l'):
7187                     client->json.storeobject(&url);
7188                     break;
7189 
7190                 case MAKENAMEID3('n','c','m'):
7191                     numPeers = int(client->json.getint());
7192                     break;
7193 
7194                 case MAKENAMEID2('t', 's'):
7195                     ts = client->json.getint();
7196                     break;
7197 
7198                 case EOO:
7199                     if (chatid != UNDEF && shard != -1 && !url.empty() && !ct.empty() && numPeers != -1)
7200                     {
7201                         client->app->chatlinkurl_result(chatid, shard, &url, &ct, numPeers, ts, API_OK);
7202                     }
7203                     else
7204                     {
7205                         client->app->chatlinkurl_result(UNDEF, -1, NULL, NULL, -1, 0, API_EINTERNAL);
7206                     }
7207                     return;
7208 
7209                 default:
7210                     if (!client->json.storeobject())
7211                     {
7212                         client->app->chatlinkurl_result(UNDEF, -1, NULL, NULL, -1, 0, API_EINTERNAL);
7213                         return;
7214                     }
7215             }
7216         }
7217     }
7218 }
7219 
CommandChatLinkClose(MegaClient * client,handle chatid,const char * title)7220 CommandChatLinkClose::CommandChatLinkClose(MegaClient *client, handle chatid, const char *title)
7221 {
7222     mChatid = chatid;
7223     mTitle = title ? string(title) : "";
7224 
7225     cmd("mcscm");
7226     arg("id", (byte*)&chatid, MegaClient::CHATHANDLE);
7227 
7228     if (title)
7229     {
7230         arg("ct", title);
7231     }
7232 
7233     notself(client);
7234     tag = client->reqtag;
7235 }
7236 
procresult()7237 void CommandChatLinkClose::procresult()
7238 {
7239     Error e;
7240     if (checkError(e, client->json))
7241     {
7242         if (e == API_OK)
7243         {
7244             textchat_map::iterator it = client->chats.find(mChatid);
7245             if (it == client->chats.end())
7246             {
7247                 LOG_err << "Chat link close succeeded for a non-existing chatroom";
7248                 client->app->chatlinkclose_result(API_ENOENT);
7249                 return;
7250             }
7251 
7252             TextChat *chat = it->second;
7253             chat->setMode(false);
7254             if (!mTitle.empty())
7255             {
7256                 chat->title = mTitle;
7257             }
7258 
7259             chat->setTag(tag ? tag : -1);
7260             client->notifychat(chat);
7261         }
7262 
7263         client->app->chatlinkclose_result(e);
7264     }
7265     else
7266     {
7267         client->json.storeobject();
7268         client->app->chatlinkclose_result(API_EINTERNAL);
7269     }
7270 }
7271 
CommandChatLinkJoin(MegaClient * client,handle publichandle,const char * unifiedkey)7272 CommandChatLinkJoin::CommandChatLinkJoin(MegaClient *client, handle publichandle, const char *unifiedkey)
7273 {
7274     cmd("mciph");
7275     arg("ph", (byte*)&publichandle, MegaClient::CHATLINKHANDLE);
7276     arg("ck", unifiedkey);
7277     tag = client->reqtag;
7278 }
7279 
procresult()7280 void CommandChatLinkJoin::procresult()
7281 {
7282     Error e;
7283     if (checkError(e, client->json))
7284     {
7285         client->app->chatlinkjoin_result(e);
7286     }
7287     else
7288     {
7289         client->json.storeobject();
7290         client->app->chatlinkjoin_result(API_EINTERNAL);
7291     }
7292 }
7293 
7294 #endif
7295 
CommandGetMegaAchievements(MegaClient * client,AchievementsDetails * details,bool registered_user)7296 CommandGetMegaAchievements::CommandGetMegaAchievements(MegaClient *client, AchievementsDetails *details, bool registered_user)
7297 {
7298     this->details = details;
7299 
7300     if (registered_user)
7301     {
7302         cmd("maf");
7303     }
7304     else
7305     {
7306         cmd("mafu");
7307     }
7308 
7309     arg("v", (m_off_t)0);
7310 
7311     tag = client->reqtag;
7312 }
7313 
procresult()7314 void CommandGetMegaAchievements::procresult()
7315 {
7316     Error e;
7317     if (checkError(e, client->json))
7318     {
7319         client->app->getmegaachievements_result(details, e);
7320         return;
7321     }
7322 
7323     details->permanent_size = 0;
7324     details->achievements.clear();
7325     details->awards.clear();
7326     details->rewards.clear();
7327 
7328     for (;;)
7329     {
7330         switch (client->json.getnameid())
7331         {
7332             case 's':
7333                 details->permanent_size = client->json.getint();
7334                 break;
7335 
7336             case 'u':
7337                 if (client->json.enterobject())
7338                 {
7339                     for (;;)
7340                     {
7341                         achievement_class_id id = achievement_class_id(client->json.getnameid());
7342                         if (id == EOO)
7343                         {
7344                             break;
7345                         }
7346                         id -= '0';   // convert to number
7347 
7348                         if (client->json.enterarray())
7349                         {
7350                             Achievement achievement;
7351                             achievement.storage = client->json.getint();
7352                             achievement.transfer = client->json.getint();
7353                             const char *exp_ts = client->json.getvalue();
7354                             char *pEnd = NULL;
7355                             achievement.expire = int(strtol(exp_ts, &pEnd, 10));
7356                             if (*pEnd == 'm')
7357                             {
7358                                 achievement.expire *= 30;
7359                             }
7360                             else if (*pEnd == 'y')
7361                             {
7362                                 achievement.expire *= 365;
7363                             }
7364 
7365                             details->achievements[id] = achievement;
7366 
7367                             while(client->json.storeobject());
7368                             client->json.leavearray();
7369                         }
7370                     }
7371 
7372                     client->json.leaveobject();
7373                 }
7374                 else
7375                 {
7376                     LOG_err << "Failed to parse Achievements of MEGA achievements";
7377                     client->json.storeobject();
7378                     client->app->getmegaachievements_result(details, API_EINTERNAL);
7379                     return;
7380                 }
7381                 break;
7382 
7383             case 'a':
7384                 if (client->json.enterarray())
7385                 {
7386                     while (client->json.enterobject())
7387                     {
7388                         Award award;
7389                         award.achievement_class = 0;
7390                         award.award_id = 0;
7391                         award.ts = 0;
7392                         award.expire = 0;
7393 
7394                         bool finished = false;
7395                         while (!finished)
7396                         {
7397                             switch (client->json.getnameid())
7398                             {
7399                             case 'a':
7400                                 award.achievement_class = achievement_class_id(client->json.getint());
7401                                 break;
7402                             case 'r':
7403                                 award.award_id = int(client->json.getint());
7404                                 break;
7405                             case MAKENAMEID2('t', 's'):
7406                                 award.ts = client->json.getint();
7407                                 break;
7408                             case 'e':
7409                                 award.expire = client->json.getint();
7410                                 break;
7411                             case 'm':
7412                                 if (client->json.enterarray())
7413                                 {
7414                                     string email;
7415                                     while(client->json.storeobject(&email))
7416                                     {
7417                                         award.emails_invited.push_back(email);
7418                                     }
7419 
7420                                     client->json.leavearray();
7421                                 }
7422                                 break;
7423                             case EOO:
7424                                 finished = true;
7425                                 break;
7426                             default:
7427                                 client->json.storeobject();
7428                                 break;
7429                             }
7430                         }
7431 
7432                         details->awards.push_back(award);
7433 
7434                         client->json.leaveobject();
7435                     }
7436 
7437                     client->json.leavearray();
7438                 }
7439                 else
7440                 {
7441                     LOG_err << "Failed to parse Awards of MEGA achievements";
7442                     client->json.storeobject();
7443                     client->app->getmegaachievements_result(details, API_EINTERNAL);
7444                     return;
7445                 }
7446                 break;
7447 
7448             case 'r':
7449                 if (client->json.enterobject())
7450                 {
7451                     for (;;)
7452                     {
7453                         nameid id = client->json.getnameid();
7454                         if (id == EOO)
7455                         {
7456                             break;
7457                         }
7458 
7459                         Reward reward;
7460                         reward.award_id = int(id - '0');   // convert to number
7461 
7462                         client->json.enterarray();
7463 
7464                         reward.storage = client->json.getint();
7465                         reward.transfer = client->json.getint();
7466                         const char *exp_ts = client->json.getvalue();
7467                         char *pEnd = NULL;
7468                         reward.expire = int(strtol(exp_ts, &pEnd, 10));
7469                         if (*pEnd == 'm')
7470                         {
7471                             reward.expire *= 30;
7472                         }
7473                         else if (*pEnd == 'y')
7474                         {
7475                             reward.expire *= 365;
7476                         }
7477 
7478                         while(client->json.storeobject());
7479                         client->json.leavearray();
7480 
7481                         details->rewards.push_back(reward);
7482                     }
7483 
7484                     client->json.leaveobject();
7485                 }
7486                 else
7487                 {
7488                     LOG_err << "Failed to parse Rewards of MEGA achievements";
7489                     client->json.storeobject();
7490                     client->app->getmegaachievements_result(details, API_EINTERNAL);
7491                     return;
7492                 }
7493                 break;
7494 
7495             case EOO:
7496                 client->app->getmegaachievements_result(details, API_OK);
7497                 return;
7498 
7499             default:
7500                 if (!client->json.storeobject())
7501                 {
7502                     LOG_err << "Failed to parse MEGA achievements";
7503                     client->app->getmegaachievements_result(details, API_EINTERNAL);
7504                     return;
7505                 }
7506                 break;
7507         }
7508     }
7509 }
7510 
CommandGetWelcomePDF(MegaClient * client)7511 CommandGetWelcomePDF::CommandGetWelcomePDF(MegaClient *client)
7512 {
7513     cmd("wpdf");
7514 
7515     tag = client->reqtag;
7516 }
7517 
procresult()7518 void CommandGetWelcomePDF::procresult()
7519 {
7520     Error e;
7521     if (checkError(e, client->json))
7522     {
7523         client->app->getwelcomepdf_result(UNDEF, NULL, e);
7524         return;
7525     }
7526 
7527     handle ph = UNDEF;
7528     byte keybuf[FILENODEKEYLENGTH];
7529     int len_key = 0;
7530     string key;
7531 
7532     for (;;)
7533     {
7534         switch (client->json.getnameid())
7535         {
7536             case MAKENAMEID2('p', 'h'):
7537                 ph = client->json.gethandle(MegaClient::NODEHANDLE);
7538                 break;
7539 
7540             case 'k':
7541                 len_key = client->json.storebinary(keybuf, sizeof keybuf);
7542                 break;
7543 
7544             case EOO:
7545                 if (ISUNDEF(ph) || len_key != FILENODEKEYLENGTH)
7546                 {
7547                     return client->app->getwelcomepdf_result(UNDEF, NULL, API_EINTERNAL);
7548                 }
7549                 key.assign((const char *) keybuf, len_key);
7550                 return client->app->getwelcomepdf_result(ph, &key, API_OK);
7551 
7552             default:
7553                 if (!client->json.storeobject())
7554                 {
7555                     LOG_err << "Failed to parse welcome PDF response";
7556                     return client->app->getwelcomepdf_result(UNDEF, NULL, API_EINTERNAL);
7557                 }
7558                 break;
7559         }
7560     }
7561 }
7562 
7563 
CommandMediaCodecs(MegaClient * c,Callback cb)7564 CommandMediaCodecs::CommandMediaCodecs(MegaClient* c, Callback cb)
7565 {
7566     cmd("mc");
7567 
7568     client = c;
7569     callback = cb;
7570 }
7571 
procresult()7572 void CommandMediaCodecs::procresult()
7573 {
7574     Error e;
7575     int version;
7576     if (checkError(e, client->json))
7577     {
7578         if (e < 0)
7579         {
7580             LOG_err << "mc result: " << e;
7581         }
7582 
7583         version = e;
7584     }
7585     else if (client->json.isnumeric())
7586     {
7587         version = static_cast<int>(client->json.getint());
7588     }
7589     else
7590     {
7591         // It's wrongly formatted, consume this one so the next command can be processed.
7592         LOG_err << "mc response badly formatted";
7593         client->json.storeobject();
7594         return;
7595     }
7596 
7597     callback(client, version);
7598 }
7599 
CommandContactLinkCreate(MegaClient * client,bool renew)7600 CommandContactLinkCreate::CommandContactLinkCreate(MegaClient *client, bool renew)
7601 {
7602     if (renew)
7603     {
7604         cmd("clr");
7605     }
7606     else
7607     {
7608         cmd("clc");
7609     }
7610 
7611     tag = client->reqtag;
7612 }
7613 
procresult()7614 void CommandContactLinkCreate::procresult()
7615 {
7616     Error e;
7617     if (checkError(e, client->json))
7618     {
7619         client->app->contactlinkcreate_result(e, UNDEF);
7620     }
7621     else
7622     {
7623         handle h = client->json.gethandle(MegaClient::CONTACTLINKHANDLE);
7624         client->app->contactlinkcreate_result(API_OK, h);
7625     }
7626 }
7627 
CommandContactLinkQuery(MegaClient * client,handle h)7628 CommandContactLinkQuery::CommandContactLinkQuery(MegaClient *client, handle h)
7629 {
7630     cmd("clg");
7631     arg("cl", (byte*)&h, MegaClient::CONTACTLINKHANDLE);
7632 
7633     arg("b", 1);    // return firstname/lastname in B64
7634 
7635     tag = client->reqtag;
7636 }
7637 
procresult()7638 void CommandContactLinkQuery::procresult()
7639 {
7640     handle h = UNDEF;
7641     string email;
7642     string firstname;
7643     string lastname;
7644     string avatar;
7645 
7646     Error e;
7647     if (checkError(e, client->json))
7648     {
7649         return client->app->contactlinkquery_result(e, h, &email, &firstname, &lastname, &avatar);
7650     }
7651 
7652     for (;;)
7653     {
7654         switch (client->json.getnameid())
7655         {
7656             case 'h':
7657                 h = client->json.gethandle(MegaClient::USERHANDLE);
7658                 break;
7659             case 'e':
7660                 client->json.storeobject(&email);
7661                 break;
7662             case MAKENAMEID2('f', 'n'):
7663                 client->json.storeobject(&firstname);
7664                 break;
7665             case MAKENAMEID2('l', 'n'):
7666                 client->json.storeobject(&lastname);
7667                 break;
7668             case MAKENAMEID2('+', 'a'):
7669                 client->json.storeobject(&avatar);
7670                 break;
7671             case EOO:
7672                 return client->app->contactlinkquery_result(API_OK, h, &email, &firstname, &lastname, &avatar);
7673             default:
7674                 if (!client->json.storeobject())
7675                 {
7676                     LOG_err << "Failed to parse query contact link response";
7677                     return client->app->contactlinkquery_result(API_EINTERNAL, h, &email, &firstname, &lastname, &avatar);
7678                 }
7679                 break;
7680         }
7681     }
7682 }
7683 
CommandContactLinkDelete(MegaClient * client,handle h)7684 CommandContactLinkDelete::CommandContactLinkDelete(MegaClient *client, handle h)
7685 {
7686     cmd("cld");
7687     if (!ISUNDEF(h))
7688     {
7689         arg("cl", (byte*)&h, MegaClient::CONTACTLINKHANDLE);
7690     }
7691     tag = client->reqtag;
7692 }
7693 
procresult()7694 void CommandContactLinkDelete::procresult()
7695 {
7696     Error e;
7697     if (checkError(e, client->json))
7698     {
7699         client->app->contactlinkdelete_result(e);
7700     }
7701     else
7702     {
7703         client->json.storeobject();
7704         client->app->contactlinkdelete_result(API_EINTERNAL);
7705     }
7706 }
7707 
CommandKeepMeAlive(MegaClient * client,int type,bool enable)7708 CommandKeepMeAlive::CommandKeepMeAlive(MegaClient *client, int type, bool enable)
7709 {
7710     if (enable)
7711     {
7712         cmd("kma");
7713     }
7714     else
7715     {
7716         cmd("kmac");
7717     }
7718     arg("t", type);
7719 
7720     tag = client->reqtag;
7721 }
7722 
procresult()7723 void CommandKeepMeAlive::procresult()
7724 {
7725     Error e;
7726     if (checkError(e, client->json))
7727     {
7728         client->app->keepmealive_result(e);
7729     }
7730     else
7731     {
7732         client->json.storeobject();
7733         client->app->keepmealive_result(API_EINTERNAL);
7734     }
7735 }
7736 
CommandMultiFactorAuthSetup(MegaClient * client,const char * pin)7737 CommandMultiFactorAuthSetup::CommandMultiFactorAuthSetup(MegaClient *client, const char *pin)
7738 {
7739     cmd("mfas");
7740     if (pin)
7741     {
7742         arg("mfa", pin);
7743     }
7744     tag = client->reqtag;
7745 }
7746 
procresult()7747 void CommandMultiFactorAuthSetup::procresult()
7748 {
7749     Error e;
7750     if (checkError(e, client->json))
7751     {
7752         return client->app->multifactorauthsetup_result(NULL, e);
7753     }
7754 
7755     string code;
7756     if (!client->json.storeobject(&code))
7757     {
7758         return client->app->multifactorauthsetup_result(NULL, API_EINTERNAL);
7759     }
7760     client->app->multifactorauthsetup_result(&code, API_OK);
7761 }
7762 
CommandMultiFactorAuthCheck(MegaClient * client,const char * email)7763 CommandMultiFactorAuthCheck::CommandMultiFactorAuthCheck(MegaClient *client, const char *email)
7764 {
7765     cmd("mfag");
7766     arg("e", email);
7767 
7768     tag = client->reqtag;
7769 }
7770 
procresult()7771 void CommandMultiFactorAuthCheck::procresult()
7772 {
7773     Error e;
7774     if (checkError(e, client->json))
7775     {
7776         client->app->multifactorauthcheck_result(e);
7777         return;
7778     }
7779 
7780     int enabled;
7781     if (client->json.isnumeric())
7782     {
7783         enabled = static_cast<int>(client->json.getint());
7784     }
7785     else
7786     {
7787         client->json.storeobject();
7788         enabled = API_EINTERNAL;
7789     }
7790 
7791     client->app->multifactorauthcheck_result(enabled);
7792 }
7793 
CommandMultiFactorAuthDisable(MegaClient * client,const char * pin)7794 CommandMultiFactorAuthDisable::CommandMultiFactorAuthDisable(MegaClient *client, const char *pin)
7795 {
7796     cmd("mfad");
7797     arg("mfa", pin);
7798 
7799     tag = client->reqtag;
7800 }
7801 
procresult()7802 void CommandMultiFactorAuthDisable::procresult()
7803 {
7804     Error e;
7805     if (checkError(e, client->json))
7806     {
7807         client->app->multifactorauthdisable_result(e);
7808     }
7809     else    // error
7810     {
7811         client->json.storeobject();
7812         client->app->multifactorauthdisable_result(API_EINTERNAL);
7813     }
7814 }
7815 
CommandGetPSA(MegaClient * client)7816 CommandGetPSA::CommandGetPSA(MegaClient *client)
7817 {
7818     cmd("gpsa");
7819 
7820     tag = client->reqtag;
7821 }
7822 
procresult()7823 void CommandGetPSA::procresult()
7824 {
7825     Error e;
7826     if (checkError(e, client->json))
7827     {
7828         return client->app->getpsa_result(e, 0, NULL, NULL, NULL, NULL, NULL);
7829     }
7830 
7831     int id = 0;
7832     string temp;
7833     string title, text, imagename, imagepath;
7834     string buttonlink, buttontext;
7835 
7836     for (;;)
7837     {
7838         switch (client->json.getnameid())
7839         {
7840             case MAKENAMEID2('i', 'd'):
7841                 id = int(client->json.getint());
7842                 break;
7843             case 't':
7844                 client->json.storeobject(&temp);
7845                 Base64::atob(temp, title);
7846                 break;
7847             case 'd':
7848                 client->json.storeobject(&temp);
7849                 Base64::atob(temp, text);
7850                 break;
7851             case MAKENAMEID3('i', 'm', 'g'):
7852                 client->json.storeobject(&imagename);
7853                 break;
7854             case 'l':
7855                 client->json.storeobject(&buttonlink);
7856                 break;
7857             case 'b':
7858                 client->json.storeobject(&temp);
7859                 Base64::atob(temp, buttontext);
7860                 break;
7861             case MAKENAMEID3('d', 's', 'p'):
7862                 client->json.storeobject(&imagepath);
7863                 break;
7864             case EOO:
7865                 imagepath.append(imagename);
7866                 imagepath.append(".png");
7867                 return client->app->getpsa_result(API_OK, id, &title, &text, &imagepath, &buttontext, &buttonlink);
7868             default:
7869                 if (!client->json.storeobject())
7870                 {
7871                     LOG_err << "Failed to parse get PSA response";
7872                     return client->app->getpsa_result(API_EINTERNAL, 0, NULL, NULL, NULL, NULL, NULL);
7873                 }
7874                 break;
7875         }
7876     }
7877 }
7878 
CommandFetchTimeZone(MegaClient * client,const char * timezone,const char * timeoffset)7879 CommandFetchTimeZone::CommandFetchTimeZone(MegaClient *client, const char *timezone, const char* timeoffset)
7880 {
7881     cmd("ftz");
7882     arg("utz", timezone);
7883     arg("uo", timeoffset);
7884 
7885     tag = client->reqtag;
7886 }
7887 
procresult()7888 void CommandFetchTimeZone::procresult()
7889 {
7890     Error e;
7891     if (checkError(e, client->json))
7892     {
7893         return client->app->fetchtimezone_result(e, NULL, NULL, -1);
7894     }
7895 
7896     string currenttz;
7897     int currentto;
7898     vector<string> timezones;
7899     vector<int> timeoffsets;
7900     string defaulttz;
7901     int defaulttzindex = -1;
7902 
7903     for (;;)
7904     {
7905         switch (client->json.getnameid())
7906         {
7907             case MAKENAMEID7('c', 'h', 'o', 'i', 'c', 'e', 's'):
7908                 if (client->json.enterobject())
7909                 {
7910                     while (client->json.storeobject(&currenttz))
7911                     {
7912                         currentto = int(client->json.getint());
7913                         timezones.push_back(currenttz);
7914                         timeoffsets.push_back(currentto);
7915                     }
7916                     client->json.leaveobject();
7917                 }
7918                 else if (!client->json.storeobject())
7919                 {
7920                     LOG_err << "Failed to parse fetch time zone response";
7921                     return client->app->fetchtimezone_result(API_EINTERNAL, NULL, NULL, -1);
7922                 }
7923                 break;
7924 
7925             case MAKENAMEID7('d', 'e', 'f', 'a', 'u', 'l', 't'):
7926                 if (client->json.isnumeric())
7927                 {
7928                     client->json.getint();
7929                 }
7930                 else
7931                 {
7932                     client->json.storeobject(&defaulttz);
7933                 }
7934                 break;
7935 
7936             case EOO:
7937                 if (!defaulttz.empty())    // default received as string
7938                 {
7939                     for (int i = 0; i < (int)timezones.size(); i++)
7940                     {
7941                         if (timezones[i] == defaulttz)
7942                         {
7943                             defaulttzindex = i;
7944                             break;
7945                         }
7946                     }
7947                 }
7948                 return client->app->fetchtimezone_result(API_OK, &timezones, &timeoffsets, defaulttzindex);
7949 
7950             default:
7951                 if (!client->json.storeobject())
7952                 {
7953                     LOG_err << "Failed to parse fetch time zone response";
7954                     return client->app->fetchtimezone_result(API_EINTERNAL, NULL, NULL, -1);
7955                 }
7956                 break;
7957         }
7958     }
7959 }
7960 
CommandSetLastAcknowledged(MegaClient * client)7961 CommandSetLastAcknowledged::CommandSetLastAcknowledged(MegaClient* client)
7962 {
7963     cmd("sla");
7964     notself(client);
7965     tag = client->reqtag;
7966 }
7967 
procresult()7968 void CommandSetLastAcknowledged::procresult()
7969 {
7970     Error e;
7971     if (checkError(e, client->json))
7972     {
7973         client->app->acknowledgeuseralerts_result(e);
7974     }
7975     else
7976     {
7977         client->json.storeobject();
7978         client->app->acknowledgeuseralerts_result(API_EINTERNAL);
7979     }
7980 }
7981 
CommandSMSVerificationSend(MegaClient * client,const string & phoneNumber,bool reVerifyingWhitelisted)7982 CommandSMSVerificationSend::CommandSMSVerificationSend(MegaClient* client, const string& phoneNumber, bool reVerifyingWhitelisted)
7983 {
7984     cmd("smss");
7985     batchSeparately = true;  // don't let any other commands that might get batched with it cause the whole batch to fail
7986 
7987     assert(isPhoneNumber(phoneNumber));
7988     arg("n", phoneNumber.c_str());
7989 
7990     if (reVerifyingWhitelisted)
7991     {
7992         arg("to", 1);   // test override
7993     }
7994 
7995     tag = client->reqtag;
7996 }
7997 
isPhoneNumber(const string & s)7998 bool CommandSMSVerificationSend::isPhoneNumber(const string& s)
7999 {
8000     for (auto i = s.size(); i--; )
8001     {
8002         if (!(isdigit(s[i]) || (i == 0 && s[i] == '+')))
8003         {
8004             return false;
8005         }
8006     }
8007     return s.size() > 6;
8008 }
8009 
procresult()8010 void CommandSMSVerificationSend::procresult()
8011 {
8012     Error e;
8013     if (checkError(e, client->json))
8014     {
8015         client->app->smsverificationsend_result(e);
8016     }
8017     else
8018     {
8019         client->json.storeobject();
8020         client->app->smsverificationsend_result(API_EINTERNAL);
8021     }
8022 }
8023 
CommandSMSVerificationCheck(MegaClient * client,const string & verificationcode)8024 CommandSMSVerificationCheck::CommandSMSVerificationCheck(MegaClient* client, const string& verificationcode)
8025 {
8026     cmd("smsv");
8027     batchSeparately = true;  // don't let any other commands that might get batched with it cause the whole batch to fail
8028 
8029     if (isVerificationCode(verificationcode))
8030     {
8031         arg("c", verificationcode.c_str());
8032     }
8033 
8034     tag = client->reqtag;
8035 }
8036 
isVerificationCode(const string & s)8037 bool CommandSMSVerificationCheck::isVerificationCode(const string& s)
8038 {
8039     for (const char c : s)
8040     {
8041         if (!isdigit(c))
8042         {
8043             return false;
8044         }
8045     }
8046     return s.size() == 6;
8047 }
8048 
procresult()8049 void CommandSMSVerificationCheck::procresult()
8050 {
8051     Error e;
8052     if (checkError(e, client->json))
8053     {
8054         return client->app->smsverificationcheck_result(e, nullptr);
8055     }
8056 
8057     string phoneNumber;
8058     if (!client->json.storeobject(&phoneNumber))
8059     {
8060         return client->app->smsverificationcheck_result(API_EINTERNAL, nullptr);
8061     }
8062 
8063     assert(CommandSMSVerificationSend::isPhoneNumber(phoneNumber));
8064     client->mSmsVerifiedPhone = phoneNumber;
8065     client->app->smsverificationcheck_result(API_OK, &phoneNumber);
8066 }
8067 
CommandGetRegisteredContacts(MegaClient * client,const map<const char *,const char * > & contacts)8068 CommandGetRegisteredContacts::CommandGetRegisteredContacts(MegaClient* client, const map<const char*, const char*>& contacts)
8069 {
8070     cmd("usabd");
8071 
8072     arg("v", 1);
8073 
8074     beginobject("e");
8075     for (const auto& pair : contacts)
8076     {
8077         arg(Base64::btoa(pair.first).c_str(), // name is text-input from user, need conversion too
8078             (byte *)pair.second, static_cast<int>(strlen(pair.second)));
8079     }
8080     endobject();
8081 
8082     tag = client->reqtag;
8083 }
8084 
procresult()8085 void CommandGetRegisteredContacts::procresult()
8086 {
8087     Error e;
8088     if (checkError(e, client->json))
8089     {
8090         client->app->getregisteredcontacts_result(e, nullptr);
8091         return;
8092     }
8093 
8094     processResult(*client->app, client->json);
8095 }
8096 
processResult(MegaApp & app,JSON & json)8097 void CommandGetRegisteredContacts::processResult(MegaApp& app, JSON& json)
8098 {
8099     vector<tuple<string, string, string>> registeredContacts;
8100 
8101     string entryUserDetail;
8102     string id;
8103     string userDetail;
8104 
8105     bool success = true;
8106     while (json.enterobject())
8107     {
8108         bool exit = false;
8109         while (!exit)
8110         {
8111             switch (json.getnameid())
8112             {
8113                 case MAKENAMEID3('e', 'u', 'd'):
8114                 {
8115                     json.storeobject(&entryUserDetail);
8116                     break;
8117                 }
8118                 case MAKENAMEID2('i', 'd'):
8119                 {
8120                     json.storeobject(&id);
8121                     break;
8122                 }
8123                 case MAKENAMEID2('u', 'd'):
8124                 {
8125                     json.storeobject(&userDetail);
8126                     break;
8127                 }
8128                 case EOO:
8129                 {
8130                     if (entryUserDetail.empty() || id.empty() || userDetail.empty())
8131                     {
8132                         LOG_err << "Missing or empty field when parsing 'get registered contacts' response";
8133                         success = false;
8134                     }
8135                     else
8136                     {
8137                         registeredContacts.emplace_back(
8138                                     make_tuple(Base64::atob(entryUserDetail), move(id),
8139                                                Base64::atob(userDetail)));
8140                     }
8141                     exit = true;
8142                     break;
8143                 }
8144                 default:
8145                 {
8146                     if (!json.storeobject())
8147                     {
8148                         LOG_err << "Failed to parse 'get registered contacts' response";
8149                         app.getregisteredcontacts_result(API_EINTERNAL, nullptr);
8150                         return;
8151                     }
8152                 }
8153             }
8154         }
8155         json.leaveobject();
8156     }
8157     if (success)
8158     {
8159         app.getregisteredcontacts_result(API_OK, &registeredContacts);
8160     }
8161     else
8162     {
8163         app.getregisteredcontacts_result(API_EINTERNAL, nullptr);
8164     }
8165 }
8166 
CommandGetCountryCallingCodes(MegaClient * client)8167 CommandGetCountryCallingCodes::CommandGetCountryCallingCodes(MegaClient* client)
8168 {
8169     cmd("smslc");
8170 
8171     tag = client->reqtag;
8172 }
8173 
procresult()8174 void CommandGetCountryCallingCodes::procresult()
8175 {
8176     Error e;
8177     if (checkError(e, client->json))
8178     {
8179         client->app->getcountrycallingcodes_result(e, nullptr);
8180         return;
8181     }
8182 
8183     processResult(*client->app, client->json);
8184 }
8185 
processResult(MegaApp & app,JSON & json)8186 void CommandGetCountryCallingCodes::processResult(MegaApp& app, JSON& json)
8187 {
8188     map<string, vector<string>> countryCallingCodes;
8189 
8190     string countryCode;
8191     vector<string> callingCodes;
8192 
8193     bool success = true;
8194     while (json.enterobject())
8195     {
8196         bool exit = false;
8197         while (!exit)
8198         {
8199             switch (json.getnameid())
8200             {
8201                 case MAKENAMEID2('c', 'c'):
8202                 {
8203                     json.storeobject(&countryCode);
8204                     break;
8205                 }
8206                 case MAKENAMEID1('l'):
8207                 {
8208                     if (json.enterarray())
8209                     {
8210                         std::string code;
8211                         while (json.storeobject(&code))
8212                         {
8213                             callingCodes.emplace_back(move(code));
8214                         }
8215                         json.leavearray();
8216                     }
8217                     break;
8218                 }
8219                 case EOO:
8220                 {
8221                     if (countryCode.empty() || callingCodes.empty())
8222                     {
8223                         LOG_err << "Missing or empty fields when parsing 'get country calling codes' response";
8224                         success = false;
8225                     }
8226                     else
8227                     {
8228                         countryCallingCodes.emplace(make_pair(move(countryCode), move(callingCodes)));
8229                     }
8230                     exit = true;
8231                     break;
8232                 }
8233                 default:
8234                 {
8235                     if (!json.storeobject())
8236                     {
8237                         LOG_err << "Failed to parse 'get country calling codes' response";
8238                         app.getcountrycallingcodes_result(API_EINTERNAL, nullptr);
8239                         return;
8240                     }
8241                 }
8242             }
8243         }
8244         json.leaveobject();
8245     }
8246     if (success)
8247     {
8248         app.getcountrycallingcodes_result(API_OK, &countryCallingCodes);
8249     }
8250     else
8251     {
8252         app.getcountrycallingcodes_result(API_EINTERNAL, nullptr);
8253     }
8254 }
8255 
CommandFolderLinkInfo(MegaClient * client,handle publichandle)8256 CommandFolderLinkInfo::CommandFolderLinkInfo(MegaClient* client, handle publichandle)
8257 {
8258     ph = publichandle;
8259 
8260     cmd("pli");
8261     arg("ph", (byte*)&publichandle, MegaClient::NODEHANDLE);
8262 
8263     tag = client->reqtag;
8264 }
8265 
procresult()8266 void CommandFolderLinkInfo::procresult()
8267 {
8268     Error e;
8269     if (checkError(e, client->json))
8270     {
8271         return client->app->folderlinkinfo_result(e, UNDEF, UNDEF, NULL, NULL, 0, 0, 0, 0, 0);
8272     }
8273     string attr;
8274     string key;
8275     handle owner = UNDEF;
8276     handle ph = 0;
8277     m_off_t currentSize = 0;
8278     m_off_t versionsSize  = 0;
8279     int numFolders = 0;
8280     int numFiles = 0;
8281     int numVersions = 0;
8282 
8283     for (;;)
8284     {
8285         switch (client->json.getnameid())
8286         {
8287         case MAKENAMEID5('a','t','t','r','s'):
8288             client->json.storeobject(&attr);
8289             break;
8290 
8291         case MAKENAMEID2('p','h'):
8292             ph = client->json.gethandle(MegaClient::NODEHANDLE);
8293             break;
8294 
8295         case 'u':
8296             owner = client->json.gethandle(MegaClient::USERHANDLE);
8297             break;
8298 
8299         case 's':
8300             if (client->json.enterarray())
8301             {
8302                 currentSize = client->json.getint();
8303                 numFiles = int(client->json.getint());
8304                 numFolders = int(client->json.getint());
8305                 versionsSize  = client->json.getint();
8306                 numVersions = int(client->json.getint());
8307                 client->json.leavearray();
8308             }
8309             break;
8310 
8311         case 'k':
8312             client->json.storeobject(&key);
8313             break;
8314 
8315         case EOO:
8316             if (attr.empty())
8317             {
8318                 LOG_err << "The folder link information doesn't contain the attr string";
8319                 return client->app->folderlinkinfo_result(API_EINCOMPLETE, UNDEF, UNDEF, NULL, NULL, 0, 0, 0, 0, 0);
8320             }
8321             if (key.size() <= 9 || key.find(":") == string::npos)
8322             {
8323                 LOG_err << "The folder link information doesn't contain a valid decryption key";
8324                 return client->app->folderlinkinfo_result(API_EKEY, UNDEF, UNDEF, NULL, NULL, 0, 0, 0, 0, 0);
8325             }
8326             if (ph != this->ph)
8327             {
8328                 LOG_err << "Folder link information: public handle doesn't match";
8329                 return client->app->folderlinkinfo_result(API_EINTERNAL, UNDEF, UNDEF, NULL, NULL, 0, 0, 0, 0, 0);
8330             }
8331 
8332             return client->app->folderlinkinfo_result(API_OK, owner, ph, &attr, &key, currentSize, numFiles, numFolders, versionsSize, numVersions);
8333 
8334         default:
8335             if (!client->json.storeobject())
8336             {
8337                 LOG_err << "Failed to parse folder link information response";
8338                 return client->app->folderlinkinfo_result(API_EINTERNAL, UNDEF, UNDEF, NULL, NULL, 0, 0, 0, 0, 0);
8339             }
8340             break;
8341         }
8342     }
8343 }
8344 
CommandBackupPut(MegaClient * client,BackupType type,handle nodeHandle,const string & localFolder,const std::string & deviceId,const string & backupName,int state,int subState,const string & extraData)8345 CommandBackupPut::CommandBackupPut(MegaClient *client, BackupType type, handle nodeHandle, const string& localFolder, const std::string &deviceId, const string& backupName, int state, int subState, const string& extraData)
8346 {
8347     assert(type != BackupType::INVALID);
8348 
8349     cmd("sp");
8350 
8351     arg("t", type);
8352     arg("h", (byte*)&nodeHandle, MegaClient::NODEHANDLE);
8353     arg("l", localFolder.c_str());
8354     arg("d", deviceId.c_str());
8355     arg("n", backupName.c_str());
8356     arg("s", state);
8357     arg("ss", subState);
8358     arg("e", extraData.c_str());
8359 
8360     tag = client->reqtag;
8361     mUpdate = false;
8362 }
8363 
CommandBackupPut(MegaClient * client,handle backupId,BackupType type,handle nodeHandle,const char * localFolder,const char * deviceId,const char * backupName,int state,int subState,const char * extraData)8364 CommandBackupPut::CommandBackupPut(MegaClient* client, handle backupId, BackupType type, handle nodeHandle, const char* localFolder, const char *deviceId, const char* backupName, int state, int subState, const char* extraData)
8365 {
8366     cmd("sp");
8367 
8368     arg("id", (byte*)&backupId, MegaClient::USERHANDLE);
8369 
8370     if (type != BackupType::INVALID)
8371     {
8372         arg("t", type);
8373     }
8374 
8375     if (nodeHandle != UNDEF)
8376     {
8377         arg("h", (byte*)&nodeHandle, MegaClient::NODEHANDLE);
8378     }
8379 
8380     if (localFolder)
8381     {
8382         arg("l", localFolder);
8383     }
8384 
8385     if (deviceId)
8386     {
8387         arg("d", deviceId);
8388     }
8389 
8390     if (backupName)
8391     {
8392         arg("n", backupName);
8393     }
8394 
8395     if (state > 0)
8396     {
8397         arg("s", state);
8398     }
8399 
8400     if (subState > 0)
8401     {
8402         arg("ss", subState);
8403     }
8404 
8405     if (extraData)
8406     {
8407         arg("e", extraData);
8408     }
8409 
8410     tag = client->reqtag;
8411     mUpdate = true;
8412 }
8413 
procresult()8414 void CommandBackupPut::procresult()
8415 {
8416     Error e;
8417     if (checkError(e, client->json))
8418     {
8419         if (mUpdate)
8420         {
8421             return client->app->backupupdate_result(e, UNDEF);
8422         }
8423 
8424         return client->app->backupput_result(e, UNDEF);
8425     }
8426 
8427     handle backupId = client->json.gethandle(MegaClient::USERHANDLE);
8428     if (mUpdate)
8429     {
8430         return client->app->backupupdate_result(API_OK, backupId);
8431     }
8432 
8433     client->app->backupput_result(API_OK, backupId);
8434 }
8435 
CommandBackupPutHeartBeat(MegaClient * client,handle backupId,uint8_t status,uint8_t progress,uint32_t uploads,uint32_t downloads,uint32_t ts,handle lastNode)8436 CommandBackupPutHeartBeat::CommandBackupPutHeartBeat(MegaClient* client, handle backupId, uint8_t status, uint8_t progress, uint32_t uploads, uint32_t downloads, uint32_t ts, handle lastNode)
8437 {
8438     cmd("sphb");
8439 
8440     arg("id", (byte*)&backupId, MegaClient::USERHANDLE);
8441     arg("s", status);
8442     arg("p", progress);
8443     arg("qu", uploads);
8444     arg("qd", downloads);
8445     arg("lts", ts);
8446     arg("lh", (byte*)&lastNode, MegaClient::NODEHANDLE);
8447 
8448     tag = client->reqtag;
8449 }
8450 
procresult()8451 void CommandBackupPutHeartBeat::procresult()
8452 {
8453     Error e;
8454     if (checkError(e, client->json))
8455     {
8456         return client->app->backupputheartbeat_result(e);
8457     }
8458 
8459     client->json.storeobject();
8460     client->app->backupputheartbeat_result(API_EINTERNAL);
8461 }
8462 
CommandBackupRemove(MegaClient * client,handle backupId)8463 CommandBackupRemove::CommandBackupRemove(MegaClient *client, handle backupId)
8464 {
8465     cmd("sr");
8466     arg("id", (byte*)&backupId, MegaClient::USERHANDLE);
8467 
8468     tag = client->reqtag;
8469 }
8470 
procresult()8471 void CommandBackupRemove::procresult()
8472 {
8473     Error e;
8474     if (checkError(e, client->json))
8475     {
8476         return client->app->backupputheartbeat_result(e);
8477     }
8478 
8479     client->json.storeobject();
8480     return client->app->backupputheartbeat_result(API_EINTERNAL);
8481 }
8482 
8483 } // namespace
8484