1 /**
2  * @file src/listeners.cpp
3  * @brief MEGAcmd: Listeners
4  *
5  * (c) 2013 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGAcmd.
8  *
9  * MEGAcmd is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * @copyright Simplified (2-clause) BSD License.
14  *
15  * You should have received a copy of the license along with this
16  * program.
17  */
18 
19 #include "listeners.h"
20 #include "configurationmanager.h"
21 #include "megacmdutils.h"
22 
23 using namespace mega;
24 
25 
26 namespace megacmd {
27 #ifdef ENABLE_CHAT
onChatsUpdate(MegaApi *,MegaTextChatList *)28 void MegaCmdGlobalListener::onChatsUpdate(MegaApi*, MegaTextChatList*)
29 {
30 
31 }
32 #endif
33 
onUsersUpdate(MegaApi * api,MegaUserList * users)34 void MegaCmdGlobalListener::onUsersUpdate(MegaApi *api, MegaUserList *users)
35 {
36     if (users)
37     {
38         if (users->size() == 1)
39         {
40             LOG_debug << " 1 user received or updated";
41         }
42         else
43         {
44             LOG_debug << users->size() << " users received or updated";
45         }
46     }
47     else //initial update or too many changes
48     {
49         MegaUserList *users = api->getContacts();
50 
51         if (users && users->size())
52         {
53             if (users->size() == 1)
54             {
55                 LOG_debug << " 1 user received or updated";
56             }
57             else
58             {
59                 LOG_debug << users->size() << " users received or updated";
60             }
61 
62             delete users;
63         }
64     }
65 }
66 
MegaCmdGlobalListener(MegaCMDLogger * logger,MegaCmdSandbox * sandboxCMD)67 MegaCmdGlobalListener::MegaCmdGlobalListener(MegaCMDLogger *logger, MegaCmdSandbox *sandboxCMD)
68 {
69     this->loggerCMD = logger;
70     this->sandboxCMD = sandboxCMD;
71 
72     ongoing = false;
73 }
74 
onNodesUpdate(MegaApi * api,MegaNodeList * nodes)75 void MegaCmdGlobalListener::onNodesUpdate(MegaApi *api, MegaNodeList *nodes)
76 {
77     long long nfolders = 0;
78     long long nfiles = 0;
79     long long rfolders = 0;
80     long long rfiles = 0;
81     if (nodes)
82     {
83         for (int i = 0; i < nodes->size(); i++)
84         {
85             MegaNode *n = nodes->get(i);
86             if (n->getType() == MegaNode::TYPE_FOLDER)
87             {
88                 if (n->isRemoved())
89                 {
90                     rfolders++;
91                 }
92                 else
93                 {
94                     nfolders++;
95                 }
96             }
97             else if (n->getType() == MegaNode::TYPE_FILE)
98             {
99                 if (n->isRemoved())
100                 {
101                     rfiles++;
102                 }
103                 else
104                 {
105                     nfiles++;
106                 }
107             }
108         }
109     }
110     else //initial update or too many changes
111     {
112         if (loggerCMD->getMaxLogLevel() >= logInfo)
113         {
114             MegaNode * nodeRoot = api->getRootNode();
115             getNumFolderFiles(nodeRoot, api, &nfiles, &nfolders);
116             delete nodeRoot;
117 
118             MegaNode * inboxNode = api->getInboxNode();
119             getNumFolderFiles(inboxNode, api, &nfiles, &nfolders);
120             delete inboxNode;
121 
122             MegaNode * rubbishNode = api->getRubbishNode();
123             getNumFolderFiles(rubbishNode, api, &nfiles, &nfolders);
124             delete rubbishNode;
125 
126             MegaNodeList *inshares = api->getInShares();
127             if (inshares)
128             {
129                 for (int i = 0; i < inshares->size(); i++)
130                 {
131                     nfolders++; //add the share itself
132                     getNumFolderFiles(inshares->get(i), api, &nfiles, &nfolders);
133                 }
134             }
135             delete inshares;
136         }
137 
138         if (nfolders)
139         {
140             LOG_debug << nfolders << " folders " << "added or updated ";
141         }
142         if (nfiles)
143         {
144             LOG_debug << nfiles << " files " << "added or updated ";
145         }
146         if (rfolders)
147         {
148             LOG_debug << rfolders << " folders " << "removed";
149         }
150         if (rfiles)
151         {
152             LOG_debug << rfiles << " files " << "removed";
153         }
154     }
155 }
156 
onAccountUpdate(MegaApi * api)157 void MegaCmdGlobalListener::onAccountUpdate(MegaApi *api)
158 {
159     if (!api->getBandwidthOverquotaDelay())
160     {
161         sandboxCMD->setOverquota(false);
162     }
163     sandboxCMD->temporalbandwidth = 0; //This will cause account details to be queried again
164 }
165 
onEvent(MegaApi * api,MegaEvent * event)166 void MegaCmdGlobalListener::onEvent(MegaApi *api, MegaEvent *event)
167 {
168     if (event->getType() == MegaEvent::EVENT_ACCOUNT_BLOCKED)
169     {
170         if (getBlocked() == event->getNumber())
171         {
172             LOG_debug << " receivied EVENT_ACCOUNT_BLOCKED: number = " << event->getNumber();
173             return;
174         }
175         setBlocked(event->getNumber()); //this should be true always
176 
177         switch (event->getNumber())
178         {
179         case MegaApi::ACCOUNT_BLOCKED_VERIFICATION_EMAIL:
180         {
181             sandboxCMD->setReasonblocked( "Your account has been temporarily suspended for your safety. "
182                                         "Please verify your email and follow its steps to unlock your account.");
183             break;
184         }
185         case MegaApi::ACCOUNT_BLOCKED_VERIFICATION_SMS:
186         {
187             if (!ongoing)
188             {
189                 ongoing = true;
190 
191                 sandboxCMD->setReasonPendingPromise();
192 
193                 api->getSessionTransferURL("", new MegaCmdListenerFuncExecuter(
194                                                [this](mega::MegaApi* api, mega::MegaRequest *request, mega::MegaError *e)
195                 {
196                     string reason("Your account has been suspended temporarily due to potential abuse. "
197                     "Please verify your phone number to unlock your account." );
198                     if (e->getValue() == MegaError::API_OK)
199                     {
200                        reason.append(" Open the following link: ");
201                        reason.append(request->getLink());
202                     }
203 
204                     sandboxCMD->setPromisedReasonblocked(reason);
205                     ongoing = false;
206                 },true));
207             }
208             break;
209         }
210         case MegaApi::ACCOUNT_BLOCKED_SUBUSER_DISABLED:
211         {
212             sandboxCMD->setReasonblocked("Your account has been disabled by your administrator. Please contact your business account administrator for further details.");
213             break;
214         }
215         default:
216         {
217             sandboxCMD->setReasonblocked(event->getText());
218             LOG_err << "Received event account blocked: " << event->getText();
219         }
220         }
221     }
222     else if (event->getType() == MegaEvent::EVENT_STORAGE)
223     {
224         if (event->getNumber() == MegaApi::STORAGE_STATE_CHANGE)
225         {
226             api->getAccountDetails();
227         }
228         else
229         {
230             int previousStatus = sandboxCMD->storageStatus;
231             sandboxCMD->storageStatus = event->getNumber();
232             if (sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_PAYWALL || sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_RED || sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_ORANGE)
233             {
234                 ConfigurationManager::savePropertyValue("ask4storage",true);
235 
236                 if (previousStatus < sandboxCMD->storageStatus)
237                 {
238                     if (sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_PAYWALL)
239                     {
240                         api->getUserData(new MegaCmdListenerFuncExecuter(
241                                         [this](mega::MegaApi* api, mega::MegaRequest *request, mega::MegaError *e)
242                         {
243                             if (e->getValue() == MegaError::API_OK)
244                             {
245                                 std::unique_ptr<char[]> myEmail(api->getMyEmail());
246                                 std::unique_ptr<MegaIntegerList> warningsList(api->getOverquotaWarningsTs());
247                                 std::string s;
248                                 s += "We have contacted you by email to " + string(myEmail.get()) + " on ";
249                                 s += getReadableTime(warningsList->get(0),"%b %e %Y");
250                                 if (warningsList->size() > 1)
251                                 {
252                                     for (int i = 1; i < warningsList->size() - 1; i++)
253                                     {
254                                         s += ", " + getReadableTime(warningsList->get(i),"%b %e %Y");
255                                     }
256                                     s += " and " + getReadableTime(warningsList->get(warningsList->size() - 1),"%b %e %Y");
257                                 }
258                                 std::unique_ptr<MegaNode> rootNode(api->getRootNode());
259                                 long long totalFiles = 0;
260                                 long long totalFolders = 0;
261                                 getNumFolderFiles(rootNode.get(),api,&totalFiles,&totalFolders);
262                                 s += ", but you still have " + std::to_string(totalFiles) + " files taking up " + sizeToText(sandboxCMD->receivedStorageSum);
263                                 s += " in your MEGA account, which requires you to upgrade your account.\n\n";
264                                 long long daysLeft = (api->getOverquotaDeadlineTs() - m_time(NULL)) / 86400;
265                                 if (daysLeft > 0)
266                                 {
267                                      s += "You have " + std::to_string(daysLeft) + " days left to upgrade. ";
268                                      s += "After that, your data is subject to deletion.\n";
269                                 }
270                                 else
271                                 {
272                                      s += "You must act immediately to save your data. From now on, your data is subject to deletion.\n";
273                                 }
274                                 s += "See \"help --upgrade\" for further details.";
275                                 broadcastMessage(s);
276                             }
277                         },true));
278                     }
279                     else
280                     {
281                         string s;
282                         if (sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_RED)
283                         {
284                             s+= "You have exeeded your available storage.\n";
285                         }
286                         else
287                         {
288                             s+= "You are running out of available storage.\n";
289                         }
290                         s+="You can change your account plan to increase your quota limit.\nSee \"help --upgrade\" for further details";
291                         broadcastMessage(s);
292                     }
293                 }
294             }
295             else
296             {
297                 ConfigurationManager::savePropertyValue("ask4storage",false);
298             }
299         }
300         LOG_info << "Received event storage changed: " << event->getNumber();
301     }
302     else if (event->getType() == MegaEvent::EVENT_STORAGE_SUM_CHANGED)
303     {
304         sandboxCMD->receivedStorageSum = event->getNumber();
305     }
306 }
307 
308 
309 ////////////////////////////////////////////
310 ///      MegaCmdMegaListener methods     ///
311 ////////////////////////////////////////////
312 
onRequestFinish(MegaApi * api,MegaRequest * request,MegaError * e)313 void MegaCmdMegaListener::onRequestFinish(MegaApi *api, MegaRequest *request, MegaError *e)
314 {
315     if (request->getType() == MegaRequest::TYPE_APP_VERSION)
316     {
317         LOG_verbose << "TYPE_APP_VERSION finished";
318     }
319     else if (request->getType() == MegaRequest::TYPE_LOGOUT)
320     {
321         LOG_debug << "Session closed";
322         sandboxCMD->resetSandBox();
323         reset();
324     }
325     else if (request->getType() == MegaRequest::TYPE_WHY_AM_I_BLOCKED)
326     {
327         if (e->getErrorCode() == MegaError::API_OK
328                 && request->getNumber() == MegaApi::ACCOUNT_NOT_BLOCKED)
329         {
330             if (getBlocked())
331             {
332                 unblock();
333             }
334         }
335 
336     }
337     else if (request->getType() == MegaRequest::TYPE_ACCOUNT_DETAILS)
338     {
339         if (e->getErrorCode() != MegaError::API_OK)
340         {
341             return;
342         }
343 
344         bool storage = (request->getNumDetails() & 0x01) != 0;
345 
346         if (storage)
347         {
348             unique_ptr<MegaAccountDetails> details(request->getMegaAccountDetails());
349             sandboxCMD->totalStorage = details->getStorageMax();
350         }
351     }
352     else if (e && ( e->getErrorCode() == MegaError::API_ESID ))
353     {
354         LOG_err << "Session is no longer valid (it might have been invalidated from elsewhere) ";
355         changeprompt(prompts[COMMAND]);
356     }
357     else if (e && request->getType() == MegaRequest::TYPE_WHY_AM_I_BLOCKED && e->getErrorCode() == API_EBLOCKED)
358     {
359         LOG_err << "Your account has been blocked. Reason: " << request->getText();
360     }
361 }
362 
MegaCmdMegaListener(MegaApi * megaApi,MegaListener * parent,MegaCmdSandbox * sandboxCMD)363 MegaCmdMegaListener::MegaCmdMegaListener(MegaApi *megaApi, MegaListener *parent, MegaCmdSandbox *sandboxCMD)
364 {
365     this->megaApi = megaApi;
366     this->listener = parent;
367     this->sandboxCMD = sandboxCMD;
368 }
369 
~MegaCmdMegaListener()370 MegaCmdMegaListener::~MegaCmdMegaListener()
371 {
372     this->listener = NULL;
373     if (megaApi)
374     {
375         megaApi->removeListener(this);
376     }
377 }
378 
379 #ifdef ENABLE_CHAT
onChatsUpdate(MegaApi * api,MegaTextChatList * chats)380 void MegaCmdMegaListener::onChatsUpdate(MegaApi *api, MegaTextChatList *chats)
381 {}
382 #endif
383 
384 #ifdef ENABLE_BACKUPS
385 //backup callbacks:
onBackupStateChanged(MegaApi * api,MegaBackup * backup)386 void MegaCmdMegaListener::onBackupStateChanged(MegaApi *api,  MegaBackup *backup)
387 {
388     LOG_verbose << " At onBackupStateChanged: " << backupSatetStr(backup->getState());
389 }
390 
onBackupStart(MegaApi * api,MegaBackup * backup)391 void MegaCmdMegaListener::onBackupStart(MegaApi *api, MegaBackup *backup)
392 {
393     LOG_verbose << " At onBackupStart";
394 }
395 
onBackupFinish(MegaApi * api,MegaBackup * backup,MegaError * error)396 void MegaCmdMegaListener::onBackupFinish(MegaApi* api, MegaBackup *backup, MegaError* error)
397 {
398     LOG_verbose << " At onBackupFinish";
399     if (error->getErrorCode() == MegaError::API_EEXPIRED)
400     {
401         LOG_warn << "Backup skipped (the time for the next one has been reached)";
402     }
403     else if (error->getErrorCode() != MegaError::API_OK)
404     {
405         LOG_err << "Backup failed: " << error->getErrorString();
406     }
407 }
408 
onBackupUpdate(MegaApi * api,MegaBackup * backup)409 void MegaCmdMegaListener::onBackupUpdate(MegaApi *api, MegaBackup *backup)
410 {
411     LOG_verbose << " At onBackupUpdate";
412 }
413 
onBackupTemporaryError(MegaApi * api,MegaBackup * backup,MegaError * error)414 void MegaCmdMegaListener::onBackupTemporaryError(MegaApi *api, MegaBackup *backup, MegaError* error)
415 {
416     LOG_verbose << " At onBackupTemporaryError";
417     if (error->getErrorCode() != MegaError::API_OK)
418     {
419         LOG_err << "Backup temporary error: " << error->getErrorString();
420     }
421 }
422 #endif
423 ////////////////////////////////////////
424 ///      MegaCmdListener methods     ///
425 ////////////////////////////////////////
426 
onRequestStart(MegaApi * api,MegaRequest * request)427 void MegaCmdListener::onRequestStart(MegaApi* api, MegaRequest *request)
428 {
429     if (!request)
430     {
431         LOG_err << " onRequestStart for undefined request ";
432         return;
433     }
434 
435     LOG_verbose << "onRequestStart request->getType(): " << request->getType();
436 }
437 
doOnRequestFinish(MegaApi * api,MegaRequest * request,MegaError * e)438 void MegaCmdListener::doOnRequestFinish(MegaApi* api, MegaRequest *request, MegaError* e)
439 {
440     if (!request)
441     {
442         LOG_err << " onRequestFinish for undefined request ";
443         return;
444     }
445 
446     LOG_verbose << "onRequestFinish request->getType(): " << request->getType();
447 
448     switch (request->getType())
449     {
450         case MegaRequest::TYPE_FETCH_NODES:
451         {
452             map<string, sync_struct *>::iterator itr;
453             int i = 0;
454 #ifdef ENABLE_SYNC
455 
456             std::shared_ptr<std::lock_guard<std::recursive_mutex>> g = std::make_shared<std::lock_guard<std::recursive_mutex>>(ConfigurationManager::settingsMutex);
457             // shared pointed lock_guard. will be freed when all resuming are complete
458 
459             for (itr = ConfigurationManager::configuredSyncs.begin(); itr != ConfigurationManager::configuredSyncs.end(); ++itr, i++)
460             {
461                 sync_struct *oldsync = ((sync_struct*)( *itr ).second );
462 
463                 MegaNode * node = api->getNodeByHandle(oldsync->handle);
464                 api->resumeSync(oldsync->localpath.c_str(), node, oldsync->fingerprint, new MegaCmdListenerFuncExecuter([g, oldsync, node](mega::MegaApi* api, mega::MegaRequest *request, mega::MegaError *e)
465                 {
466                     std::unique_ptr<char []>nodepath (api->getNodePath(node));
467 
468                     if ( e->getErrorCode() == MegaError::API_OK )
469                     {
470                         if (request->getNumber())
471                         {
472                             oldsync->fingerprint = request->getNumber();
473                         }
474                         oldsync->active = true;
475                         oldsync->loadedok = true;
476 
477                         LOG_info << "Loaded sync: " << oldsync->localpath << " to " << nodepath.get();
478                     }
479                     else
480                     {
481                         oldsync->loadedok = false;
482                         oldsync->active = false;
483 
484                         LOG_err << "Failed to resume sync: " << oldsync->localpath << " to " << nodepath.get();
485                     }
486                     delete node;
487                 }, true));
488             }
489 #endif
490             informProgressUpdate(PROGRESS_COMPLETE, request->getTotalBytes(), this->clientID, "Fetching nodes");
491             break;
492         }
493 
494         default:
495             break;
496     }
497 }
498 
onRequestUpdate(MegaApi * api,MegaRequest * request)499 void MegaCmdListener::onRequestUpdate(MegaApi* api, MegaRequest *request)
500 {
501     if (!request)
502     {
503         LOG_err << " onRequestUpdate for undefined request ";
504         return;
505     }
506 
507     LOG_verbose << "onRequestUpdate request->getType(): " << request->getType();
508 
509     switch (request->getType())
510     {
511         case MegaRequest::TYPE_FETCH_NODES:
512         {
513             unsigned int cols = getNumberOfCols(80);
514             string outputString;
515             outputString.resize(cols+1);
516             for (unsigned int i = 0; i < cols; i++)
517             {
518                 outputString[i] = '.';
519             }
520 
521             outputString[cols] = '\0';
522             char *ptr = (char *)outputString.c_str();
523             sprintf(ptr, "%s", "Fetching nodes ||");
524             ptr += strlen("Fetching nodes ||");
525             *ptr = '.'; //replace \0 char
526 
527 
528             float oldpercent = percentFetchnodes;
529             if (request->getTotalBytes() == 0)
530             {
531                 percentFetchnodes = 0;
532             }
533             else
534             {
535                 percentFetchnodes = float(request->getTransferredBytes() * 1.0 / request->getTotalBytes() * 100.0);
536             }
537             if (alreadyFinished || ( ( percentFetchnodes == oldpercent ) && ( oldpercent != 0 )) )
538             {
539                 return;
540             }
541             if (percentFetchnodes < 0)
542             {
543                 percentFetchnodes = 0;
544             }
545 
546             char aux[40];
547             if (request->getTotalBytes() < 0)
548             {
549                 return;                         // after a 100% this happens
550             }
551             if (request->getTransferredBytes() < 0.001 * request->getTotalBytes())
552             {
553                 return;                                                            // after a 100% this happens
554             }
555 
556             if (request->getTotalBytes() < 1048576)
557             {
558                 sprintf(aux,"||(%lld/%lld KB: %.2f %%) ", request->getTransferredBytes() / 1024, request->getTotalBytes() / 1024, percentFetchnodes);
559             }
560             else
561             {
562                 sprintf(aux,"||(%lld/%lld MB: %.2f %%) ", request->getTransferredBytes() / 1024 / 1024, request->getTotalBytes() / 1024 / 1024, percentFetchnodes);
563             }
564 
565             sprintf((char *)outputString.c_str() + cols - strlen(aux), "%s",                         aux);
566             for (int i = 0; i <= ( cols - strlen("Fetching nodes ||") - strlen(aux)) * 1.0 * percentFetchnodes / 100.0; i++)
567             {
568                 *ptr++ = '#';
569             }
570 
571             if (percentFetchnodes == 100 && !alreadyFinished)
572             {
573                 alreadyFinished = true;
574                 //cout << outputString << endl;
575                 LOG_debug << outputString;
576             }
577             else
578             {
579                 LOG_debug << outputString;
580                 //cout << outputString << '\r' << flush;
581             }
582 
583             informProgressUpdate(request->getTransferredBytes(), request->getTotalBytes(), this->clientID, "Fetching nodes");
584 
585 
586             break;
587         }
588 
589         default:
590             LOG_debug << "onRequestUpdate of unregistered type of request: " << request->getType();
591             break;
592     }
593 }
594 
onRequestTemporaryError(MegaApi * api,MegaRequest * request,MegaError * e)595 void MegaCmdListener::onRequestTemporaryError(MegaApi *api, MegaRequest *request, MegaError* e)
596 {
597 }
598 
599 
~MegaCmdListener()600 MegaCmdListener::~MegaCmdListener()
601 {
602 }
603 
MegaCmdListener(MegaApi * megaApi,MegaRequestListener * listener,int clientID)604 MegaCmdListener::MegaCmdListener(MegaApi *megaApi, MegaRequestListener *listener, int clientID)
605 {
606     this->megaApi = megaApi;
607     this->listener = listener;
608     percentFetchnodes = 0.0f;
609     alreadyFinished = false;
610     this->clientID = clientID;
611 }
612 
613 
614 ////////////////////////////////////////////////
615 ///      MegaCmdTransferListener methods     ///
616 ////////////////////////////////////////////////
617 
onTransferStart(MegaApi * api,MegaTransfer * transfer)618 void MegaCmdTransferListener::onTransferStart(MegaApi* api, MegaTransfer *transfer)
619 {
620     if (listener)
621     {
622         listener->onTransferStart(api,transfer);
623     }
624     if (!transfer)
625     {
626         LOG_err << " onTransferStart for undefined Transfer ";
627         return;
628     }
629 
630     LOG_verbose << "onTransferStart Transfer->getType(): " << transfer->getType();
631 }
632 
doOnTransferFinish(MegaApi * api,MegaTransfer * transfer,MegaError * e)633 void MegaCmdTransferListener::doOnTransferFinish(MegaApi* api, MegaTransfer *transfer, MegaError* e)
634 {
635     if (listener)
636     {
637         listener->onTransferFinish(api,transfer,e);
638     }
639     if (!transfer)
640     {
641         LOG_err << " onTransferFinish for undefined transfer ";
642         return;
643     }
644 
645     LOG_verbose << "doOnTransferFinish Transfer->getType(): " << transfer->getType();
646     informProgressUpdate(PROGRESS_COMPLETE, transfer->getTotalBytes(), clientID);
647 
648 }
649 
650 
onTransferUpdate(MegaApi * api,MegaTransfer * transfer)651 void MegaCmdTransferListener::onTransferUpdate(MegaApi* api, MegaTransfer *transfer)
652 {
653     if (listener)
654     {
655         listener->onTransferUpdate(api,transfer);
656     }
657 
658     if (!transfer)
659     {
660         LOG_err << " onTransferUpdate for undefined Transfer ";
661         return;
662     }
663 
664     unsigned int cols = getNumberOfCols(80);
665 
666     string outputString;
667     outputString.resize(cols + 1);
668     for (unsigned int i = 0; i < cols; i++)
669     {
670         outputString[i] = '.';
671     }
672 
673     outputString[cols] = '\0';
674     char *ptr = (char *)outputString.c_str();
675     sprintf(ptr, "%s", "TRANSFERRING ||");
676     ptr += strlen("TRANSFERRING ||");
677     *ptr = '.'; //replace \0 char
678 
679 
680     float oldpercent = percentDownloaded;
681     if (transfer->getTotalBytes() == 0)
682     {
683         percentDownloaded = 0;
684     }
685     else
686     {
687         percentDownloaded = float(transfer->getTransferredBytes() * 1.0 / transfer->getTotalBytes() * 100.0);
688     }
689     if (alreadyFinished || ( (percentDownloaded == oldpercent ) && ( oldpercent != 0 ) ) )
690     {
691         return;
692     }
693     if (percentDownloaded < 0)
694     {
695         percentDownloaded = 0;
696     }
697 
698     char aux[40];
699     if (transfer->getTotalBytes() < 0)
700     {
701         return; // after a 100% this happens
702     }
703     if (transfer->getTransferredBytes() < 0.001 * transfer->getTotalBytes())
704     {
705         return; // after a 100% this happens
706     }
707 
708     if (transfer->getTotalBytes() < 1048576)
709     {
710         sprintf(aux,"||(%lld/%lld KB: %.2f %%) ", (long long)(transfer->getTransferredBytes() / 1024), (long long)(transfer->getTotalBytes() / 1024), (float)percentDownloaded);
711 
712     }
713     else
714     {
715         sprintf(aux,"||(%lld/%lld MB: %.2f %%) ", (long long)(transfer->getTransferredBytes() / 1024 / 1024), (long long)(transfer->getTotalBytes() / 1024 / 1024), (float)percentDownloaded);
716     }
717     sprintf((char *)outputString.c_str() + cols - strlen(aux), "%s",                         aux);
718     for (int i = 0; i <= ( cols - strlen("TRANSFERRING ||") - strlen(aux)) * 1.0 * percentDownloaded / 100.0; i++)
719     {
720         *ptr++ = '#';
721     }
722 
723     if (percentDownloaded == 100 && !alreadyFinished)
724     {
725         alreadyFinished = true;
726         //cout << outputString << endl;
727         LOG_debug << outputString;
728     }
729     else
730     {
731         LOG_debug << outputString;
732         //cout << outputString << '\r' << flush;
733     }
734 
735     LOG_verbose << "onTransferUpdate transfer->getType(): " << transfer->getType() << " clientID=" << this->clientID;
736 
737     informTransferUpdate(transfer, this->clientID);
738 }
739 
740 
onTransferTemporaryError(MegaApi * api,MegaTransfer * transfer,MegaError * e)741 void MegaCmdTransferListener::onTransferTemporaryError(MegaApi *api, MegaTransfer *transfer, MegaError* e)
742 {
743     if (listener)
744     {
745         listener->onTransferTemporaryError(api,transfer,e);
746     }
747 }
748 
749 
~MegaCmdTransferListener()750 MegaCmdTransferListener::~MegaCmdTransferListener()
751 {
752 
753 }
754 
MegaCmdTransferListener(MegaApi * megaApi,MegaCmdSandbox * sandboxCMD,MegaTransferListener * listener,int clientID)755 MegaCmdTransferListener::MegaCmdTransferListener(MegaApi *megaApi, MegaCmdSandbox *sandboxCMD, MegaTransferListener *listener, int clientID)
756 {
757     this->megaApi = megaApi;
758     this->sandboxCMD = sandboxCMD;
759     this->listener = listener;
760     percentDownloaded = 0.0f;
761     alreadyFinished = false;
762     this->clientID = clientID;
763 }
764 
onTransferData(MegaApi * api,MegaTransfer * transfer,char * buffer,size_t size)765 bool MegaCmdTransferListener::onTransferData(MegaApi *api, MegaTransfer *transfer, char *buffer, size_t size)
766 {
767     return true;
768 }
769 
770 
771 /////////////////////////////////////////////////////
772 ///      MegaCmdMultiTransferListener methods     ///
773 /////////////////////////////////////////////////////
774 
onTransferStart(MegaApi * api,MegaTransfer * transfer)775 void MegaCmdMultiTransferListener::onTransferStart(MegaApi* api, MegaTransfer *transfer)
776 {
777     if (!transfer)
778     {
779         LOG_err << " onTransferStart for undefined Transfer ";
780         return;
781     }
782     alreadyFinished = false;
783     if (totalbytes == 0)
784     {
785         percentDownloaded = 0;
786     }
787     else
788     {
789         percentDownloaded = float((transferredbytes + getOngoingTransferredBytes()) * 1.0 / totalbytes * 1.0);
790     }
791 
792     onTransferUpdate(api,transfer);
793 
794     LOG_verbose << "onTransferStart Transfer->getType(): " << transfer->getType();
795 }
796 
doOnTransferFinish(MegaApi * api,MegaTransfer * transfer,MegaError * e)797 void MegaCmdMultiTransferListener::doOnTransferFinish(MegaApi* api, MegaTransfer *transfer, MegaError* e)
798 {
799     finished++;
800     finalerror = (finalerror!=API_OK)?finalerror:e->getErrorCode();
801 
802     if (!transfer)
803     {
804         LOG_err << " onTransferFinish for undefined transfer ";
805         return;
806     }
807 
808     LOG_verbose << "doOnTransferFinish MegaCmdMultiTransferListener Transfer->getType(): " << transfer->getType() << " transferring " << transfer->getFileName();
809 
810     if (e->getErrorCode() == API_OK)
811     {
812         // communicate status info
813         string s= "endtransfer:";
814         s+=((transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)?"D":"U");
815         s+=":";
816         if (transfer->getType() == MegaTransfer::TYPE_UPLOAD)
817         {
818             MegaNode *n = api->getNodeByHandle(transfer->getNodeHandle());
819             if (n)
820             {
821                 const char *path = api->getNodePath(n);
822                 if (path)
823                 {
824                     s+=path;
825                 }
826                 delete [] path;
827             }
828         }
829         else
830         {
831             s+=transfer->getPath();
832         }
833         informStateListenerByClientId(this->clientID, s);
834     }
835 
836     map<int, long long>::iterator itr = ongoingtransferredbytes.find(transfer->getTag());
837     if ( itr!= ongoingtransferredbytes.end())
838     {
839         ongoingtransferredbytes.erase(itr);
840     }
841 
842     itr = ongoingtotalbytes.find(transfer->getTag());
843     if ( itr!= ongoingtotalbytes.end())
844     {
845         ongoingtotalbytes.erase(itr);
846     }
847 
848     transferredbytes+=transfer->getTransferredBytes();
849     totalbytes+=transfer->getTotalBytes();
850 }
851 
waitMultiEnd()852 void MegaCmdMultiTransferListener::waitMultiEnd()
853 {
854     for (int i=0; i < started; i++)
855     {
856         wait();
857     }
858 }
859 
860 
onTransferUpdate(MegaApi * api,MegaTransfer * transfer)861 void MegaCmdMultiTransferListener::onTransferUpdate(MegaApi* api, MegaTransfer *transfer)
862 {
863     if (!transfer)
864     {
865         LOG_err << " onTransferUpdate for undefined Transfer ";
866         return;
867     }
868     ongoingtransferredbytes[transfer->getTag()] = transfer->getTransferredBytes();
869     ongoingtotalbytes[transfer->getTag()] = transfer->getTotalBytes();
870 
871     unsigned int cols = getNumberOfCols(80);
872 
873     string outputString;
874     outputString.resize(cols + 1);
875     for (unsigned int i = 0; i < cols; i++)
876     {
877         outputString[i] = '.';
878     }
879 
880     outputString[cols] = '\0';
881     char *ptr = (char *)outputString.c_str();
882     sprintf(ptr, "%s", "TRANSFERRING ||");
883     ptr += strlen("TRANSFERRING ||");
884     *ptr = '.'; //replace \0 char
885 
886 
887     float oldpercent = percentDownloaded;
888     if ((totalbytes + getOngoingTotalBytes() ) == 0)
889     {
890         percentDownloaded = 0;
891     }
892     else
893     {
894         percentDownloaded = float((transferredbytes + getOngoingTransferredBytes()) * 1.0 / (totalbytes + getOngoingTotalBytes() ) * 100.0);
895     }
896     if (alreadyFinished || ( (percentDownloaded == oldpercent ) && ( oldpercent != 0 ) ) )
897     {
898         return;
899     }
900     if (percentDownloaded < 0)
901     {
902         percentDownloaded = 0;
903     }
904     assert(percentDownloaded <=100);
905 
906     char aux[40];
907     if (transfer->getTotalBytes() < 0)
908     {
909         return; // after a 100% this happens
910     }
911     if (transfer->getTransferredBytes() < 0.001 * transfer->getTotalBytes())
912     {
913         return; // after a 100% this happens
914     }
915     if (totalbytes + getOngoingTotalBytes() < 1048576)
916     {
917         sprintf(aux,"||(%lld/%lld KB: %.2f %%) ", (transferredbytes + getOngoingTransferredBytes()) / 1024, (totalbytes + getOngoingTotalBytes() ) / 1024, percentDownloaded);
918     }
919     else
920     {
921         sprintf(aux,"||(%lld/%lld MB: %.2f %%) ", (transferredbytes + getOngoingTransferredBytes()) / 1024 / 1024, (totalbytes + getOngoingTotalBytes() ) / 1024 / 1024, percentDownloaded);
922     }
923     sprintf((char *)outputString.c_str() + cols - strlen(aux), "%s",                         aux);
924     for (int i = 0; i <= ( cols - strlen("TRANSFERRING ||") - strlen(aux)) * 1.0 * min (100.0f, percentDownloaded) / 100.0; i++)
925     {
926         *ptr++ = '#';
927     }
928 
929     if (percentDownloaded == 100 && !alreadyFinished)
930     {
931         alreadyFinished = true;
932         //cout << outputString << endl;
933         LOG_debug << outputString;
934     }
935     else
936     {
937         LOG_debug << outputString;
938         //cout << outputString << '\r' << flush;
939     }
940 
941     LOG_verbose << "onTransferUpdate transfer->getType(): " << transfer->getType() << " clientID=" << this->clientID;
942 
943     informProgressUpdate((transferredbytes + getOngoingTransferredBytes()),(totalbytes + getOngoingTotalBytes() ), clientID);
944     progressinformed = true;
945 
946 }
947 
948 
onTransferTemporaryError(MegaApi * api,MegaTransfer * transfer,MegaError * e)949 void MegaCmdMultiTransferListener::onTransferTemporaryError(MegaApi *api, MegaTransfer *transfer, MegaError* e)
950 {
951 }
952 
953 
~MegaCmdMultiTransferListener()954 MegaCmdMultiTransferListener::~MegaCmdMultiTransferListener()
955 {
956 }
957 
getFinalerror() const958 int MegaCmdMultiTransferListener::getFinalerror() const
959 {
960     return finalerror;
961 }
962 
getTotalbytes() const963 long long MegaCmdMultiTransferListener::getTotalbytes() const
964 {
965     return totalbytes;
966 }
967 
getOngoingTransferredBytes()968 long long MegaCmdMultiTransferListener::getOngoingTransferredBytes()
969 {
970     long long total = 0;
971     for (map<int, long long>::iterator itr = ongoingtransferredbytes.begin(); itr!= ongoingtransferredbytes.end(); itr++)
972     {
973         total += itr->second;
974     }
975     return total;
976 }
977 
getOngoingTotalBytes()978 long long MegaCmdMultiTransferListener::getOngoingTotalBytes()
979 {
980     long long total = 0;
981     for (map<int, long long>::iterator itr = ongoingtotalbytes.begin(); itr!= ongoingtotalbytes.end(); itr++)
982     {
983         total += itr->second;
984     }
985     return total;
986 }
987 
getProgressinformed() const988 bool MegaCmdMultiTransferListener::getProgressinformed() const
989 {
990     return progressinformed;
991 }
992 
MegaCmdMultiTransferListener(MegaApi * megaApi,MegaCmdSandbox * sandboxCMD,MegaTransferListener * listener,int clientID)993 MegaCmdMultiTransferListener::MegaCmdMultiTransferListener(MegaApi *megaApi, MegaCmdSandbox *sandboxCMD, MegaTransferListener *listener, int clientID)
994 {
995     this->megaApi = megaApi;
996     this->sandboxCMD = sandboxCMD;
997     this->listener = listener;
998     percentDownloaded = 0.0f;
999     alreadyFinished = false;
1000     this->clientID = clientID;
1001 
1002     started = 0;
1003     finished = 0;
1004     totalbytes = 0;
1005     transferredbytes = 0;
1006 
1007     progressinformed = false;
1008 
1009     finalerror = MegaError::API_OK;
1010 
1011 }
1012 
onTransferData(MegaApi * api,MegaTransfer * transfer,char * buffer,size_t size)1013 bool MegaCmdMultiTransferListener::onTransferData(MegaApi *api, MegaTransfer *transfer, char *buffer, size_t size)
1014 {
1015     return true;
1016 }
1017 
onNewTransfer()1018 void MegaCmdMultiTransferListener::onNewTransfer()
1019 {
1020     started ++;
1021 }
1022 
1023 ////////////////////////////////////////
1024 ///  MegaCmdGlobalTransferListener   ///
1025 ////////////////////////////////////////
1026 const int MegaCmdGlobalTransferListener::MAXCOMPLETEDTRANSFERSBUFFER = 10000;
1027 
MegaCmdGlobalTransferListener(MegaApi * megaApi,MegaCmdSandbox * sandboxCMD,MegaTransferListener * parent)1028 MegaCmdGlobalTransferListener::MegaCmdGlobalTransferListener(MegaApi *megaApi, MegaCmdSandbox *sandboxCMD, MegaTransferListener *parent)
1029 {
1030     this->megaApi = megaApi;
1031     this->sandboxCMD = sandboxCMD;
1032     this->listener = parent;
1033 };
1034 
onTransferFinish(MegaApi * api,MegaTransfer * transfer,MegaError * error)1035 void MegaCmdGlobalTransferListener::onTransferFinish(MegaApi* api, MegaTransfer *transfer, MegaError* error)
1036 {
1037     completedTransfersMutex.lock();
1038     completedTransfers.push_front(transfer->copy());
1039 
1040     // source
1041     MegaNode * node = api->getNodeByHandle(transfer->getNodeHandle());
1042     if (node)
1043     {
1044         char * nodepath = api->getNodePath(node);
1045         completedPathsByHandle[transfer->getNodeHandle()]=nodepath;
1046         delete []nodepath;
1047         delete node;
1048     }
1049 
1050     if (completedTransfers.size()>MAXCOMPLETEDTRANSFERSBUFFER)
1051     {
1052         MegaTransfer * todelete = completedTransfers.back();
1053         completedPathsByHandle.erase(todelete->getNodeHandle()); //TODO: this can be potentially eliminate a handle that has been added twice
1054         delete todelete;
1055         completedTransfers.pop_back();
1056     }
1057     completedTransfersMutex.unlock();
1058 }
1059 
onTransferStart(MegaApi * api,MegaTransfer * transfer)1060 void MegaCmdGlobalTransferListener::onTransferStart(MegaApi* api, MegaTransfer *transfer) {};
onTransferUpdate(MegaApi * api,MegaTransfer * transfer)1061 void MegaCmdGlobalTransferListener::onTransferUpdate(MegaApi* api, MegaTransfer *transfer) {};
onTransferTemporaryError(MegaApi * api,MegaTransfer * transfer,MegaError * e)1062 void MegaCmdGlobalTransferListener::onTransferTemporaryError(MegaApi *api, MegaTransfer *transfer, MegaError* e)
1063 {
1064     if (e && e->getErrorCode() == MegaError::API_EOVERQUOTA && e->getValue())
1065     {
1066         if (!sandboxCMD->isOverquota())
1067         {
1068             LOG_warn  << "Reached bandwidth quota. Your download could not proceed "
1069                          "because it would take you over the current free transfer allowance for your IP address. "
1070                          "This limit is dynamic and depends on the amount of unused bandwidth we have available. "
1071                          "You can change your account plan to increase such bandwidth. "
1072                          "See \"help --upgrade\" for further details";
1073         }
1074         sandboxCMD->setOverquota(true);
1075         sandboxCMD->timeOfOverquota = m_time(NULL);
1076         sandboxCMD->secondsOverQuota=e->getValue();
1077     }
1078 };
1079 
onTransferData(MegaApi * api,MegaTransfer * transfer,char * buffer,size_t size)1080 bool MegaCmdGlobalTransferListener::onTransferData(MegaApi *api, MegaTransfer *transfer, char *buffer, size_t size) {return false;};
1081 
~MegaCmdGlobalTransferListener()1082 MegaCmdGlobalTransferListener::~MegaCmdGlobalTransferListener()
1083 {
1084     completedTransfersMutex.lock();
1085     while (completedTransfers.size())
1086     {
1087         delete completedTransfers.front();
1088         completedTransfers.pop_front();
1089     }
1090     completedTransfersMutex.unlock();
1091 }
1092 
onTransferData(MegaApi * api,MegaTransfer * transfer,char * buffer,size_t size)1093 bool MegaCmdCatTransferListener::onTransferData(MegaApi *api, MegaTransfer *transfer, char *buffer, size_t size)
1094 {
1095     if (!ls->isClientConnected())
1096     {
1097         LOG_debug << " CatTransfer listener, cancelled transfer due to client disconnected";
1098         api->cancelTransfer(transfer);
1099     }
1100     else
1101     {
1102 
1103         LOG_debug << " CatTransfer listener, streaming " << size << " bytes";  //TODO: verbose
1104         *ls << string(buffer,size);
1105     }
1106 
1107     return true;
1108 }
1109 } //end namespace
1110