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(¤cy, 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(¤ttz))
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, ®isteredContacts);
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