1 /**
2  * @file src/megacmdexecuter.cpp
3  * @brief MEGAcmd: Executer of the commands
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 "megacmdexecuter.h"
20 #include "megacmd.h"
21 
22 #include "megacmdutils.h"
23 #include "configurationmanager.h"
24 #include "megacmdlogger.h"
25 #include "comunicationsmanager.h"
26 #include "listeners.h"
27 #include "megacmdversion.h"
28 
29 #include <iomanip>
30 #include <string>
31 #include <ctime>
32 
33 #include <set>
34 
35 #include <signal.h>
36 
37 
38 #if (__cplusplus >= 201700L)
39     #include <filesystem>
40     namespace fs = std::filesystem;
41     #define MEGACMDEXECUTER_FILESYSTEM
42 #elif !defined(__MINGW32__) && !defined(__ANDROID__) && (!defined(__GNUC__) || (__GNUC__*100+__GNUC_MINOR__) >= 503)
43 #define MEGACMDEXECUTER_FILESYSTEM
44 #ifdef WIN32
45     #include <filesystem>
46     namespace fs = std::experimental::filesystem;
47 #else
48     #include <experimental/filesystem>
49     namespace fs = std::experimental::filesystem;
50 #endif
51 #endif
52 
53 
54 using namespace mega;
55 
56 namespace megacmd {
57 using namespace std;
58 
59 static const char* rootnodenames[] = { "ROOT", "INBOX", "RUBBISH" };
60 static const char* rootnodepaths[] = { "/", "//in", "//bin" };
61 
62 #define SSTR( x ) static_cast< const std::ostringstream & >( \
63         ( std::ostringstream() << std::dec << x ) ).str()
64 
65 
66 #ifdef HAVE_GLOB_H
resolvewildcard(const std::string & pattern)67 std::vector<std::string> resolvewildcard(const std::string& pattern) {
68 
69     vector<std::string> filenames;
70     glob_t glob_result;
71     memset(&glob_result, 0, sizeof(glob_result));
72 
73     if (!glob(pattern.c_str(), GLOB_TILDE, NULL, &glob_result))
74     {
75         for(size_t i = 0; i < glob_result.gl_pathc; ++i) {
76             filenames.push_back(std::string(glob_result.gl_pathv[i]));
77         }
78     }
79 
80     globfree(&glob_result);
81     return filenames;
82 }
83 #endif
84 
85 /**
86  * @brief updateprompt updates prompt with the current user/location
87  * @param api
88  * @param handle
89  */
updateprompt(MegaApi * api)90 void MegaCmdExecuter::updateprompt(MegaApi *api)
91 {
92     if (!api) api = this->api;
93     MegaHandle handle = cwd;
94 
95     string newprompt;
96 
97     MegaNode *n = api->getNodeByHandle(handle);
98 
99     MegaUser *u = api->getMyUser();
100     if (u)
101     {
102         const char *email = u->getEmail();
103         newprompt.append(email);
104         delete u;
105     }
106 
107     if (n)
108     {
109         char *np = api->getNodePath(n);
110 
111         if (!newprompt.empty())
112         {
113             newprompt.append(":");
114         }
115 
116         if (np)
117         {
118             newprompt.append(np);
119         }
120 
121         delete n;
122         delete []np;
123     }
124 
125     if (getBlocked())
126     {
127         newprompt.append("[BLOCKED]");
128     }
129 
130     if (newprompt.empty()) //i.e. !u && !n
131     {
132         newprompt = prompts[0];
133     }
134     else
135     {
136         newprompt.append("$ ");
137     }
138 
139     changeprompt(newprompt.c_str());
140 }
141 
MegaCmdExecuter(MegaApi * api,MegaCMDLogger * loggerCMD,MegaCmdSandbox * sandboxCMD)142 MegaCmdExecuter::MegaCmdExecuter(MegaApi *api, MegaCMDLogger *loggerCMD, MegaCmdSandbox *sandboxCMD)
143 {
144     signingup = false;
145     confirming = false;
146 
147     this->api = api;
148     this->loggerCMD = loggerCMD;
149     this->sandboxCMD = sandboxCMD;
150     this->globalTransferListener = new MegaCmdGlobalTransferListener(api, sandboxCMD);
151     api->addTransferListener(globalTransferListener);
152     cwd = UNDEF;
153     fsAccessCMD = new MegaFileSystemAccess();
154     session = NULL;
155 }
156 
~MegaCmdExecuter()157 MegaCmdExecuter::~MegaCmdExecuter()
158 {
159     delete fsAccessCMD;
160     delete []session;
161     for (std::vector< MegaNode * >::iterator it = nodesToConfirmDelete.begin(); it != nodesToConfirmDelete.end(); ++it)
162     {
163         delete *it;
164     }
165     nodesToConfirmDelete.clear();
166     delete globalTransferListener;
167 }
168 
169 // list available top-level nodes and contacts/incoming shares
listtrees()170 void MegaCmdExecuter::listtrees()
171 {
172     for (int i = 0; i < (int)( sizeof rootnodenames / sizeof *rootnodenames ); i++)
173     {
174         OUTSTREAM << rootnodenames[i] << " on " << rootnodepaths[i] << endl;
175         if (!api->isLoggedIn())
176         {
177             break;                     //only show /root
178         }
179     }
180 
181     MegaShareList * msl = api->getInSharesList();
182     for (int i = 0; i < msl->size(); i++)
183     {
184         MegaShare *share = msl->get(i);
185         MegaNode *n = api->getNodeByHandle(share->getNodeHandle());
186 
187         OUTSTREAM << "INSHARE on //from/" << share->getUser() << ":" << n->getName() << " (" << getAccessLevelStr(share->getAccess()) << ")" << endl;
188         delete n;
189     }
190 
191     delete ( msl );
192 }
193 
includeIfIsExported(MegaApi * api,MegaNode * n,void * arg)194 bool MegaCmdExecuter::includeIfIsExported(MegaApi *api, MegaNode * n, void *arg)
195 {
196     if (n->isExported())
197     {
198         (( vector<MegaNode*> * )arg )->push_back(n->copy());
199         return true;
200     }
201     return false;
202 }
203 
includeIfIsShared(MegaApi * api,MegaNode * n,void * arg)204 bool MegaCmdExecuter::includeIfIsShared(MegaApi *api, MegaNode * n, void *arg)
205 {
206     if (n->isShared())
207     {
208         (( vector<MegaNode*> * )arg )->push_back(n->copy());
209         return true;
210     }
211     return false;
212 }
213 
includeIfIsPendingOutShare(MegaApi * api,MegaNode * n,void * arg)214 bool MegaCmdExecuter::includeIfIsPendingOutShare(MegaApi *api, MegaNode * n, void *arg)
215 {
216     MegaShareList* pendingoutShares = api->getPendingOutShares(n);
217     if (pendingoutShares && pendingoutShares->size())
218     {
219         (( vector<MegaNode*> * )arg )->push_back(n->copy());
220         return true;
221     }
222     if (pendingoutShares)
223     {
224         delete pendingoutShares;
225     }
226     return false;
227 }
228 
229 
includeIfIsSharedOrPendingOutShare(MegaApi * api,MegaNode * n,void * arg)230 bool MegaCmdExecuter::includeIfIsSharedOrPendingOutShare(MegaApi *api, MegaNode * n, void *arg)
231 {
232     if (n->isShared())
233     {
234         (( vector<MegaNode*> * )arg )->push_back(n->copy());
235         return true;
236     }
237     MegaShareList* pendingoutShares = api->getPendingOutShares(n);
238     if (pendingoutShares && pendingoutShares->size())
239     {
240         (( vector<MegaNode*> * )arg )->push_back(n->copy());
241         return true;
242     }
243     if (pendingoutShares)
244     {
245         delete pendingoutShares;
246     }
247     return false;
248 }
249 
250 struct patternNodeVector
251 {
252     string pattern;
253     bool usepcre;
254     vector<MegaNode*> *nodesMatching;
255 };
256 
257 struct criteriaNodeVector
258 {
259     string pattern;
260     bool usepcre;
261     m_time_t minTime;
262     m_time_t maxTime;
263 
264     int64_t maxSize;
265     int64_t minSize;
266 
267     vector<MegaNode*> *nodesMatching;
268 };
269 
includeIfMatchesPattern(MegaApi * api,MegaNode * n,void * arg)270 bool MegaCmdExecuter::includeIfMatchesPattern(MegaApi *api, MegaNode * n, void *arg)
271 {
272     struct patternNodeVector *pnv = (struct patternNodeVector*)arg;
273     if (patternMatches(n->getName(), pnv->pattern.c_str(), pnv->usepcre))
274     {
275         pnv->nodesMatching->push_back(n->copy());
276         return true;
277     }
278     return false;
279 }
280 
281 
includeIfMatchesCriteria(MegaApi * api,MegaNode * n,void * arg)282 bool MegaCmdExecuter::includeIfMatchesCriteria(MegaApi *api, MegaNode * n, void *arg)
283 {
284     struct criteriaNodeVector *pnv = (struct criteriaNodeVector*)arg;
285 
286     if ( pnv->maxTime != -1 && (n->getModificationTime() >= pnv->maxTime) )
287     {
288         return false;
289     }
290     if ( pnv->minTime != -1 && (n->getModificationTime() <= pnv->minTime) )
291     {
292         return false;
293     }
294 
295     if ( pnv->maxSize != -1 && (n->getType() != MegaNode::TYPE_FILE || (n->getSize() > pnv->maxSize) ) )
296     {
297         return false;
298     }
299 
300     if ( pnv->minSize != -1 && (n->getType() != MegaNode::TYPE_FILE || (n->getSize() < pnv->minSize) ) )
301     {
302         return false;
303     }
304 
305     if (!patternMatches(n->getName(), pnv->pattern.c_str(), pnv->usepcre))
306     {
307         return false;
308     }
309 
310     pnv->nodesMatching->push_back(n->copy());
311     return true;
312 }
313 
processTree(MegaNode * n,bool processor (MegaApi *,MegaNode *,void *),void * (arg))314 bool MegaCmdExecuter::processTree(MegaNode *n, bool processor(MegaApi *, MegaNode *, void *), void *( arg ))
315 {
316     if (!n)
317     {
318         return false;
319     }
320     bool toret = true;
321     MegaNodeList *children = api->getChildren(n);
322     if (children)
323     {
324         for (int i = 0; i < children->size(); i++)
325         {
326             bool childret = processTree(children->get(i), processor, arg);
327             toret = toret && childret;
328         }
329 
330         delete children;
331     }
332 
333     bool currentret = processor(api, n, arg);
334     return toret && currentret;
335 }
336 
337 
338 // returns node pointer determined by path relative to cwd
339 // path naming conventions:
340 // * path is relative to cwd
341 // * /path is relative to ROOT
342 // * //in is in INBOX
343 // * //bin is in RUBBISH
344 // * X: is user X's INBOX
345 // * X:SHARE is share SHARE from user X
346 // * H:HANDLE is node with handle HANDLE
347 // * : and / filename components, as well as the \, must be escaped by \.
348 // (correct UTF-8 encoding is assumed)
349 // returns NULL if path malformed or not found
nodebypath(const char * ptr,string * user,string * namepart)350 MegaNode* MegaCmdExecuter::nodebypath(const char* ptr, string* user, string* namepart)
351 {
352     if (ptr && ptr[0] == 'H' && ptr[1] == ':')
353     {
354         MegaNode * n = api->getNodeByHandle(api->base64ToHandle(ptr+2));
355         if (n)
356         {
357             return n;
358         }
359     }
360 
361     string rest;
362     MegaNode *baseNode = getBaseNode(ptr, rest);
363 
364     if (baseNode && !rest.size())
365     {
366         return baseNode;
367     }
368 
369     if (!rest.size() && !baseNode)
370     {
371         string path(ptr);
372         if (path.size() && path.find("@") != string::npos && path.find_last_of(":") == (path.size() - 1))
373         {
374             if (user)
375             {
376                 *user = path.substr(0,path.size()-1);
377             }
378         }
379     }
380 
381     while (baseNode)
382     {
383         size_t possep = rest.find('/');
384         string curName = rest.substr(0,possep);
385 
386         if (curName != ".")
387         {
388             MegaNode * nextNode = NULL;
389             if (curName == "..")
390             {
391                 nextNode = api->getParentNode(baseNode);
392             }
393             else
394             {
395                 replaceAll(curName, "\\\\", "\\"); //unescape '\\'
396                 replaceAll(curName, "\\ ", " "); //unescape '\ '
397                 bool isversion = nodeNameIsVersion(curName);
398                 if (isversion)
399                 {
400                     MegaNode *childNode = api->getChildNode(baseNode, curName.substr(0,curName.size()-11).c_str());
401                     if (childNode)
402                     {
403                         MegaNodeList *versionNodes = api->getVersions(childNode);
404                         if (versionNodes)
405                         {
406                             for (int i = 0; i < versionNodes->size(); i++)
407                             {
408                                 MegaNode *versionNode = versionNodes->get(i);
409                                 if ( curName.substr(curName.size()-10) == SSTR(versionNode->getModificationTime()) )
410                                 {
411                                     nextNode = versionNode->copy();
412                                     break;
413                                 }
414                             }
415                             delete versionNodes;
416                         }
417                         delete childNode;
418                     }
419                 }
420                 else
421                 {
422                     nextNode = api->getChildNode(baseNode,curName.c_str());
423                 }
424             }
425 
426             // mv command target? return name part of not found
427             if (namepart && !nextNode && ( possep == string::npos)) //if this is the last part, we will pass that one, so that a mv command know the name to give the new node
428             {
429                 *namepart = rest;
430                 return baseNode;
431             }
432 
433             if (nextNode != baseNode)
434             {
435                 delete baseNode;
436             }
437             baseNode = nextNode;
438         }
439 
440         if (possep != string::npos && possep != (rest.size() - 1) )
441         {
442             rest = rest.substr(possep+1);
443         }
444         else
445         {
446             return baseNode;
447         }
448     }
449 
450     return NULL;
451 }
452 
453 /**
454  * @brief MegaCmdExecuter::getPathsMatching Gets paths of nodes matching a pattern given its path parts and a parent node
455  *
456  * @param parentNode node for reference for relative paths
457  * @param pathParts path pattern (separated in strings)
458  * @param pathsMatching for the returned paths
459  * @param usepcre use PCRE expressions if available
460  * @param pathPrefix prefix to append to paths
461  */
getPathsMatching(MegaNode * parentNode,deque<string> pathParts,vector<string> * pathsMatching,bool usepcre,string pathPrefix)462 void MegaCmdExecuter::getPathsMatching(MegaNode *parentNode, deque<string> pathParts, vector<string> *pathsMatching, bool usepcre, string pathPrefix)
463 {
464     if (!pathParts.size())
465     {
466         return;
467     }
468 
469     string currentPart = pathParts.front();
470     pathParts.pop_front();
471 
472     if (currentPart == "." || currentPart == "")
473     {
474         if (pathParts.size() == 0  /*&& currentPart == "."*/) //last leave.  // for consistency we also take parent when ended in / even if it's not a folder
475          {
476              pathsMatching->push_back(pathPrefix+currentPart);
477          }
478 
479         //ignore this part
480         return getPathsMatching(parentNode, pathParts, pathsMatching, usepcre, pathPrefix+"./");
481     }
482     if (currentPart == "..")
483     {
484         if (parentNode->getParentHandle())
485         {
486             if (!pathParts.size())
487             {
488                 pathsMatching->push_back(pathPrefix+"..");
489             }
490 
491             parentNode = api->getNodeByHandle(parentNode->getParentHandle());
492             return getPathsMatching(parentNode, pathParts, pathsMatching, usepcre, pathPrefix+"../");
493             delete parentNode;
494         }
495         else
496         {
497             return; //trying to access beyond root node
498         }
499     }
500 
501     MegaNodeList* children = api->getChildren(parentNode);
502     if (children)
503     {
504         bool isversion = nodeNameIsVersion(currentPart);
505 
506         for (int i = 0; i < children->size(); i++)
507         {
508             MegaNode *childNode = children->get(i);
509             // get childname from its path: alternative: childNode->getName()
510             char *childNodePath = api->getNodePath(childNode);
511             char *aux;
512             aux = childNodePath+strlen(childNodePath);
513             while (aux>childNodePath){
514                 if (*aux=='/' && *(aux-1) != '\\')  break;
515                 aux--;
516             }
517             if (*aux=='/') aux++;
518             string childname(aux);
519             delete []childNodePath;
520 
521             if (isversion)
522             {
523 
524                 if (childNode && patternMatches(childname.c_str(), currentPart.substr(0,currentPart.size()-11).c_str(), usepcre))
525                 {
526                     MegaNodeList *versionNodes = api->getVersions(childNode);
527                     if (versionNodes)
528                     {
529                         for (int i = 0; i < versionNodes->size(); i++)
530                         {
531                             MegaNode *versionNode = versionNodes->get(i);
532                             if ( currentPart.substr(currentPart.size()-10) == SSTR(versionNode->getModificationTime()) )
533                             {
534                                 if (pathParts.size() == 0) //last leave
535                                 {
536                                     pathsMatching->push_back(pathPrefix+childname+"#"+SSTR(versionNode->getModificationTime())); //TODO: def version separator elswhere
537                                 }
538                                 else
539                                 {
540                                     getPathsMatching(versionNode, pathParts, pathsMatching, usepcre,pathPrefix+childname+"#"+SSTR(versionNode->getModificationTime())+"/");
541                                 }
542 
543                                 break;
544                             }
545                         }
546                         delete versionNodes;
547                     }
548                 }
549             }
550             else
551             {
552                 if (patternMatches(childname.c_str(), currentPart.c_str(), usepcre))
553                 {
554                     if (pathParts.size() == 0) //last leave
555                     {
556                         pathsMatching->push_back(pathPrefix+childname);
557                     }
558                     else
559                     {
560                         getPathsMatching(childNode, pathParts, pathsMatching, usepcre,pathPrefix+childname+"/");
561                     }
562                 }
563 
564 
565             }
566 
567         }
568 
569         delete children;
570     }
571 }
572 
573 /**
574  * @brief MegaCmdExecuter::nodesPathsbypath returns paths of nodes that match a determined by path pattern
575  * path naming conventions:
576  * path is relative to cwd
577  * /path is relative to ROOT
578  * //in is in INBOX
579  * //bin is in RUBBISH
580  * X: is user X's INBOX
581  * X:SHARE is share SHARE from user X
582  * H:HANDLE is node with handle HANDLE
583  * : and / filename components, as well as the \, must be escaped by \.
584  * (correct UTF-8 encoding is assumed)
585  *
586  * You take the ownership of the returned value
587  * @param ptr
588  * @param usepcre use PCRE expressions if available
589  * @param user
590  * @param namepart
591  * @return
592  */
nodesPathsbypath(const char * ptr,bool usepcre,string * user,string * namepart)593 vector <string> * MegaCmdExecuter::nodesPathsbypath(const char* ptr, bool usepcre, string* user, string* namepart)
594 {
595     vector<string> *pathsMatching = new vector<string> ();
596 
597     if (ptr && ptr[0] == 'H' && ptr[1] == ':')
598     {
599         MegaNode * n = api->getNodeByHandle(api->base64ToHandle(ptr+2));
600         if (n)
601         {
602             char * nodepath = api->getNodePath(n);
603             pathsMatching->push_back(nodepath);
604             delete []nodepath;
605             return pathsMatching;
606         }
607     }
608 
609     string rest;
610     bool isrelative;
611     MegaNode *baseNode = getBaseNode(ptr, rest, &isrelative);
612 
613     if (baseNode)
614     {
615         string pathPrefix;
616         if (!isrelative)
617         {
618             char * nodepath = api->getNodePath(baseNode);
619             pathPrefix=nodepath;
620             if (pathPrefix.size() && pathPrefix.at(pathPrefix.size()-1)!='/')
621                 pathPrefix+="/";
622             delete []nodepath;
623         }
624 
625         if (string(ptr).find("//from/") == 0)
626         {
627             pathPrefix.insert(0,"//from/");
628         }
629 
630         deque<string> c;
631         getPathParts(rest, &c);
632 
633         if (!c.size())
634         {
635             char * nodepath = api->getNodePath(baseNode);
636             pathsMatching->push_back(nodepath);
637             delete []nodepath;
638         }
639         else
640         {
641             getPathsMatching((MegaNode *)baseNode, c, (vector<string> *)pathsMatching, usepcre, pathPrefix);
642         }
643         delete baseNode;
644     }
645     else if (!strncmp(ptr,"//from/",max(3,min((int)strlen(ptr)-1,7)))) //pattern trying to match inshares
646     {
647 
648         string matching = ptr;
649         unescapeifRequired(matching);
650         unique_ptr<MegaShareList> inShares(api->getInSharesList());
651         if (inShares)
652         {
653             for (int i = 0; i < inShares->size(); i++)
654             {
655                 unique_ptr<MegaNode> n(api->getNodeByHandle(inShares->get(i)->getNodeHandle()));
656                 string tomatch = string("//from/")+inShares->get(i)->getUser() + ":"+n->getName();
657 
658                 if (patternMatches(tomatch.c_str(), matching.c_str(), false))
659                 {
660                     pathsMatching->push_back(tomatch);
661                 }
662             }
663         }
664     }
665 
666     return pathsMatching;
667 }
668 
669 /**
670  *  You take the ownership of the nodes added in nodesMatching
671  * @brief getNodesMatching
672  * @param parentNode
673  * @param c
674  * @param nodesMatching
675  */
getNodesMatching(MegaNode * parentNode,deque<string> pathParts,vector<MegaNode * > * nodesMatching,bool usepcre)676 void MegaCmdExecuter::getNodesMatching(MegaNode *parentNode, deque<string> pathParts, vector<MegaNode *> *nodesMatching, bool usepcre)
677 {
678     if (!pathParts.size())
679     {
680         return;
681     }
682 
683     string currentPart = pathParts.front();
684     pathParts.pop_front();
685 
686     if (currentPart == "." || currentPart == "")
687     {
688         if (pathParts.size() == 0  /*&& currentPart == "."*/) //last leave.  // for consistency we also take parent when ended in / even if it's not a folder
689         {
690             if (parentNode)
691             {
692                 nodesMatching->push_back(parentNode->copy());
693                 return;
694             }
695         }
696         else
697         {
698             //ignore this part
699             return getNodesMatching(parentNode, pathParts, nodesMatching, usepcre);
700         }
701     }
702     if (currentPart == "..")
703     {
704         if (parentNode->getParentHandle())
705         {
706             MegaNode *newparentNode = api->getNodeByHandle(parentNode->getParentHandle());
707             if (!pathParts.size()) //last leave
708             {
709                 if (newparentNode)
710                 {
711                     nodesMatching->push_back(newparentNode);
712                 }
713                 return;
714             }
715             else
716             {
717                 getNodesMatching(newparentNode, pathParts, nodesMatching, usepcre);
718                 delete newparentNode;
719                 return;
720             }
721 
722         }
723         else
724         {
725             return; //trying to access beyond root node
726         }
727 
728     }
729 
730     MegaNodeList* children = api->getChildren(parentNode);
731     if (children)
732     {
733         bool isversion = nodeNameIsVersion(currentPart);
734 
735         for (int i = 0; i < children->size(); i++)
736         {
737             MegaNode *childNode = children->get(i);
738             if (isversion)
739             {
740 
741                 if (childNode && patternMatches(childNode->getName(), currentPart.substr(0,currentPart.size()-11).c_str(), usepcre))
742                 {
743                     MegaNodeList *versionNodes = api->getVersions(childNode);
744                     if (versionNodes)
745                     {
746                         for (int i = 0; i < versionNodes->size(); i++)
747                         {
748                             MegaNode *versionNode = versionNodes->get(i);
749                             if ( currentPart.substr(currentPart.size()-10) == SSTR(versionNode->getModificationTime()) )
750                             {
751                                 if (pathParts.size() == 0) //last leave
752                                 {
753                                     nodesMatching->push_back(versionNode->copy());
754                                 }
755                                 else
756                                 {
757                                     getNodesMatching(versionNode, pathParts, nodesMatching, usepcre);
758                                 }
759 
760                                 break;
761                             }
762                         }
763                         delete versionNodes;
764                     }
765                 }
766             }
767             else
768             {
769 
770                 if (patternMatches(childNode->getName(), currentPart.c_str(), usepcre))
771                 {
772                     if (pathParts.size() == 0) //last leave
773                     {
774                         nodesMatching->push_back(childNode->copy());
775                     }
776                     else
777                     {
778                         getNodesMatching(childNode, pathParts, nodesMatching, usepcre);
779                     }
780 
781                 }
782             }
783         }
784 
785         delete children;
786     }
787 }
788 
789 // TODO: docs
getBaseNode(string thepath,string & rest,bool * isrelative)790 MegaNode * MegaCmdExecuter::getBaseNode(string thepath, string &rest, bool *isrelative)
791 {
792     if (isrelative != NULL)
793     {
794         *isrelative = false;
795     }
796     MegaNode *baseNode = NULL;
797     rest = string();
798     if (thepath == "//bin")
799     {
800         baseNode = api->getRubbishNode();
801         rest = "";
802     }
803     else if (thepath == "//in")
804     {
805         baseNode = api->getInboxNode();
806         rest = "";
807     }
808     else if (thepath == "/")
809     {
810         baseNode = api->getRootNode();
811         rest = "";
812     }
813     else if (thepath.find("//bin/") == 0 )
814     {
815         baseNode = api->getRubbishNode();
816         rest = thepath.substr(6);
817     }
818     else if (thepath.find("//in/") == 0 )
819     {
820         baseNode = api->getInboxNode();
821         rest = thepath.substr(5);
822     }
823     else if (thepath.find("/") == 0 && !(thepath.find("//from/") == 0 ))
824     {
825         if ( thepath.find("//f") == 0 && string("//from/").find(thepath.substr(0,thepath.find("*"))) == 0)
826         {
827             return NULL;
828         }
829         baseNode = api->getRootNode();
830         rest = thepath.substr(1);
831     }
832     else if ( thepath == "//from/*" )
833     {
834         return NULL;
835     }
836     else
837     {
838         bool from = false;
839         if  (thepath.find("//from/") == 0 && thepath != "//from/*" )
840         {
841             thepath = thepath.substr(7);
842             from = true;
843         }
844         size_t possep = thepath.find('/');
845         string base = thepath.substr(0,possep);
846         size_t possepcol = base.find(":");
847         size_t possepat = base.find("@");
848 
849         if ( possepcol != string::npos && possepat != string::npos  && possepat < possepcol && possepcol < (base.size() + 1) )
850         {
851             string userName = base.substr(0,possepcol);
852             string inshareName = base.substr(possepcol + 1);
853             unescapeifRequired(inshareName);
854 
855             MegaUserList * usersList = api->getContacts();
856             MegaUser *u = NULL;
857             for (int i = 0; i < usersList->size(); i++)
858             {
859                 if (usersList->get(i)->getEmail() == userName)
860                 {
861                     u = usersList->get(i);
862                     break;
863                 }
864             }
865             if (u)
866             {
867                 MegaNodeList* inshares = api->getInShares(u);
868                 for (int i = 0; i < inshares->size(); i++)
869                 {
870                     if (inshares->get(i)->getName() == inshareName)
871                     {
872                         baseNode = inshares->get(i)->copy();
873                         break;
874                     }
875                 }
876 
877                 delete inshares;
878             }
879             delete usersList;
880 
881             if (possep != string::npos && possep != (thepath.size() - 1) )
882             {
883                 rest = thepath.substr(possep+1);
884             }
885         }
886         else if (!from)
887         {
888             baseNode = api->getNodeByHandle(cwd);
889             rest = thepath;
890             if (isrelative != NULL)
891             {
892                 *isrelative = true;
893             }
894         }
895     }
896 
897     return baseNode;
898 }
899 
getPathParts(string path,deque<string> * c)900 void MegaCmdExecuter::getPathParts(string path, deque<string> *c)
901 {
902     size_t possep = path.find('/');
903     do
904     {
905         string curName = path.substr(0,possep);
906         replaceAll(curName, "\\\\", "\\"); //unescape '\\'
907         replaceAll(curName, "\\ ", " "); //unescape '\ '
908         c->push_back(curName);
909         if (possep != string::npos && possep < (path.size()+1))
910         {
911             path = path.substr(possep+1);
912         }
913         else
914         {
915             break;
916         }
917         possep = path.find('/');
918         if (possep == string::npos ||  !(possep < (path.size()+1)))
919         {
920             string curName = path.substr(0,possep);
921             replaceAll(curName, "\\\\", "\\"); //unescape '\\'
922             replaceAll(curName, "\\ ", " "); //unescape '\ '
923             c->push_back(curName);
924 
925             break;
926         }
927     } while (path.size());
928 }
929 
checkAndInformPSA(CmdPetition * inf,bool enforce)930 bool MegaCmdExecuter::checkAndInformPSA(CmdPetition *inf, bool enforce)
931 {
932     bool toret = false;
933     m_time_t now = m_time();
934     if ( enforce || (now - sandboxCMD->timeOfPSACheck > 86400) )
935     {
936         MegaUser *u = api->getMyUser();
937         if (!u)
938         {
939             LOG_debug << "No PSA request (not logged into an account)";
940             return toret; //Not logged in, no reason to get PSA
941         }
942         delete u;
943 
944         sandboxCMD->timeOfPSACheck = now;
945 
946         LOG_verbose << "Getting PSA";
947         MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
948         api->getPSA(megaCmdListener);
949         megaCmdListener->wait();
950         if (megaCmdListener->getError()->getErrorCode() == MegaError::API_ENOENT)
951         {
952             LOG_verbose << "No new PSA available";
953             sandboxCMD->lastPSAnumreceived = max(0,sandboxCMD->lastPSAnumreceived);
954         }
955         else if(checkNoErrors(megaCmdListener->getError(), "get PSA"))
956         {
957             sandboxCMD->lastPSAnumreceived = megaCmdListener->getRequest()->getNumber();
958 
959             LOG_debug << "Informing PSA #" << megaCmdListener->getRequest()->getNumber() << ": " << megaCmdListener->getRequest()->getName();
960 
961             stringstream oss;
962 
963             oss << "<" << megaCmdListener->getRequest()->getName() << ">";
964             oss << megaCmdListener->getRequest()->getText();
965 
966             string action = megaCmdListener->getRequest()->getPassword();
967             string link = megaCmdListener->getRequest()->getLink();
968             if (!action.length())
969             {
970                 action = "read more: ";
971             }
972 
973             if (link.size())
974             {
975                 oss << endl << action << ": " << link;
976             }
977 
978             oss << endl << " Execute \"psa --discard\" to stop seeing this message";
979 
980             if (inf)
981             {
982                 informStateListener(oss.str(), inf->clientID);
983             }
984             else
985             {
986                 broadcastMessage(oss.str());
987             }
988             toret = true;
989         }
990         delete megaCmdListener;
991     }
992     return toret;
993 }
994 
995 
checkNoErrors(int errorCode,string message)996 bool MegaCmdExecuter::checkNoErrors(int errorCode, string message)
997 {
998     MegaErrorPrivate e(errorCode);
999     return checkNoErrors(&e, message);
1000 }
1001 
checkNoErrors(MegaError * error,string message)1002 bool MegaCmdExecuter::checkNoErrors(MegaError *error, string message)
1003 {
1004     if (!error)
1005     {
1006         LOG_fatal << "No MegaError at request: " << message;
1007         return false;
1008     }
1009     if (error->getErrorCode() == MegaError::API_OK)
1010     {
1011         return true;
1012     }
1013 
1014     setCurrentOutCode(error->getErrorCode());
1015     if (error->getErrorCode() == MegaError::API_EBLOCKED)
1016     {
1017         auto reason = sandboxCMD->getReasonblocked();
1018         LOG_err << "Failed to " << message << ". Account blocked." <<( reason.empty()?"":" Reason: "+reason);
1019     }
1020     else if ((error->getErrorCode() == MegaError::API_EPAYWALL) || (error->getErrorCode() == MegaError::API_EOVERQUOTA && sandboxCMD->storageStatus == MegaApi::STORAGE_STATE_RED))
1021     {
1022         LOG_err << "Failed to " << message << ": Reached storage quota. "
1023                          "You can change your account plan to increase your quota limit. "
1024                          "See \"help --upgrade\" for further details";
1025     }
1026     else
1027     {
1028         LOG_err << "Failed to " << message << ": " << error->getErrorString();
1029     }
1030 
1031     return false;
1032 }
1033 
1034 /**
1035  * @brief MegaCmdExecuter::nodesbypath
1036  * returns nodes determined by path pattern
1037  * path naming conventions:
1038  * path is relative to cwd
1039  * /path is relative to ROOT
1040  * //in is in INBOX
1041  * //bin is in RUBBISH
1042  * X: is user X's INBOX
1043  * X:SHARE is share SHARE from user X
1044  * H:HANDLE is node with handle HANDLE
1045  * : and / filename components, as well as the \, must be escaped by \.
1046  * (correct UTF-8 encoding is assumed)
1047  * @param ptr
1048  * @param usepcre use PCRE expressions if available
1049  * @param user
1050  * @return List of MegaNode*.  You take the ownership of those MegaNode*
1051  */
nodesbypath(const char * ptr,bool usepcre,string * user)1052 vector <MegaNode*> * MegaCmdExecuter::nodesbypath(const char* ptr, bool usepcre, string* user)
1053 {
1054     vector<MegaNode *> *nodesMatching = new vector<MegaNode *> ();
1055 
1056     if (ptr && ptr[0] == 'H' && ptr[1] == ':')
1057     {
1058         MegaNode * n = api->getNodeByHandle(api->base64ToHandle(ptr+2));
1059         if (n)
1060         {
1061             nodesMatching->push_back(n);
1062             return nodesMatching;
1063         }
1064     }
1065 
1066     string rest;
1067     MegaNode *baseNode = getBaseNode(ptr, rest);
1068 
1069     if (baseNode)
1070     {
1071         if (!rest.size())
1072         {
1073             nodesMatching->push_back(baseNode);
1074             return nodesMatching;
1075         }
1076 
1077         deque<string> c;
1078         getPathParts(rest, &c);
1079 
1080         if (!c.size())
1081         {
1082             nodesMatching->push_back(baseNode);
1083             return nodesMatching;
1084         }
1085         else
1086         {
1087             getNodesMatching(baseNode, c, nodesMatching, usepcre);
1088         }
1089         delete baseNode;
1090     }
1091     else if (!strncmp(ptr,"//from/",max(3,min((int)strlen(ptr)-1,7)))) //pattern trying to match inshares
1092     {
1093         unique_ptr<MegaShareList> inShares(api->getInSharesList());
1094         if (inShares)
1095         {
1096             string matching = ptr;
1097             unescapeifRequired(matching);
1098             for (int i = 0; i < inShares->size(); i++)
1099             {
1100                 MegaNode* n = api->getNodeByHandle(inShares->get(i)->getNodeHandle());
1101                 string tomatch = string("//from/")+inShares->get(i)->getUser() + ":"+n->getName();
1102                 if (patternMatches(tomatch.c_str(), matching.c_str(), false))
1103                 {
1104                     nodesMatching->push_back(n);
1105                 }
1106                 else
1107                 {
1108                     delete n;
1109                 }
1110             }
1111         }
1112     }
1113 
1114     return nodesMatching;
1115 }
1116 
dumpNode(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,int extended_info,bool showversions,int depth,const char * title)1117 void MegaCmdExecuter::dumpNode(MegaNode* n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, int extended_info, bool showversions, int depth, const char* title)
1118 {
1119     if (!title && !( title = n->getName()))
1120     {
1121         title = "CRYPTO_ERROR";
1122     }
1123 
1124     if (depth)
1125     {
1126         for (int i = depth - 1; i--; )
1127         {
1128             OUTSTREAM << "\t";
1129         }
1130     }
1131 
1132     OUTSTREAM << title;
1133 
1134     if (getFlag(clflags, "show-handles"))
1135     {
1136         std::unique_ptr<char []> handle {api->handleToBase64(n->getHandle())};
1137         OUTSTREAM << " <H:" << handle.get() << ">";
1138     }
1139 
1140     if (extended_info)
1141     {
1142         OUTSTREAM << " (";
1143         switch (n->getType())
1144         {
1145             case MegaNode::TYPE_FILE:
1146                 OUTSTREAM << sizeToText(n->getSize(), false);
1147 
1148                 const char* p;
1149                 if (( p = strchr(n->getAttrString()->c_str(), ':')))
1150                 {
1151                     OUTSTREAM << ", has attributes " << p + 1;
1152                 }
1153 
1154                 if (INVALID_HANDLE != n->getPublicHandle())
1155 //            if (n->isExported())
1156                 {
1157                     OUTSTREAM << ", shared as exported";
1158                     if (n->getExpirationTime())
1159                     {
1160                         OUTSTREAM << " temporal";
1161                     }
1162                     else
1163                     {
1164                         OUTSTREAM << " permanent";
1165                     }
1166                     OUTSTREAM << " file link";
1167                     if (extended_info > 1)
1168                     {
1169                         char * publicLink = n->getPublicLink();
1170                         OUTSTREAM << ": " << publicLink;
1171                         if (n->getExpirationTime())
1172                         {
1173                             if (n->isExpired())
1174                             {
1175                                 OUTSTREAM << " expired at ";
1176                             }
1177                             else
1178                             {
1179                                 OUTSTREAM << " expires at ";
1180                             }
1181                             OUTSTREAM << " at " << getReadableTime(n->getExpirationTime(), timeFormat);
1182                         }
1183                         delete []publicLink;
1184                     }
1185                 }
1186                 break;
1187 
1188             case MegaNode::TYPE_FOLDER:
1189             {
1190                 OUTSTREAM << "folder";
1191                 MegaShareList* outShares = api->getOutShares(n);
1192                 if (outShares)
1193                 {
1194                     for (int i = 0; i < outShares->size(); i++)
1195                     {
1196                         if (outShares->get(i)->getNodeHandle() == n->getHandle())
1197                         {
1198                             OUTSTREAM << ", shared with " << outShares->get(i)->getUser() << ", access "
1199                                       << getAccessLevelStr(outShares->get(i)->getAccess());
1200                         }
1201                     }
1202 
1203                     MegaShareList* pendingoutShares = api->getPendingOutShares(n);
1204                     if (pendingoutShares)
1205                     {
1206                         for (int i = 0; i < pendingoutShares->size(); i++)
1207                         {
1208                             if (pendingoutShares->get(i)->getNodeHandle() == n->getHandle())
1209                             {
1210                                 OUTSTREAM << ", shared (still pending)";
1211                                 if (pendingoutShares->get(i)->getUser())
1212                                 {
1213                                     OUTSTREAM << " with " << pendingoutShares->get(i)->getUser();
1214                                 }
1215                                 OUTSTREAM << " access " << getAccessLevelStr(pendingoutShares->get(i)->getAccess());
1216                             }
1217                         }
1218 
1219                         delete pendingoutShares;
1220                     }
1221 
1222                     if (UNDEF != n->getPublicHandle())
1223                     {
1224                         OUTSTREAM << ", shared as exported";
1225                         if (n->getExpirationTime())
1226                         {
1227                             OUTSTREAM << " temporal";
1228                         }
1229                         else
1230                         {
1231                             OUTSTREAM << " permanent";
1232                         }
1233                         OUTSTREAM << " folder link";
1234                         if (extended_info > 1)
1235                         {
1236                             char * publicLink = n->getPublicLink();
1237                             OUTSTREAM << ": " << publicLink;
1238                             delete []publicLink;
1239                         }
1240                     }
1241                     delete outShares;
1242                 }
1243 
1244                 if (n->isInShare())
1245                 {
1246                     OUTSTREAM << ", inbound " << api->getAccess(n) << " share";
1247                 }
1248                 break;
1249             }
1250             case MegaNode::TYPE_ROOT:
1251             {
1252                 OUTSTREAM << "root node";
1253                 break;
1254             }
1255             case MegaNode::TYPE_INCOMING:
1256             {
1257                 OUTSTREAM << "inbox";
1258                 break;
1259             }
1260             case MegaNode::TYPE_RUBBISH:
1261             {
1262                 OUTSTREAM << "rubbish";
1263                 break;
1264             }
1265             default:
1266                 OUTSTREAM << "unsupported type: " <<  n->getType() <<" , please upgrade";
1267         }
1268         OUTSTREAM << ")" << ( n->isRemoved() ? " (DELETED)" : "" );
1269     }
1270 
1271     OUTSTREAM << endl;
1272 
1273     if (showversions && n->getType() == MegaNode::TYPE_FILE)
1274     {
1275         MegaNodeList *versionNodes = api->getVersions(n);
1276         if (versionNodes)
1277         {
1278             for (int i = 0; i < versionNodes->size(); i++)
1279             {
1280                 MegaNode *versionNode = versionNodes->get(i);
1281 
1282                 if (versionNode->getHandle() != n->getHandle())
1283                 {
1284                     string fullname(n->getName()?n->getName():"NO_NAME");
1285                     fullname += "#";
1286                     fullname += SSTR(versionNode->getModificationTime());
1287                     OUTSTREAM << "  " << fullname;
1288                     if (versionNode->getName() && !strcmp(versionNode->getName(),n->getName()) )
1289                     {
1290                         OUTSTREAM << "[" << (versionNode->getName()?versionNode->getName():"NO_NAME") << "]";
1291                     }
1292                     OUTSTREAM << " (" << getReadableTime(versionNode->getModificationTime(), timeFormat) << ")";
1293                     if (extended_info)
1294                     {
1295                         OUTSTREAM << " (" << sizeToText(versionNode->getSize(), false) << ")";
1296                     }
1297 
1298 
1299                     if (getFlag(clflags, "show-handles"))
1300                     {
1301                         std::unique_ptr<char []> handle {api->handleToBase64(versionNode->getHandle())};
1302                         OUTSTREAM << " <H:" << handle.get() << ">";
1303                     }
1304 
1305                     OUTSTREAM << endl;
1306                 }
1307             }
1308         }
1309     }
1310 }
1311 
dumpNodeSummaryHeader(const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions)1312 void MegaCmdExecuter::dumpNodeSummaryHeader(const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions)
1313 {
1314     int datelength = getReadableTime(m_time(), timeFormat).size();
1315 
1316     OUTSTREAM << "FLAGS";
1317     OUTSTREAM << " ";
1318     OUTSTREAM << getFixLengthString("VERS", 4);
1319     OUTSTREAM << " ";
1320     OUTSTREAM << getFixLengthString("SIZE  ", 10 -1, ' ', true); //-1 because of "FLAGS"
1321     OUTSTREAM << " ";
1322     OUTSTREAM << getFixLengthString("DATE      ", datelength+1, ' ', true);
1323     if (getFlag(clflags, "show-handles"))
1324     {
1325         OUTSTREAM << " ";
1326         OUTSTREAM << "   HANDLE";
1327     }
1328     OUTSTREAM << " ";
1329     OUTSTREAM << "NAME";
1330     OUTSTREAM << endl;
1331 }
1332 
dumpNodeSummary(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,bool humanreadable,const char * title)1333 void MegaCmdExecuter::dumpNodeSummary(MegaNode *n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, bool humanreadable, const char *title)
1334 {
1335     if (!title && !( title = n->getName()))
1336     {
1337         title = "CRYPTO_ERROR";
1338     }
1339 
1340     switch (n->getType())
1341     {
1342     case MegaNode::TYPE_FILE:
1343         OUTSTREAM << "-";
1344         break;
1345     case MegaNode::TYPE_FOLDER:
1346         OUTSTREAM << "d";
1347         break;
1348     case MegaNode::TYPE_ROOT:
1349         OUTSTREAM << "r";
1350         break;
1351     case MegaNode::TYPE_INCOMING:
1352         OUTSTREAM << "i";
1353         break;
1354     case MegaNode::TYPE_RUBBISH:
1355         OUTSTREAM << "b";
1356         break;
1357     default:
1358         OUTSTREAM << "x";
1359         break;
1360     }
1361 
1362     if (UNDEF != n->getPublicHandle())
1363     {
1364         OUTSTREAM << "e";
1365         if (n->getExpirationTime())
1366         {
1367             OUTSTREAM << "t";
1368         }
1369         else
1370         {
1371             OUTSTREAM << "p";
1372         }
1373     }
1374     else
1375     {
1376         OUTSTREAM << "--";
1377     }
1378 
1379     if (n->isShared())
1380     {
1381         OUTSTREAM << "s";
1382     }
1383     else if (n->isInShare())
1384     {
1385         OUTSTREAM << "i";
1386     }
1387     else
1388     {
1389         OUTSTREAM << "-";
1390     }
1391 
1392     OUTSTREAM << " ";
1393 
1394     if (n->isFile())
1395     {
1396         MegaNodeList *versionNodes = api->getVersions(n);
1397         int nversions = versionNodes ? versionNodes->size() : 0;
1398         if (nversions > 999)
1399         {
1400             OUTSTREAM << getFixLengthString(">999", 4, ' ', true);
1401         }
1402         else
1403         {
1404             OUTSTREAM << getFixLengthString(SSTR(nversions), 4, ' ', true);
1405         }
1406 
1407         delete versionNodes;
1408     }
1409     else
1410     {
1411         OUTSTREAM << getFixLengthString("-", 4, ' ', true);
1412     }
1413 
1414     OUTSTREAM << " ";
1415 
1416     if (n->isFile())
1417     {
1418         if (humanreadable)
1419         {
1420             OUTSTREAM << getFixLengthString(sizeToText(n->getSize()), 10, ' ', true);
1421         }
1422         else
1423         {
1424             OUTSTREAM << getFixLengthString(SSTR(n->getSize()), 10, ' ', true);
1425         }
1426     }
1427     else
1428     {
1429         OUTSTREAM << getFixLengthString("-", 10, ' ', true);
1430     }
1431 
1432     if (n->isFile())
1433     {
1434         OUTSTREAM << " " << getReadableTime(n->getModificationTime(), timeFormat);
1435     }
1436     else
1437     {
1438         OUTSTREAM << " " << getReadableTime(n->getCreationTime(), timeFormat);
1439     }
1440 
1441     if (getFlag(clflags, "show-handles"))
1442     {
1443         std::unique_ptr<char []> handle {api->handleToBase64(n->getHandle())};
1444         OUTSTREAM << " H:" << handle.get();
1445     }
1446 
1447     OUTSTREAM << " " << title;
1448     OUTSTREAM << endl;
1449 }
1450 
1451 
1452 
1453 #ifdef ENABLE_BACKUPS
1454 
createOrModifyBackup(string local,string remote,string speriod,int numBackups)1455 void MegaCmdExecuter::createOrModifyBackup(string local, string remote, string speriod, int numBackups)
1456 {
1457     LocalPath locallocal = LocalPath::fromPath(local, *fsAccessCMD);
1458     std::unique_ptr<FileAccess> fa = fsAccessCMD->newfileaccess();
1459     if (!fa->isfolder(locallocal))
1460     {
1461         setCurrentOutCode(MCMD_NOTFOUND);
1462         LOG_err << "Local path must be an existing folder: " << local;
1463         return;
1464     }
1465 
1466 
1467     int64_t period = -1;
1468 
1469     if (!speriod.size())
1470     {
1471         MegaBackup *backup = api->getBackupByPath(local.c_str());
1472         if (!backup)
1473         {
1474             backup = api->getBackupByTag(toInteger(local, -1));
1475         }
1476         if (backup)
1477         {
1478             speriod = backup->getPeriodString();
1479             if (!speriod.size())
1480             {
1481                 period = backup->getPeriod();
1482             }
1483             delete backup;
1484         }
1485         else
1486         {
1487             setCurrentOutCode(MCMD_EARGS);
1488             LOG_err << "      " << getUsageStr("backup");
1489             return;
1490         }
1491     }
1492     if (speriod.find(" ") == string::npos && period == -1)
1493     {
1494         period = 10 * getTimeStampAfter(0,speriod);
1495         speriod = "";
1496     }
1497 
1498     if (numBackups == -1)
1499     {
1500         MegaBackup *backup = api->getBackupByPath(local.c_str());
1501         if (!backup)
1502         {
1503             backup = api->getBackupByTag(toInteger(local, -1));
1504         }
1505         if (backup)
1506         {
1507             numBackups = backup->getMaxBackups();
1508             delete backup;
1509         }
1510     }
1511     if (numBackups == -1)
1512     {
1513         setCurrentOutCode(MCMD_EARGS);
1514         LOG_err << "      " << getUsageStr("backup");
1515         return;
1516     }
1517 
1518     MegaNode *n = NULL;
1519     if (remote.size())
1520     {
1521         n = nodebypath(remote.c_str());
1522     }
1523     else
1524     {
1525         MegaBackup *backup = api->getBackupByPath(local.c_str());
1526         if (!backup)
1527         {
1528             backup = api->getBackupByTag(toInteger(local, -1));
1529         }
1530         if (backup)
1531         {
1532             n = api->getNodeByHandle(backup->getMegaHandle());
1533             delete backup;
1534         }
1535     }
1536 
1537     if (n)
1538     {
1539         if (n->getType() != MegaNode::TYPE_FOLDER)
1540         {
1541             setCurrentOutCode(MCMD_INVALIDTYPE);
1542             LOG_err << remote << " must be a valid folder";
1543         }
1544         else
1545         {
1546             if (establishBackup(local, n, period, speriod, numBackups) )
1547             {
1548                 mtxBackupsMap.lock();
1549                 ConfigurationManager::saveBackups(&ConfigurationManager::configuredBackups);
1550                 mtxBackupsMap.unlock();
1551                 OUTSTREAM << "Backup established: " << local << " into " << remote << " period="
1552                           << ((period != -1)?getReadablePeriod(period/10):"\""+speriod+"\"")
1553                           << " Number-of-Backups=" << numBackups << endl;
1554             }
1555         }
1556         delete n;
1557     }
1558     else
1559     {
1560         setCurrentOutCode(MCMD_NOTFOUND);
1561         LOG_err << remote << " not found";
1562     }
1563 }
1564 #endif
1565 
printTreeSuffix(int depth,vector<bool> & lastleaf)1566 void MegaCmdExecuter::printTreeSuffix(int depth, vector<bool> &lastleaf)
1567 {
1568 #ifdef _WIN32
1569     const wchar_t *c0 = L" ";
1570     const wchar_t *c1 = L"\u2502";
1571     const wchar_t *c2 = L"\u2514";
1572     const wchar_t *c3 = L"\u251c";
1573     const wchar_t *c4 = L"\u2500\u2500";
1574 #else
1575     const char *c0 = " ";
1576     const char *c1 = "\u2502";
1577     const char *c2 = "\u2514";
1578     const char *c3 = "\u251c";
1579     const char *c4 = "\u2500\u2500";
1580 #endif
1581     for (int i = 0; i < depth-1; i++)
1582     {
1583         OUTSTREAM << (lastleaf.at(i)?c0:c1) << "   ";
1584     }
1585     if (lastleaf.size())
1586     {
1587         OUTSTREAM << (lastleaf.back()?c2:c3) << c4 << " ";
1588     }
1589 }
1590 
dumptree(MegaNode * n,bool treelike,vector<bool> & lastleaf,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,int recurse,int extended_info,bool showversions,int depth,string pathRelativeTo)1591 void MegaCmdExecuter::dumptree(MegaNode* n, bool treelike, vector<bool> &lastleaf, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, int recurse, int extended_info, bool showversions, int depth, string pathRelativeTo)
1592 {
1593     if (depth || ( n->getType() == MegaNode::TYPE_FILE ))
1594     {
1595         if (treelike) printTreeSuffix(depth, lastleaf);
1596 
1597         if (pathRelativeTo != "NULL")
1598         {
1599             if (!n->getName())
1600             {
1601                 dumpNode(n, timeFormat, clflags, cloptions, extended_info, showversions, treelike?0:depth, "CRYPTO_ERROR");
1602             }
1603             else
1604             {
1605                 char * nodepath = api->getNodePath(n);
1606 
1607                 char *pathToShow = NULL;
1608                 if (pathRelativeTo != "")
1609                 {
1610                     pathToShow = strstr(nodepath, pathRelativeTo.c_str());
1611                 }
1612 
1613                 if (pathToShow == nodepath)     //found at beginning
1614                 {
1615                     pathToShow += pathRelativeTo.size();
1616                     if (( *pathToShow == '/' ) && ( pathRelativeTo != "/" ))
1617                     {
1618                         pathToShow++;
1619                     }
1620                 }
1621                 else
1622                 {
1623                     pathToShow = nodepath;
1624                 }
1625 
1626                 dumpNode(n, timeFormat, clflags, cloptions, extended_info, showversions, treelike?0:depth, pathToShow);
1627 
1628                 delete []nodepath;
1629             }
1630         }
1631         else
1632         {
1633                 dumpNode(n, timeFormat, clflags, cloptions, extended_info, showversions, treelike?0:depth);
1634         }
1635 
1636         if (!recurse && depth)
1637         {
1638             return;
1639         }
1640     }
1641 
1642     if (n->getType() != MegaNode::TYPE_FILE)
1643     {
1644         MegaNodeList* children = api->getChildren(n);
1645         if (children)
1646         {
1647             for (int i = 0; i < children->size(); i++)
1648             {
1649                 vector<bool> lfs = lastleaf;
1650                 lfs.push_back(i==(children->size()-1));
1651                 dumptree(children->get(i), treelike, lfs, timeFormat, clflags, cloptions, recurse, extended_info, showversions, depth + 1);
1652             }
1653 
1654             delete children;
1655         }
1656     }
1657 }
1658 
dumpTreeSummary(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,int recurse,bool show_versions,int depth,bool humanreadable,string pathRelativeTo)1659 void MegaCmdExecuter::dumpTreeSummary(MegaNode *n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, int recurse, bool show_versions, int depth, bool humanreadable, string pathRelativeTo)
1660 {
1661     char * nodepath = api->getNodePath(n);
1662 
1663     string scryptoerror = "CRYPTO_ERROR";
1664 
1665     char *pathToShow = NULL;
1666     if (pathRelativeTo != "")
1667     {
1668         pathToShow = strstr(nodepath, pathRelativeTo.c_str());
1669     }
1670 
1671     if (pathToShow == nodepath) //found at beginning
1672     {
1673         pathToShow += pathRelativeTo.size();
1674         if (( *pathToShow == '/' ) && ( pathRelativeTo != "/" ))
1675         {
1676             pathToShow++;
1677         }
1678     }
1679     else
1680     {
1681         pathToShow = nodepath;
1682     }
1683 
1684     if (!pathToShow && !( pathToShow = (char *)n->getName()))
1685     {
1686         pathToShow = (char *)scryptoerror.c_str();
1687     }
1688 
1689     if (n->getType() != MegaNode::TYPE_FILE)
1690     {
1691         MegaNodeList* children = api->getChildren(n);
1692         if (children)
1693         {
1694             if (depth)
1695             {
1696                 OUTSTREAM << endl;
1697             }
1698 
1699             if (recurse)
1700             {
1701                 OUTSTREAM << pathToShow << ":" << endl;
1702             }
1703 
1704             for (int i = 0; i < children->size(); i++)
1705             {
1706                 dumpNodeSummary(children->get(i), timeFormat, clflags, cloptions, humanreadable);
1707             }
1708 
1709             if (show_versions)
1710             {
1711                 for (int i = 0; i < children->size(); i++)
1712                 {
1713                     MegaNode *c = children->get(i);
1714 
1715                     MegaNodeList *vers = api->getVersions(c);
1716                     if (vers &&  vers->size() > 1)
1717                     {
1718                         OUTSTREAM << endl << "Versions of " << pathToShow << "/" << c->getName() << ":" << endl;
1719 
1720                         for (int i = 0; i < vers->size(); i++)
1721                         {
1722                             dumpNodeSummary(vers->get(i), timeFormat, clflags, cloptions, humanreadable);
1723                         }
1724                     }
1725                     delete vers;
1726                 }
1727             }
1728 
1729             if (recurse)
1730             {
1731                 for (int i = 0; i < children->size(); i++)
1732                 {
1733                     MegaNode *c = children->get(i);
1734                     dumpTreeSummary(c, timeFormat, clflags, cloptions, recurse, show_versions, depth + 1, humanreadable);
1735                 }
1736             }
1737             delete children;
1738         }
1739     }
1740     else // file
1741     {
1742         if (!depth)
1743         {
1744 
1745             dumpNodeSummary(n, timeFormat, clflags, cloptions, humanreadable);
1746 
1747             if (show_versions)
1748             {
1749                 MegaNodeList *vers = api->getVersions(n);
1750                 if (vers &&  vers->size() > 1)
1751                 {
1752                     OUTSTREAM << endl << "Versions of " << pathToShow << ":" << endl;
1753 
1754                     for (int i = 0; i < vers->size(); i++)
1755                     {
1756                         string nametoshow = n->getName()+string("#")+SSTR(vers->get(i)->getModificationTime());
1757                         dumpNodeSummary(vers->get(i), timeFormat, clflags, cloptions, humanreadable, nametoshow.c_str() );
1758                     }
1759                 }
1760                 delete vers;
1761             }
1762         }
1763 
1764     }
1765     delete []nodepath;
1766 }
1767 
1768 
1769 /**
1770  * @brief Tests if a path can be created
1771  * @param path
1772  * @return
1773  */
TestCanWriteOnContainingFolder(string * path)1774 bool MegaCmdExecuter::TestCanWriteOnContainingFolder(string *path)
1775 {
1776 #ifdef _WIN32
1777     replaceAll(*path,"/","\\");
1778 #endif
1779     string localpath;
1780     fsAccessCMD->path2local(path, &localpath);
1781     size_t lastpart = fsAccessCMD->lastpartlocal(&localpath);
1782     string containingFolder = ".";
1783     if (lastpart)
1784     {
1785         string firstpartlocal(localpath, 0, lastpart - fsAccessCMD->localseparator.size());
1786         fsAccessCMD->local2path(&firstpartlocal, &containingFolder);
1787     }
1788 
1789     LocalPath localcontainingFolder = LocalPath::fromPath(containingFolder, *fsAccessCMD);
1790     std::unique_ptr<FileAccess> fa = fsAccessCMD->newfileaccess();
1791     if (!fa->isfolder(localcontainingFolder))
1792     {
1793         setCurrentOutCode(MCMD_INVALIDTYPE);
1794         LOG_err << containingFolder << " is not a valid Download Folder";
1795         return false;
1796     }
1797 
1798     if (!canWrite(containingFolder))
1799     {
1800         setCurrentOutCode(MCMD_NOTPERMITTED);
1801         LOG_err << "Write not allowed in " << containingFolder;
1802         return false;
1803     }
1804     return true;
1805 }
1806 
getPcrByContact(string contactEmail)1807 MegaContactRequest * MegaCmdExecuter::getPcrByContact(string contactEmail)
1808 {
1809     MegaContactRequestList *icrl = api->getIncomingContactRequests();
1810     if (icrl)
1811     {
1812         for (int i = 0; i < icrl->size(); i++)
1813         {
1814             if (icrl->get(i)->getSourceEmail() == contactEmail)
1815             {
1816                 return icrl->get(i);
1817 
1818                 delete icrl;
1819             }
1820         }
1821 
1822         delete icrl;
1823     }
1824     return NULL;
1825 }
1826 
getDisplayPath(string givenPath,MegaNode * n)1827 string MegaCmdExecuter::getDisplayPath(string givenPath, MegaNode* n)
1828 {
1829     char * pathToNode = api->getNodePath(n);
1830     if (!pathToNode)
1831     {
1832         LOG_err << " GetNodePath failed for: " << givenPath;
1833         return givenPath;
1834     }
1835 
1836     char * pathToShow = pathToNode;
1837 
1838     string pathRelativeTo = "NULL";
1839     string cwpath = getCurrentPath();
1840     string toret="";
1841 
1842 
1843     if (givenPath.find('/') == 0 )
1844     {
1845         pathRelativeTo = "";
1846     }
1847     else if(givenPath.find("../") == 0 || givenPath.find("./") == 0 )
1848     {
1849         pathRelativeTo = "";
1850         MegaNode *n = api->getNodeByHandle(cwd);
1851         while(true)
1852         {
1853             if(givenPath.find("./") == 0)
1854             {
1855                 givenPath=givenPath.substr(2);
1856                 toret+="./";
1857                 if (n)
1858                 {
1859                     char *npath = api->getNodePath(n);
1860                     pathRelativeTo = string(npath);
1861                     delete []npath;
1862                 }
1863                 return toret;
1864 
1865             }
1866             else if(givenPath.find("../") == 0)
1867             {
1868                 givenPath=givenPath.substr(3);
1869                 toret+="../";
1870                 MegaNode *aux = n;
1871                 if (n)
1872                 {
1873                     n=api->getNodeByHandle(n->getParentHandle());
1874                 }
1875                 delete aux;
1876                 if (n)
1877                 {
1878                     char *npath = api->getNodePath(n);
1879                     pathRelativeTo = string(npath);
1880                     delete []npath;
1881                 }
1882             }
1883             else
1884             {
1885                 break;
1886             }
1887         }
1888         delete n;
1889     }
1890     else
1891     {
1892         if (cwpath == "/") //TODO: //bin /X:share ...
1893         {
1894             pathRelativeTo = cwpath;
1895         }
1896         else
1897         {
1898             pathRelativeTo = cwpath + "/";
1899         }
1900     }
1901 
1902     if (( "" == givenPath ) && !strcmp(pathToNode, cwpath.c_str()))
1903     {
1904         assert(strlen(pathToNode)>0);
1905         pathToNode[0] = '.';
1906         pathToNode[1] = '\0';
1907     }
1908 
1909     if (pathRelativeTo != "")
1910     {
1911         pathToShow = strstr(pathToNode, pathRelativeTo.c_str());
1912     }
1913 
1914     if (pathToShow == pathToNode)     //found at beginning
1915     {
1916         if (strcmp(pathToNode, "/"))
1917         {
1918             pathToShow += pathRelativeTo.size();
1919         }
1920     }
1921     else
1922     {
1923         pathToShow = pathToNode;
1924     }
1925 
1926     toret+=pathToShow;
1927     delete []pathToNode;
1928     return toret;
1929 }
1930 
dumpListOfExported(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,string givenPath)1931 int MegaCmdExecuter::dumpListOfExported(MegaNode* n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, string givenPath)
1932 {
1933     int toret = 0;
1934     vector<MegaNode *> listOfExported;
1935     processTree(n, includeIfIsExported, (void*)&listOfExported);
1936     for (std::vector< MegaNode * >::iterator it = listOfExported.begin(); it != listOfExported.end(); ++it)
1937     {
1938         MegaNode * n = *it;
1939         if (n)
1940         {
1941             string pathToShow = getDisplayPath(givenPath, n);
1942             dumpNode(n, timeFormat, clflags, cloptions, 2, 1, false, pathToShow.c_str());
1943 
1944             delete n;
1945         }
1946     }
1947     toret = int(listOfExported.size());
1948     listOfExported.clear();
1949     return toret;
1950 }
1951 
1952 /**
1953  * @brief listnodeshares For a node, it prints all the shares it has
1954  * @param n
1955  * @param name
1956  */
listnodeshares(MegaNode * n,string name)1957 void MegaCmdExecuter::listnodeshares(MegaNode* n, string name)
1958 {
1959     MegaShareList* outShares = api->getOutShares(n);
1960     if (outShares)
1961     {
1962         for (int i = 0; i < outShares->size(); i++)
1963         {
1964             OUTSTREAM << (name.size() ? name : n->getName());
1965 
1966             if (outShares->get(i))
1967             {
1968                 OUTSTREAM << ", shared with " << outShares->get(i)->getUser() << " (" << getAccessLevelStr(outShares->get(i)->getAccess()) << ")"
1969                           << endl;
1970             }
1971             else
1972             {
1973                 OUTSTREAM << ", shared as exported folder link" << endl;
1974             }
1975         }
1976 
1977         delete outShares;
1978     }
1979 }
1980 
dumpListOfShared(MegaNode * n,string givenPath)1981 void MegaCmdExecuter::dumpListOfShared(MegaNode* n, string givenPath)
1982 {
1983     vector<MegaNode *> listOfShared;
1984     processTree(n, includeIfIsShared, (void*)&listOfShared);
1985     if (!listOfShared.size())
1986     {
1987         setCurrentOutCode(MCMD_NOTFOUND);
1988         LOG_err << "No shared found for given path: " << givenPath;
1989     }
1990     for (std::vector< MegaNode * >::iterator it = listOfShared.begin(); it != listOfShared.end(); ++it)
1991     {
1992         MegaNode * n = *it;
1993         if (n)
1994         {
1995             string pathToShow = getDisplayPath(givenPath, n);
1996             //dumpNode(n, 3, 1,pathToShow.c_str());
1997             listnodeshares(n, pathToShow);
1998 
1999             delete n;
2000         }
2001     }
2002 
2003     listOfShared.clear();
2004 }
2005 
2006 //includes pending and normal shares
dumpListOfAllShared(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,string givenPath)2007 void MegaCmdExecuter::dumpListOfAllShared(MegaNode* n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, string givenPath)
2008 {
2009     vector<MegaNode *> listOfShared;
2010     processTree(n, includeIfIsSharedOrPendingOutShare, (void*)&listOfShared);
2011     for (std::vector< MegaNode * >::iterator it = listOfShared.begin(); it != listOfShared.end(); ++it)
2012     {
2013         MegaNode * n = *it;
2014         if (n)
2015         {
2016             string pathToShow = getDisplayPath(givenPath, n);
2017             dumpNode(n, timeFormat, clflags, cloptions, 3, false, 1, pathToShow.c_str());
2018             //notice: some nodes may be dumped twice
2019 
2020             delete n;
2021         }
2022     }
2023 
2024     listOfShared.clear();
2025 }
2026 
dumpListOfPendingShares(MegaNode * n,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,string givenPath)2027 void MegaCmdExecuter::dumpListOfPendingShares(MegaNode* n, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, string givenPath)
2028 {
2029     vector<MegaNode *> listOfShared;
2030     processTree(n, includeIfIsPendingOutShare, (void*)&listOfShared);
2031 
2032     for (std::vector< MegaNode * >::iterator it = listOfShared.begin(); it != listOfShared.end(); ++it)
2033     {
2034         MegaNode * n = *it;
2035         if (n)
2036         {
2037             string pathToShow = getDisplayPath(givenPath, n);
2038             dumpNode(n, timeFormat, clflags, cloptions, 3, false, 1, pathToShow.c_str());
2039 
2040             delete n;
2041         }
2042     }
2043 
2044     listOfShared.clear();
2045 }
2046 
2047 
loginWithPassword(char * password)2048 void MegaCmdExecuter::loginWithPassword(char *password)
2049 {
2050     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
2051     sandboxCMD->resetSandBox();
2052     api->login(login.c_str(), password, megaCmdListener);
2053     actUponLogin(megaCmdListener);
2054     delete megaCmdListener;
2055 }
2056 
changePassword(const char * newpassword,string pin2fa)2057 void MegaCmdExecuter::changePassword(const char *newpassword, string pin2fa)
2058 {
2059     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
2060     api->changePassword(NULL, newpassword, megaCmdListener);
2061     megaCmdListener->wait();
2062 
2063     if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EMFAREQUIRED)
2064     {
2065         MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL);
2066         if (!pin2fa.size())
2067         {
2068             pin2fa = askforUserResponse("Enter the code generated by your authentication app: ");
2069         }
2070         LOG_verbose << " Using confirmation pin: " << pin2fa;
2071         api->multiFactorAuthChangePassword(NULL, newpassword, pin2fa.c_str(), megaCmdListener2);
2072         megaCmdListener2->wait();
2073         if (megaCmdListener2->getError()->getErrorCode() == MegaError::API_EFAILED)
2074         {
2075             setCurrentOutCode(megaCmdListener2->getError()->getErrorCode());
2076             LOG_err << "Password unchanged: invalid authentication code";
2077         }
2078         else if (checkNoErrors(megaCmdListener2->getError(), "change password with auth code"))
2079         {
2080             OUTSTREAM << "Password changed succesfully" << endl;
2081         }
2082 
2083     }
2084     else if (!checkNoErrors(megaCmdListener->getError(), "change password"))
2085     {
2086         LOG_err << "Please, ensure you enter the old password correctly";
2087     }
2088     else
2089     {
2090         OUTSTREAM << "Password changed succesfully" << endl;
2091     }
2092     delete megaCmdListener;
2093 }
2094 
str_localtime(char s[32],::mega::m_time_t t)2095 void str_localtime(char s[32], ::mega::m_time_t t)
2096 {
2097     struct tm tms;
2098     strftime(s, 32, "%c", m_localtime(t, &tms));
2099 }
2100 
2101 
actUponGetExtendedAccountDetails(SynchronousRequestListener * srl,int timeout)2102 void MegaCmdExecuter::actUponGetExtendedAccountDetails(SynchronousRequestListener *srl, int timeout)
2103 {
2104     if (timeout == -1)
2105     {
2106         srl->wait();
2107     }
2108     else
2109     {
2110         int trywaitout = srl->trywait(timeout);
2111         if (trywaitout)
2112         {
2113             LOG_err << "GetExtendedAccountDetails took too long, it may have failed. No further actions performed";
2114             return;
2115         }
2116     }
2117 
2118     if (checkNoErrors(srl->getError(), "failed to GetExtendedAccountDetails"))
2119     {
2120         char timebuf[32], timebuf2[32];
2121 
2122         LOG_verbose << "actUponGetExtendedAccountDetails ok";
2123 
2124         MegaAccountDetails *details = srl->getRequest()->getMegaAccountDetails();
2125         if (details)
2126         {
2127             OUTSTREAM << "    Available storage: "
2128                       << getFixLengthString(sizeToText(details->getStorageMax()), 9, ' ', true)
2129                       << "ytes" << endl;
2130             MegaNode *n = api->getRootNode();
2131             if (n)
2132             {
2133                 OUTSTREAM << "        In ROOT:      "
2134                           << getFixLengthString(sizeToText(details->getStorageUsed(n->getHandle())), 9, ' ', true) << "ytes in "
2135                           << getFixLengthString(SSTR(details->getNumFiles(n->getHandle())),5,' ',true) << " file(s) and "
2136                           << getFixLengthString(SSTR(details->getNumFolders(n->getHandle())),5,' ',true) << " folder(s)" << endl;
2137                 delete n;
2138             }
2139 
2140             n = api->getInboxNode();
2141             if (n)
2142             {
2143                 OUTSTREAM << "        In INBOX:     "
2144                           << getFixLengthString( sizeToText(details->getStorageUsed(n->getHandle())), 9, ' ', true ) << "ytes in "
2145                           << getFixLengthString(SSTR(details->getNumFiles(n->getHandle())),5,' ',true) << " file(s) and "
2146                           << getFixLengthString(SSTR(details->getNumFolders(n->getHandle())),5,' ',true) << " folder(s)" << endl;
2147                 delete n;
2148             }
2149 
2150             n = api->getRubbishNode();
2151             if (n)
2152             {
2153                 OUTSTREAM << "        In RUBBISH:   "
2154                           << getFixLengthString(sizeToText(details->getStorageUsed(n->getHandle())), 9, ' ', true) << "ytes in "
2155                           << getFixLengthString(SSTR(details->getNumFiles(n->getHandle())),5,' ',true) << " file(s) and "
2156                           << getFixLengthString(SSTR(details->getNumFolders(n->getHandle())),5,' ',true) << " folder(s)" << endl;
2157                 delete n;
2158             }
2159 
2160             long long usedinVersions = details->getVersionStorageUsed();
2161 
2162             OUTSTREAM << "        Total size taken up by file versions: "
2163                       << getFixLengthString(sizeToText(usedinVersions), 12, ' ', true) << "ytes"<< endl;
2164 
2165 
2166             MegaNodeList *inshares = api->getInShares();
2167             if (inshares)
2168             {
2169                 for (int i = 0; i < inshares->size(); i++)
2170                 {
2171                     n = inshares->get(i);
2172                     OUTSTREAM << "        In INSHARE " << n->getName() << ": "
2173                               << getFixLengthString(sizeToText(details->getStorageUsed(n->getHandle())), 9, ' ', true) << "ytes in "
2174                               << getFixLengthString(SSTR(details->getNumFiles(n->getHandle())),5,' ',true) << " file(s) and "
2175                               << getFixLengthString(SSTR(details->getNumFolders(n->getHandle())),5,' ',true) << " folder(s)" << endl;
2176                 }
2177             }
2178             delete inshares;
2179 
2180             OUTSTREAM << "    Pro level: " << details->getProLevel() << endl;
2181             if (details->getProLevel())
2182             {
2183                 if (details->getProExpiration())
2184                 {
2185                     str_localtime(timebuf, details->getProExpiration());
2186                     OUTSTREAM << "        " << "Pro expiration date: " << timebuf << endl;
2187                 }
2188             }
2189             char * subscriptionMethod = details->getSubscriptionMethod();
2190             OUTSTREAM << "    Subscription type: " << subscriptionMethod << endl;
2191             delete []subscriptionMethod;
2192             OUTSTREAM << "    Account balance:" << endl;
2193             for (int i = 0; i < details->getNumBalances(); i++)
2194             {
2195                 MegaAccountBalance * balance = details->getBalance(i);
2196                 char sbalance[50];
2197                 sprintf(sbalance, "    Balance: %.3s %.02f", balance->getCurrency(), balance->getAmount());
2198                 OUTSTREAM << "    " << "Balance: " << sbalance << endl;
2199             }
2200 
2201             if (details->getNumPurchases())
2202             {
2203                 OUTSTREAM << "Purchase history:" << endl;
2204                 for (int i = 0; i < details->getNumPurchases(); i++)
2205                 {
2206                     MegaAccountPurchase *purchase = details->getPurchase(i);
2207 
2208                     char spurchase[150];
2209 
2210                     str_localtime(timebuf, purchase->getTimestamp());
2211                     sprintf(spurchase, "ID: %.11s Time: %s Amount: %.3s %.02f Payment method: %d\n",
2212                         purchase->getHandle(), timebuf, purchase->getCurrency(), purchase->getAmount(), purchase->getMethod());
2213                     OUTSTREAM << "    " << spurchase << endl;
2214                 }
2215             }
2216 
2217             if (details->getNumTransactions())
2218             {
2219                 OUTSTREAM << "Transaction history:" << endl;
2220                 for (int i = 0; i < details->getNumTransactions(); i++)
2221                 {
2222                     MegaAccountTransaction *transaction = details->getTransaction(i);
2223                     char stransaction[100];
2224                     str_localtime(timebuf, transaction->getTimestamp());
2225                     sprintf(stransaction, "ID: %.11s Time: %s Amount: %.3s %.02f\n",
2226                         transaction->getHandle(), timebuf, transaction->getCurrency(), transaction->getAmount());
2227                     OUTSTREAM << "    " << stransaction << endl;
2228                 }
2229             }
2230 
2231             int alive_sessions = 0;
2232             OUTSTREAM << "Current Active Sessions:" << endl;
2233             char sdetails[500];
2234             for (int i = 0; i < details->getNumSessions(); i++)
2235             {
2236                 MegaAccountSession * session = details->getSession(i);
2237                 if (session->isAlive())
2238                 {
2239                     sdetails[0]='\0';
2240                     str_localtime(timebuf, session->getCreationTimestamp());
2241                     str_localtime(timebuf2, session->getMostRecentUsage());
2242 
2243                     char *sid = api->userHandleToBase64(session->getHandle());
2244 
2245                     if (session->isCurrent())
2246                     {
2247                         sprintf(sdetails, "    * Current Session\n");
2248                     }
2249 
2250                     char * userAgent = session->getUserAgent();
2251                     char * country = session->getCountry();
2252                     char * ip = session->getIP();
2253 
2254                     sprintf(sdetails, "%s    Session ID: %s\n    Session start: %s\n    Most recent activity: %s\n    IP: %s\n    Country: %.2s\n    User-Agent: %s\n    -----\n",
2255                     sdetails,
2256                     sid,
2257                     timebuf,
2258                     timebuf2,
2259                     ip,
2260                     country,
2261                     userAgent
2262                     );
2263                     OUTSTREAM << sdetails;
2264                     delete []sid;
2265                     delete []userAgent;
2266                     delete []country;
2267                     delete []ip;
2268                     alive_sessions++;
2269                 }
2270                 delete session;
2271             }
2272 
2273             if (alive_sessions)
2274             {
2275                 OUTSTREAM << alive_sessions << " active sessions opened" << endl;
2276             }
2277             delete details;
2278         }
2279     }
2280 }
2281 
actUponFetchNodes(MegaApi * api,SynchronousRequestListener * srl,int timeout)2282 bool MegaCmdExecuter::actUponFetchNodes(MegaApi *api, SynchronousRequestListener *srl, int timeout)
2283 {
2284     if (timeout == -1)
2285     {
2286         srl->wait();
2287     }
2288     else
2289     {
2290         int trywaitout = srl->trywait(timeout);
2291         if (trywaitout)
2292         {
2293             LOG_err << "Fetch nodes took too long, it may have failed. No further actions performed";
2294             return false;
2295         }
2296     }
2297 
2298     if (srl->getError()->getErrorCode() == MegaError::API_EBLOCKED)
2299     {
2300         LOG_verbose << " EBLOCKED after fetch nodes. quering for reason...";
2301 
2302         MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
2303         api->whyAmIBlocked(megaCmdListener); //This shall cause event that sets reasonblocked
2304         megaCmdListener->wait();
2305         auto reason = sandboxCMD->getReasonblocked();
2306         LOG_warn << "Failed to fetch nodes. Account blocked." <<( reason.empty()?"":" Reason: "+reason);
2307     }
2308     else if (checkNoErrors(srl->getError(), "fetch nodes"))
2309     {
2310         LOG_verbose << "actUponFetchNodes ok";
2311 
2312         return true;
2313     }
2314     return false;
2315 }
2316 
actUponLogin(SynchronousRequestListener * srl,int timeout)2317 int MegaCmdExecuter::actUponLogin(SynchronousRequestListener *srl, int timeout)
2318 {
2319     if (timeout == -1)
2320     {
2321         srl->wait();
2322     }
2323     else
2324     {
2325         int trywaitout = srl->trywait(timeout);
2326         if (trywaitout)
2327         {
2328             LOG_err << "Login took too long, it may have failed. No further actions performed";
2329             return MegaError::API_EAGAIN;
2330         }
2331     }
2332 
2333     LOG_debug << "actUponLogin login";
2334     setCurrentOutCode(srl->getError()->getErrorCode());
2335 
2336     if (srl->getRequest()->getEmail())
2337     {
2338         LOG_debug << "actUponLogin login email: " << srl->getRequest()->getEmail();
2339     }
2340 
2341 
2342     if (srl->getError()->getErrorCode() == MegaError::API_EMFAREQUIRED) // failed to login
2343     {
2344         return srl->getError()->getErrorCode();
2345     }
2346 
2347 
2348     if (srl->getError()->getErrorCode() == MegaError::API_ENOENT) // failed to login
2349     {
2350         LOG_err << "Login failed: invalid email or password";
2351     }
2352     else if (srl->getError()->getErrorCode() == MegaError::API_EFAILED)
2353     {
2354         LOG_err << "Login failed: incorrect authentication";
2355     }
2356     else if (srl->getError()->getErrorCode() == MegaError::API_EINCOMPLETE)
2357     {
2358         LOG_err << "Login failed: unconfirmed account. Please confirm your account";
2359     }
2360     else if (checkNoErrors(srl->getError(), "Login")) //login success:
2361     {
2362         LOG_debug << "Login correct ... " << (srl->getRequest()->getEmail()?srl->getRequest()->getEmail():"");
2363         /* Restoring configured values */
2364         session = srl->getApi()->dumpSession();
2365         ConfigurationManager::saveSession(session);
2366         mtxSyncMap.lock();
2367         ConfigurationManager::loadsyncs();
2368         mtxSyncMap.unlock();
2369 #ifdef ENABLE_BACKUPS
2370         mtxBackupsMap.lock();
2371         ConfigurationManager::loadbackups();
2372         mtxBackupsMap.unlock();
2373 #endif
2374 
2375         ConfigurationManager::loadExcludedNames();
2376         ConfigurationManager::loadConfiguration(false);
2377         std::vector<string> vexcludednames(ConfigurationManager::excludedNames.begin(), ConfigurationManager::excludedNames.end());
2378         api->setExcludedNames(&vexcludednames);
2379 
2380         long long maxspeeddownload = ConfigurationManager::getConfigurationValue("maxspeeddownload", -1);
2381         if (maxspeeddownload != -1) api->setMaxDownloadSpeed(maxspeeddownload);
2382         long long maxspeedupload = ConfigurationManager::getConfigurationValue("maxspeedupload", -1);
2383         if (maxspeedupload != -1) api->setMaxUploadSpeed(maxspeedupload);
2384 
2385         api->useHttpsOnly(ConfigurationManager::getConfigurationValue("https", false));
2386         api->disableGfxFeatures(!ConfigurationManager::getConfigurationValue("graphics", true));
2387 
2388 #ifndef _WIN32
2389         string permissionsFiles = ConfigurationManager::getConfigurationSValue("permissionsFiles");
2390         if (permissionsFiles.size())
2391         {
2392             int perms = permissionsFromReadable(permissionsFiles);
2393             if (perms != -1)
2394             {
2395                 api->setDefaultFilePermissions(perms);
2396             }
2397         }
2398         string permissionsFolders = ConfigurationManager::getConfigurationSValue("permissionsFolders");
2399         if (permissionsFolders.size())
2400         {
2401             int perms = permissionsFromReadable(permissionsFolders);
2402             if (perms != -1)
2403             {
2404                 api->setDefaultFolderPermissions(perms);
2405             }
2406         }
2407 #endif
2408 
2409         LOG_info << "Fetching nodes ... ";
2410         MegaApi *api = srl->getApi();
2411         int clientID = static_cast<MegaCmdListener*>(srl)->clientID;
2412 
2413         fetchNodes(api, clientID);
2414     }
2415 
2416 #if defined(_WIN32) || defined(__APPLE__)
2417 
2418     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
2419     srl->getApi()->getLastAvailableVersion("BdARkQSQ",megaCmdListener);
2420     megaCmdListener->wait();
2421 
2422     if (!megaCmdListener->getError())
2423     {
2424         LOG_fatal << "No MegaError at getLastAvailableVersion: ";
2425     }
2426     else if (megaCmdListener->getError()->getErrorCode() != MegaError::API_OK)
2427     {
2428         LOG_debug << "Couldn't get latests available version: " << megaCmdListener->getError()->getErrorString();
2429     }
2430     else
2431     {
2432         if (megaCmdListener->getRequest()->getNumber() != MEGACMD_CODE_VERSION)
2433         {
2434 
2435             OUTSTREAM << "---------------------------------------------------------------------" << endl;
2436             OUTSTREAM << "--        There is a new version available of megacmd: " << getLeftAlignedStr(megaCmdListener->getRequest()->getName(),12) << "--" << endl;
2437             OUTSTREAM << "--        Please, update this one: See \"update --help\".          --" << endl;
2438             OUTSTREAM << "--        Or download the latest from https://mega.nz/cmd          --" << endl;
2439             OUTSTREAM << "---------------------------------------------------------------------" << endl;
2440         }
2441     }
2442     delete megaCmdListener;
2443 
2444     //this goes here in case server is launched directly and thus we ensure that's shown at the beginning
2445     int autoupdate = ConfigurationManager::getConfigurationValue("autoupdate", -1);
2446     bool enabledupdaterhere = false;
2447     if (autoupdate == -1 || autoupdate == 2)
2448     {
2449         OUTSTREAM << "ENABLING AUTOUPDATE BY DEFAULT. You can disable it with \"update --auto=off\"" << endl;
2450         autoupdate = 1;
2451         enabledupdaterhere = true;
2452     }
2453 
2454     if (autoupdate >= 1)
2455     {
2456         startcheckingForUpdates();
2457     }
2458     if (enabledupdaterhere)
2459     {
2460         ConfigurationManager::savePropertyValue("autoupdate", 2); //save to special value to indicate first listener that it is enabled
2461     }
2462 
2463 #endif
2464     return srl->getError()->getErrorCode();
2465 }
2466 
fetchNodes(MegaApi * api,int clientID)2467 void MegaCmdExecuter::fetchNodes(MegaApi *api, int clientID)
2468 {
2469     if (!api) api = this->api;
2470     MegaCmdListener * megaCmdListener = new MegaCmdListener(api, NULL, clientID);
2471     api->fetchNodes(megaCmdListener);
2472     if (!actUponFetchNodes(api, megaCmdListener))
2473     {
2474         //Ideally we should enter an state that indicates that we are not fully logged.
2475         //Specially when the account is blocked
2476         return;
2477     }
2478 
2479     // This is the actual acting upon fetch nodes ended correctly:
2480 
2481     api->enableTransferResumption();
2482 
2483     MegaNode *cwdNode = ( cwd == UNDEF ) ? NULL : api->getNodeByHandle(cwd);
2484     if (( cwd == UNDEF ) || !cwdNode)
2485     {
2486         MegaNode *rootNode = api->getRootNode();
2487         cwd = rootNode->getHandle();
2488         delete rootNode;
2489     }
2490     if (cwdNode)
2491     {
2492         delete cwdNode;
2493     }
2494 
2495     setloginInAtStartup(false); //to enable all commands before giving clients the green light!
2496     informStateListeners("loged:"); // tell the clients login ended, before providing them the first prompt
2497     updateprompt(api);
2498     LOG_debug << " Fetch nodes correctly";
2499 
2500     MegaUser *u = api->getMyUser();
2501     if (u)
2502     {
2503         LOG_info << "Login complete as " << u->getEmail();
2504         delete u;
2505     }
2506 
2507     if (ConfigurationManager::getConfigurationValue("ask4storage", true))
2508     {
2509         ConfigurationManager::savePropertyValue("ask4storage",false);
2510         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
2511         api->getAccountDetails(megaCmdListener);
2512         megaCmdListener->wait();
2513         // we don't call getAccountDetails on startup always: we ask on first login (no "ask4storage") or previous state was STATE_RED | STATE_ORANGE
2514         // if we were green, don't need to ask: if there are changes they will be received via action packet indicating STATE_CHANGE
2515     }
2516 
2517     checkAndInformPSA(NULL); // this needs broacasting in case there's another Shell running.
2518     // no need to enforce, because time since last check should has been restored
2519 
2520 #ifdef ENABLE_BACKUPS
2521     mtxBackupsMap.lock();
2522     if (ConfigurationManager::configuredBackups.size())
2523     {
2524         LOG_info << "Restablishing backups ... ";
2525         unsigned int i=0;
2526         for (map<string, backup_struct *>::iterator itr = ConfigurationManager::configuredBackups.begin();
2527              itr != ConfigurationManager::configuredBackups.end(); ++itr, i++)
2528         {
2529             backup_struct *thebackup = itr->second;
2530 
2531             MegaNode * node = api->getNodeByHandle(thebackup->handle);
2532             if (establishBackup(thebackup->localpath, node, thebackup->period, thebackup->speriod, thebackup->numBackups))
2533             {
2534                 thebackup->failed = false;
2535                 const char *nodepath = api->getNodePath(node);
2536                 LOG_debug << "Succesfully resumed backup: " << thebackup->localpath << " to " << nodepath;
2537                 delete []nodepath;
2538             }
2539             else
2540             {
2541                 thebackup->failed = true;
2542                 char *nodepath = api->getNodePath(node);
2543                 LOG_err << "Failed to resume backup: " << thebackup->localpath << " to " << nodepath;
2544                 delete []nodepath;
2545             }
2546 
2547             delete node;
2548         }
2549 
2550         ConfigurationManager::saveBackups(&ConfigurationManager::configuredBackups);
2551     }
2552     mtxBackupsMap.unlock();
2553 #endif
2554 
2555 #ifdef HAVE_LIBUV
2556     // restart webdav
2557     int port = ConfigurationManager::getConfigurationValue("webdav_port", -1);
2558     if (port != -1)
2559     {
2560         bool localonly = ConfigurationManager::getConfigurationValue("webdav_localonly", -1);
2561         bool tls = ConfigurationManager::getConfigurationValue("webdav_tls", false);
2562         string pathtocert, pathtokey;
2563         pathtocert = ConfigurationManager::getConfigurationSValue("webdav_cert");
2564         pathtokey = ConfigurationManager::getConfigurationSValue("webdav_key");
2565 
2566         api->httpServerEnableFolderServer(true);
2567         if (api->httpServerStart(localonly, port, tls, pathtocert.c_str(), pathtokey.c_str()))
2568         {
2569             list<string> servedpaths = ConfigurationManager::getConfigurationValueList("webdav_served_locations");
2570             bool modified = false;
2571 
2572             for ( std::list<string>::iterator it = servedpaths.begin(); it != servedpaths.end(); ++it)
2573             {
2574                 string pathToServe = *it;
2575                 if (pathToServe.size())
2576                 {
2577                     MegaNode *n = nodebypath(pathToServe.c_str());
2578                     if (n)
2579                     {
2580                         char *l = api->httpServerGetLocalWebDavLink(n);
2581                         char *actualNodePath = api->getNodePath(n);
2582                         LOG_debug << "Serving via webdav: " << actualNodePath << ": " << l;
2583 
2584                         if (pathToServe != actualNodePath)
2585                         {
2586                             it = servedpaths.erase(it);
2587                             servedpaths.insert(it,string(actualNodePath));
2588                             modified = true;
2589                         }
2590                         delete []l;
2591                         delete []actualNodePath;
2592                         delete n;
2593                     }
2594                     else
2595                     {
2596                         LOG_warn << "Could no find location to server via webdav: " << pathToServe;
2597                     }
2598                 }
2599             }
2600             if (modified)
2601             {
2602                 ConfigurationManager::savePropertyValueList("webdav_served_locations", servedpaths);
2603             }
2604 
2605             LOG_info << "Webdav server restored due to saved configuration";
2606         }
2607         else
2608         {
2609             LOG_err << "Failed to initialize WEBDAV server. Ensure the port is free.";
2610         }
2611     }
2612 
2613     //ftp
2614     // restart ftp
2615     int portftp = ConfigurationManager::getConfigurationValue("ftp_port", -1);
2616 
2617     if (portftp != -1)
2618     {
2619         bool localonly = ConfigurationManager::getConfigurationValue("ftp_localonly", -1);
2620         bool tls = ConfigurationManager::getConfigurationValue("ftp_tls", false);
2621         string pathtocert, pathtokey;
2622         pathtocert = ConfigurationManager::getConfigurationSValue("ftp_cert");
2623         pathtokey = ConfigurationManager::getConfigurationSValue("ftp_key");
2624         int dataPortRangeBegin = ConfigurationManager::getConfigurationValue("ftp_port_data_begin", 1500);
2625         int dataPortRangeEnd = ConfigurationManager::getConfigurationValue("ftp_port_data_end", 1500+100);
2626 
2627         if (api->ftpServerStart(localonly, portftp, dataPortRangeBegin, dataPortRangeEnd, tls, pathtocert.c_str(), pathtokey.c_str()))
2628         {
2629             list<string> servedpaths = ConfigurationManager::getConfigurationValueList("ftp_served_locations");
2630             bool modified = false;
2631 
2632             for ( std::list<string>::iterator it = servedpaths.begin(); it != servedpaths.end(); ++it)
2633             {
2634                 string pathToServe = *it;
2635                 if (pathToServe.size())
2636                 {
2637                     MegaNode *n = nodebypath(pathToServe.c_str());
2638                     if (n)
2639                     {
2640                         char *l = api->ftpServerGetLocalLink(n);
2641                         char *actualNodePath = api->getNodePath(n);
2642                         LOG_debug << "Serving via ftp: " << pathToServe << ": " << l << ". Data Channel Port Range: " << dataPortRangeBegin << "-" << dataPortRangeEnd;
2643 
2644                         if (pathToServe != actualNodePath)
2645                         {
2646                             it = servedpaths.erase(it);
2647                             servedpaths.insert(it,string(actualNodePath));
2648                             modified = true;
2649                         }
2650                         delete []l;
2651                         delete []actualNodePath;
2652                         delete n;
2653                     }
2654                     else
2655                     {
2656                         LOG_warn << "Could no find location to server via ftp: " << pathToServe;
2657                     }
2658                 }
2659             }
2660             if (modified)
2661             {
2662                 ConfigurationManager::savePropertyValueList("ftp_served_locations", servedpaths);
2663             }
2664 
2665             LOG_info << "FTP server restored due to saved configuration";
2666         }
2667         else
2668         {
2669             LOG_err << "Failed to initialize FTP server. Ensure the port is free.";
2670         }
2671     }
2672 #endif
2673 }
2674 
actUponLogout(SynchronousRequestListener * srl,bool keptSession,int timeout)2675 void MegaCmdExecuter::actUponLogout(SynchronousRequestListener *srl, bool keptSession, int timeout)
2676 {
2677     if (!timeout)
2678     {
2679         srl->wait();
2680     }
2681     else
2682     {
2683         int trywaitout = srl->trywait(timeout);
2684         if (trywaitout)
2685         {
2686             LOG_err << "Logout took too long, it may have failed. No further actions performed";
2687             return;
2688         }
2689     }
2690 
2691     if (srl->getError()->getErrorCode() == MegaError::API_ESID || checkNoErrors(srl->getError(), "logout"))
2692     {
2693         LOG_verbose << "actUponLogout logout ok";
2694         cwd = UNDEF;
2695         delete []session;
2696         session = NULL;
2697         mtxSyncMap.lock();
2698         ConfigurationManager::unloadConfiguration();
2699         if (!keptSession)
2700         {
2701             ConfigurationManager::saveSession("");
2702             ConfigurationManager::saveBackups(&ConfigurationManager::configuredBackups);
2703             ConfigurationManager::saveSyncs(&ConfigurationManager::configuredSyncs);
2704         }
2705         ConfigurationManager::clearConfigurationFile();
2706         mtxSyncMap.unlock();
2707     }
2708     updateprompt(api);
2709 }
2710 
actUponCreateFolder(SynchronousRequestListener * srl,int timeout)2711 int MegaCmdExecuter::actUponCreateFolder(SynchronousRequestListener *srl, int timeout)
2712 {
2713     if (!timeout)
2714     {
2715         srl->wait();
2716     }
2717     else
2718     {
2719         int trywaitout = srl->trywait(timeout);
2720         if (trywaitout)
2721         {
2722             LOG_err << "actUponCreateFolder took too long, it may have failed. No further actions performed";
2723             return 1;
2724         }
2725     }
2726     if (checkNoErrors(srl->getError(), "create folder"))
2727     {
2728         LOG_verbose << "actUponCreateFolder Create Folder ok";
2729         return 0;
2730     }
2731 
2732     return 2;
2733 }
2734 
confirmDelete()2735 void MegaCmdExecuter::confirmDelete()
2736 {
2737     if (nodesToConfirmDelete.size())
2738     {
2739         MegaNode * nodeToConfirmDelete = nodesToConfirmDelete.front();
2740         nodesToConfirmDelete.erase(nodesToConfirmDelete.begin());
2741         doDeleteNode(nodeToConfirmDelete,api);
2742     }
2743 
2744 
2745     if (nodesToConfirmDelete.size())
2746     {
2747         string newprompt("Are you sure to delete ");
2748         newprompt+=nodesToConfirmDelete.front()->getName();
2749         newprompt+=" ? (Yes/No/All/None): ";
2750         setprompt(AREYOUSURETODELETE,newprompt);
2751     }
2752     else
2753     {
2754         setprompt(COMMAND);
2755     }
2756 
2757 }
2758 
discardDelete()2759 void MegaCmdExecuter::discardDelete()
2760 {
2761     if (nodesToConfirmDelete.size()){
2762         delete nodesToConfirmDelete.front();
2763         nodesToConfirmDelete.erase(nodesToConfirmDelete.begin());
2764     }
2765     if (nodesToConfirmDelete.size())
2766     {
2767         string newprompt("Are you sure to delete ");
2768         newprompt+=nodesToConfirmDelete.front()->getName();
2769         newprompt+=" ? (Yes/No/All/None): ";
2770         setprompt(AREYOUSURETODELETE,newprompt);
2771     }
2772     else
2773     {
2774         setprompt(COMMAND);
2775     }
2776 }
2777 
2778 
confirmDeleteAll()2779 void MegaCmdExecuter::confirmDeleteAll()
2780 {
2781 
2782     while (nodesToConfirmDelete.size())
2783     {
2784         MegaNode * nodeToConfirmDelete = nodesToConfirmDelete.front();
2785         nodesToConfirmDelete.erase(nodesToConfirmDelete.begin());
2786         doDeleteNode(nodeToConfirmDelete,api);
2787     }
2788 
2789     setprompt(COMMAND);
2790 }
2791 
discardDeleteAll()2792 void MegaCmdExecuter::discardDeleteAll()
2793 {
2794     while (nodesToConfirmDelete.size()){
2795         delete nodesToConfirmDelete.front();
2796         nodesToConfirmDelete.erase(nodesToConfirmDelete.begin());
2797     }
2798     setprompt(COMMAND);
2799 }
2800 
2801 
doDeleteNode(MegaNode * nodeToDelete,MegaApi * api)2802 void MegaCmdExecuter::doDeleteNode(MegaNode *nodeToDelete,MegaApi* api)
2803 {
2804     char *nodePath = api->getNodePath(nodeToDelete);
2805     if (nodePath)
2806     {
2807         LOG_verbose << "Deleting: "<< nodePath;
2808     }
2809     else
2810     {
2811         LOG_warn << "Deleting node whose path could not be found " << nodeToDelete->getName();
2812     }
2813     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
2814     MegaNode *parent = api->getParentNode(nodeToDelete);
2815     if (parent && parent->getType() == MegaNode::TYPE_FILE)
2816     {
2817         api->removeVersion(nodeToDelete, megaCmdListener);
2818     }
2819     else
2820     {
2821         api->remove(nodeToDelete, megaCmdListener);
2822     }
2823     megaCmdListener->wait();
2824     string msj = "delete node ";
2825     if (nodePath)
2826     {
2827         msj += nodePath;
2828     }
2829     else
2830     {
2831         msj += nodeToDelete->getName();
2832     }
2833     checkNoErrors(megaCmdListener->getError(), msj);
2834     delete megaCmdListener;
2835     delete []nodePath;
2836     delete nodeToDelete;
2837 
2838 }
2839 
deleteNodeVersions(MegaNode * nodeToDelete,MegaApi * api,int force)2840 int MegaCmdExecuter::deleteNodeVersions(MegaNode *nodeToDelete, MegaApi* api, int force)
2841 {
2842     if (nodeToDelete->getType() == MegaNode::TYPE_FILE && api->getNumVersions(nodeToDelete) < 2)
2843     {
2844         if (!force)
2845         {
2846             LOG_err << "No versions found for " << nodeToDelete->getName();
2847         }
2848         return MCMDCONFIRM_YES; //nothing to do, no sense asking
2849     }
2850 
2851     int confirmationResponse;
2852 
2853     if (nodeToDelete->getType() != MegaNode::TYPE_FILE)
2854     {
2855         string confirmationQuery("Are you sure todelete the version histories of files within ");
2856         confirmationQuery += nodeToDelete->getName();
2857         confirmationQuery += "? (Yes/No): ";
2858 
2859         confirmationResponse = force?MCMDCONFIRM_ALL:askforConfirmation(confirmationQuery);
2860 
2861         if (confirmationResponse == MCMDCONFIRM_YES || confirmationResponse == MCMDCONFIRM_ALL)
2862         {
2863             MegaNodeList *children = api->getChildren(nodeToDelete);
2864             if (children)
2865             {
2866                 for (int i = 0; i < children->size(); i++)
2867                 {
2868                     MegaNode *child = children->get(i);
2869                     deleteNodeVersions(child, api, true);
2870                 }
2871                 delete children;
2872             }
2873         }
2874     }
2875     else
2876     {
2877 
2878         string confirmationQuery("Are you sure todelete the version histories of ");
2879         confirmationQuery += nodeToDelete->getName();
2880         confirmationQuery += "? (Yes/No): ";
2881         confirmationResponse = force?MCMDCONFIRM_ALL:askforConfirmation(confirmationQuery);
2882 
2883         if (confirmationResponse == MCMDCONFIRM_YES || confirmationResponse == MCMDCONFIRM_ALL)
2884         {
2885 
2886             MegaNodeList *versionsToDelete = api->getVersions(nodeToDelete);
2887             if (versionsToDelete)
2888             {
2889                 for (int i = 0; i < versionsToDelete->size(); i++)
2890                 {
2891                     MegaNode *versionNode = versionsToDelete->get(i);
2892 
2893                     if (versionNode->getHandle() != nodeToDelete->getHandle())
2894                     {
2895                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
2896                         api->removeVersion(versionNode,megaCmdListener);
2897                         megaCmdListener->wait();
2898                         string fullname(versionNode->getName()?versionNode->getName():"NO_NAME");
2899                         fullname += "#";
2900                         fullname += SSTR(versionNode->getModificationTime());
2901                         if (checkNoErrors(megaCmdListener->getError(), "remove version: "+fullname))
2902                         {
2903                             LOG_verbose << " Removed " << fullname << " (" << getReadableTime(versionNode->getModificationTime()) << ")";
2904                         }
2905                         delete megaCmdListener;
2906                     }
2907                 }
2908                 delete versionsToDelete;
2909             }
2910         }
2911     }
2912     return confirmationResponse;
2913 }
2914 
2915 /**
2916  * @brief MegaCmdExecuter::deleteNode
2917  * @param nodeToDelete this function will delete this accordingly
2918  * @param api
2919  * @param recursive
2920  * @param force
2921  * @return confirmation code
2922  */
deleteNode(MegaNode * nodeToDelete,MegaApi * api,int recursive,int force)2923 int MegaCmdExecuter::deleteNode(MegaNode *nodeToDelete, MegaApi* api, int recursive, int force)
2924 {
2925     if (( nodeToDelete->getType() != MegaNode::TYPE_FILE ) && !recursive)
2926     {
2927         char *nodePath = api->getNodePath(nodeToDelete);
2928         setCurrentOutCode(MCMD_INVALIDTYPE);
2929         LOG_err << "Unable to delete folder: " << nodePath << ". Use -r to delete a folder recursively";
2930         delete nodeToDelete;
2931         delete []nodePath;
2932     }
2933     else
2934     {
2935         if (!getCurrentThreadIsCmdShell() && interactiveThread() && !force && nodeToDelete->getType() != MegaNode::TYPE_FILE)
2936         {
2937             bool alreadythere = false;
2938             for (std::vector< MegaNode * >::iterator it = nodesToConfirmDelete.begin(); it != nodesToConfirmDelete.end(); ++it)
2939             {
2940                 if (((MegaNode*)*it)->getHandle() == nodeToDelete->getHandle())
2941                 {
2942                     alreadythere= true;
2943                 }
2944             }
2945             if (!alreadythere)
2946             {
2947                 nodesToConfirmDelete.push_back(nodeToDelete);
2948                 if (getprompt() != AREYOUSURETODELETE)
2949                 {
2950                     string newprompt("Are you sure to delete ");
2951                     newprompt+=nodeToDelete->getName();
2952                     newprompt+=" ? (Yes/No/All/None): ";
2953                     setprompt(AREYOUSURETODELETE,newprompt);
2954                 }
2955             }
2956             else
2957             {
2958                 delete nodeToDelete;
2959             }
2960 
2961             return MCMDCONFIRM_NO; //default return
2962         }
2963         else if (!force && nodeToDelete->getType() != MegaNode::TYPE_FILE)
2964         {
2965             string confirmationQuery("Are you sure to delete ");
2966             confirmationQuery+=nodeToDelete->getName();
2967             confirmationQuery+=" ? (Yes/No/All/None): ";
2968 
2969             int confirmationResponse = askforConfirmation(confirmationQuery);
2970 
2971             if (confirmationResponse == MCMDCONFIRM_YES || confirmationResponse == MCMDCONFIRM_ALL)
2972             {
2973                 LOG_debug << "confirmation received";
2974                 doDeleteNode(nodeToDelete, api);
2975             }
2976             else
2977             {
2978                 delete nodeToDelete;
2979                 LOG_debug << "confirmation denied";
2980             }
2981             return confirmationResponse;
2982         }
2983         else //force
2984         {
2985             doDeleteNode(nodeToDelete, api);
2986             return MCMDCONFIRM_ALL;
2987         }
2988     }
2989 
2990     return MCMDCONFIRM_NO; //default return
2991 }
2992 
downloadNode(string path,MegaApi * api,MegaNode * node,bool background,bool ignorequotawarn,int clientID,MegaCmdMultiTransferListener * multiTransferListener)2993 void MegaCmdExecuter::downloadNode(string path, MegaApi* api, MegaNode *node, bool background, bool ignorequotawarn, int clientID, MegaCmdMultiTransferListener *multiTransferListener)
2994 {
2995     if (sandboxCMD->isOverquota() && !ignorequotawarn)
2996     {
2997         m_time_t ts = m_time();
2998         // in order to speedup and not flood the server we only ask for the details every 1 minute or after account changes
2999         if (!sandboxCMD->temporalbandwidth || (ts - sandboxCMD->lastQuerytemporalBandwith ) > 60 )
3000         {
3001             LOG_verbose << " Updating temporal bandwith ";
3002             sandboxCMD->lastQuerytemporalBandwith = ts;
3003 
3004             MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3005             api->getExtendedAccountDetails(false, false, false, megaCmdListener);
3006             megaCmdListener->wait();
3007 
3008             if (checkNoErrors(megaCmdListener->getError(), "get account details"))
3009             {
3010                 MegaAccountDetails *details = megaCmdListener->getRequest()->getMegaAccountDetails();
3011                 sandboxCMD->istemporalbandwidthvalid = details->isTemporalBandwidthValid();
3012                 if (details && details->isTemporalBandwidthValid())
3013                 {
3014                     sandboxCMD->temporalbandwidth = details->getTemporalBandwidth();
3015                     sandboxCMD->temporalbandwithinterval = details->getTemporalBandwidthInterval();
3016                 }
3017             }
3018             delete megaCmdListener;
3019         }
3020 
3021         OUTSTREAM << "Transfer not started. " << endl;
3022         if (sandboxCMD->istemporalbandwidthvalid)
3023         {
3024             OUTSTREAM << "You have utilized " << sizeToText(sandboxCMD->temporalbandwidth) << " of data transfer in the last "
3025                       << sandboxCMD->temporalbandwithinterval << " hours, "
3026                       "which took you over our current limit";
3027         }
3028         else
3029         {
3030             OUTSTREAM << "You have reached your bandwith quota";
3031         }
3032         OUTSTREAM << ". To circumvent this limit, "
3033                   "you can upgrade to Pro, which will give you your own bandwidth "
3034                   "package and also ample extra storage space. "
3035                      "Alternatively, you can try again in " << secondsToText(sandboxCMD->secondsOverQuota-(ts-sandboxCMD->timeOfOverquota)) <<
3036                      "." << endl << "See \"help --upgrade\" for further details" << endl;
3037         OUTSTREAM << "Use --ignore-quota-warn to initiate nevertheless" << endl;
3038         return;
3039     }
3040 
3041     if (!ignorequotawarn)
3042     {
3043         MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3044         api->queryTransferQuota(node->getSize(),megaCmdListener);
3045         megaCmdListener->wait();
3046         if (checkNoErrors(megaCmdListener->getError(), "query transfer quota"))
3047         {
3048             if (megaCmdListener->getRequest() && megaCmdListener->getRequest()->getFlag() )
3049             {
3050                 OUTSTREAM << "Transfer not started: proceding will exceed transfer quota. "
3051                              "Use --ignore-quota-warn to initiate nevertheless" << endl;
3052                 return;
3053             }
3054         }
3055         delete megaCmdListener;
3056     }
3057 
3058     MegaCmdTransferListener *megaCmdTransferListener = NULL;
3059     if (!background)
3060     {
3061         if (!multiTransferListener)
3062         {
3063             megaCmdTransferListener = new MegaCmdTransferListener(api, sandboxCMD, multiTransferListener, clientID);
3064         }
3065         multiTransferListener->onNewTransfer();
3066     }
3067 #ifdef _WIN32
3068     replaceAll(path,"/","\\");
3069 #endif
3070     LOG_debug << "Starting download: " << node->getName() << " to : " << path;
3071 
3072     if (multiTransferListener && !background)
3073     {
3074         api->startDownload(node, path.c_str(), multiTransferListener);
3075     }
3076     else
3077     {
3078         api->startDownload(node, path.c_str(), megaCmdTransferListener);
3079     }
3080     if (megaCmdTransferListener)
3081     {
3082         megaCmdTransferListener->wait();
3083 #ifdef _WIN32
3084             Sleep(100); //give a while to print end of transfer
3085 #endif
3086         if (checkNoErrors(megaCmdTransferListener->getError(), "download node"))
3087         {
3088             LOG_info << "Download complete: " << megaCmdTransferListener->getTransfer()->getPath();
3089         }
3090         delete megaCmdTransferListener;
3091     }
3092 }
3093 
uploadNode(string path,MegaApi * api,MegaNode * node,string newname,bool background,bool ignorequotawarn,int clientID,MegaCmdMultiTransferListener * multiTransferListener)3094 void MegaCmdExecuter::uploadNode(string path, MegaApi* api, MegaNode *node, string newname, bool background, bool ignorequotawarn, int clientID, MegaCmdMultiTransferListener *multiTransferListener)
3095 {
3096     if (!ignorequotawarn)
3097     { //TODO: reenable this if ever queryBandwidthQuota applies to uploads as well
3098 //        MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3099 //        api->queryBandwidthQuota(node->getSize(),megaCmdListener);
3100 //        megaCmdListener->wait();
3101 //        if (checkNoErrors(megaCmdListener->getError(), "query bandwidth quota"))
3102 //        {
3103 //            if (megaCmdListener->getRequest() && megaCmdListener->getRequest()->getFlag() )
3104 //            {
3105 //                OUTSTREAM << "Transfer not started: proceding will exceed bandwith quota. "
3106 //                             "Use --ignore-quota-warn to initiate nevertheless" << endl;
3107 //                return;
3108 //            }
3109 //        }
3110     }
3111     unescapeifRequired(path);
3112 
3113     LocalPath locallocal = LocalPath::fromPath(path, *fsAccessCMD);
3114     std::unique_ptr<FileAccess> fa = fsAccessCMD->newfileaccess();
3115     if (!fa->fopen(locallocal, true, false))
3116     {
3117         setCurrentOutCode(MCMD_NOTFOUND);
3118         LOG_err << "Unable to open local path: " << path;
3119         return;
3120     }
3121 
3122     MegaCmdTransferListener *megaCmdTransferListener = NULL;
3123     if (!background)
3124     {
3125         if (!multiTransferListener)
3126         {
3127             megaCmdTransferListener = new MegaCmdTransferListener(api, sandboxCMD, multiTransferListener, clientID);
3128         }
3129         multiTransferListener->onNewTransfer();
3130     }
3131 
3132 #ifdef _WIN32
3133     replaceAll(path,"/","\\");
3134 #endif
3135 
3136     LOG_debug << "Starting upload: " << path << " to : " << node->getName() << (newname.size()?"/":"") << newname;
3137 
3138 
3139     MegaTransferListener *thelistener;
3140     if (multiTransferListener && !background)
3141     {
3142        thelistener = multiTransferListener;
3143     }
3144     else
3145     {
3146         thelistener = megaCmdTransferListener;
3147     }
3148 
3149     if (newname.size())
3150     {
3151 
3152         api->startUpload(removeTrailingSeparators(path).c_str(), node, newname.c_str(), thelistener);
3153     }
3154     else
3155     {
3156         api->startUpload(removeTrailingSeparators(path).c_str(), node, thelistener);
3157     }
3158     if (megaCmdTransferListener)
3159     {
3160         megaCmdTransferListener->wait();
3161 #ifdef _WIN32
3162             Sleep(100); //give a while to print end of transfer
3163 #endif
3164         if (megaCmdTransferListener->getError()->getErrorCode() == API_EREAD)
3165         {
3166             setCurrentOutCode(MCMD_NOTFOUND);
3167             LOG_err << "Could not find local path: " << path;
3168         }
3169         else if (checkNoErrors(megaCmdTransferListener->getError(), "Upload node"))
3170         {
3171             char * destinyPath = api->getNodePath(node);
3172             LOG_info << "Upload complete: " << path << " to " << destinyPath << newname;
3173             delete []destinyPath;
3174         }
3175         delete megaCmdTransferListener;
3176     }
3177 }
3178 
3179 
amIPro()3180 bool MegaCmdExecuter::amIPro()
3181 {
3182     int prolevel = -1;
3183     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3184     api->getAccountDetails(megaCmdListener);
3185     megaCmdListener->wait();
3186     if (checkNoErrors(megaCmdListener->getError(), "export node"))
3187     {
3188         MegaAccountDetails *details = megaCmdListener->getRequest()->getMegaAccountDetails();
3189         prolevel = details->getProLevel();
3190     }
3191     delete megaCmdListener;
3192     return prolevel > 0;
3193 }
3194 
exportNode(MegaNode * n,int64_t expireTime,std::string password,bool force)3195 void MegaCmdExecuter::exportNode(MegaNode *n, int64_t expireTime, std::string password, bool force)
3196 {
3197     bool copyrightAccepted = false;
3198 
3199     copyrightAccepted = ConfigurationManager::getConfigurationValue("copyrightAccepted", false) || force;
3200     if (!copyrightAccepted)
3201     {
3202         MegaNodeList * mnl = api->getPublicLinks();
3203         copyrightAccepted = mnl->size();
3204         delete mnl;
3205     }
3206 
3207     int confirmationResponse = copyrightAccepted?MCMDCONFIRM_YES:MCMDCONFIRM_NO;
3208     if (!copyrightAccepted)
3209     {
3210         string confirmationQuery("MEGA respects the copyrights of others and requires that users of the MEGA cloud service comply with the laws of copyright.\n"
3211                                  "You are strictly prohibited from using the MEGA cloud service to infringe copyrights.\n"
3212                                  "You may not upload, download, store, share, display, stream, distribute, email, link to, "
3213                                  "transmit or otherwise make available any files, data or content that infringes any copyright "
3214                                  "or other proprietary rights of any person or entity.");
3215 
3216         confirmationQuery+=" Do you accept this terms? (Yes/No): ";
3217 
3218         confirmationResponse = askforConfirmation(confirmationQuery);
3219     }
3220 
3221     if (confirmationResponse == MCMDCONFIRM_YES || confirmationResponse == MCMDCONFIRM_ALL)
3222     {
3223         ConfigurationManager::savePropertyValue("copyrightAccepted",true);
3224         MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3225         api->exportNode(n, expireTime, megaCmdListener);
3226         megaCmdListener->wait();
3227         if (checkNoErrors(megaCmdListener->getError(), "export node"))
3228         {
3229             MegaNode *nexported = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
3230             if (nexported)
3231             {
3232                 char *nodepath = api->getNodePath(nexported);
3233                 char *publiclink = nexported->getPublicLink();
3234                 string publicPassProtectedLink = string();
3235 
3236                 if (amIPro() && password.size() )
3237                 {
3238                     MegaCmdListener *megaCmdListener2 = new MegaCmdListener(api, NULL);
3239                     api->encryptLinkWithPassword(publiclink, password.c_str(), megaCmdListener2);
3240                     megaCmdListener2->wait();
3241                     if (checkNoErrors(megaCmdListener2->getError(), "protect public link with password"))
3242                     {
3243                         publicPassProtectedLink = megaCmdListener2->getRequest()->getText();
3244                     }
3245                     delete megaCmdListener2;
3246                 }
3247                 else if (password.size())
3248                 {
3249                     LOG_err << "Only PRO users can protect links with passwords. Showing UNPROTECTED link";
3250                 }
3251 
3252                 OUTSTREAM << "Exported " << nodepath << ": "
3253                           << (publicPassProtectedLink.size()?publicPassProtectedLink:publiclink);
3254 
3255 
3256                 if (nexported->getExpirationTime())
3257                 {
3258                     OUTSTREAM << " expires at " << getReadableTime(nexported->getExpirationTime());
3259                 }
3260                 OUTSTREAM << endl;
3261                 delete[] nodepath;
3262                 delete[] publiclink;
3263                 delete nexported;
3264             }
3265             else
3266             {
3267                 setCurrentOutCode(MCMD_NOTFOUND);
3268                 LOG_err << "Exported node not found!";
3269             }
3270         }
3271         delete megaCmdListener;
3272     }
3273 }
3274 
disableExport(MegaNode * n)3275 void MegaCmdExecuter::disableExport(MegaNode *n)
3276 {
3277     if (!n->isExported())
3278     {
3279         setCurrentOutCode(MCMD_INVALIDSTATE);
3280         LOG_err << "Could not disable export: node not exported.";
3281         return;
3282     }
3283     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3284 
3285     api->disableExport(n, megaCmdListener);
3286     megaCmdListener->wait();
3287     if (checkNoErrors(megaCmdListener->getError(), "disable export"))
3288     {
3289         MegaNode *nexported = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
3290         if (nexported)
3291         {
3292             char *nodepath = api->getNodePath(nexported);
3293             OUTSTREAM << "Disabled export: " << nodepath << endl;
3294             delete[] nodepath;
3295             delete nexported;
3296         }
3297         else
3298         {
3299             setCurrentOutCode(MCMD_NOTFOUND);
3300             LOG_err << "Exported node not found!";
3301         }
3302     }
3303 
3304     delete megaCmdListener;
3305 }
3306 
shareNode(MegaNode * n,string with,int level)3307 void MegaCmdExecuter::shareNode(MegaNode *n, string with, int level)
3308 {
3309     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
3310 
3311     api->share(n, with.c_str(), level, megaCmdListener);
3312     megaCmdListener->wait();
3313     if (checkNoErrors(megaCmdListener->getError(), ( level != MegaShare::ACCESS_UNKNOWN ) ? "share node" : "disable share"))
3314     {
3315         MegaNode *nshared = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
3316         if (nshared)
3317         {
3318             char *nodepath = api->getNodePath(nshared);
3319             if (megaCmdListener->getRequest()->getAccess() == MegaShare::ACCESS_UNKNOWN)
3320             {
3321                 OUTSTREAM << "Stopped sharing " << nodepath << " with " << megaCmdListener->getRequest()->getEmail() << endl;
3322             }
3323             else
3324             {
3325                 OUTSTREAM << "Shared " << nodepath << " : " << megaCmdListener->getRequest()->getEmail()
3326                           << " accessLevel=" << megaCmdListener->getRequest()->getAccess() << endl;
3327             }
3328             delete[] nodepath;
3329             delete nshared;
3330         }
3331         else
3332         {
3333             setCurrentOutCode(MCMD_NOTFOUND);
3334             LOG_err << "Shared node not found!";
3335         }
3336     }
3337 
3338     delete megaCmdListener;
3339 }
3340 
disableShare(MegaNode * n,string with)3341 void MegaCmdExecuter::disableShare(MegaNode *n, string with)
3342 {
3343     shareNode(n, with, MegaShare::ACCESS_UNKNOWN);
3344 }
3345 
makedir(string remotepath,bool recursive,MegaNode * parentnode)3346 int MegaCmdExecuter::makedir(string remotepath, bool recursive, MegaNode *parentnode)
3347 {
3348     MegaNode *currentnode;
3349     if (parentnode)
3350     {
3351         currentnode = parentnode;
3352     }
3353     else
3354     {
3355         currentnode = api->getNodeByHandle(cwd);
3356     }
3357     if (currentnode)
3358     {
3359         string rest = remotepath;
3360         while (rest.length())
3361         {
3362             bool lastleave = false;
3363             size_t possep = rest.find_first_of("/");
3364             if (possep == string::npos)
3365             {
3366                 possep = rest.length();
3367                 lastleave = true;
3368             }
3369 
3370             string newfoldername = rest.substr(0, possep);
3371             if (!rest.length())
3372             {
3373                 break;
3374             }
3375             if (newfoldername.length())
3376             {
3377                 MegaNode *existing_node = api->getChildNode(currentnode, newfoldername.c_str());
3378                 if (!existing_node)
3379                 {
3380                     if (!recursive && !lastleave)
3381                     {
3382                         LOG_err << "Use -p to create folders recursively";
3383                         if (currentnode != parentnode)
3384                             delete currentnode;
3385                         return MCMD_EARGS;
3386                     }
3387                     LOG_verbose << "Creating (sub)folder: " << newfoldername;
3388                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
3389                     api->createFolder(newfoldername.c_str(), currentnode, megaCmdListener);
3390                     actUponCreateFolder(megaCmdListener);
3391                     delete megaCmdListener;
3392                     MegaNode *prevcurrentNode = currentnode;
3393                     currentnode = api->getChildNode(currentnode, newfoldername.c_str());
3394                     if (prevcurrentNode != parentnode)
3395                         delete prevcurrentNode;
3396                     if (!currentnode)
3397                     {
3398                         LOG_err << "Couldn't get node for created subfolder: " << newfoldername;
3399                         if (currentnode != parentnode)
3400                             delete currentnode;
3401                         return MCMD_INVALIDSTATE;
3402                     }
3403                 }
3404                 else
3405                 {
3406                     if (currentnode != parentnode)
3407                         delete currentnode;
3408                     currentnode = existing_node;
3409                 }
3410 
3411                 if (lastleave && existing_node)
3412                 {
3413                     LOG_err << ((existing_node->getType() == MegaNode::TYPE_FILE)?"File":"Folder") << " already exists: " << remotepath;
3414                     if (currentnode != parentnode)
3415                         delete currentnode;
3416                     return MCMD_INVALIDSTATE;
3417                 }
3418             }
3419 
3420             //string rest = rest.substr(possep+1,rest.length()-possep-1);
3421             if (!lastleave)
3422             {
3423                 rest = rest.substr(possep + 1, rest.length());
3424             }
3425             else
3426             {
3427                 break;
3428             }
3429         }
3430         if (currentnode != parentnode)
3431             delete currentnode;
3432     }
3433     else
3434     {
3435         return MCMD_EARGS;
3436     }
3437     return MCMD_OK;
3438 
3439 }
3440 
3441 
getCurrentPath()3442 string MegaCmdExecuter::getCurrentPath()
3443 {
3444     string toret;
3445     MegaNode *ncwd = api->getNodeByHandle(cwd);
3446     if (ncwd)
3447     {
3448         char *currentPath = api->getNodePath(ncwd);
3449         toret = string(currentPath);
3450         delete []currentPath;
3451         delete ncwd;
3452     }
3453     return toret;
3454 }
3455 
getVersionsSize(MegaNode * n)3456 long long MegaCmdExecuter::getVersionsSize(MegaNode *n)
3457 {
3458     long long toret = 0;
3459 
3460     MegaNodeList *versionNodes = api->getVersions(n);
3461     if (versionNodes)
3462     {
3463         for (int i = 0; i < versionNodes->size(); i++)
3464         {
3465             MegaNode *versionNode = versionNodes->get(i);
3466             toret += api->getSize(versionNode);
3467         }
3468         delete versionNodes;
3469     }
3470 
3471     MegaNodeList *children = api->getChildren(n);
3472     if (children)
3473     {
3474         for (int i = 0; i < children->size(); i++)
3475         {
3476             MegaNode *child = children->get(i);
3477             toret += getVersionsSize(child);
3478         }
3479         delete children;
3480     }
3481     return toret;
3482 }
3483 
getInfoFromFolder(MegaNode * n,MegaApi * api,long long * nfiles,long long * nfolders,long long * nversions)3484 void MegaCmdExecuter::getInfoFromFolder(MegaNode *n, MegaApi *api, long long *nfiles, long long *nfolders, long long *nversions)
3485 {
3486     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
3487     api->getFolderInfo(n, megaCmdListener);
3488     *nfiles = 0;
3489     *nfolders = 0;
3490     megaCmdListener->wait();
3491     if (checkNoErrors(megaCmdListener->getError(), "getting folder info"))
3492     {
3493         MegaFolderInfo * mfi = megaCmdListener->getRequest()->getMegaFolderInfo();
3494         if (mfi)
3495         {
3496             *nfiles  = mfi->getNumFiles();
3497             *nfolders  = mfi->getNumFolders();
3498             if (nversions)
3499             {
3500                 *nversions = mfi->getNumVersions();
3501             }
3502             delete mfi;
3503         }
3504     }
3505 }
3506 
listpaths(bool usepcre,string askedPath,bool discardFiles)3507 vector<string> MegaCmdExecuter::listpaths(bool usepcre, string askedPath, bool discardFiles)
3508 {
3509     vector<string> paths;
3510     if ((int)askedPath.size())
3511     {
3512         vector<string> *pathsToList = nodesPathsbypath(askedPath.c_str(), usepcre);
3513         if (pathsToList)
3514         {
3515             for (std::vector< string >::iterator it = pathsToList->begin(); it != pathsToList->end(); ++it)
3516             {
3517                 string nodepath= *it;
3518                 MegaNode *ncwd = api->getNodeByHandle(cwd);
3519                 if (ncwd)
3520                 {
3521                     MegaNode * n = nodebypath(nodepath.c_str());
3522                     if (n)
3523                     {
3524                         if (n->getType() != MegaNode::TYPE_FILE)
3525                         {
3526                             nodepath += "/";
3527                         }
3528                         if (!( discardFiles && ( n->getType() == MegaNode::TYPE_FILE )))
3529                         {
3530                             paths.push_back(nodepath);
3531                         }
3532 
3533                         delete n;
3534                     }
3535                     else
3536                     {
3537                         LOG_debug << "Unexpected: matching path has no associated node: " << nodepath << ". Could have been deleted in the process";
3538                     }
3539                     delete ncwd;
3540                 }
3541                 else
3542                 {
3543                     setCurrentOutCode(MCMD_INVALIDSTATE);
3544                     LOG_err << "Couldn't find woking folder (it might been deleted)";
3545                 }
3546             }
3547             pathsToList->clear();
3548             delete pathsToList;
3549         }
3550     }
3551 
3552     return paths;
3553 }
3554 
3555 #ifdef _WIN32
3556 //TODO: try to use these functions from somewhere else
toUtf16String(const std::string & s,UINT codepage=CP_UTF8)3557 static std::wstring toUtf16String(const std::string& s, UINT codepage = CP_UTF8)
3558 {
3559     std::wstring ws;
3560     ws.resize(s.size() + 1);
3561     int nwchars = MultiByteToWideChar(codepage, 0, s.data(), int(s.size()), (LPWSTR)ws.data(), int(ws.size()));
3562     ws.resize(nwchars);
3563     return ws;
3564 }
3565 
toUtf8String(const std::wstring & ws,UINT codepage=CP_UTF8)3566 std::string toUtf8String(const std::wstring& ws, UINT codepage = CP_UTF8)
3567 {
3568     std::string s;
3569     s.resize((ws.size() + 1) * 4);
3570     int nchars = WideCharToMultiByte(codepage, 0, ws.data(), int(ws.size()), (LPSTR)s.data(), int(s.size()), NULL, NULL);
3571     s.resize(nchars);
3572     return s;
3573 }
3574 
3575 
replaceW(std::wstring & str,const std::wstring & from,const std::wstring & to)3576 bool replaceW(std::wstring& str, const std::wstring& from, const std::wstring& to)
3577 {
3578     size_t start_pos = str.find(from);
3579     if (start_pos == std::wstring::npos)
3580     {
3581         return false;
3582     }
3583     str.replace(start_pos, from.length(), to);
3584     return true;
3585 }
3586 #endif
3587 
listlocalpathsstartingby(string askedPath,bool discardFiles)3588 vector<string> MegaCmdExecuter::listlocalpathsstartingby(string askedPath, bool discardFiles)
3589 {
3590     vector<string> paths;
3591 
3592 #ifdef WIN32
3593     string actualaskedPath = fs::u8path(askedPath).u8string();
3594     char sep = (!askedPath.empty() && askedPath.find('/') != string::npos ) ?'/':'\\';
3595     size_t postlastsep = actualaskedPath.find_last_of("/\\");
3596 #else
3597     string actualaskedPath = askedPath;
3598     char sep = '/';
3599     size_t postlastsep = actualaskedPath.find_last_of(sep);
3600 #endif
3601 
3602     if (postlastsep == 0) postlastsep++; // absolute paths
3603     string containingfolder = postlastsep == string::npos ? string() : actualaskedPath.substr(0, postlastsep);
3604 
3605     bool removeprefix = false;
3606     bool requiresseparatorafterunit = false;
3607     if (!containingfolder.size())
3608     {
3609         containingfolder = ".";
3610         removeprefix= true;
3611     }
3612 #ifdef WIN32
3613     else if (containingfolder.find(":") == 1 && (containingfolder.size() < 3 || ( containingfolder.at(2) != '/' && containingfolder.at(2) != '\\')))
3614     {
3615         requiresseparatorafterunit = true;
3616     }
3617 #endif
3618 
3619 #ifdef MEGACMDEXECUTER_FILESYSTEM
3620     for (fs::directory_iterator iter(fs::u8path(containingfolder)); iter != fs::directory_iterator(); ++iter)
3621     {
3622         if (!discardFiles || iter->status().type() == fs::file_type::directory)
3623         {
3624 #ifdef _WIN32
3625             wstring path = iter->path().wstring();
3626 #else
3627             string path = iter->path().string();
3628 #endif
3629             if (removeprefix) path = path.substr(2);
3630             if (requiresseparatorafterunit) path.insert(2, 1, sep);
3631             if (iter->status().type() == fs::file_type::directory)
3632             {
3633                 path.append(1, sep);
3634             }
3635 #ifdef _WIN32
3636                 // try to mimic the exact startup of the asked path to allow mix of '\' & '/'
3637                 fs::path paskedpath = fs::u8path(askedPath);
3638                 paskedpath.make_preferred();
3639                 wstring toreplace = paskedpath.wstring();
3640                 if (path.find(toreplace) == 0)
3641                 {
3642                     replaceW(path, toreplace, toUtf16String(askedPath));
3643                 }
3644 #endif
3645 #ifdef _WIN32
3646             paths.push_back(toUtf8String(path));
3647 #else
3648             paths.push_back(path);
3649 #endif
3650 
3651         }
3652     }
3653 
3654 #elif defined(HAVE_DIRENT_H)
3655     DIR *dir;
3656     if ((dir = opendir (containingfolder.c_str())) != NULL)
3657     {
3658         struct dirent *entry;
3659         while ((entry = readdir (dir)) != NULL)
3660         {
3661             if (!discardFiles || entry->d_type == DT_DIR)
3662             {
3663                 string path = containingfolder;
3664                 if (path != "/")
3665                     path.append(1, sep);
3666                 path.append(entry->d_name);
3667                 if (removeprefix) path = path.substr(2);
3668                 if (path.size() && entry->d_type == DT_DIR)
3669                 {
3670                     path.append(1, sep);
3671                 }
3672                 paths.push_back(path);
3673             }
3674         }
3675 
3676         closedir(dir);
3677     }
3678 #endif
3679     return paths;
3680 }
3681 
getlistusers()3682 vector<string> MegaCmdExecuter::getlistusers()
3683 {
3684     vector<string> users;
3685 
3686     MegaUserList* usersList = api->getContacts();
3687     if (usersList)
3688     {
3689         for (int i = 0; i < usersList->size(); i++)
3690         {
3691             users.push_back(usersList->get(i)->getEmail());
3692         }
3693 
3694         delete usersList;
3695     }
3696     return users;
3697 }
3698 
getNodeAttrs(string nodePath)3699 vector<string> MegaCmdExecuter::getNodeAttrs(string nodePath)
3700 {
3701     vector<string> attrs;
3702 
3703     MegaNode *n = nodebypath(nodePath.c_str());
3704     if (n)
3705     {
3706         //List node custom attributes
3707         MegaStringList *attrlist = n->getCustomAttrNames();
3708         if (attrlist)
3709         {
3710             for (int a = 0; a < attrlist->size(); a++)
3711             {
3712                 attrs.push_back(attrlist->get(a));
3713             }
3714 
3715             delete attrlist;
3716         }
3717         delete n;
3718     }
3719     return attrs;
3720 }
3721 
getUserAttrs()3722 vector<string> MegaCmdExecuter::getUserAttrs()
3723 {
3724     vector<string> attrs;
3725     int i = 0;
3726     do
3727     {
3728         const char *catrn = api->userAttributeToString(i);
3729         if (strlen(catrn))
3730         {
3731             attrs.push_back(catrn);
3732         }
3733         else
3734         {
3735             delete [] catrn;
3736             break;
3737         }
3738         delete [] catrn;
3739         i++;
3740     } while (true);
3741 
3742     return attrs;
3743 }
3744 
getsessions()3745 vector<string> MegaCmdExecuter::getsessions()
3746 {
3747     vector<string> sessions;
3748     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
3749     api->getExtendedAccountDetails(true, true, true, megaCmdListener);
3750     int trywaitout = megaCmdListener->trywait(3000);
3751     if (trywaitout)
3752     {
3753         return sessions;
3754     }
3755 
3756     if (checkNoErrors(megaCmdListener->getError(), "get sessions"))
3757     {
3758         MegaAccountDetails *details = megaCmdListener->getRequest()->getMegaAccountDetails();
3759         if (details)
3760         {
3761             int numSessions = details->getNumSessions();
3762             for (int i = 0; i < numSessions; i++)
3763             {
3764                 MegaAccountSession * session = details->getSession(i);
3765                 if (session)
3766                 {
3767                     if (session->isAlive())
3768                     {
3769                         std::unique_ptr<char []> handle {api->userHandleToBase64(session->getHandle())};
3770                         sessions.push_back(handle.get());
3771                     }
3772                     delete session;
3773                 }
3774             }
3775 
3776             delete details;
3777         }
3778     }
3779     delete megaCmdListener;
3780     return sessions;
3781 }
3782 
getlistfilesfolders(string location)3783 vector<string> MegaCmdExecuter::getlistfilesfolders(string location)
3784 {
3785     vector<string> toret;
3786 #ifdef MEGACMDEXECUTER_FILESYSTEM
3787     for (fs::directory_iterator iter(fs::u8path(location)); iter != fs::directory_iterator(); ++iter)
3788     {
3789         toret.push_back(iter->path().filename().u8string());
3790     }
3791 
3792 #elif defined(HAVE_DIRENT_H)
3793     DIR *dir;
3794     struct dirent *entry;
3795     if ((dir = opendir (location.c_str())) != NULL)
3796     {
3797         while ((entry = readdir (dir)) != NULL)
3798         {
3799             if (IsFolder(location + entry->d_name))
3800             {
3801                 toret.push_back(entry->d_name + string("/"));
3802             }
3803             else
3804             {
3805                 toret.push_back(entry->d_name);
3806             }
3807         }
3808         closedir (dir);
3809     }
3810 #endif
3811     return toret;
3812 }
3813 
signup(string name,string passwd,string email)3814 void MegaCmdExecuter::signup(string name, string passwd, string email)
3815 {
3816     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
3817 
3818     size_t spos = name.find(" ");
3819     string firstname = name.substr(0, spos);
3820     string lastname;
3821     if (spos != string::npos && ((spos + 1) < name.length()))
3822     {
3823         lastname = name.substr(spos+1);
3824     }
3825 
3826     OUTSTREAM << "Singinup up. name=" << firstname << ". surname=" << lastname<< endl;
3827 
3828     api->createAccount(email.c_str(), passwd.c_str(), firstname.c_str(), lastname.c_str(), megaCmdListener);
3829     megaCmdListener->wait();
3830     if (checkNoErrors(megaCmdListener->getError(), "create account <" + email + ">"))
3831     {
3832         OUTSTREAM << "Account <" << email << "> created succesfully. You will receive a confirmation link. Use \"confirm\" with the provided link to confirm that account" << endl;
3833     }
3834 
3835     delete megaCmdListener;
3836 
3837     MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL);
3838     api->localLogout(megaCmdListener2);
3839     megaCmdListener2->wait();
3840     checkNoErrors(megaCmdListener2->getError(), "logging out from ephemeral account");
3841     delete megaCmdListener2;
3842 }
3843 
signupWithPassword(string passwd)3844 void MegaCmdExecuter::signupWithPassword(string passwd)
3845 {
3846     return signup(name, passwd, login);
3847 }
3848 
confirm(string passwd,string email,string link)3849 void MegaCmdExecuter::confirm(string passwd, string email, string link)
3850 {
3851     MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL);
3852     api->confirmAccount(link.c_str(), passwd.c_str(), megaCmdListener2);
3853     megaCmdListener2->wait();
3854     if (megaCmdListener2->getError()->getErrorCode() == MegaError::API_ENOENT)
3855     {
3856         LOG_err << "Invalid password";
3857     }
3858     else if (checkNoErrors(megaCmdListener2->getError(), "confirm account"))
3859     {
3860         OUTSTREAM << "Account " << email << " confirmed succesfully. You can login with it now" << endl;
3861     }
3862 
3863     delete megaCmdListener2;
3864 }
3865 
confirmWithPassword(string passwd)3866 void MegaCmdExecuter::confirmWithPassword(string passwd)
3867 {
3868     return confirm(passwd, login, link);
3869 }
3870 
IsFolder(string path)3871 bool MegaCmdExecuter::IsFolder(string path)
3872 {
3873 #ifdef _WIN32
3874     replaceAll(path,"/","\\");
3875 #endif
3876     LocalPath localpath = LocalPath::fromPath(path, *fsAccessCMD);
3877     std::unique_ptr<FileAccess> fa = fsAccessCMD->newfileaccess();
3878     return fa->isfolder(localpath);
3879 }
3880 
printTransfersHeader(const unsigned int PATHSIZE,bool printstate)3881 void MegaCmdExecuter::printTransfersHeader(const unsigned int PATHSIZE, bool printstate)
3882 {
3883     OUTSTREAM << "TYPE     TAG  " << getFixLengthString("SOURCEPATH ",PATHSIZE) << getFixLengthString("DESTINYPATH ",PATHSIZE)
3884               << "  " << getFixLengthString("    PROGRESS",21);
3885     if (printstate)
3886     {
3887         OUTSTREAM  << "  " << "STATE";
3888     }
3889     OUTSTREAM << endl;
3890 }
3891 
printTransfer(MegaTransfer * transfer,const unsigned int PATHSIZE,bool printstate)3892 void MegaCmdExecuter::printTransfer(MegaTransfer *transfer, const unsigned int PATHSIZE, bool printstate)
3893 {
3894     //Direction
3895 #ifdef _WIN32
3896     OUTSTREAM << " " << ((transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)?"D":"U") << " ";
3897 #else
3898     OUTSTREAM << " " << ((transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)?"\u21d3":"\u21d1") << " ";
3899 #endif
3900     //TODO: handle TYPE_LOCAL_TCP_DOWNLOAD
3901 
3902     //type (transfer/normal)
3903     if (transfer->isSyncTransfer())
3904     {
3905 #ifdef _WIN32
3906         OUTSTREAM << "S";
3907 #else
3908         OUTSTREAM << "\u21f5";
3909 #endif
3910     }
3911 #ifdef ENABLE_BACKUPS
3912     else if (transfer->isBackupTransfer())
3913     {
3914 #ifdef _WIN32
3915         OUTSTREAM << "B";
3916 #else
3917         OUTSTREAM << "\u23eb";
3918 #endif
3919     }
3920 #endif
3921     else
3922     {
3923         OUTSTREAM << " " ;
3924     }
3925 
3926     OUTSTREAM << " " ;
3927 
3928     //tag
3929     OUTSTREAM << getRightAlignedString(SSTR(transfer->getTag()),7) << " ";
3930 
3931     if (transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)
3932     {
3933         // source
3934         MegaNode * node = api->getNodeByHandle(transfer->getNodeHandle());
3935         if (node)
3936         {
3937             char * nodepath = api->getNodePath(node);
3938             OUTSTREAM << getFixLengthString(nodepath,PATHSIZE);
3939             delete []nodepath;
3940 
3941             delete node;
3942         }
3943         else
3944         {
3945             globalTransferListener->completedTransfersMutex.lock();
3946             OUTSTREAM << getFixLengthString(globalTransferListener->completedPathsByHandle[transfer->getNodeHandle()],PATHSIZE);
3947             globalTransferListener->completedTransfersMutex.unlock();
3948         }
3949 
3950         OUTSTREAM << " ";
3951 
3952         //destination
3953         string dest = transfer->getParentPath() ? transfer->getParentPath() : "";
3954         dest.append(transfer->getFileName());
3955         OUTSTREAM << getFixLengthString(dest,PATHSIZE);
3956     }
3957     else
3958     {
3959 
3960         //source
3961         string source(transfer->getParentPath()?transfer->getParentPath():"");
3962         source.append(transfer->getFileName());
3963 
3964         OUTSTREAM << getFixLengthString(source, PATHSIZE);
3965         OUTSTREAM << " ";
3966 
3967         //destination
3968         MegaNode * parentNode = api->getNodeByHandle(transfer->getParentHandle());
3969         if (parentNode)
3970         {
3971             char * parentnodepath = api->getNodePath(parentNode);
3972             OUTSTREAM << getFixLengthString(parentnodepath ,PATHSIZE);
3973             delete []parentnodepath;
3974 
3975             delete parentNode;
3976         }
3977         else
3978         {
3979             OUTSTREAM << getFixLengthString("",PATHSIZE,'-');
3980             LOG_warn << "Could not find destination (parent handle "<< ((transfer->getParentHandle()==INVALID_HANDLE)?" invalid":" valid")
3981                      <<" ) for upload transfer. Source=" << transfer->getParentPath() << transfer->getFileName();
3982         }
3983     }
3984 
3985     //progress
3986     float percent;
3987     if (transfer->getTotalBytes() == 0)
3988     {
3989         percent = 0;
3990     }
3991     else
3992     {
3993         percent = float(transfer->getTransferredBytes()*1.0/transfer->getTotalBytes());
3994     }
3995     OUTSTREAM << "  " << getFixLengthString(percentageToText(percent),7,' ',true)
3996               << " of " << getFixLengthString(sizeToText(transfer->getTotalBytes()),10,' ',true);
3997 
3998     //state
3999     if (printstate)
4000     {
4001         OUTSTREAM << "  " << getTransferStateStr(transfer->getState());
4002     }
4003 
4004     OUTSTREAM << endl;
4005 }
4006 
printTransferColumnDisplayer(ColumnDisplayer * cd,MegaTransfer * transfer,bool printstate)4007 void MegaCmdExecuter::printTransferColumnDisplayer(ColumnDisplayer *cd, MegaTransfer *transfer, bool printstate)
4008 {
4009     //Direction
4010     string type;
4011 #ifdef _WIN32
4012     type += getutf8fromUtf16((transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)?L"\u25bc":L"\u25b2");
4013 #else
4014     type += (transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)?"\u21d3":"\u21d1";
4015 #endif
4016     //TODO: handle TYPE_LOCAL_TCP_DOWNLOAD
4017 
4018     //type (transfer/normal)
4019     if (transfer->isSyncTransfer())
4020     {
4021 #ifdef _WIN32
4022         type += getutf8fromUtf16(L"\u21a8");
4023 #else
4024         type += "\u21f5";
4025 #endif
4026     }
4027 #ifdef ENABLE_BACKUPS
4028     else if (transfer->isBackupTransfer())
4029     {
4030 #ifdef _WIN32
4031         type += getutf8fromUtf16(L"\u2191");
4032 #else
4033         type += "\u23eb";
4034 #endif
4035     }
4036 #endif
4037 
4038     cd->addValue("TYPE",type);
4039     cd->addValue("TAG", SSTR(transfer->getTag())); //TODO: do SSTR within ColumnDisplayer
4040 
4041     if (transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)
4042     {
4043         // source
4044         MegaNode * node = api->getNodeByHandle(transfer->getNodeHandle());
4045         if (node)
4046         {
4047             char * nodepath = api->getNodePath(node);
4048             cd->addValue("SOURCEPATH",nodepath);
4049             delete []nodepath;
4050 
4051             delete node;
4052         }
4053         else
4054         {
4055             globalTransferListener->completedTransfersMutex.lock();
4056             cd->addValue("SOURCEPATH",globalTransferListener->completedPathsByHandle[transfer->getNodeHandle()]);
4057             globalTransferListener->completedTransfersMutex.unlock();
4058         }
4059 
4060         //destination
4061         string dest = transfer->getParentPath() ? transfer->getParentPath() : "";
4062         dest.append(transfer->getFileName());
4063         cd->addValue("DESTINYPATH",dest);
4064     }
4065     else
4066     {
4067         //source
4068         string source(transfer->getParentPath()?transfer->getParentPath():"");
4069         source.append(transfer->getFileName());
4070 
4071         cd->addValue("SOURCEPATH",source);
4072 
4073         //destination
4074         MegaNode * parentNode = api->getNodeByHandle(transfer->getParentHandle());
4075         if (parentNode)
4076         {
4077             char * parentnodepath = api->getNodePath(parentNode);
4078             cd->addValue("DESTINYPATH",parentnodepath);
4079             delete []parentnodepath;
4080 
4081             delete parentNode;
4082         }
4083         else
4084         {
4085             cd->addValue("DESTINYPATH","---------");
4086 
4087             LOG_warn << "Could not find destination (parent handle "<< ((transfer->getParentHandle()==INVALID_HANDLE)?" invalid":" valid")
4088                      <<" ) for upload transfer. Source=" << transfer->getParentPath() << transfer->getFileName();
4089         }
4090     }
4091 
4092     //progress
4093     float percent;
4094     if (transfer->getTotalBytes() == 0)
4095     {
4096         percent = 0;
4097     }
4098     else
4099     {
4100         percent = float(transfer->getTransferredBytes()*1.0/transfer->getTotalBytes());
4101     }
4102 
4103     stringstream osspercent;
4104     osspercent << percentageToText(percent) << " of " << getFixLengthString(sizeToText(transfer->getTotalBytes()),10,' ',true);
4105     cd->addValue("PROGRESS",osspercent.str());
4106 
4107     //state
4108     if (printstate)
4109     {
4110         cd->addValue("STATE",getTransferStateStr(transfer->getState()));
4111     }
4112 }
4113 
printSyncHeader(const unsigned int PATHSIZE,ColumnDisplayer * cd)4114 void MegaCmdExecuter::printSyncHeader(const unsigned int PATHSIZE, ColumnDisplayer *cd)
4115 {
4116     if (cd)
4117     {
4118         cd->addHeader("LOCALPATH", false);
4119         cd->addHeader("REMOTEPATH", false);
4120         return;
4121     }
4122     OUTSTREAM << "ID ";
4123     OUTSTREAM << getFixLengthString("LOCALPATH ", PATHSIZE) << " ";
4124     OUTSTREAM << getFixLengthString("REMOTEPATH ", PATHSIZE) << " ";
4125 
4126     OUTSTREAM << getFixLengthString("ActState", 10) << " ";
4127     OUTSTREAM << getFixLengthString("SyncState", 9) << " ";
4128     OUTSTREAM << getRightAlignedString("SIZE", 8) << " ";
4129     OUTSTREAM << getRightAlignedString("FILES", 6) << " ";
4130     OUTSTREAM << getRightAlignedString("DIRS", 6);
4131     OUTSTREAM << endl;
4132 }
4133 
4134 #ifdef ENABLE_BACKUPS
4135 
printBackupHeader(const unsigned int PATHSIZE)4136 void MegaCmdExecuter::printBackupHeader(const unsigned int PATHSIZE)
4137 {
4138     OUTSTREAM << "TAG  " << " ";
4139     OUTSTREAM << getFixLengthString("LOCALPATH ", PATHSIZE) << " ";
4140     OUTSTREAM << getFixLengthString("REMOTEPARENTPATH ", PATHSIZE) << " ";
4141     OUTSTREAM << getRightAlignedString("STATUS", 14);
4142     OUTSTREAM << endl;
4143 }
4144 
4145 
printBackupSummary(int tag,const char * localfolder,const char * remoteparentfolder,string status,const unsigned int PATHSIZE)4146 void MegaCmdExecuter::printBackupSummary(int tag, const char * localfolder, const char *remoteparentfolder, string status, const unsigned int PATHSIZE)
4147 {
4148     OUTSTREAM << getFixLengthString(SSTR(tag),5) << " "
4149               << getFixLengthString(localfolder, PATHSIZE) << " "
4150               << getFixLengthString((remoteparentfolder?remoteparentfolder:"INVALIDPATH"), PATHSIZE) << " "
4151               << getRightAlignedString(status, 14)
4152               << endl;
4153 }
4154 
printBackupDetails(MegaBackup * backup,const char * timeFormat)4155 void MegaCmdExecuter::printBackupDetails(MegaBackup *backup, const char *timeFormat)
4156 {
4157     if (backup)
4158     {
4159         string speriod = (backup->getPeriod() == -1)?backup->getPeriodString():getReadablePeriod(backup->getPeriod()/10);
4160         OUTSTREAM << "  Max Backups:   " << backup->getMaxBackups() << endl;
4161         OUTSTREAM << "  Period:         " << "\"" << speriod << "\"" << endl;
4162         OUTSTREAM << "  Next backup scheduled for: " << getReadableTime(backup->getNextStartTime(), timeFormat);
4163 
4164         OUTSTREAM << endl;
4165         OUTSTREAM << "  " << " -- CURRENT/LAST BACKUP --" << endl;
4166         OUTSTREAM << "  " << getFixLengthString("FILES UP/TOT", 15);
4167         OUTSTREAM << "  " << getFixLengthString("FOLDERS CREATED", 15);
4168         OUTSTREAM << "  " << getRightAlignedString("PROGRESS     ", 22);
4169 
4170         MegaTransferList * ml = backup->getFailedTransfers();
4171         if (ml && ml->size())
4172         {
4173             OUTSTREAM << "  " << getRightAlignedString("FAILED TRANSFERS", 17);
4174         }
4175         OUTSTREAM << endl;
4176 
4177         string sfiles = SSTR(backup->getNumberFiles()) + "/" + SSTR(backup->getTotalFiles());
4178         OUTSTREAM << "  " << getRightAlignedString(sfiles, 8) << "       ";
4179         OUTSTREAM << "  " << getRightAlignedString(SSTR(backup->getNumberFolders()), 8) << "       ";
4180         long long trabytes = backup->getTransferredBytes();
4181         long long totbytes = backup->getTotalBytes();
4182         double percent = totbytes?double(trabytes)/double(totbytes):0;
4183 
4184         string sprogress = sizeProgressToText(trabytes, totbytes) + "  " + percentageToText(float(percent));
4185         OUTSTREAM << "  " << getRightAlignedString(sprogress,22);
4186         if (ml && ml->size())
4187         {
4188             OUTSTREAM << getRightAlignedString(SSTR(backup->getFailedTransfers()->size()), 17);
4189         }
4190         delete ml;
4191         OUTSTREAM << endl;
4192     }
4193 }
4194 
printBackupHistory(MegaBackup * backup,const char * timeFormat,MegaNode * parentnode,const unsigned int PATHSIZE)4195 void MegaCmdExecuter::printBackupHistory(MegaBackup *backup, const char *timeFormat, MegaNode *parentnode, const unsigned int PATHSIZE)
4196 {
4197     bool firstinhistory = true;
4198     MegaStringList *msl = api->getBackupFolders(backup->getTag());
4199     if (msl)
4200     {
4201         for (int i = 0; i < msl->size(); i++)
4202         {
4203             int datelength = getReadableTime(m_time(), timeFormat).size();
4204 
4205             if (firstinhistory)
4206             {
4207                 OUTSTREAM << "  " << " -- SAVED BACKUPS --" << endl;
4208 
4209                 // print header
4210                 OUTSTREAM << "  " << getFixLengthString("NAME", PATHSIZE) << " ";
4211                 OUTSTREAM << getFixLengthString("DATE", datelength+1) << " ";
4212                 OUTSTREAM << getRightAlignedString("STATUS", 11)<< " ";
4213                 OUTSTREAM << getRightAlignedString("FILES", 6)<< " ";
4214                 OUTSTREAM << getRightAlignedString("FOLDERS", 7);
4215                 OUTSTREAM << endl;
4216 
4217                 firstinhistory = false;
4218             }
4219 
4220             string bpath = msl->get(i);
4221             size_t pos = bpath.find("_bk_");
4222             string btime = "";
4223             if (pos != string::npos)
4224             {
4225                 btime = bpath.substr(pos+4);
4226             }
4227 
4228             pos = bpath.find_last_of("/\\");
4229             string backupInstanceName = bpath;
4230             if (pos != string::npos)
4231             {
4232                 backupInstanceName = bpath.substr(pos+1);
4233             }
4234 
4235             string printableDate = "UNKNOWN";
4236             if (btime.size())
4237             {
4238                 struct tm dt;
4239                 fillStructWithSYYmdHMS(btime,dt);
4240                 printableDate = getReadableTime(m_mktime(&dt), timeFormat);
4241             }
4242 
4243             string backupInstanceStatus="NOT_FOUND";
4244             long long nfiles = 0;
4245             long long nfolders = 0;
4246             if (parentnode)
4247             {
4248                 MegaNode *backupInstanceNode = nodebypath(msl->get(i));
4249                 if (backupInstanceNode)
4250                 {
4251                     backupInstanceStatus = backupInstanceNode->getCustomAttr("BACKST");
4252 
4253                     getNumFolderFiles(backupInstanceNode, api, &nfiles, &nfolders);
4254 
4255                 }
4256 
4257                 delete backupInstanceNode;
4258             }
4259 
4260             OUTSTREAM << "  " << getFixLengthString(backupInstanceName, PATHSIZE) << " ";
4261             OUTSTREAM << getFixLengthString(printableDate, datelength+1) << " ";
4262             OUTSTREAM << getRightAlignedString(backupInstanceStatus, 11) << " ";
4263             OUTSTREAM << getRightAlignedString(SSTR(nfiles), 6)<< " ";
4264             OUTSTREAM << getRightAlignedString(SSTR(nfolders), 7);
4265             //OUTSTREAM << getRightAlignedString("PROGRESS", 10);// some info regarding progress or the like in case of failure could be interesting. Although we don't know total files/folders/bytes
4266             OUTSTREAM << endl;
4267 
4268         }
4269         delete msl;
4270     }
4271 }
4272 
printBackup(int tag,MegaBackup * backup,const char * timeFormat,const unsigned int PATHSIZE,bool extendedinfo,bool showhistory,MegaNode * parentnode)4273 void MegaCmdExecuter::printBackup(int tag, MegaBackup *backup, const char *timeFormat, const unsigned int PATHSIZE, bool extendedinfo, bool showhistory, MegaNode *parentnode)
4274 {
4275     if (backup)
4276     {
4277         const char *nodepath = NULL;
4278         bool deleteparentnode = false;
4279 
4280         if (!parentnode)
4281         {
4282             parentnode = api->getNodeByHandle(backup->getMegaHandle());
4283             if (parentnode)
4284             {
4285                 nodepath = api->getNodePath(parentnode);
4286                 deleteparentnode = true;
4287             }
4288         }
4289         else
4290         {
4291             nodepath = api->getNodePath(parentnode);
4292         }
4293 
4294         printBackupSummary(tag, backup->getLocalFolder(),nodepath,backupSatetStr(backup->getState()), PATHSIZE);
4295         if (extendedinfo)
4296         {
4297             printBackupDetails(backup, timeFormat);
4298         }
4299         delete []nodepath;
4300 
4301         if (showhistory && parentnode)
4302         {
4303             printBackupHistory(backup, timeFormat, parentnode, PATHSIZE);
4304         }
4305 
4306         if (deleteparentnode)
4307         {
4308             delete parentnode;
4309         }
4310     }
4311     else
4312     {
4313         OUTSTREAM << "BACKUP not found " << endl;
4314     }
4315 }
4316 
printBackup(backup_struct * backupstruct,const char * timeFormat,const unsigned int PATHSIZE,bool extendedinfo,bool showhistory)4317 void MegaCmdExecuter::printBackup(backup_struct *backupstruct, const char *timeFormat, const unsigned int PATHSIZE, bool extendedinfo, bool showhistory)
4318 {
4319     if (backupstruct->tag >= 0)
4320     {
4321         MegaBackup *backup = api->getBackupByTag(backupstruct->tag);
4322         if (backup)
4323         {
4324             printBackup(backupstruct->tag, backup, timeFormat, PATHSIZE, extendedinfo, showhistory);
4325             delete backup;
4326         }
4327         else
4328         {
4329             OUTSTREAM << "BACKUP not found: " << backupstruct->tag << endl;
4330         }
4331     }
4332     else
4333     { //merely print configuration
4334         printBackupSummary(backupstruct->tag, backupstruct->localpath.c_str(),"UNKOWN"," FAILED", PATHSIZE);
4335         if (extendedinfo)
4336         {
4337             string speriod = (backupstruct->period == -1)?backupstruct->speriod:getReadablePeriod(backupstruct->period/10);
4338             OUTSTREAM << "         Period: " << "\"" << speriod << "\"" << endl;
4339             OUTSTREAM << "   Max. Backups: " << backupstruct->numBackups << endl;
4340         }
4341     }
4342 }
4343 #endif
4344 
printSync(int i,string key,const char * nodepath,sync_struct * thesync,MegaNode * n,long long nfiles,long long nfolders,const unsigned int PATHSIZE,megacmd::ColumnDisplayer * cd)4345 void MegaCmdExecuter::printSync(int i, string key, const char *nodepath, sync_struct * thesync, MegaNode *n, long long nfiles, long long nfolders, const unsigned int PATHSIZE, megacmd::ColumnDisplayer *cd)
4346 {
4347     if (cd)
4348     {
4349         cd->addValue("ID", SSTR(i));
4350         cd->addValue("LOCALPATH", key);
4351         cd->addValue("REMOTEPATH", nodepath);
4352 
4353         string sstate(key);
4354         sstate = rtrim(sstate, '/');
4355     #ifdef _WIN32
4356         sstate = rtrim(sstate, '\\');
4357     #endif
4358         string psstate;
4359         fsAccessCMD->path2local(&sstate,&psstate);
4360         int statepath = api->syncPathState(&psstate);
4361 
4362         MegaSync *msync = api->getSyncByNode(n);
4363         string syncstate = "REMOVED";
4364         if (msync)
4365         {
4366             syncstate = getSyncStateStr(msync->getState());
4367         }
4368 
4369         string statetoprint;
4370         if (thesync->active)
4371         {
4372             statetoprint = syncstate;
4373         }
4374         else
4375         {
4376             if (msync)
4377             {
4378                 statetoprint = "Disabling:";
4379                 statetoprint+=syncstate;
4380             }
4381             else
4382             {
4383                 statetoprint = "Disabled";
4384             }
4385         }
4386         delete msync;
4387         cd->addValue("ActState", statetoprint);
4388         cd->addValue("SyncState", getSyncPathStateStr(statepath));
4389         cd->addValue("SIZE", sizeToText(api->getSize(n)));
4390         cd->addValue("FILES", SSTR(nfiles));
4391         cd->addValue("DIRS", SSTR(nfolders));
4392 
4393         return;
4394     }
4395 
4396     //tag
4397     OUTSTREAM << getRightAlignedString(SSTR(i),2) << " ";
4398 
4399     OUTSTREAM << getFixLengthString(key,PATHSIZE) << " ";
4400 
4401     OUTSTREAM << getFixLengthString(nodepath,PATHSIZE) << " ";
4402 
4403     string sstate(key);
4404     sstate = rtrim(sstate, '/');
4405 #ifdef _WIN32
4406     sstate = rtrim(sstate, '\\');
4407 #endif
4408     string psstate;
4409     fsAccessCMD->path2local(&sstate,&psstate);
4410     int statepath = api->syncPathState(&psstate);
4411 
4412     MegaSync *msync = api->getSyncByNode(n);
4413     string syncstate = "REMOVED";
4414     if (msync)
4415     {
4416         syncstate = getSyncStateStr(msync->getState());
4417     }
4418 
4419     string statetoprint;
4420     if (thesync->active)
4421     {
4422         statetoprint = syncstate;
4423     }
4424     else
4425     {
4426         if (msync)
4427         {
4428             statetoprint = "Disabling:";
4429             statetoprint+=syncstate;
4430         }
4431         else
4432         {
4433             statetoprint = "Disabled";
4434         }
4435     }
4436     delete msync;
4437 
4438     OUTSTREAM << getFixLengthString(statetoprint,10) << " ";
4439     OUTSTREAM << getFixLengthString(getSyncPathStateStr(statepath),9) << " ";
4440 
4441     OUTSTREAM << getRightAlignedString(sizeToText(api->getSize(n), false),8) << " ";
4442 
4443     OUTSTREAM << getRightAlignedString(SSTR(nfiles),6) << " ";
4444     OUTSTREAM << getRightAlignedString(SSTR(nfolders),6) << " ";
4445 
4446     OUTSTREAM << endl;
4447 
4448 }
4449 
doFind(MegaNode * nodeBase,const char * timeFormat,std::map<std::string,int> * clflags,std::map<std::string,std::string> * cloptions,string word,int printfileinfo,string pattern,bool usepcre,m_time_t minTime,m_time_t maxTime,int64_t minSize,int64_t maxSize)4450 void MegaCmdExecuter::doFind(MegaNode* nodeBase, const char *timeFormat, std::map<std::string, int> *clflags, std::map<std::string, std::string> *cloptions, string word, int printfileinfo, string pattern, bool usepcre, m_time_t minTime, m_time_t maxTime, int64_t minSize, int64_t maxSize)
4451 {
4452     struct criteriaNodeVector pnv;
4453     pnv.pattern = pattern;
4454 
4455     vector<MegaNode *> listOfMatches;
4456     pnv.nodesMatching = &listOfMatches;
4457     pnv.usepcre = usepcre;
4458 
4459     pnv.minTime = minTime;
4460     pnv.maxTime = maxTime;
4461     pnv.minSize = minSize;
4462     pnv.maxSize = maxSize;
4463 
4464 
4465     processTree(nodeBase, includeIfMatchesCriteria, (void*)&pnv);
4466 
4467 
4468     for (std::vector< MegaNode * >::iterator it = listOfMatches.begin(); it != listOfMatches.end(); ++it)
4469     {
4470         MegaNode * n = *it;
4471         if (n)
4472         {
4473             string pathToShow;
4474 
4475             if ( word.size() > 0 && ( (word.find("/") == 0) || (word.find("..") != string::npos)) )
4476             {
4477                 char * nodepath = api->getNodePath(n);
4478                 pathToShow = string(nodepath);
4479                 delete [] nodepath;
4480             }
4481             else
4482             {
4483                 pathToShow = getDisplayPath("", n);
4484             }
4485             if (printfileinfo)
4486             {
4487                 dumpNode(n, timeFormat, clflags, cloptions, 3, false, 1, pathToShow.c_str());
4488             }
4489             else
4490             {
4491                 OUTSTREAM << pathToShow;
4492 
4493                 if (getFlag(clflags, "show-handles"))
4494                 {
4495                     std::unique_ptr<char []> handle {api->handleToBase64(n->getHandle())};
4496                     OUTSTREAM << " <H:" << handle.get() << ">";
4497                 }
4498 
4499                 OUTSTREAM << endl;
4500             }
4501             //notice: some nodes may be dumped twice
4502 
4503             delete n;
4504         }
4505     }
4506 
4507     listOfMatches.clear();
4508 }
4509 
getLPWD()4510 string MegaCmdExecuter::getLPWD()
4511 {
4512     string relativePath = ".";
4513     string absolutePath = "Unknown";
4514     LocalPath localRelativePath = LocalPath::fromPath(relativePath, *fsAccessCMD);
4515     LocalPath localAbsolutePath;
4516     if (fsAccessCMD->expanselocalpath(localRelativePath, localAbsolutePath))
4517     {
4518         absolutePath = localAbsolutePath.toPath(*fsAccessCMD);
4519     }
4520 
4521     return absolutePath;
4522 }
4523 
4524 
move(MegaNode * n,string destiny)4525 void MegaCmdExecuter::move(MegaNode * n, string destiny)
4526 {
4527     MegaNode* tn; //target node
4528     string newname;
4529 
4530     // source node must exist
4531     if (!n)
4532     {
4533         return;
4534     }
4535 
4536 
4537     char * nodepath = api->getNodePath(n);
4538     LOG_debug << "Moving : " << nodepath << " to " << destiny;
4539     delete []nodepath;
4540 
4541     // we have four situations:
4542     // 1. target path does not exist - fail
4543     // 2. target node exists and is folder - move
4544     // 3. target node exists and is file - delete and rename (unless same)
4545     // 4. target path exists, but filename does not - rename
4546     if (( tn = nodebypath(destiny.c_str(), NULL, &newname)))
4547     {
4548         if (tn->getHandle() == n->getHandle())
4549         {
4550             LOG_err << "Source and destiny are the same";
4551         }
4552         else
4553         {
4554             if (newname.size()) //target not found, but tn has what was before the last "/" in the path.
4555             {
4556                 if (tn->getType() == MegaNode::TYPE_FILE)
4557                 {
4558                     setCurrentOutCode(MCMD_INVALIDTYPE);
4559                     LOG_err << destiny << ": Not a directory";
4560                     delete tn;
4561                     delete n;
4562                     return;
4563                 }
4564                 else //move and rename!
4565                 {
4566                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4567                     api->moveNode(n, tn, megaCmdListener);
4568                     megaCmdListener->wait();
4569                     if (checkNoErrors(megaCmdListener->getError(), "move"))
4570                     {
4571                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4572                         api->renameNode(n, newname.c_str(), megaCmdListener);
4573                         megaCmdListener->wait();
4574                         checkNoErrors(megaCmdListener->getError(), "rename");
4575                         delete megaCmdListener;
4576                     }
4577                     else
4578                     {
4579                         LOG_debug << "Won't rename, since move failed " << n->getName() << " to " << tn->getName() << " : " << megaCmdListener->getError()->getErrorCode();
4580                     }
4581                     delete megaCmdListener;
4582                 }
4583             }
4584             else //target found
4585             {
4586                 if (tn->getType() == MegaNode::TYPE_FILE) //move & remove old & rename new
4587                 {
4588                     // (there should never be any orphaned filenodes)
4589                     MegaNode *tnParentNode = api->getNodeByHandle(tn->getParentHandle());
4590                     if (tnParentNode)
4591                     {
4592                         delete tnParentNode;
4593 
4594                         //move into the parent of target node
4595                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4596                         std::unique_ptr<MegaNode> parentNode {api->getNodeByHandle(tn->getParentHandle())};
4597                         api->moveNode(n, parentNode.get(), megaCmdListener);
4598                         megaCmdListener->wait();
4599                         if (checkNoErrors(megaCmdListener->getError(), "move node"))
4600                         {
4601                             const char* name_to_replace = tn->getName();
4602 
4603                             //remove (replaced) target node
4604                             if (n != tn) //just in case moving to same location
4605                             {
4606                                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4607                                 api->remove(tn, megaCmdListener); //remove target node
4608                                 megaCmdListener->wait();
4609                                 if (!checkNoErrors(megaCmdListener->getError(), "remove target node"))
4610                                 {
4611                                     LOG_err << "Couldnt move " << n->getName() << " to " << tn->getName() << " : " << megaCmdListener->getError()->getErrorCode();
4612                                 }
4613                                 delete megaCmdListener;
4614                             }
4615 
4616                             // rename moved node with the new name
4617                             if (strcmp(name_to_replace, n->getName()))
4618                             {
4619                                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4620                                 api->renameNode(n, name_to_replace, megaCmdListener);
4621                                 megaCmdListener->wait();
4622                                 if (!checkNoErrors(megaCmdListener->getError(), "rename moved node"))
4623                                 {
4624                                     LOG_err << "Failed to rename moved node: " << megaCmdListener->getError()->getErrorString();
4625                                 }
4626                                 delete megaCmdListener;
4627                             }
4628                         }
4629                         delete megaCmdListener;
4630                     }
4631                     else
4632                     {
4633                         setCurrentOutCode(MCMD_INVALIDSTATE);
4634                         LOG_fatal << "Destiny node is orphan!!!";
4635                     }
4636                 }
4637                 else // target is a folder
4638                 {
4639                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4640                     api->moveNode(n, tn, megaCmdListener);
4641                     megaCmdListener->wait();
4642                     checkNoErrors(megaCmdListener->getError(), "move node");
4643                     delete megaCmdListener;
4644                 }
4645             }
4646         }
4647         delete tn;
4648     }
4649     else //target not found (not even its folder), cant move
4650     {
4651         setCurrentOutCode(MCMD_NOTFOUND);
4652         LOG_err << destiny << ": No such directory";
4653     }
4654 }
4655 
4656 
isValidFolder(string destiny)4657 bool MegaCmdExecuter::isValidFolder(string destiny)
4658 {
4659     bool isdestinyavalidfolder = true;
4660     MegaNode *ndestiny = nodebypath(destiny.c_str());;
4661     if (ndestiny)
4662     {
4663         if (ndestiny->getType() == MegaNode::TYPE_FILE)
4664         {
4665             isdestinyavalidfolder = false;
4666         }
4667         delete ndestiny;
4668     }
4669     else
4670     {
4671         isdestinyavalidfolder = false;
4672     }
4673     return isdestinyavalidfolder;
4674 }
4675 
restartsyncs()4676 void MegaCmdExecuter::restartsyncs()
4677 {
4678     std::lock_guard<std::recursive_mutex> g(ConfigurationManager::settingsMutex);
4679     map<string, sync_struct *>::iterator itr;
4680     for (itr = ConfigurationManager::configuredSyncs.begin(); itr != ConfigurationManager::configuredSyncs.end(); ++itr)
4681     {
4682         string key = ( *itr ).first;
4683         sync_struct *thesync = ((sync_struct*)( *itr ).second );
4684         if (thesync->active)
4685         {
4686             MegaNode * n = api->getNodeByHandle(thesync->handle);
4687             if (n)
4688             {
4689                 char * nodepath = api->getNodePath(n);
4690                 LOG_info << "Restarting sync "<< key << ": " << nodepath;
4691                 delete []nodepath;
4692                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4693                 api->disableSync(n, megaCmdListener);
4694                 megaCmdListener->wait();
4695 
4696                 if (checkNoErrors(megaCmdListener->getError(), "stop sync"))
4697                 {
4698                     thesync->active = false;
4699 
4700                     MegaSync *msync = api->getSyncByNode(n);
4701                     if (!msync)
4702                     {
4703                         MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL);
4704                         api->syncFolder(thesync->localpath.c_str(), n, megaCmdListener2);
4705                         megaCmdListener2->wait();
4706 
4707                         if (checkNoErrors(megaCmdListener2->getError(), "resume sync"))
4708                         {
4709                             thesync->active = true;
4710                             thesync->loadedok = true;
4711 
4712                             if (megaCmdListener2->getRequest()->getNumber())
4713                             {
4714                                 thesync->fingerprint = megaCmdListener2->getRequest()->getNumber();
4715                             }
4716                         }
4717                         else
4718                         {
4719                             thesync->active = false;
4720                             thesync->loadedok = false;
4721                         }
4722                         delete megaCmdListener2;
4723                         delete msync;
4724                     }
4725                     else
4726                     {
4727                         setCurrentOutCode(MCMD_INVALIDSTATE);
4728                         LOG_err << "Failed to restart sync: " << key << ". You will need to manually reenable or restart MEGAcmd";
4729                     }
4730                 }
4731                 delete megaCmdListener;
4732                 delete n;
4733             }
4734         }
4735     }
4736 }
4737 
getNodePathString(MegaNode * n)4738 std::string MegaCmdExecuter::getNodePathString(MegaNode *n)
4739 {
4740     const char *path = api->getNodePath(n);
4741     string toret(path);
4742     delete[] path;
4743     return toret;
4744 }
4745 
copyNode(MegaNode * n,string destiny,MegaNode * tn,string & targetuser,string & newname)4746 void MegaCmdExecuter::copyNode(MegaNode *n, string destiny, MegaNode * tn, string &targetuser, string &newname)
4747 {
4748     if (tn)
4749     {
4750         if (tn->getHandle() == n->getHandle())
4751         {
4752             LOG_err << "Source and destiny are the same";
4753         }
4754         else
4755         {
4756             if (newname.size()) //target not found, but tn has what was before the last "/" in the path.
4757             {
4758                 if (n->getType() == MegaNode::TYPE_FILE)
4759                 {
4760                     LOG_debug << "copy with new name: \"" << getNodePathString(n) << "\" to \"" << destiny << "\" newname=" << newname;
4761                     //copy with new name
4762                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4763                     api->copyNode(n, tn, newname.c_str(), megaCmdListener); //only works for files
4764                     megaCmdListener->wait();
4765                     checkNoErrors(megaCmdListener->getError(), "copy node");
4766                     delete megaCmdListener;
4767                 }
4768                 else //copy & rename
4769                 {
4770                     LOG_debug << "copy & rename: \"" << getNodePathString(n) << "\" to \"" << destiny << "\"";
4771                     //copy with new name
4772                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4773                     api->copyNode(n, tn, megaCmdListener);
4774                     megaCmdListener->wait();
4775                     if (checkNoErrors(megaCmdListener->getError(), "copy node"))
4776                     {
4777                         MegaNode * newNode = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
4778                         if (newNode)
4779                         {
4780                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4781                             api->renameNode(newNode, newname.c_str(), megaCmdListener);
4782                             megaCmdListener->wait();
4783                             checkNoErrors(megaCmdListener->getError(), "rename new node");
4784                             delete megaCmdListener;
4785                             delete newNode;
4786                         }
4787                         else
4788                         {
4789                             LOG_err << " Couldn't find new node created upon cp";
4790                         }
4791                     }
4792                     delete megaCmdListener;
4793                 }
4794             }
4795             else
4796             { //target exists
4797                 if (tn->getType() == MegaNode::TYPE_FILE)
4798                 {
4799                     if (n->getType() == MegaNode::TYPE_FILE)
4800                     {
4801                         LOG_debug << "overwriding target: \"" << getNodePathString(n) << "\" to \"" << destiny << "\"";
4802 
4803                         // overwrite target if source and target are files
4804                         MegaNode *tnParentNode = api->getNodeByHandle(tn->getParentHandle());
4805                         if (tnParentNode) // (there should never be any orphaned filenodes)
4806                         {
4807                             const char* name_to_replace = tn->getName();
4808                             //copy with new name
4809                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4810                             api->copyNode(n, tnParentNode, name_to_replace, megaCmdListener);
4811                             megaCmdListener->wait();
4812                             if (checkNoErrors(megaCmdListener->getError(), "copy with new name") )
4813                             {
4814                                 MegaHandle newNodeHandle = megaCmdListener->getRequest()->getNodeHandle();
4815                                 delete megaCmdListener;
4816                                 delete tnParentNode;
4817 
4818                                 //remove target node
4819                                 if (tn->getHandle() != newNodeHandle )//&& newNodeHandle != n->getHandle())
4820                                 {
4821                                     megaCmdListener = new MegaCmdListener(NULL);
4822                                     api->remove(tn, megaCmdListener);
4823                                     megaCmdListener->wait();
4824                                     checkNoErrors(megaCmdListener->getError(), "delete target node");
4825                                     delete megaCmdListener;
4826                                 }
4827                             }
4828                         }
4829                         else
4830                         {
4831                             setCurrentOutCode(MCMD_INVALIDSTATE);
4832                             LOG_fatal << "Destiny node is orphan!!!";
4833                         }
4834                     }
4835                     else
4836                     {
4837                         setCurrentOutCode(MCMD_INVALIDTYPE);
4838                         LOG_err << "Cannot overwrite file with folder";
4839                         return;
4840                     }
4841                 }
4842                 else //copying into folder
4843                 {
4844                     LOG_debug << "Copying into folder: \"" << getNodePathString(n) << "\" to \"" << destiny << "\"";
4845 
4846                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4847                     api->copyNode(n, tn, megaCmdListener);
4848                     megaCmdListener->wait();
4849                     checkNoErrors(megaCmdListener->getError(), "copy node");
4850                     delete megaCmdListener;
4851                 }
4852             }
4853         }
4854     }
4855     else if (targetuser.size())
4856     {
4857         LOG_debug << "Sending to user: \"" << getNodePathString(n) << "\" to \"" << targetuser << "\"";
4858 
4859         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4860         api->sendFileToUser(n,targetuser.c_str(),megaCmdListener);
4861         megaCmdListener->wait();
4862         checkNoErrors(megaCmdListener->getError(), "send file to user");
4863         delete megaCmdListener;
4864     }
4865     else
4866     {
4867         setCurrentOutCode(MCMD_NOTFOUND);
4868         LOG_err << destiny << " Couldn't find destination";
4869     }
4870 }
4871 
4872 
4873 #ifdef ENABLE_BACKUPS
establishBackup(string pathToBackup,MegaNode * n,int64_t period,string speriod,int numBackups)4874 bool MegaCmdExecuter::establishBackup(string pathToBackup, MegaNode *n, int64_t period, string speriod,  int numBackups)
4875 {
4876     bool attendpastbackups = true; //TODO: receive as parameter
4877     static int backupcounter = 0;
4878     LocalPath localrelativepath = LocalPath::fromPath(pathToBackup, *fsAccessCMD);
4879     LocalPath localabsolutepath;
4880     fsAccessCMD->expanselocalpath(localrelativepath, localabsolutepath);
4881 
4882     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
4883     api->setBackup(localabsolutepath.toPath(*fsAccessCMD).c_str(), n, attendpastbackups, period, speriod.c_str(), numBackups, megaCmdListener);
4884     megaCmdListener->wait();
4885     if (checkNoErrors(megaCmdListener->getError(), "establish backup"))
4886     {
4887         mtxBackupsMap.lock();
4888 
4889         backup_struct *thebackup = NULL;
4890         if (ConfigurationManager::configuredBackups.find(megaCmdListener->getRequest()->getFile()) != ConfigurationManager::configuredBackups.end())
4891         {
4892             thebackup = ConfigurationManager::configuredBackups[megaCmdListener->getRequest()->getFile()];
4893             if (thebackup->id == -1)
4894             {
4895                 thebackup->id = backupcounter++;
4896             }
4897         }
4898         else
4899         {
4900             thebackup = new backup_struct;
4901             thebackup->id = backupcounter++;
4902             ConfigurationManager::configuredBackups[megaCmdListener->getRequest()->getFile()] = thebackup;
4903         }
4904         thebackup->active = true;
4905         thebackup->handle = megaCmdListener->getRequest()->getNodeHandle();
4906         thebackup->localpath = string(megaCmdListener->getRequest()->getFile());
4907         thebackup->numBackups = numBackups;
4908         thebackup->period = period;
4909         thebackup->speriod = speriod;
4910         thebackup->failed = false;
4911         thebackup->tag = megaCmdListener->getRequest()->getTransferTag();
4912 
4913         char * nodepath = api->getNodePath(n);
4914         LOG_info << "Added backup: " << megaCmdListener->getRequest()->getFile() << " to " << nodepath;
4915         mtxBackupsMap.unlock();
4916         delete []nodepath;
4917         delete megaCmdListener;
4918 
4919         return true;
4920     }
4921     else
4922     {
4923         bool foundbytag = false;
4924         // find by tag in configured (modification failed)
4925         for (std::map<std::string, backup_struct *>::iterator itr = ConfigurationManager::configuredBackups.begin();
4926              itr != ConfigurationManager::configuredBackups.end(); itr++)
4927         {
4928             if (itr->second->tag == megaCmdListener->getRequest()->getTransferTag())
4929             {
4930                 backup_struct *thebackup = itr->second;
4931 
4932                 foundbytag = true;
4933                 thebackup->handle = megaCmdListener->getRequest()->getNodeHandle();
4934                 thebackup->localpath = string(megaCmdListener->getRequest()->getFile());
4935                 thebackup->numBackups = megaCmdListener->getRequest()->getNumRetry();
4936                 thebackup->period = megaCmdListener->getRequest()->getNumber();
4937                 thebackup->speriod = string(megaCmdListener->getRequest()->getText());;
4938                 thebackup->failed = true;
4939             }
4940         }
4941 
4942 
4943         if (!foundbytag)
4944         {
4945             std::map<std::string, backup_struct *>::iterator itr = ConfigurationManager::configuredBackups.find(megaCmdListener->getRequest()->getFile());
4946             if ( itr != ConfigurationManager::configuredBackups.end())
4947             {
4948                 if (megaCmdListener->getError()->getErrorCode() != MegaError::API_EEXIST)
4949                 {
4950                     itr->second->failed = true;
4951                 }
4952                 itr->second->id = backupcounter++;
4953             }
4954         }
4955     }
4956     delete megaCmdListener;
4957     return false;
4958 }
4959 #endif
4960 
confirmCancel(const char * confirmlink,const char * pass)4961 void MegaCmdExecuter::confirmCancel(const char* confirmlink, const char* pass)
4962 {
4963     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
4964     api->confirmCancelAccount(confirmlink, pass, megaCmdListener);
4965     megaCmdListener->wait();
4966     if (megaCmdListener->getError()->getErrorCode() == MegaError::API_ETOOMANY)
4967     {
4968         LOG_err << "Confirm cancel account failed: too many attempts";
4969     }
4970     else if (megaCmdListener->getError()->getErrorCode() == MegaError::API_ENOENT
4971              || megaCmdListener->getError()->getErrorCode() == MegaError::API_EKEY)
4972     {
4973         LOG_err << "Confirm cancel account failed: invalid link/password";
4974     }
4975     else if (checkNoErrors(megaCmdListener->getError(), "confirm cancel account"))
4976     {
4977         OUTSTREAM << "CONFIRM Account cancelled succesfully" << endl;
4978         MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL);
4979         api->localLogout(megaCmdListener2);
4980         actUponLogout(megaCmdListener2, false);
4981         delete megaCmdListener2;
4982     }
4983     delete megaCmdListener;
4984 }
4985 
processPath(string path,bool usepcre,bool & firstone,void (* nodeprocessor)(MegaCmdExecuter *,MegaNode *,bool),MegaCmdExecuter * context)4986 void MegaCmdExecuter::processPath(string path, bool usepcre, bool &firstone, void (*nodeprocessor)(MegaCmdExecuter *, MegaNode *, bool), MegaCmdExecuter *context)
4987 {
4988 
4989     if (isRegExp(path))
4990     {
4991         vector<MegaNode *> *nodes = nodesbypath(path.c_str(), usepcre);
4992         if (nodes)
4993         {
4994             if (!nodes->size())
4995             {
4996                 setCurrentOutCode(MCMD_NOTFOUND);
4997                 LOG_err << "Path not found: " << path;
4998             }
4999             for (std::vector< MegaNode * >::iterator it = nodes->begin(); it != nodes->end(); ++it)
5000             {
5001                 MegaNode * n = *it;
5002                 if (n)
5003                 {
5004                     nodeprocessor(context, n, firstone);
5005                     firstone = false;
5006                     delete n;
5007                 }
5008                 else
5009                 {
5010                     setCurrentOutCode(MCMD_NOTFOUND);
5011                     LOG_err << "Path not found: " << path;
5012                     return;
5013                 }
5014             }
5015         }
5016     }
5017     else // non-regexp
5018     {
5019         MegaNode *n = nodebypath(path.c_str());
5020         if (n)
5021         {
5022             nodeprocessor(context, n, firstone);
5023             firstone = false;
5024             delete n;
5025         }
5026         else
5027         {
5028             setCurrentOutCode(MCMD_NOTFOUND);
5029             LOG_err << "Path not found: " << path;
5030             return;
5031         }
5032     }
5033 }
5034 
5035 
5036 #ifdef HAVE_LIBUV
5037 
forwarderRemoveWebdavLocation(MegaCmdExecuter * context,MegaNode * n,bool firstone)5038 void forwarderRemoveWebdavLocation(MegaCmdExecuter* context, MegaNode *n, bool firstone) {
5039     context->removeWebdavLocation(n, firstone);
5040 }
forwarderAddWebdavLocation(MegaCmdExecuter * context,MegaNode * n,bool firstone)5041 void forwarderAddWebdavLocation(MegaCmdExecuter* context, MegaNode *n, bool firstone) {
5042     context->addWebdavLocation(n, firstone);
5043 }
forwarderRemoveFtpLocation(MegaCmdExecuter * context,MegaNode * n,bool firstone)5044 void forwarderRemoveFtpLocation(MegaCmdExecuter* context, MegaNode *n, bool firstone) {
5045     context->removeFtpLocation(n, firstone);
5046 }
forwarderAddFtpLocation(MegaCmdExecuter * context,MegaNode * n,bool firstone)5047 void forwarderAddFtpLocation(MegaCmdExecuter* context, MegaNode *n, bool firstone) {
5048     context->addFtpLocation(n, firstone);
5049 }
5050 
removeWebdavLocation(MegaNode * n,bool firstone,string name)5051 void MegaCmdExecuter::removeWebdavLocation(MegaNode *n, bool firstone, string name)
5052 {
5053     char *actualNodePath = api->getNodePath(n);
5054     api->httpServerRemoveWebDavAllowedNode(n->getHandle());
5055 
5056     mtxWebDavLocations.lock();
5057     list<string> servedpaths = ConfigurationManager::getConfigurationValueList("webdav_served_locations");
5058     size_t sizeprior = servedpaths.size();
5059     servedpaths.remove(actualNodePath);
5060     size_t sizeafter = servedpaths.size();
5061     if (!sizeafter)
5062     {
5063         api->httpServerStop();
5064         ConfigurationManager::savePropertyValue("webdav_port", -1); //so as not to load server on startup
5065     }
5066     ConfigurationManager::savePropertyValueList("webdav_served_locations", servedpaths);
5067     mtxWebDavLocations.unlock();
5068 
5069     if (sizeprior != sizeafter)
5070     {
5071         OUTSTREAM << (name.size()?name:actualNodePath) << " no longer served via webdav" << endl;
5072     }
5073     else
5074     {
5075         setCurrentOutCode(MCMD_NOTFOUND);
5076         LOG_err << (name.size()?name:actualNodePath) << " is not served via webdav";
5077     }
5078     delete []actualNodePath;
5079 }
5080 
addWebdavLocation(MegaNode * n,bool firstone,string name)5081 void MegaCmdExecuter::addWebdavLocation(MegaNode *n, bool firstone, string name)
5082 {
5083     char *actualNodePath = api->getNodePath(n);
5084     char *l = api->httpServerGetLocalWebDavLink(n);
5085     OUTSTREAM << "Serving via webdav " << (name.size()?name:actualNodePath) << ": " << l << endl;
5086 
5087     mtxWebDavLocations.lock();
5088     list<string> servedpaths = ConfigurationManager::getConfigurationValueList("webdav_served_locations");
5089     servedpaths.push_back(actualNodePath);
5090     servedpaths.sort();
5091     servedpaths.unique();
5092     ConfigurationManager::savePropertyValueList("webdav_served_locations", servedpaths);
5093     mtxWebDavLocations.unlock();
5094 
5095     delete []l;
5096     delete []actualNodePath;
5097 }
5098 
removeFtpLocation(MegaNode * n,bool firstone,string name)5099 void MegaCmdExecuter::removeFtpLocation(MegaNode *n, bool firstone, string name)
5100 {
5101     char *actualNodePath = api->getNodePath(n);
5102     api->ftpServerRemoveAllowedNode(n->getHandle());
5103 
5104     mtxFtpLocations.lock();
5105     list<string> servedpaths = ConfigurationManager::getConfigurationValueList("ftp_served_locations");
5106     size_t sizeprior = servedpaths.size();
5107     servedpaths.remove(actualNodePath);
5108     size_t sizeafter = servedpaths.size();
5109     if (!sizeafter)
5110     {
5111         api->ftpServerStop();
5112         ConfigurationManager::savePropertyValue("ftp_port", -1); //so as not to load server on startup
5113     }
5114     ConfigurationManager::savePropertyValueList("ftp_served_locations", servedpaths);
5115     mtxFtpLocations.unlock();
5116 
5117     if (sizeprior != sizeafter)
5118     {
5119         OUTSTREAM << (name.size()?name:actualNodePath) << " no longer served via ftp" << endl;
5120     }
5121     else
5122     {
5123         setCurrentOutCode(MCMD_NOTFOUND);
5124         LOG_err << (name.size()?name:actualNodePath)  << " is not served via ftp";
5125     }
5126     delete []actualNodePath;
5127 }
5128 
addFtpLocation(MegaNode * n,bool firstone,string name)5129 void MegaCmdExecuter::addFtpLocation(MegaNode *n, bool firstone, string name)
5130 {
5131     char *actualNodePath = api->getNodePath(n);
5132     char *l = api->ftpServerGetLocalLink(n);
5133     OUTSTREAM << "Serving via ftp " << (name.size()?name:n->getName())  << ": " << l << endl;
5134 
5135     mtxFtpLocations.lock();
5136     list<string> servedpaths = ConfigurationManager::getConfigurationValueList("ftp_served_locations");
5137     servedpaths.push_back(actualNodePath);
5138     servedpaths.sort();
5139     servedpaths.unique();
5140     ConfigurationManager::savePropertyValueList("ftp_served_locations", servedpaths);
5141     mtxFtpLocations.unlock();
5142 
5143     delete []l;
5144     delete []actualNodePath;
5145 }
5146 
5147 #endif
5148 
5149 
catFile(MegaNode * n)5150 void MegaCmdExecuter::catFile(MegaNode *n)
5151 {
5152     if (n->getType() != MegaNode::TYPE_FILE)
5153     {
5154         LOG_err << " Unable to cat: not a file";
5155         setCurrentOutCode(MCMD_EARGS);
5156         return;
5157     }
5158 
5159     long long nsize = api->getSize(n);
5160     if (!nsize)
5161     {
5162         return;
5163     }
5164     long long end = nsize;
5165     long long start = 0;
5166 
5167     MegaCmdCatTransferListener *mcctl = new MegaCmdCatTransferListener(&OUTSTREAM, api, sandboxCMD);
5168     api->startStreaming(n, start, end-start, mcctl);
5169     mcctl->wait();
5170     if (checkNoErrors(mcctl->getError(), "cat streaming from " +SSTR(start) + " to " + SSTR(end) ))
5171     {
5172         char * npath = api->getNodePath(n);
5173         LOG_verbose << "Streamed: " << npath << " from " << start << " to " << end;
5174         delete []npath;
5175     }
5176 
5177     delete mcctl;
5178 }
5179 
printInfoFile(MegaNode * n,bool & firstone,int PATHSIZE)5180 void MegaCmdExecuter::printInfoFile(MegaNode *n, bool &firstone, int PATHSIZE)
5181 {
5182     char * nodepath = api->getNodePath(n);
5183     char *fattrs = n->getFileAttrString();
5184     if (fattrs == NULL)
5185     {
5186         LOG_warn << " Unable to get attributes for node " << nodepath;
5187     }
5188 
5189     if (firstone)
5190     {
5191         OUTSTREAM << getFixLengthString("FILE", PATHSIZE);
5192         OUTSTREAM << getFixLengthString("WIDTH", 7);
5193         OUTSTREAM << getFixLengthString("HEIGHT", 7);
5194         OUTSTREAM << getFixLengthString("FPS", 4);
5195         OUTSTREAM << getFixLengthString("PLAYTIME", 10);
5196         OUTSTREAM << endl;
5197         firstone = false;
5198     }
5199 
5200     OUTSTREAM << getFixLengthString(nodepath, PATHSIZE-1) << " ";
5201     delete []nodepath;
5202 
5203     OUTSTREAM << getFixLengthString( (n->getWidth() == -1) ? "---" : SSTR(n->getWidth()) , 6) << " ";
5204     OUTSTREAM << getFixLengthString( (n->getHeight() == -1) ? "---" : SSTR(n->getHeight()) , 6) << " ";
5205 
5206     if (fattrs == NULL)
5207     {
5208         OUTSTREAM << getFixLengthString("---", 3) << " ";
5209     }
5210     else
5211     {
5212         MediaProperties mp = MediaProperties::decodeMediaPropertiesAttributes(fattrs, (uint32_t*)(n->getNodeKey()->data() + FILENODEKEYLENGTH / 2) );
5213         OUTSTREAM << getFixLengthString( (mp.fps == 0) ? "---" : SSTR(mp.fps) , 3) << " ";
5214     }
5215     OUTSTREAM << getFixLengthString( (n->getHeight() == -1) ? "---" : getReadablePeriod(n->getDuration()) , 10) << " ";
5216 
5217     OUTSTREAM << endl;
5218 }
5219 
printUserAttribute(int a,string user,bool onlylist)5220 bool MegaCmdExecuter::printUserAttribute(int a, string user, bool onlylist)
5221 {
5222     const char *catrn = api->userAttributeToString(a);
5223     string attrname = catrn;
5224     delete [] catrn;
5225 
5226     const char *catrln = api->userAttributeToLongName(a);
5227     string longname = catrln;
5228     delete [] catrln;
5229 
5230     if (attrname.size())
5231     {
5232         if (onlylist)
5233         {
5234             OUTSTREAM << longname << " (" << attrname << ")" << endl;
5235         }
5236         else
5237         {
5238             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
5239             if (user.size())
5240             {
5241                 api->getUserAttribute(user.c_str(), a, megaCmdListener);
5242             }
5243             else
5244             {
5245                 api->getUserAttribute(a, megaCmdListener);
5246             }
5247             megaCmdListener->wait();
5248             if (checkNoErrors(megaCmdListener->getError(), string("get user attribute ") + attrname))
5249             {
5250                 int iattr = megaCmdListener->getRequest()->getParamType();
5251                 const char *value = megaCmdListener->getRequest()->getText();
5252                 //if (!value) value = megaCmdListener->getRequest()->getMegaStringMap()->;
5253                 string svalue;
5254                 try
5255                 {
5256                     if (value)
5257                     {
5258                         svalue = string(value);
5259                     }
5260                     else
5261                     {
5262 
5263                         svalue = "NOT PRINTABLE";
5264                     }
5265 
5266                 }
5267                 catch (exception e)
5268                 {
5269                     svalue = "NOT PRINTABLE";
5270                 }
5271                 OUTSTREAM << "\t" << longname << " (" << attrname << ")" << " = " << svalue << endl;
5272             }
5273 
5274             delete megaCmdListener;
5275         }
5276         return true;
5277     }
5278     return false;
5279 }
5280 
setProxy(const std::string & url,const std::string & username,const std::string & password,int proxyType)5281 bool MegaCmdExecuter::setProxy(const std::string &url, const std::string &username, const std::string &password, int proxyType)
5282 {
5283     MegaProxy mpx;
5284 
5285     if (url.size())
5286     {
5287         mpx.setProxyURL(url.c_str());
5288     }
5289 
5290     if (username.size())
5291     {
5292         mpx.setCredentials(username.c_str(), password.c_str());
5293     }
5294 
5295     mpx.setProxyType(proxyType);
5296 
5297     std::vector<MegaApi *> megaapis;
5298     //TODO: add apiFolders to that list
5299     megaapis.push_back(api);
5300     bool failed = false;
5301     for (auto api: megaapis)
5302     {
5303         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
5304         api->setProxySettings(&mpx, megaCmdListener);
5305         megaCmdListener->wait();
5306         if (checkNoErrors(megaCmdListener->getError(), "(un)setting proxy"))
5307         {
5308             //TODO: connectivity check! // review connectivity check branch
5309         }
5310         else
5311         {
5312             failed = true;
5313         }
5314         delete megaCmdListener;
5315     }
5316 
5317     if (!failed)
5318     {
5319         ConfigurationManager::savePropertyValue("proxy_url", mpx.getProxyURL()?mpx.getProxyURL():"");
5320         ConfigurationManager::savePropertyValue("proxy_type", mpx.getProxyType());
5321 
5322         ConfigurationManager::savePropertyValue("proxy_username", mpx.getUsername()?mpx.getUsername():"");
5323         ConfigurationManager::savePropertyValue("proxy_password", mpx.getPassword()?mpx.getPassword():"");
5324 
5325         if (mpx.getProxyType() == MegaProxy::PROXY_NONE)
5326         {
5327             OUTSTREAM << "Proxy unset correctly" << endl ;
5328         }
5329         else
5330         {
5331             OUTSTREAM << "Proxy set: " << (mpx.getProxyURL()?mpx.getProxyURL():"")
5332                       << " type = " << getProxyTypeStr(mpx.getProxyType()) << endl;
5333 
5334             broadcastMessage(string("Using proxy: ").append((mpx.getProxyURL()?mpx.getProxyURL():""))
5335                              .append(" type = ").append(getProxyTypeStr(mpx.getProxyType())), true);
5336         }
5337     }
5338     else
5339     {
5340         LOG_err << "Unable to configure proxy";
5341         broadcastMessage("Unable to configure proxy", true);
5342     }
5343 
5344     return !failed;
5345 }
5346 
executecommand(vector<string> words,map<string,int> * clflags,map<string,string> * cloptions)5347 void MegaCmdExecuter::executecommand(vector<string> words, map<string, int> *clflags, map<string, string> *cloptions)
5348 {
5349     MegaNode* n = NULL;
5350     if (words[0] == "ls")
5351     {
5352         if (!api->isFilesystemAvailable())
5353         {
5354             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5355             LOG_err << "Not logged in.";
5356             return;
5357         }
5358         int recursive = getFlag(clflags, "R") + getFlag(clflags, "r");
5359         int extended_info = getFlag(clflags, "a");
5360         int show_versions = getFlag(clflags, "versions");
5361         bool summary = getFlag(clflags, "l");
5362         bool firstprint = true;
5363         bool humanreadable = getFlag(clflags, "h");
5364         bool treelike = getFlag(clflags,"tree");
5365         recursive += treelike?1:0;
5366 
5367         if ((int)words.size() > 1)
5368         {
5369             unescapeifRequired(words[1]);
5370 
5371             string rNpath = "NULL";
5372             if (words[1].find('/') != string::npos)
5373             {
5374                 string cwpath = getCurrentPath();
5375                 if (words[1].find(cwpath) == string::npos)
5376                 {
5377                     rNpath = "";
5378                 }
5379                 else
5380                 {
5381                     rNpath = cwpath;
5382                 }
5383             }
5384 
5385             if (isRegExp(words[1]))
5386             {
5387                 vector<string> *pathsToList = nodesPathsbypath(words[1].c_str(), getFlag(clflags,"use-pcre"));
5388                 if (pathsToList && pathsToList->size())
5389                 {
5390                     for (std::vector< string >::iterator it = pathsToList->begin(); it != pathsToList->end(); ++it)
5391                     {
5392                         string nodepath= *it;
5393                         MegaNode *ncwd = api->getNodeByHandle(cwd);
5394                         if (ncwd)
5395                         {
5396                             MegaNode * n = nodebypath(nodepath.c_str());
5397                             if (n)
5398                             {
5399                                 if (!n->getType() == MegaNode::TYPE_FILE)
5400                                 {
5401                                     OUTSTREAM << nodepath << ": " << endl;
5402                                 }
5403                                 if (summary)
5404                                 {
5405                                     if (firstprint)
5406                                     {
5407                                         dumpNodeSummaryHeader(getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions);
5408                                         firstprint = false;
5409                                     }
5410                                     dumpTreeSummary(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions, recursive, show_versions, 0, humanreadable, rNpath);
5411                                 }
5412                                 else
5413                                 {
5414                                     vector<bool> lfs;
5415                                     dumptree(n, treelike, lfs, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, recursive, extended_info, show_versions, 0, rNpath);
5416                                 }
5417                                 if (( !n->getType() == MegaNode::TYPE_FILE ) && (( it + 1 ) != pathsToList->end()))
5418                                 {
5419                                     OUTSTREAM << endl;
5420                                 }
5421                                 delete n;
5422                             }
5423                             else
5424                             {
5425                                 LOG_debug << "Unexpected: matching path has no associated node: " << nodepath << ". Could have been deleted in the process";
5426                             }
5427                             delete ncwd;
5428                         }
5429                         else
5430                         {
5431                             setCurrentOutCode(MCMD_INVALIDSTATE);
5432                             LOG_err << "Couldn't find woking folder (it might been deleted)";
5433                         }
5434                     }
5435                     pathsToList->clear();
5436                     delete pathsToList;
5437                 }
5438                 else
5439                 {
5440                     setCurrentOutCode(MCMD_NOTFOUND);
5441                     LOG_err << "Couldn't find \"" << words[1] << "\"";
5442                 }
5443             }
5444             else
5445             {
5446                 n = nodebypath(words[1].c_str());
5447                 if (n)
5448                 {
5449                     if (summary)
5450                     {
5451                         if (firstprint)
5452                         {
5453                             dumpNodeSummaryHeader(getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions);
5454                             firstprint = false;
5455                         }
5456                         dumpTreeSummary(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions, recursive, show_versions, 0, humanreadable, rNpath);
5457                     }
5458                     else
5459                     {
5460                         if (treelike) OUTSTREAM << words[1] << endl;
5461                         vector<bool> lfs;
5462                         dumptree(n, treelike, lfs, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, recursive, extended_info, show_versions, 0, rNpath);
5463                     }
5464                     delete n;
5465                 }
5466                 else
5467                 {
5468                     setCurrentOutCode(MCMD_NOTFOUND);
5469                     LOG_err << "Couldn't find " << words[1];
5470                 }
5471             }
5472         }
5473         else
5474         {
5475             n = api->getNodeByHandle(cwd);
5476             if (n)
5477             {
5478                 if (summary)
5479                 {
5480                     if (firstprint)
5481                     {
5482                         dumpNodeSummaryHeader(getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions);
5483                         firstprint = false;
5484                     }
5485                     dumpTreeSummary(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","SHORT")), clflags, cloptions, recursive, show_versions, 0, humanreadable);
5486                 }
5487                 else
5488                 {
5489                     if (treelike) OUTSTREAM << "." << endl;
5490                     vector<bool> lfs;
5491                     dumptree(n, treelike, lfs, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, recursive, extended_info, show_versions);
5492                 }
5493                 delete n;
5494             }
5495         }
5496         return;
5497     }
5498     else if (words[0] == "find")
5499     {
5500         string pattern = getOption(cloptions, "pattern", "*");
5501         int printfileinfo = getFlag(clflags,"l");
5502 
5503         if (!api->isFilesystemAvailable())
5504         {
5505             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5506             LOG_err << "Not logged in.";
5507             return;
5508         }
5509 
5510         m_time_t minTime = -1;
5511         m_time_t maxTime = -1;
5512         string mtimestring = getOption(cloptions, "mtime", "");
5513         if ("" != mtimestring && !getMinAndMaxTime(mtimestring, &minTime, &maxTime))
5514         {
5515             setCurrentOutCode(MCMD_EARGS);
5516             LOG_err << "Invalid time " << mtimestring;
5517             return;
5518         }
5519 
5520         int64_t minSize = -1;
5521         int64_t maxSize = -1;
5522         string sizestring = getOption(cloptions, "size", "");
5523         if ("" != sizestring && !getMinAndMaxSize(sizestring, &minSize, &maxSize))
5524         {
5525             setCurrentOutCode(MCMD_EARGS);
5526             LOG_err << "Invalid time " << sizestring;
5527             return;
5528         }
5529 
5530 
5531         if (words.size() <= 1)
5532         {
5533             n = api->getNodeByHandle(cwd);
5534             doFind(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, "", printfileinfo, pattern, getFlag(clflags,"use-pcre"), minTime, maxTime, minSize, maxSize);
5535             delete n;
5536         }
5537         for (int i = 1; i < (int)words.size(); i++)
5538         {
5539             if (isRegExp(words[i]))
5540             {
5541                 vector<MegaNode *> *nodesToFind = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
5542                 if (nodesToFind->size())
5543                 {
5544                     for (std::vector< MegaNode * >::iterator it = nodesToFind->begin(); it != nodesToFind->end(); ++it)
5545                     {
5546                         MegaNode * nodeToFind = *it;
5547                         if (nodeToFind)
5548                         {
5549                             doFind(nodeToFind, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i], printfileinfo, pattern, getFlag(clflags,"use-pcre"), minTime, maxTime, minSize, maxSize);
5550                             delete nodeToFind;
5551                         }
5552                     }
5553                     nodesToFind->clear();
5554                 }
5555                 else
5556                 {
5557                     setCurrentOutCode(MCMD_NOTFOUND);
5558                     LOG_err << words[i] << ": No such file or directory";
5559                 }
5560                 delete nodesToFind;
5561             }
5562             else
5563             {
5564                 n = nodebypath(words[i].c_str());
5565                 if (!n)
5566                 {
5567                     setCurrentOutCode(MCMD_NOTFOUND);
5568                     LOG_err << "Couldn't find " << words[i];
5569                 }
5570                 else
5571                 {
5572                     doFind(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i], printfileinfo, pattern, getFlag(clflags,"use-pcre"), minTime, maxTime, minSize, maxSize);
5573                     delete n;
5574                 }
5575             }
5576         }
5577     }
5578 #if defined(_WIN32) || defined(__APPLE__)
5579     else if (words[0] == "update")
5580     {
5581         string sauto = getOption(cloptions, "auto", "");
5582         transform(sauto.begin(), sauto.end(), sauto.begin(), ::tolower);
5583 
5584         if (sauto == "off")
5585         {
5586             stopcheckingForUpdates();
5587             OUTSTREAM << "Automatic updates disabled" << endl;
5588         }
5589         else if (sauto == "on")
5590         {
5591             startcheckingForUpdates();
5592             OUTSTREAM << "Automatic updates enabled" << endl;
5593         }
5594         else if (sauto == "query")
5595         {
5596             OUTSTREAM << "Automatic updates " << (ConfigurationManager::getConfigurationValue("autoupdate", false)?"enabled":"disabled") << endl;
5597         }
5598         else
5599         {
5600             setCurrentOutCode(MCMD_EARGS);
5601             LOG_err << "      " << getUsageStr("update");
5602         }
5603 
5604         return;
5605     }
5606 #endif
5607     else if (words[0] == "cd")
5608     {
5609         if (!api->isFilesystemAvailable())
5610         {
5611             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5612             LOG_err << "Not logged in.";
5613             return;
5614         }
5615         if (words.size() > 1)
5616         {
5617             if (( n = nodebypath(words[1].c_str())))
5618             {
5619                 if (n->getType() == MegaNode::TYPE_FILE)
5620                 {
5621                     setCurrentOutCode(MCMD_NOTFOUND);
5622                     LOG_err << words[1] << ": Not a directory";
5623                 }
5624                 else
5625                 {
5626                     cwd = n->getHandle();
5627 
5628                     updateprompt(api);
5629                 }
5630                 delete n;
5631             }
5632             else
5633             {
5634                 setCurrentOutCode(MCMD_NOTFOUND);
5635                 LOG_err << words[1] << ": No such file or directory";
5636             }
5637         }
5638         else
5639         {
5640             MegaNode * rootNode = api->getRootNode();
5641             if (!rootNode)
5642             {
5643                 LOG_err << "nodes not fetched";
5644                 setCurrentOutCode(MCMD_NOFETCH);
5645                 delete rootNode;
5646                 return;
5647             }
5648             cwd = rootNode->getHandle();
5649             updateprompt(api);
5650 
5651             delete rootNode;
5652         }
5653 
5654         return;
5655     }
5656     else if (words[0] == "rm")
5657     {
5658         if (!api->isFilesystemAvailable())
5659         {
5660             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5661             LOG_err << "Not logged in.";
5662             return;
5663         }
5664         if (words.size() > 1)
5665         {
5666             if (interactiveThread() && nodesToConfirmDelete.size())
5667             {
5668                 //clear all previous nodes to confirm delete (could have been not cleared in case of ctrl+c)
5669                 for (std::vector< MegaNode * >::iterator it = nodesToConfirmDelete.begin(); it != nodesToConfirmDelete.end(); ++it)
5670                 {
5671                     delete *it;
5672                 }
5673                 nodesToConfirmDelete.clear();
5674             }
5675 
5676             bool force = getFlag(clflags, "f");
5677             bool none = false;
5678 
5679             for (unsigned int i = 1; i < words.size(); i++)
5680             {
5681                 unescapeifRequired(words[i]);
5682                 if (isRegExp(words[i]))
5683                 {
5684                     vector<MegaNode *> *nodesToDelete = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
5685                     if (nodesToDelete->size())
5686                     {
5687                         for (std::vector< MegaNode * >::iterator it = nodesToDelete->begin(); !none && it != nodesToDelete->end(); ++it)
5688                         {
5689                             MegaNode * nodeToDelete = *it;
5690                             if (nodeToDelete)
5691                             {
5692                                 int confirmationCode = deleteNode(nodeToDelete, api, getFlag(clflags, "r"), force);
5693                                 if (confirmationCode == MCMDCONFIRM_ALL)
5694                                 {
5695                                     force = true;
5696                                 }
5697                                 else if (confirmationCode == MCMDCONFIRM_NONE)
5698                                 {
5699                                     none = true;
5700                                 }
5701 
5702                             }
5703                         }
5704                         nodesToDelete->clear();
5705                     }
5706                     else
5707                     {
5708                         setCurrentOutCode(MCMD_NOTFOUND);
5709                         LOG_err << words[i] << ": No such file or directory";
5710                     }
5711                     delete nodesToDelete;
5712                 }
5713                 else if (!none)
5714                 {
5715                     MegaNode * nodeToDelete = nodebypath(words[i].c_str());
5716                     if (nodeToDelete)
5717                     {
5718                         int confirmationCode = deleteNode(nodeToDelete, api, getFlag(clflags, "r"), force);
5719                         if (confirmationCode == MCMDCONFIRM_ALL)
5720                         {
5721                             force = true;
5722                         }
5723                         else if (confirmationCode == MCMDCONFIRM_NONE)
5724                         {
5725                             none = true;
5726                         }
5727                     }
5728                     else
5729                     {
5730                         setCurrentOutCode(MCMD_NOTFOUND);
5731                         LOG_err << words[i] << ": No such file or directory";
5732                     }
5733                 }
5734             }
5735         }
5736         else
5737         {
5738             setCurrentOutCode(MCMD_EARGS);
5739             LOG_err << "      " << getUsageStr("rm");
5740         }
5741 
5742         return;
5743     }
5744     else if (words[0] == "mv")
5745     {
5746         if (!api->isFilesystemAvailable())
5747         {
5748             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5749             LOG_err << "Not logged in.";
5750             return;
5751         }
5752         if (words.size() > 2)
5753         {
5754             string destiny = words[words.size()-1];
5755             unescapeifRequired(destiny);
5756 
5757             if (words.size() > 3 && !isValidFolder(destiny))
5758             {
5759                 setCurrentOutCode(MCMD_INVALIDTYPE);
5760                 LOG_err << destiny << " must be a valid folder";
5761                 return;
5762             }
5763 
5764             for (unsigned int i=1;i<(words.size()-1);i++)
5765             {
5766                 string source = words[i];
5767                 unescapeifRequired(source);
5768 
5769                 if (isRegExp(source))
5770                 {
5771                     vector<MegaNode *> *nodesToList = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
5772                     if (nodesToList)
5773                     {
5774                         if (!nodesToList->size())
5775                         {
5776                             setCurrentOutCode(MCMD_NOTFOUND);
5777                             LOG_err << source << ": No such file or directory";
5778                         }
5779 
5780                         bool destinyisok=true;
5781                         if (nodesToList->size() > 1 && !isValidFolder(destiny))
5782                         {
5783                             destinyisok = false;
5784                             setCurrentOutCode(MCMD_INVALIDTYPE);
5785                             LOG_err << destiny << " must be a valid folder";
5786                         }
5787 
5788                         if (destinyisok)
5789                         {
5790                             for (std::vector< MegaNode * >::iterator it = nodesToList->begin(); it != nodesToList->end(); ++it)
5791                             {
5792                                 MegaNode * n = *it;
5793                                 if (n)
5794                                 {
5795                                     move(n, destiny);
5796                                     delete n;
5797                                 }
5798                             }
5799                         }
5800 
5801                         nodesToList->clear();
5802                         delete nodesToList;
5803                     }
5804                 }
5805                 else
5806                 {
5807                     if (( n = nodebypath(source.c_str())) )
5808                     {
5809                         move(n, destiny);
5810                         delete n;
5811                     }
5812                     else
5813                     {
5814                         setCurrentOutCode(MCMD_NOTFOUND);
5815                         LOG_err << source << ": No such file or directory";
5816                     }
5817                 }
5818             }
5819 
5820         }
5821         else
5822         {
5823             setCurrentOutCode(MCMD_EARGS);
5824             LOG_err << "      " << getUsageStr("mv");
5825         }
5826 
5827         return;
5828     }
5829     else if (words[0] == "cp")
5830     {
5831         if (!api->isFilesystemAvailable())
5832         {
5833             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5834             LOG_err << "Not logged in.";
5835             return;
5836         }
5837         if (words.size() > 2)
5838         {
5839             string destiny = words[words.size()-1];
5840             string targetuser;
5841             string newname;
5842             MegaNode *tn = nodebypath(destiny.c_str(), &targetuser, &newname);
5843 
5844             if (words.size() > 3 && !isValidFolder(destiny) && !targetuser.size())
5845             {
5846                 setCurrentOutCode(MCMD_INVALIDTYPE);
5847                 LOG_err << destiny << " must be a valid folder";
5848                 return;
5849             }
5850 
5851             for (unsigned int i=1;i<(words.size()-1);i++)
5852             {
5853                 string source = words[i];
5854 
5855                 if (isRegExp(source))
5856                 {
5857                     vector<MegaNode *> *nodesToCopy = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
5858                     if (nodesToCopy)
5859                     {
5860                         if (!nodesToCopy->size())
5861                         {
5862                             setCurrentOutCode(MCMD_NOTFOUND);
5863                             LOG_err << source << ": No such file or directory";
5864                         }
5865 
5866                         bool destinyisok=true;
5867                         if (nodesToCopy->size() > 1 && !isValidFolder(destiny) && !targetuser.size())
5868                         {
5869                             destinyisok = false;
5870                             setCurrentOutCode(MCMD_INVALIDTYPE);
5871                             LOG_err << destiny << " must be a valid folder";
5872                         }
5873 
5874                         if (destinyisok)
5875                         {
5876                             for (std::vector< MegaNode * >::iterator it = nodesToCopy->begin(); it != nodesToCopy->end(); ++it)
5877                             {
5878                                 MegaNode * n = *it;
5879                                 if (n)
5880                                 {
5881                                     copyNode(n, destiny, tn, targetuser, newname);
5882                                     delete n;
5883                                 }
5884                             }
5885                         }
5886                         nodesToCopy->clear();
5887                         delete nodesToCopy;
5888                     }
5889                 }
5890                 else if (( n = nodebypath(source.c_str())))
5891                 {
5892                     copyNode(n, destiny, tn, targetuser, newname);
5893                     delete n;
5894                 }
5895                 else
5896                 {
5897                     setCurrentOutCode(MCMD_NOTFOUND);
5898                     LOG_err << source << ": No such file or directory";
5899                 }
5900             }
5901             delete tn;
5902         }
5903         else
5904         {
5905             setCurrentOutCode(MCMD_EARGS);
5906             LOG_err << "      " << getUsageStr("cp");
5907         }
5908 
5909         return;
5910     }
5911     else if (words[0] == "du")
5912     {
5913         if (!api->isFilesystemAvailable())
5914         {
5915             setCurrentOutCode(MCMD_NOTLOGGEDIN);
5916             LOG_err << "Not logged in.";
5917             return;
5918         }
5919 
5920         int PATHSIZE = getintOption(cloptions,"path-display-size");
5921         if (!PATHSIZE)
5922         {
5923             // get screen size for output purposes
5924             unsigned int width = getNumberOfCols(75);
5925             PATHSIZE = min(50,int(width-22));
5926         }
5927         PATHSIZE = max(0, PATHSIZE);
5928 
5929         long long totalSize = 0;
5930         long long currentSize = 0;
5931         long long totalVersionsSize = 0;
5932         string dpath;
5933         if (words.size() == 1)
5934         {
5935             words.push_back(".");
5936         }
5937 
5938         bool humanreadable = getFlag(clflags, "h");
5939         bool show_versions_size = getFlag(clflags, "versions");
5940         bool firstone = true;
5941 
5942         for (unsigned int i = 1; i < words.size(); i++)
5943         {
5944             unescapeifRequired(words[i]);
5945             if (isRegExp(words[i]))
5946             {
5947                 vector<MegaNode *> *nodesToList = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
5948                 if (nodesToList)
5949                 {
5950                     for (std::vector< MegaNode * >::iterator it = nodesToList->begin(); it != nodesToList->end(); ++it)
5951                     {
5952                         MegaNode * n = *it;
5953                         if (n)
5954                         {
5955                             if (firstone)//print header
5956                             {
5957                                 OUTSTREAM << getFixLengthString("FILENAME",PATHSIZE) << getFixLengthString("SIZE", 12, ' ', true);
5958                                 if (show_versions_size)
5959                                 {
5960                                     OUTSTREAM << getFixLengthString("S.WITH VERS", 12, ' ', true);;
5961                                 }
5962                                 OUTSTREAM << endl;
5963                                 firstone = false;
5964                             }
5965                             currentSize = api->getSize(n);
5966                             totalSize += currentSize;
5967 
5968                             dpath = getDisplayPath(words[i], n);
5969                             OUTSTREAM << getFixLengthString(dpath+":",PATHSIZE) << getFixLengthString(sizeToText(currentSize, true, humanreadable), 12, ' ', true);
5970                             if (show_versions_size)
5971                             {
5972                                 long long sizeWithVersions = getVersionsSize(n);
5973                                 OUTSTREAM << getFixLengthString(sizeToText(sizeWithVersions, true, humanreadable), 12, ' ', true);
5974                                 totalVersionsSize += sizeWithVersions;
5975                             }
5976 
5977                             OUTSTREAM << endl;
5978                             delete n;
5979                         }
5980                     }
5981 
5982                     nodesToList->clear();
5983                     delete nodesToList;
5984                 }
5985             }
5986             else
5987             {
5988                 if (!( n = nodebypath(words[i].c_str())))
5989                 {
5990                     setCurrentOutCode(MCMD_NOTFOUND);
5991                     LOG_err << words[i] << ": No such file or directory";
5992                     return;
5993                 }
5994 
5995                 currentSize = api->getSize(n);
5996                 totalSize += currentSize;
5997                 dpath = getDisplayPath(words[i], n);
5998                 if (dpath.size())
5999                 {
6000                     if (firstone)//print header
6001                     {
6002                         OUTSTREAM << getFixLengthString("FILENAME",PATHSIZE) << getFixLengthString("SIZE", 12, ' ', true);
6003                         if (show_versions_size)
6004                         {
6005                             OUTSTREAM << getFixLengthString("S.WITH VERS", 12, ' ', true);;
6006                         }
6007                         OUTSTREAM << endl;
6008                         firstone = false;
6009                     }
6010 
6011                     OUTSTREAM << getFixLengthString(dpath+":",PATHSIZE) << getFixLengthString(sizeToText(currentSize, true, humanreadable), 12, ' ', true);
6012                     if (show_versions_size)
6013                     {
6014                         long long sizeWithVersions = getVersionsSize(n);
6015                         OUTSTREAM << getFixLengthString(sizeToText(sizeWithVersions, true, humanreadable), 12, ' ', true);
6016                         totalVersionsSize += sizeWithVersions;
6017                     }
6018                     OUTSTREAM << endl;
6019 
6020                 }
6021                 delete n;
6022             }
6023         }
6024 
6025         if (!firstone)
6026         {
6027             for (int i = 0; i < PATHSIZE+12 +(show_versions_size?12:0) ; i++)
6028             {
6029                 OUTSTREAM << "-";
6030             }
6031             OUTSTREAM << endl;
6032 
6033             OUTSTREAM << getFixLengthString("Total storage used:",PATHSIZE) << getFixLengthString(sizeToText(totalSize, true, humanreadable), 12, ' ', true);
6034             //OUTSTREAM << "Total storage used: " << setw(22) << sizeToText(totalSize, true, humanreadable);
6035             if (show_versions_size)
6036             {
6037                 OUTSTREAM << getFixLengthString(sizeToText(totalVersionsSize, true, humanreadable), 12, ' ', true);
6038             }
6039             OUTSTREAM << endl;
6040         }
6041         return;
6042     }
6043     else if (words[0] == "cat")
6044     {
6045         if (words.size() < 2)
6046         {
6047             setCurrentOutCode(MCMD_EARGS);
6048             LOG_err << "      " << getUsageStr("cat");
6049             return;
6050         }
6051 
6052         for (int i = 1; i < (int)words.size(); i++)
6053         {
6054             if (isPublicLink(words[i]))
6055             {
6056                 string publicLink = words[i];
6057                 if (isEncryptedLink(publicLink))
6058                 {
6059                     string linkPass = getOption(cloptions, "password", "");
6060                     if (!linkPass.size())
6061                     {
6062                         linkPass = askforUserResponse("Enter password: ");
6063                     }
6064 
6065                     if (linkPass.size())
6066                     {
6067                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
6068                         api->decryptPasswordProtectedLink(publicLink.c_str(), linkPass.c_str(), megaCmdListener);
6069                         megaCmdListener->wait();
6070                         if (checkNoErrors(megaCmdListener->getError(), "decrypt password protected link"))
6071                         {
6072                             publicLink = megaCmdListener->getRequest()->getText();
6073                             delete megaCmdListener;
6074                         }
6075                         else
6076                         {
6077                             setCurrentOutCode(MCMD_NOTPERMITTED);
6078                             LOG_err << "Invalid password";
6079                             delete megaCmdListener;
6080                             return;
6081                         }
6082                     }
6083                     else
6084                     {
6085                         setCurrentOutCode(MCMD_EARGS);
6086                         LOG_err << "Need a password to decrypt provided link (--password=PASSWORD)";
6087                         return;
6088                     }
6089                 }
6090 
6091                 if (getLinkType(publicLink) == MegaNode::TYPE_FILE)
6092                 {
6093                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
6094                     api->getPublicNode(publicLink.c_str(), megaCmdListener);
6095                     megaCmdListener->wait();
6096 
6097                     if (!checkNoErrors(megaCmdListener->getError(), "cat public node"))
6098                     {
6099                         if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EARGS)
6100                         {
6101                             LOG_err << "The link provided might be incorrect: " << publicLink.c_str();
6102                         }
6103                         else if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EINCOMPLETE)
6104                         {
6105                             LOG_err << "The key is missing or wrong " << publicLink.c_str();
6106                         }
6107                     }
6108                     else
6109                     {
6110                         if (megaCmdListener->getRequest()->getFlag())
6111                         {
6112                             LOG_err << "Key not valid " << publicLink.c_str();
6113                             setCurrentOutCode(MCMD_EARGS);
6114                         }
6115                         else
6116                         {
6117                             MegaNode *n = megaCmdListener->getRequest()->getPublicMegaNode();
6118                             if (n)
6119                             {
6120                                 catFile(n);
6121                                 delete n;
6122                             }
6123                         }
6124 
6125                     }
6126                     delete megaCmdListener;
6127                 }
6128                 else //TODO: detect if referenced file within public link and in that case, do login and cat it
6129                 {
6130                     LOG_err << "Public link is not a file";
6131                     setCurrentOutCode(MCMD_EARGS);
6132                 }
6133             }
6134             else if (!api->isFilesystemAvailable())
6135             {
6136                 setCurrentOutCode(MCMD_NOTLOGGEDIN);
6137                 LOG_err << "Unable to cat " << words[i] << ": Not logged in.";
6138             }
6139             else
6140             {
6141                 unescapeifRequired(words[i]);
6142                 if (isRegExp(words[i]))
6143                 {
6144                     vector<MegaNode *> *nodes = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
6145                     if (nodes)
6146                     {
6147                         if (!nodes->size())
6148                         {
6149                             setCurrentOutCode(MCMD_NOTFOUND);
6150                             LOG_err << "Nodes not found: " << words[i];
6151                         }
6152                         for (std::vector< MegaNode * >::iterator it = nodes->begin(); it != nodes->end(); ++it)
6153                         {
6154                             MegaNode * n = *it;
6155                             if (n)
6156                             {
6157                                 catFile(n);
6158                                 delete n;
6159                             }
6160                         }
6161                     }
6162                 }
6163                 else
6164                 {
6165                     MegaNode *n = nodebypath(words[i].c_str());
6166                     if (n)
6167                     {
6168                         catFile(n);
6169                         delete n;
6170                     }
6171                     else
6172                     {
6173                         setCurrentOutCode(MCMD_NOTFOUND);
6174                         LOG_err << "Node not found: " << words[i];
6175                     }
6176                 }
6177             }
6178         }
6179     }
6180     else if (words[0] == "mediainfo")
6181     {
6182         if (!api->isFilesystemAvailable())
6183         {
6184             setCurrentOutCode(MCMD_NOTLOGGEDIN);
6185             LOG_err << "Not logged in.";
6186             return;
6187         }
6188         if (words.size() < 2)
6189         {
6190             setCurrentOutCode(MCMD_EARGS);
6191             LOG_err << "      " << getUsageStr("mediainfo");
6192             return;
6193         }
6194 
6195         int PATHSIZE = getintOption(cloptions,"path-display-size");
6196         if (!PATHSIZE)
6197         {
6198             // get screen size for output purposes
6199             unsigned int width = getNumberOfCols(75);
6200             PATHSIZE = min(50,int(width-28));
6201         }
6202         PATHSIZE = max(0, PATHSIZE);
6203 
6204         bool firstone = true;
6205         for (int i = 1; i < (int)words.size(); i++)
6206         {
6207             unescapeifRequired(words[i]);
6208             if (isRegExp(words[i]))
6209             {
6210                 vector<MegaNode *> *nodes = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
6211                 if (nodes)
6212                 {
6213                     if (!nodes->size())
6214                     {
6215                         setCurrentOutCode(MCMD_NOTFOUND);
6216                         LOG_err << "Nodes not found: " << words[i];
6217                     }
6218                     for (std::vector< MegaNode * >::iterator it = nodes->begin(); it != nodes->end(); ++it)
6219                     {
6220                         MegaNode * n = *it;
6221                         if (n)
6222                         {
6223                             printInfoFile(n, firstone, PATHSIZE);
6224                             delete n;
6225                         }
6226                     }
6227                 }
6228             }
6229             else
6230             {
6231                 MegaNode *n = nodebypath(words[i].c_str());
6232                 if (n)
6233                 {
6234                     printInfoFile(n, firstone, PATHSIZE);
6235                     delete n;
6236                 }
6237                 else
6238                 {
6239                     setCurrentOutCode(MCMD_NOTFOUND);
6240                     LOG_err << "Node not found: " << words[i];
6241                 }
6242             }
6243         }
6244     }
6245     else if (words[0] == "get")
6246     {
6247         int clientID = getintOption(cloptions, "clientID", -1);
6248         if (words.size() > 1 && words.size() < 4)
6249         {
6250             string path = "./";
6251             bool background = getFlag(clflags,"q");
6252             if (background)
6253             {
6254                 clientID = -1;
6255             }
6256 
6257             MegaCmdMultiTransferListener *megaCmdMultiTransferListener = new MegaCmdMultiTransferListener(api, sandboxCMD, NULL, clientID);
6258 
6259             bool ignorequotawarn = getFlag(clflags,"ignore-quota-warn");
6260             bool destinyIsFolder = false;
6261 
6262             if (isPublicLink(words[1]))
6263             {
6264                 string publicLink = words[1];
6265                 if (isEncryptedLink(publicLink))
6266                 {
6267                     string linkPass = getOption(cloptions, "password", "");
6268                     if (!linkPass.size())
6269                     {
6270                         linkPass = askforUserResponse("Enter password: ");
6271                     }
6272 
6273                     if (linkPass.size())
6274                     {
6275                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
6276                         api->decryptPasswordProtectedLink(publicLink.c_str(), linkPass.c_str(), megaCmdListener);
6277                         megaCmdListener->wait();
6278                         if (checkNoErrors(megaCmdListener->getError(), "decrypt password protected link"))
6279                         {
6280                             publicLink = megaCmdListener->getRequest()->getText();
6281                             delete megaCmdListener;
6282                         }
6283                         else
6284                         {
6285                             setCurrentOutCode(MCMD_NOTPERMITTED);
6286                             LOG_err << "Invalid password";
6287                             delete megaCmdListener;
6288                             return;
6289                         }
6290                     }
6291                     else
6292                     {
6293                         setCurrentOutCode(MCMD_EARGS);
6294                         LOG_err << "Need a password to decrypt provided link (--password=PASSWORD)";
6295                         return;
6296                     }
6297                 }
6298 
6299                 if (getLinkType(publicLink) == MegaNode::TYPE_FILE)
6300                 {
6301                     if (words.size() > 2)
6302                     {
6303                         path = words[2];
6304                         destinyIsFolder = IsFolder(path);
6305                         if (destinyIsFolder)
6306                         {
6307                             if (! (path.find_last_of("/") == path.size()-1) && ! (path.find_last_of("\\") == path.size()-1))
6308                             {
6309 #ifdef _WIN32
6310                                 path+="\\";
6311 #else
6312                                 path+="/";
6313 #endif
6314                             }
6315                             if (!canWrite(path))
6316                             {
6317                                 setCurrentOutCode(MCMD_NOTPERMITTED);
6318                                 LOG_err << "Write not allowed in " << path;
6319                                 delete megaCmdMultiTransferListener;
6320                                 return;
6321                             }
6322                         }
6323                         else
6324                         {
6325                             if (!TestCanWriteOnContainingFolder(&path))
6326                             {
6327                                 delete megaCmdMultiTransferListener;
6328                                 return;
6329                             }
6330                         }
6331                     }
6332                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
6333                     api->getPublicNode(publicLink.c_str(), megaCmdListener);
6334                     megaCmdListener->wait();
6335 
6336                     if (!megaCmdListener->getError())
6337                     {
6338                         LOG_fatal << "No error in listener at get public node";
6339                     }
6340                     else if (!checkNoErrors(megaCmdListener->getError(), "get public node"))
6341                     {
6342                         if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EARGS)
6343                         {
6344                             LOG_err << "The link provided might be incorrect: " << publicLink.c_str();
6345                         }
6346                         else if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EINCOMPLETE)
6347                         {
6348                             LOG_err << "The key is missing or wrong " << publicLink.c_str();
6349                         }
6350                     }
6351                     else
6352                     {
6353                         if (megaCmdListener->getRequest() && megaCmdListener->getRequest()->getFlag())
6354                         {
6355                             LOG_err << "Key not valid " << publicLink.c_str();
6356                         }
6357                         if (megaCmdListener->getRequest())
6358                         {
6359                             if (destinyIsFolder && getFlag(clflags,"m"))
6360                             {
6361                                 while( (path.find_last_of("/") == path.size()-1) || (path.find_last_of("\\") == path.size()-1))
6362                                 {
6363                                     path=path.substr(0,path.size()-1);
6364                                 }
6365                             }
6366                             MegaNode *n = megaCmdListener->getRequest()->getPublicMegaNode();
6367                             downloadNode(path, api, n, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6368                             delete n;
6369                         }
6370                         else
6371                         {
6372                             LOG_err << "Empty Request at get";
6373                         }
6374                     }
6375                     delete megaCmdListener;
6376                 }
6377                 else if (getLinkType(publicLink) == MegaNode::TYPE_FOLDER)
6378                 {
6379                     if (words.size() > 2)
6380                     {
6381                         path = words[2];
6382                         destinyIsFolder = IsFolder(path);
6383                         if (destinyIsFolder)
6384                         {
6385                             if (! (path.find_last_of("/") == path.size()-1) && ! (path.find_last_of("\\") == path.size()-1))
6386                             {
6387 #ifdef _WIN32
6388                                 path+="\\";
6389 #else
6390                                 path+="/";
6391 #endif
6392                             }
6393                             if (!canWrite(words[2]))
6394                             {
6395                                 setCurrentOutCode(MCMD_NOTPERMITTED);
6396                                 LOG_err << "Write not allowed in " << words[2];
6397                                 delete megaCmdMultiTransferListener;
6398                                 return;
6399                             }
6400                         }
6401                         else
6402                         {
6403                             setCurrentOutCode(MCMD_INVALIDTYPE);
6404                             LOG_err << words[2] << " is not a valid Download Folder";
6405                             delete megaCmdMultiTransferListener;
6406                             return;
6407                         }
6408                     }
6409 
6410                     MegaApi* apiFolder = getFreeApiFolder();
6411                     char *accountAuth = api->getAccountAuth();
6412                     apiFolder->setAccountAuth(accountAuth);
6413                     delete []accountAuth;
6414 
6415                     MegaCmdListener *megaCmdListener = new MegaCmdListener(apiFolder, NULL);
6416                     apiFolder->loginToFolder(publicLink.c_str(), megaCmdListener);
6417                     megaCmdListener->wait();
6418                     if (checkNoErrors(megaCmdListener->getError(), "login to folder"))
6419                     {
6420                         MegaCmdListener *megaCmdListener2 = new MegaCmdListener(apiFolder, NULL);
6421                         apiFolder->fetchNodes(megaCmdListener2);
6422                         megaCmdListener2->wait();
6423                         if (checkNoErrors(megaCmdListener2->getError(), "access folder link " + publicLink))
6424                         {
6425                             MegaNode *nodeToDownload = NULL;
6426                             bool usedRoot = false;
6427                             string shandle = getPublicLinkHandle(publicLink);
6428                             if (shandle.size())
6429                             {
6430                                 handle thehandle = apiFolder->base64ToHandle(shandle.c_str());
6431                                 nodeToDownload = apiFolder->getNodeByHandle(thehandle);
6432                             }
6433                             else
6434                             {
6435                                 nodeToDownload = apiFolder->getRootNode();
6436                                 usedRoot = true;
6437                             }
6438 
6439                             if (nodeToDownload)
6440                             {
6441                                 if (destinyIsFolder && getFlag(clflags,"m"))
6442                                 {
6443                                     while( (path.find_last_of("/") == path.size()-1) || (path.find_last_of("\\") == path.size()-1))
6444                                     {
6445                                         path=path.substr(0,path.size()-1);
6446                                     }
6447                                 }
6448                                 MegaNode *authorizedNode = apiFolder->authorizeNode(nodeToDownload);
6449                                 if (authorizedNode != NULL)
6450                                 {
6451                                     downloadNode(path, api, authorizedNode, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6452                                     delete authorizedNode;
6453                                 }
6454                                 else
6455                                 {
6456                                     LOG_debug << "Node couldn't be authorized: " << publicLink << ". Downloading as non-loged user";
6457                                     downloadNode(path, apiFolder, nodeToDownload, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6458                                 }
6459                                 delete nodeToDownload;
6460                             }
6461                             else
6462                             {
6463                                 setCurrentOutCode(MCMD_INVALIDSTATE);
6464                                 if (usedRoot)
6465                                 {
6466                                     LOG_err << "Couldn't get root folder for folder link";
6467                                 }
6468                                 else
6469                                 {
6470                                     LOG_err << "Failed to get node corresponding to handle within public link " << shandle;
6471                                 }
6472                             }
6473                         }
6474                         delete megaCmdListener2;
6475                     }
6476                     delete megaCmdListener;
6477                     freeApiFolder(apiFolder);
6478                 }
6479                 else
6480                 {
6481                     setCurrentOutCode(MCMD_INVALIDTYPE);
6482                     LOG_err << "Invalid link: " << publicLink;
6483                 }
6484             }
6485             else //remote file
6486             {
6487                 if (!api->isFilesystemAvailable())
6488                 {
6489                     setCurrentOutCode(MCMD_NOTLOGGEDIN);
6490                     LOG_err << "Not logged in.";
6491                     return;
6492                 }
6493                 unescapeifRequired(words[1]);
6494 
6495                 if (isRegExp(words[1]))
6496                 {
6497                     vector<MegaNode *> *nodesToGet = nodesbypath(words[1].c_str(), getFlag(clflags,"use-pcre"));
6498                     if (nodesToGet)
6499                     {
6500                         if (words.size() > 2)
6501                         {
6502                             path = words[2];
6503                             destinyIsFolder = IsFolder(path);
6504                             if (destinyIsFolder)
6505                             {
6506                                 if (! (path.find_last_of("/") == path.size()-1) && ! (path.find_last_of("\\") == path.size()-1))
6507                                 {
6508 #ifdef _WIN32
6509                                     path+="\\";
6510 #else
6511                                     path+="/";
6512 #endif
6513                                 }
6514                                 if (!canWrite(words[2]))
6515                                 {
6516                                     setCurrentOutCode(MCMD_NOTPERMITTED);
6517                                     LOG_err << "Write not allowed in " << words[2];
6518                                     for (std::vector< MegaNode * >::iterator it = nodesToGet->begin(); it != nodesToGet->end(); ++it)
6519                                     {
6520                                         delete (MegaNode *)*it;
6521                                     }
6522                                     delete nodesToGet;
6523                                     delete megaCmdMultiTransferListener;
6524                                     return;
6525                                 }
6526                             }
6527                             else if (nodesToGet->size()>1) //several files into one file!
6528                             {
6529                                 setCurrentOutCode(MCMD_INVALIDTYPE);
6530                                 LOG_err << words[2] << " is not a valid Download Folder";
6531                                 for (std::vector< MegaNode * >::iterator it = nodesToGet->begin(); it != nodesToGet->end(); ++it)
6532                                 {
6533                                     delete (MegaNode *)*it;
6534                                 }
6535                                 delete nodesToGet;
6536                                 delete megaCmdMultiTransferListener;
6537                                 return;
6538                             }
6539                             else //destiny non existing or a file
6540                             {
6541                                 if (!TestCanWriteOnContainingFolder(&path))
6542                                 {
6543                                     for (std::vector< MegaNode * >::iterator it = nodesToGet->begin(); it != nodesToGet->end(); ++it)
6544                                     {
6545                                         delete (MegaNode *)*it;
6546                                     }
6547                                     delete nodesToGet;
6548                                     delete megaCmdMultiTransferListener;
6549                                     return;
6550                                 }
6551                             }
6552                         }
6553                         if (destinyIsFolder && getFlag(clflags,"m"))
6554                         {
6555                             while( (path.find_last_of("/") == path.size()-1) || (path.find_last_of("\\") == path.size()-1))
6556                             {
6557                                 path=path.substr(0,path.size()-1);
6558                             }
6559                         }
6560                         for (std::vector< MegaNode * >::iterator it = nodesToGet->begin(); it != nodesToGet->end(); ++it)
6561                         {
6562                             MegaNode * n = *it;
6563                             if (n)
6564                             {
6565                                 downloadNode(path, api, n, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6566                                 delete n;
6567                             }
6568                         }
6569                         if (!nodesToGet->size())
6570                         {
6571                             setCurrentOutCode(MCMD_NOTFOUND);
6572                             LOG_err << "Couldn't find " << words[1];
6573                         }
6574 
6575                         nodesToGet->clear();
6576                         delete nodesToGet;
6577                     }
6578                 }
6579                 else //not regexp
6580                 {
6581                     MegaNode *n = nodebypath(words[1].c_str());
6582                     if (n)
6583                     {
6584                         if (words.size() > 2)
6585                         {
6586                             if (n->getType() == MegaNode::TYPE_FILE)
6587                             {
6588                                 path = words[2];
6589                                 destinyIsFolder = IsFolder(path);
6590                                 if (destinyIsFolder)
6591                                 {
6592                                     if (! (path.find_last_of("/") == path.size()-1) && ! (path.find_last_of("\\") == path.size()-1))
6593                                     {
6594 #ifdef _WIN32
6595                                         path+="\\";
6596 #else
6597                                         path+="/";
6598 #endif
6599                                     }
6600                                     if (!canWrite(words[2]))
6601                                     {
6602                                         setCurrentOutCode(MCMD_NOTPERMITTED);
6603                                         LOG_err << "Write not allowed in " << words[2];
6604                                         delete megaCmdMultiTransferListener;
6605                                         return;
6606                                     }
6607                                 }
6608                                 else
6609                                 {
6610                                     if (!TestCanWriteOnContainingFolder(&path))
6611                                     {
6612                                         delete megaCmdMultiTransferListener;
6613                                         return;
6614                                     }
6615                                 }
6616                             }
6617                             else
6618                             {
6619                                 path = words[2];
6620                                 destinyIsFolder = IsFolder(path);
6621                                 if (destinyIsFolder)
6622                                 {
6623                                     if (! (path.find_last_of("/") == path.size()-1) && ! (path.find_last_of("\\") == path.size()-1))
6624                                     {
6625 #ifdef _WIN32
6626                                         path+="\\";
6627 #else
6628                                         path+="/";
6629 #endif
6630                                     }
6631                                     if (!canWrite(words[2]))
6632                                     {
6633                                         setCurrentOutCode(MCMD_NOTPERMITTED);
6634                                         LOG_err << "Write not allowed in " << words[2];
6635                                         delete megaCmdMultiTransferListener;
6636                                         return;
6637                                     }
6638                                 }
6639                                 else
6640                                 {
6641                                     setCurrentOutCode(MCMD_INVALIDTYPE);
6642                                     LOG_err << words[2] << " is not a valid Download Folder";
6643                                     delete megaCmdMultiTransferListener;
6644                                     return;
6645                                 }
6646                             }
6647                         }
6648                         if (destinyIsFolder && getFlag(clflags,"m"))
6649                         {
6650                             while( (path.find_last_of("/") == path.size()-1) || (path.find_last_of("\\") == path.size()-1))
6651                             {
6652                                 path=path.substr(0,path.size()-1);
6653                             }
6654                         }
6655                         downloadNode(path, api, n, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6656                         delete n;
6657                     }
6658                     else
6659                     {
6660                         setCurrentOutCode(MCMD_NOTFOUND);
6661                         LOG_err << "Couldn't find file";
6662                     }
6663                 }
6664             }
6665 
6666             megaCmdMultiTransferListener->waitMultiEnd();
6667             if (megaCmdMultiTransferListener->getFinalerror() != MegaError::API_OK)
6668             {
6669                 setCurrentOutCode(megaCmdMultiTransferListener->getFinalerror());
6670                 LOG_err << "Download failed. error code: " << MegaError::getErrorString(megaCmdMultiTransferListener->getFinalerror());
6671             }
6672 
6673             if (megaCmdMultiTransferListener->getProgressinformed() || getCurrentOutCode() == MCMD_OK )
6674             {
6675                 informProgressUpdate(PROGRESS_COMPLETE, megaCmdMultiTransferListener->getTotalbytes(), clientID);
6676             }
6677             delete megaCmdMultiTransferListener;
6678         }
6679         else
6680         {
6681             setCurrentOutCode(MCMD_EARGS);
6682             LOG_err << "      " << getUsageStr("get");
6683         }
6684 
6685         return;
6686     }
6687 #ifdef ENABLE_BACKUPS
6688 
6689     else if (words[0] == "backup")
6690     {
6691         bool dodelete = getFlag(clflags,"d");
6692         bool abort = getFlag(clflags,"a");
6693         bool listinfo = getFlag(clflags,"l");
6694         bool listhistory = getFlag(clflags,"h");
6695 
6696 //        //TODO: do the following functionality
6697 //        bool stop = getFlag(clflags,"s");
6698 //        bool resume = getFlag(clflags,"r");
6699 //        bool initiatenow = getFlag(clflags,"i");
6700 
6701         int PATHSIZE = getintOption(cloptions,"path-display-size");
6702         if (!PATHSIZE)
6703         {
6704             // get screen size for output purposes
6705             unsigned int width = getNumberOfCols(75);
6706             PATHSIZE = min(60,int((width-46)/2));
6707         }
6708         PATHSIZE = max(0, PATHSIZE);
6709 
6710         bool firstbackup = true;
6711         string speriod=getOption(cloptions, "period");
6712         int numBackups = int(getintOption(cloptions, "num-backups", -1));
6713 
6714         if (words.size() == 3)
6715         {
6716             string local = words.at(1);
6717             string remote = words.at(2);
6718             unescapeifRequired(local);
6719             unescapeifRequired(remote);
6720 
6721             createOrModifyBackup(local, remote, speriod, numBackups);
6722         }
6723         else if (words.size() == 2)
6724         {
6725             string local = words.at(1);
6726             unescapeifRequired(local);
6727 
6728             MegaBackup *backup = api->getBackupByPath(local.c_str());
6729             if (!backup)
6730             {
6731                 backup = api->getBackupByTag(toInteger(local, -1));
6732             }
6733             map<string, backup_struct *>::iterator itr;
6734             if (backup)
6735             {
6736                 int backupid = -1;
6737                 for ( itr = ConfigurationManager::configuredBackups.begin(); itr != ConfigurationManager::configuredBackups.end(); itr++ )
6738                 {
6739                     if (itr->second->tag == backup->getTag())
6740                     {
6741                         backupid = itr->second->id;
6742                         break;
6743                     }
6744                 }
6745                 if (backupid == -1)
6746                 {
6747                     LOG_err << " Requesting info of unregistered backup: " << local;
6748                 }
6749 
6750                 if (dodelete)
6751                 {
6752                     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
6753                     api->removeBackup(backup->getTag(), megaCmdListener);
6754                     megaCmdListener->wait();
6755                     if (checkNoErrors(megaCmdListener->getError(), "remove backup"))
6756                     {
6757                         if (backupid != -1)
6758                         {
6759                           ConfigurationManager::configuredBackups.erase(itr);
6760                         }
6761                         mtxBackupsMap.lock();
6762                         ConfigurationManager::saveBackups(&ConfigurationManager::configuredBackups);
6763                         mtxBackupsMap.unlock();
6764                         OUTSTREAM << " Backup removed succesffuly: " << local << endl;
6765                     }
6766                 }
6767                 else if (abort)
6768                 {
6769                     MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
6770                     api->abortCurrentBackup(backup->getTag(), megaCmdListener);
6771                     megaCmdListener->wait();
6772                     if (checkNoErrors(megaCmdListener->getError(), "abort backup"))
6773                     {
6774                         OUTSTREAM << " Backup aborted succesffuly: " << local << endl;
6775                     }
6776                 }
6777                 else
6778                 {
6779                     if (speriod.size() || numBackups != -1)
6780                     {
6781                         createOrModifyBackup(backup->getLocalFolder(), "", speriod, numBackups);
6782                     }
6783                     else
6784                     {
6785                         if(firstbackup)
6786                         {
6787                             printBackupHeader(PATHSIZE);
6788                             firstbackup = false;
6789                         }
6790 
6791                         printBackup(backup->getTag(), backup, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), PATHSIZE, listinfo, listhistory);
6792                     }
6793                 }
6794                 delete backup;
6795             }
6796             else
6797             {
6798                 if (dodelete) //remove from configured backups
6799                 {
6800                     bool deletedok = false;
6801                     for ( itr = ConfigurationManager::configuredBackups.begin(); itr != ConfigurationManager::configuredBackups.end(); itr++ )
6802                     {
6803                         if (itr->second->tag == -1 && itr->second->localpath == local)
6804                         {
6805                             mtxBackupsMap.lock();
6806                             ConfigurationManager::configuredBackups.erase(itr);
6807                             ConfigurationManager::saveBackups(&ConfigurationManager::configuredBackups);
6808                             mtxBackupsMap.unlock();
6809                             OUTSTREAM << " Backup removed succesffuly: " << local << endl;
6810                             deletedok = true;
6811 
6812                             break;
6813                         }
6814                     }
6815 
6816                     if (!deletedok)
6817                     {
6818                         setCurrentOutCode(MCMD_NOTFOUND);
6819                         OUTSTREAM << "Backup not found: " << local << endl;
6820                     }
6821                 }
6822                 else
6823                 {
6824                     setCurrentOutCode(MCMD_NOTFOUND);
6825                     LOG_err << "Backup not found: " << local;
6826                 }
6827             }
6828         }
6829         else if (words.size() == 1) //list backups
6830         {
6831             mtxBackupsMap.lock();
6832             for (map<string, backup_struct *>::iterator itr = ConfigurationManager::configuredBackups.begin(); itr != ConfigurationManager::configuredBackups.end(); itr++ )
6833             {
6834                 if(firstbackup)
6835                 {
6836                     printBackupHeader(PATHSIZE);
6837                     firstbackup = false;
6838                 }
6839                 printBackup(itr->second, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), PATHSIZE, listinfo, listhistory);
6840             }
6841             if (!ConfigurationManager::configuredBackups.size())
6842             {
6843                 setCurrentOutCode(MCMD_NOTFOUND);
6844                 OUTSTREAM << "No backup configured. " << endl << " Usage: " << getUsageStr("backup") << endl;
6845             }
6846             mtxBackupsMap.unlock();
6847 
6848         }
6849         else
6850         {
6851             setCurrentOutCode(MCMD_EARGS);
6852             LOG_err << "      " << getUsageStr("backup");
6853         }
6854     }
6855 #endif
6856     else if (words[0] == "put")
6857     {
6858         int clientID = getintOption(cloptions, "clientID", -1);
6859 
6860         if (!api->isFilesystemAvailable())
6861         {
6862             setCurrentOutCode(MCMD_NOTLOGGEDIN);
6863             LOG_err << "Not logged in.";
6864             return;
6865         }
6866 
6867         bool background = getFlag(clflags,"q");
6868         if (background)
6869         {
6870             clientID = -1;
6871         }
6872 
6873         MegaCmdMultiTransferListener *megaCmdMultiTransferListener = new MegaCmdMultiTransferListener(api, sandboxCMD, NULL, clientID);
6874 
6875         bool ignorequotawarn = getFlag(clflags,"ignore-quota-warn");
6876 
6877         if (words.size() > 1)
6878         {
6879             string targetuser;
6880             string newname = "";
6881             string destination = "";
6882 
6883             MegaNode *n = NULL;
6884 
6885             if (words.size() > 2)
6886             {
6887                 destination = words[words.size() - 1];
6888                 n = nodebypath(destination.c_str(), &targetuser, &newname);
6889 
6890                 if (!n && getFlag(clflags,"c"))
6891                 {
6892                     string destinationfolder(destination,0,destination.find_last_of("/"));
6893                     newname=string(destination,destination.find_last_of("/")+1,destination.size());
6894                     MegaNode *cwdNode = api->getNodeByHandle(cwd);
6895                     makedir(destinationfolder,true,cwdNode);
6896                     n = nodebypath(destinationfolder.c_str());
6897                     delete cwdNode;
6898                 }
6899             }
6900             else
6901             {
6902                 n = api->getNodeByHandle(cwd);
6903                 words.push_back(".");
6904             }
6905             if (n)
6906             {
6907                 if (n->getType() != MegaNode::TYPE_FILE)
6908                 {
6909                     for (int i = 1; i < max(1, (int)words.size() - 1); i++)
6910                     {
6911                         if (words[i] == ".")
6912                         {
6913                             words[i] = getLPWD();
6914                         }
6915 
6916 #ifdef HAVE_GLOB_H
6917                         if (!newname.size()
6918 #ifdef MEGACMDEXECUTER_FILESYSTEM
6919                                 && !fs::exists(words[i])
6920 #endif
6921                                 && hasWildCards(words[i]))
6922                         {
6923                             auto paths = resolvewildcard(words[i]);
6924                             if (!paths.size())
6925                             {
6926                                 setCurrentOutCode(MCMD_NOTFOUND);
6927                                 LOG_err << words[i] << " not found";
6928                             }
6929                             for (auto path : paths)
6930                             {
6931                                 uploadNode(path, api, n, newname, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6932                             }
6933                         }
6934                         else
6935 #endif
6936                         {
6937                             uploadNode(words[i], api, n, newname, background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6938                         }
6939                     }
6940                 }
6941                 else if (words.size() == 3 && !IsFolder(words[1])) //replace file
6942                 {
6943                     unique_ptr<MegaNode> pn(api->getNodeByHandle(n->getParentHandle()));
6944                     if (pn)
6945                     {
6946 #if defined(HAVE_GLOB_H) && defined(MEGACMDEXECUTER_FILESYSTEM)
6947                         if (!fs::exists(words[1]) && hasWildCards(words[1]))
6948                         {
6949                             LOG_err << "Invalid target for wildcard expression: " << words[1] << ". Folder expected";
6950                             setCurrentOutCode(MCMD_INVALIDTYPE);
6951                         }
6952                         else
6953 #endif
6954                         {
6955                             uploadNode(words[1], api, pn.get(), n->getName(), background, ignorequotawarn, clientID, megaCmdMultiTransferListener);
6956                         }
6957                     }
6958                     else
6959                     {
6960                         setCurrentOutCode(MCMD_NOTFOUND);
6961                         LOG_err << "Destination is not valid. Parent folder cannot be found";
6962                     }
6963                 }
6964                 else
6965                 {
6966                     setCurrentOutCode(MCMD_INVALIDTYPE);
6967                     LOG_err << "Destination is not valid (expected folder or alike)";
6968                 }
6969                 delete n;
6970 
6971 
6972                 megaCmdMultiTransferListener->waitMultiEnd();
6973 
6974                 checkNoErrors(megaCmdMultiTransferListener->getFinalerror(), "upload");
6975 
6976                 if (megaCmdMultiTransferListener->getProgressinformed() || getCurrentOutCode() == MCMD_OK )
6977                 {
6978                     informProgressUpdate(PROGRESS_COMPLETE, megaCmdMultiTransferListener->getTotalbytes(), clientID);
6979                 }
6980                 delete megaCmdMultiTransferListener;
6981             }
6982             else
6983             {
6984                 setCurrentOutCode(MCMD_NOTFOUND);
6985                 LOG_err << "Couln't find destination folder: " << destination << ". Use -c to create folder structure";
6986             }
6987         }
6988         else
6989         {
6990             setCurrentOutCode(MCMD_EARGS);
6991             LOG_err << "      " << getUsageStr("put");
6992         }
6993 
6994         return;
6995     }
6996     else if (words[0] == "log")
6997     {
6998         if (words.size() == 1)
6999         {
7000             if (!getFlag(clflags, "s") && !getFlag(clflags, "c"))
7001             {
7002                 OUTSTREAM << "MEGAcmd log level = " << getLogLevelStr(loggerCMD->getCmdLoggerLevel()) << endl;
7003                 OUTSTREAM << "SDK log level = " << getLogLevelStr(loggerCMD->getApiLoggerLevel()) << endl;
7004             }
7005             else if (getFlag(clflags, "s"))
7006             {
7007                 OUTSTREAM << "SDK log level = " << getLogLevelStr(loggerCMD->getApiLoggerLevel()) << endl;
7008             }
7009             else if (getFlag(clflags, "c"))
7010             {
7011                 OUTSTREAM << "MEGAcmd log level = " << getLogLevelStr(loggerCMD->getCmdLoggerLevel()) << endl;
7012             }
7013         }
7014         else
7015         {
7016             int newLogLevel = getLogLevelNum(words[1].c_str());
7017             if (newLogLevel == -1)
7018             {
7019                 setCurrentOutCode(MCMD_EARGS);
7020                 LOG_err << "Invalid log level";
7021                 return;
7022             }
7023             newLogLevel = max(newLogLevel, (int)MegaApi::LOG_LEVEL_FATAL);
7024             newLogLevel = min(newLogLevel, (int)MegaApi::LOG_LEVEL_MAX);
7025             if (!getFlag(clflags, "s") && !getFlag(clflags, "c"))
7026             {
7027                 loggerCMD->setCmdLoggerLevel(newLogLevel);
7028                 loggerCMD->setApiLoggerLevel(newLogLevel);
7029                 OUTSTREAM << "MEGAcmd log level = " << getLogLevelStr(loggerCMD->getCmdLoggerLevel()) << endl;
7030                 OUTSTREAM << "SDK log level = " << getLogLevelStr(loggerCMD->getApiLoggerLevel()) << endl;
7031             }
7032             else if (getFlag(clflags, "s"))
7033             {
7034                 loggerCMD->setApiLoggerLevel(newLogLevel);
7035                 OUTSTREAM << "SDK log level = " << getLogLevelStr(loggerCMD->getApiLoggerLevel()) << endl;
7036             }
7037             else if (getFlag(clflags, "c"))
7038             {
7039                 loggerCMD->setCmdLoggerLevel(newLogLevel);
7040                 OUTSTREAM << "MEGAcmd log level = " << getLogLevelStr(loggerCMD->getCmdLoggerLevel()) << endl;
7041             }
7042         }
7043 
7044         return;
7045     }
7046     else if (words[0] == "pwd")
7047     {
7048         if (!api->isFilesystemAvailable())
7049         {
7050             setCurrentOutCode(MCMD_NOTLOGGEDIN);
7051             LOG_err << "Not logged in.";
7052             return;
7053         }
7054         string cwpath = getCurrentPath();
7055 
7056         OUTSTREAM << cwpath << endl;
7057 
7058         return;
7059     }
7060     else if (words[0] == "lcd") //this only makes sense for interactive mode
7061     {
7062         if (words.size() > 1)
7063         {
7064             LocalPath localpath = LocalPath::fromPath(words[1], *fsAccessCMD);
7065             if (fsAccessCMD->chdirlocal(localpath)) // maybe this is already checked in chdir
7066             {
7067                 LOG_debug << "Local folder changed to: " << words[1];
7068             }
7069             else
7070             {
7071                 setCurrentOutCode(MCMD_INVALIDTYPE);
7072                 LOG_err << "Not a valid folder: " << words[1];
7073             }
7074         }
7075         else
7076         {
7077             setCurrentOutCode(MCMD_EARGS);
7078             LOG_err << "      " << getUsageStr("lcd");
7079         }
7080 
7081         return;
7082     }
7083     else if (words[0] == "lpwd")
7084     {
7085         string absolutePath = getLPWD();
7086 
7087         OUTSTREAM << absolutePath << endl;
7088         return;
7089     }
7090     else if (words[0] == "ipc")
7091     {
7092         if (!api->isFilesystemAvailable())
7093         {
7094             setCurrentOutCode(MCMD_NOTLOGGEDIN);
7095             LOG_err << "Not logged in.";
7096             return;
7097         }
7098         if (words.size() > 1)
7099         {
7100             int action;
7101             string saction;
7102 
7103             if (getFlag(clflags, "a"))
7104             {
7105                 action = MegaContactRequest::REPLY_ACTION_ACCEPT;
7106                 saction = "Accept";
7107             }
7108             else if (getFlag(clflags, "d"))
7109             {
7110                 action = MegaContactRequest::REPLY_ACTION_DENY;
7111                 saction = "Reject";
7112             }
7113             else if (getFlag(clflags, "i"))
7114             {
7115                 action = MegaContactRequest::REPLY_ACTION_IGNORE;
7116                 saction = "Ignore";
7117             }
7118             else
7119             {
7120                 setCurrentOutCode(MCMD_EARGS);
7121                 LOG_err << "      " << getUsageStr("ipc");
7122                 return;
7123             }
7124 
7125             MegaContactRequest * cr;
7126             string shandle = words[1];
7127             handle thehandle = api->base64ToUserHandle(shandle.c_str());
7128 
7129             if (shandle.find('@') != string::npos)
7130             {
7131                 cr = getPcrByContact(shandle);
7132             }
7133             else
7134             {
7135                 cr = api->getContactRequestByHandle(thehandle);
7136             }
7137             if (cr)
7138             {
7139                 MegaCmdListener *megaCmdListener = new MegaCmdListener(api, NULL);
7140                 api->replyContactRequest(cr, action, megaCmdListener);
7141                 megaCmdListener->wait();
7142                 if (checkNoErrors(megaCmdListener->getError(), "reply ipc"))
7143                 {
7144                     OUTSTREAM << saction << "ed invitation by " << cr->getSourceEmail() << endl;
7145                 }
7146                 delete megaCmdListener;
7147                 delete cr;
7148             }
7149             else
7150             {
7151                 setCurrentOutCode(MCMD_NOTFOUND);
7152                 LOG_err << "Could not find invitation " << shandle;
7153             }
7154         }
7155         else
7156         {
7157             setCurrentOutCode(MCMD_EARGS);
7158             LOG_err << "      " << getUsageStr("ipc");
7159             return;
7160         }
7161         return;
7162     }
7163     else if (words[0] == "https")
7164     {
7165         if (words.size() > 1 && (words[1] == "on" || words[1] == "off"))
7166         {
7167             bool onlyhttps = words[1] == "on";
7168             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7169             api->useHttpsOnly(onlyhttps,megaCmdListener);
7170             megaCmdListener->wait();
7171             if (checkNoErrors(megaCmdListener->getError(), "change https"))
7172             {
7173                 OUTSTREAM << "File transfer now uses " << (api->usingHttpsOnly()?"HTTPS":"HTTP") << endl;
7174                 ConfigurationManager::savePropertyValue("https", api->usingHttpsOnly());
7175             }
7176             delete megaCmdListener;
7177             return;
7178         }
7179         else if (words.size() > 1)
7180         {
7181             setCurrentOutCode(MCMD_EARGS);
7182             LOG_err << "      " << getUsageStr("https");
7183             return;
7184         }
7185         else
7186         {
7187             OUTSTREAM << "File transfer is done using " << (api->usingHttpsOnly()?"HTTPS":"HTTP") << endl;
7188         }
7189         return;
7190     }
7191     else if (words[0] == "graphics")
7192     {
7193         if (words.size() == 2 && (words[1] == "on" || words[1] == "off"))
7194         {
7195             bool enableGraphics = words[1] == "on";
7196 
7197             api->disableGfxFeatures(!enableGraphics);
7198             ConfigurationManager::savePropertyValue("graphics", !api->areGfxFeaturesDisabled());
7199         }
7200         else if (words.size() > 1)
7201         {
7202             setCurrentOutCode(MCMD_EARGS);
7203             LOG_err << "      " << getUsageStr("https");
7204             return;
7205         }
7206 
7207         OUTSTREAM << "Graphic features " << (api->areGfxFeaturesDisabled()?"disabled":"enabled") << endl;
7208         return;
7209     }
7210 #ifndef _WIN32
7211     else if (words[0] == "permissions")
7212     {
7213         bool filesflagread = getFlag(clflags, "files");
7214         bool foldersflagread = getFlag(clflags, "folders");
7215 
7216         bool filesflag = filesflagread || (!filesflagread && !foldersflagread);
7217         bool foldersflag = foldersflagread || (!filesflagread && !foldersflagread);
7218 
7219         bool setperms = getFlag(clflags, "s");
7220 
7221         if ( (!setperms && words.size() > 1) || (setperms && words.size() != 2) || ( setperms && filesflagread  && foldersflagread ) || (setperms && !filesflagread && !foldersflagread))
7222         {
7223             setCurrentOutCode(MCMD_EARGS);
7224             LOG_err << "      " << getUsageStr("permissions");
7225             return;
7226         }
7227 
7228         int permvalue = -1;
7229         if (setperms)
7230         {
7231              if (words[1].size() != 3)
7232              {
7233                  setCurrentOutCode(MCMD_EARGS);
7234                  LOG_err << "Invalid permissions value: " << words[1];
7235              }
7236              else
7237              {
7238                  int owner = words[1].at(0) - '0';
7239                  int group = words[1].at(1) - '0';
7240                  int others = words[1].at(2) - '0';
7241                  if ( (owner < 6) || (owner == 6 && foldersflag) || (owner > 7) || (group < 0) || (group > 7) || (others < 0) || (others > 7) )
7242                  {
7243                      setCurrentOutCode(MCMD_EARGS);
7244                      LOG_err << "Invalid permissions value: " << words[1];
7245                  }
7246                  else
7247                  {
7248                      permvalue = (owner << 6) + ( group << 3) + others;
7249                  }
7250              }
7251         }
7252 
7253         if (filesflag)
7254         {
7255             if (setperms && permvalue != -1)
7256             {
7257                 api->setDefaultFilePermissions(permvalue);
7258                 ConfigurationManager::savePropertyValue("permissionsFiles", readablePermissions(permvalue));
7259             }
7260             int filepermissions = api->getDefaultFilePermissions();
7261             int owner  = (filepermissions >> 6) & 0x07;
7262             int group  = (filepermissions >> 3) & 0x07;
7263             int others = filepermissions & 0x07;
7264 
7265             OUTSTREAM << "Default files permissions: " << owner << group << others << endl;
7266         }
7267         if (foldersflag)
7268         {
7269             if (setperms && permvalue != -1)
7270             {
7271                 api->setDefaultFolderPermissions(permvalue);
7272                 ConfigurationManager::savePropertyValue("permissionsFolders", readablePermissions(permvalue));
7273             }
7274             int folderpermissions = api->getDefaultFolderPermissions();
7275             int owner  = (folderpermissions >> 6) & 0x07;
7276             int group  = (folderpermissions >> 3) & 0x07;
7277             int others = folderpermissions & 0x07;
7278             OUTSTREAM << "Default folders permissions: " << owner << group << others << endl;
7279         }
7280     }
7281 #endif
7282     else if (words[0] == "deleteversions")
7283     {
7284         bool deleteall = getFlag(clflags, "all");
7285         bool forcedelete = getFlag(clflags, "f");
7286         if (deleteall && words.size()>1)
7287         {
7288             setCurrentOutCode(MCMD_EARGS);
7289             LOG_err << "      " << getUsageStr("deleteversions");
7290             return;
7291         }
7292         if (deleteall)
7293         {
7294             string confirmationQuery("Are you sure todelete the version histories of all files? (Yes/No): ");
7295 
7296             int confirmationResponse = forcedelete?MCMDCONFIRM_YES:askforConfirmation(confirmationQuery);
7297 
7298             while ( (confirmationResponse != MCMDCONFIRM_YES) && (confirmationResponse != MCMDCONFIRM_NO) )
7299             {
7300                 confirmationResponse = askforConfirmation(confirmationQuery);
7301             }
7302             if (confirmationResponse == MCMDCONFIRM_YES)
7303             {
7304 
7305                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7306                 api->removeVersions(megaCmdListener);
7307                 megaCmdListener->wait();
7308                 if (checkNoErrors(megaCmdListener->getError(), "remove all versions"))
7309                 {
7310                     OUTSTREAM << "File versions deleted succesfully. Please note that the current files were not deleted, just their history." << endl;
7311                 }
7312                 delete megaCmdListener;
7313             }
7314         }
7315         else
7316         {
7317             for (unsigned int i = 1; i < words.size(); i++)
7318             {
7319                 if (isRegExp(words[i]))
7320                 {
7321                     vector<MegaNode *> *nodesToDeleteVersions = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
7322                     if (nodesToDeleteVersions && nodesToDeleteVersions->size())
7323                     {
7324                         for (std::vector< MegaNode * >::iterator it = nodesToDeleteVersions->begin(); it != nodesToDeleteVersions->end(); ++it)
7325                         {
7326                             MegaNode * nodeToDeleteVersions = *it;
7327                             if (nodeToDeleteVersions)
7328                             {
7329                                 int ret = deleteNodeVersions(nodeToDeleteVersions, api, forcedelete);
7330                                 forcedelete = forcedelete || (ret == MCMDCONFIRM_ALL);
7331                             }
7332                         }
7333                     }
7334                     else
7335                     {
7336                         setCurrentOutCode(MCMD_NOTFOUND);
7337                         LOG_err << "No node found: " << words[i];
7338                     }
7339                     delete nodesToDeleteVersions;
7340                 }
7341                 else // non-regexp
7342                 {
7343                     MegaNode *n = nodebypath(words[i].c_str());
7344                     if (n)
7345                     {
7346                         int ret = deleteNodeVersions(n, api, forcedelete);
7347                         forcedelete = forcedelete || (ret == MCMDCONFIRM_ALL);
7348                     }
7349                     else
7350                     {
7351                         setCurrentOutCode(MCMD_NOTFOUND);
7352                         LOG_err << "Node not found: " << words[i];
7353                     }
7354                     delete n;
7355                 }
7356             }
7357         }
7358     }
7359 #ifdef HAVE_LIBUV
7360     else if (words[0] == "webdav")
7361     {
7362         bool remove = getFlag(clflags, "d");
7363         bool all = getFlag(clflags, "all");
7364 
7365         if (words.size() == 1 && remove && !all)
7366         {
7367             setCurrentOutCode(MCMD_EARGS);
7368             LOG_err << "      " << getUsageStr("webdav");
7369             return;
7370         }
7371 
7372         if (words.size() == 1 && !remove)
7373         {
7374             //List served nodes
7375             MegaNodeList *webdavnodes = api->httpServerGetWebDavAllowedNodes();
7376             if (webdavnodes)
7377             {
7378                 bool found = false;
7379 
7380                 for (int a = 0; a < webdavnodes->size(); a++)
7381                 {
7382                     MegaNode *n= webdavnodes->get(a);
7383                     if (n)
7384                     {
7385                         char *link = api->httpServerGetLocalWebDavLink(n); //notice this is not only consulting but also creating,
7386                         //had it been deleted in the meantime this will recreate it
7387                         if (link)
7388                         {
7389                             if (!found)
7390                             {
7391                                 OUTSTREAM << "WEBDAV SERVED LOCATIONS:" << endl;
7392                             }
7393                             found = true;
7394                             char * nodepath = api->getNodePath(n);
7395                             OUTSTREAM << nodepath << ": " << link << endl;
7396                             delete []nodepath;
7397                             delete []link;
7398                         }
7399                     }
7400                 }
7401 
7402                 if(!found)
7403                 {
7404                     OUTSTREAM << "No webdav links found" << endl;
7405                 }
7406 
7407                 delete webdavnodes;
7408 
7409            }
7410            else
7411            {
7412                OUTSTREAM << "Webdav server might not running. Add a new location to serve." << endl;
7413            }
7414 
7415            return;
7416         }
7417 
7418         if (!remove)
7419         {
7420             //create new link:
7421             bool tls = getFlag(clflags, "tls");
7422             int port = getintOption(cloptions, "port", 4443);
7423             bool localonly = !getFlag(clflags, "public");
7424 
7425             string pathtocert = getOption(cloptions, "certificate", "");
7426             string pathtokey = getOption(cloptions, "key", "");
7427 
7428             bool serverstarted = api->httpServerIsRunning();
7429             if (!serverstarted)
7430             {
7431                 LOG_info << "Starting http server";
7432                 api->httpServerEnableFolderServer(true);
7433     //            api->httpServerEnableOfflineAttribute(true); //TODO: we might want to offer this as parameter
7434                 if (api->httpServerStart(localonly, port, tls, pathtocert.c_str(), pathtokey.c_str()))
7435                 {
7436                     ConfigurationManager::savePropertyValue("webdav_port", port);
7437                     ConfigurationManager::savePropertyValue("webdav_localonly", localonly);
7438                     ConfigurationManager::savePropertyValue("webdav_tls", tls);
7439                     if (pathtocert.size())
7440                     {
7441                         ConfigurationManager::savePropertyValue("webdav_cert", pathtocert);
7442                     }
7443                     if (pathtokey.size())
7444                     {
7445                         ConfigurationManager::savePropertyValue("webdav_key", pathtokey);
7446                     }
7447                 }
7448                 else
7449                 {
7450                     setCurrentOutCode(MCMD_EARGS);
7451                     LOG_err << "Failed to initialize WEBDAV server. Ensure the port is free.";
7452                     return;
7453                 }
7454             }
7455         }
7456 
7457         //add/remove
7458         if (remove && all)
7459         {
7460             api->httpServerRemoveWebDavAllowedNodes();
7461             api->httpServerStop();
7462 
7463             list<string> servedpaths;
7464             ConfigurationManager::savePropertyValueList("webdav_served_locations", servedpaths);
7465             ConfigurationManager::savePropertyValue("webdav_port", -1); //so as not to load server on startup
7466             OUTSTREAM << "Wevdav server stopped: no path will be served." << endl;
7467         }
7468         else
7469         {
7470             bool firstone = true;
7471             for (unsigned int i = 1; i < words.size(); i++)
7472             {
7473                 string pathToServe = words[i];
7474                 if (remove)
7475                 {
7476                     processPath(pathToServe, getFlag(clflags,"use-pcre"), firstone, forwarderRemoveWebdavLocation, this);
7477                 }
7478                 else
7479                 {
7480                     processPath(pathToServe, getFlag(clflags,"use-pcre"), firstone, forwarderAddWebdavLocation, this);
7481                 }
7482             }
7483         }
7484     }
7485     else if (words[0] == "ftp")
7486     {
7487         bool remove = getFlag(clflags, "d");
7488         bool all = getFlag(clflags, "all");
7489 
7490         if (words.size() == 1 && remove && !all)
7491         {
7492             setCurrentOutCode(MCMD_EARGS);
7493             LOG_err << "      " << getUsageStr("ftp");
7494             return;
7495         }
7496 
7497         if (words.size() == 1 && !remove)
7498         {
7499             //List served nodes
7500             MegaNodeList *ftpnodes = api->ftpServerGetAllowedNodes();
7501             if (ftpnodes)
7502             {
7503                 bool found = false;
7504 
7505                 for (int a = 0; a < ftpnodes->size(); a++)
7506                 {
7507                     MegaNode *n= ftpnodes->get(a);
7508                     if (n)
7509                     {
7510                         char *link = api->ftpServerGetLocalLink(n); //notice this is not only consulting but also creating,
7511                         //had it been deleted in the meantime this will recreate it
7512                         if (link)
7513                         {
7514                             if (!found)
7515                             {
7516                                 OUTSTREAM << "FTP SERVED LOCATIONS:" << endl;
7517                             }
7518                             found = true;
7519                             char * nodepath = api->getNodePath(n);
7520                             OUTSTREAM << nodepath << ": " << link << endl;
7521                             delete []nodepath;
7522                             delete []link;
7523                         }
7524                     }
7525                 }
7526 
7527                 if(!found)
7528                 {
7529                     OUTSTREAM << "No ftp links found" << endl;
7530                 }
7531 
7532                 delete ftpnodes;
7533 
7534            }
7535            else
7536            {
7537                OUTSTREAM << "Ftp server might not running. Add a new location to serve." << endl;
7538            }
7539 
7540            return;
7541         }
7542 
7543         if (!remove)
7544         {
7545             //create new link:
7546             bool tls = getFlag(clflags, "tls");
7547             int port = getintOption(cloptions, "port", 4990);
7548             bool localonly = !getFlag(clflags, "public");
7549 
7550             string dataPorts = getOption(cloptions, "data-ports");
7551             size_t seppos = dataPorts.find("-");
7552             int dataPortRangeBegin = 1500;
7553             istringstream is(dataPorts.substr(0,seppos));
7554             is >> dataPortRangeBegin;
7555             int dataPortRangeEnd = dataPortRangeBegin + 100;
7556             if (seppos != string::npos && seppos < (dataPorts.size()+1))
7557             {
7558                 istringstream is(dataPorts.substr(seppos+1));
7559                 is >> dataPortRangeEnd;
7560             }
7561 
7562             string pathtocert = getOption(cloptions, "certificate", "");
7563             string pathtokey = getOption(cloptions, "key", "");
7564 
7565             if (tls && (!pathtocert.size() || !pathtokey.size()))
7566             {
7567                 setCurrentOutCode(MCMD_EARGS);
7568                 LOG_err << "Path to certificate/key not indicated";
7569                 return;
7570             }
7571 
7572             bool serverstarted = api->ftpServerIsRunning();
7573             if (!serverstarted)
7574             {
7575                 if (api->ftpServerStart(localonly, port, dataPortRangeBegin, dataPortRangeEnd, tls, pathtocert.c_str(), pathtokey.c_str()))
7576                 {
7577                     ConfigurationManager::savePropertyValue("ftp_port", port);
7578                     ConfigurationManager::savePropertyValue("ftp_port_data_begin", dataPortRangeBegin);
7579                     ConfigurationManager::savePropertyValue("ftp_port_data_end", dataPortRangeEnd);
7580                     ConfigurationManager::savePropertyValue("ftp_localonly", localonly);
7581                     ConfigurationManager::savePropertyValue("ftp_tls", tls);
7582                     if (pathtocert.size())
7583                     {
7584                         ConfigurationManager::savePropertyValue("ftp_cert", pathtocert);
7585                     }
7586                     if (pathtokey.size())
7587                     {
7588                         ConfigurationManager::savePropertyValue("ftp_key", pathtokey);
7589                     }
7590                     LOG_info << "Started ftp server at port " << port << ". Data Channel Port Range: " << dataPortRangeBegin << "-" << dataPortRangeEnd;
7591                 }
7592                 else
7593                 {
7594                     setCurrentOutCode(MCMD_EARGS);
7595                     LOG_err << "Failed to initialize FTP server. Ensure the port is free.";
7596                     return;
7597                 }
7598             }
7599         }
7600 
7601         //add/remove
7602         if (remove && all)
7603         {
7604             api->ftpServerRemoveAllowedNodes();
7605             api->ftpServerStop();
7606 
7607             list<string> servedpaths;
7608             ConfigurationManager::savePropertyValueList("ftp_served_locations", servedpaths);
7609             ConfigurationManager::savePropertyValue("ftp_port", -1); //so as not to load server on startup
7610             OUTSTREAM << "ftp server stopped: no path will be served." << endl;
7611         }
7612         else
7613         {
7614             bool firstone = true;
7615             for (unsigned int i = 1; i < words.size(); i++)
7616             {
7617                 string pathToServe = words[i];
7618                 if (remove)
7619                 {
7620                     processPath(pathToServe, getFlag(clflags,"use-pcre"), firstone, forwarderRemoveFtpLocation, this);
7621                 }
7622                 else
7623                 {
7624                     processPath(pathToServe, getFlag(clflags,"use-pcre"), firstone, forwarderAddFtpLocation, this);
7625                 }
7626             }
7627         }
7628     }
7629 #endif
7630 #ifdef ENABLE_SYNC
7631     else if (words[0] == "exclude")
7632     {
7633         api->enableTransferResumption();
7634 
7635         if (getFlag(clflags, "a"))
7636         {
7637             for (unsigned int i=1;i<words.size(); i++)
7638             {
7639                 ConfigurationManager::addExcludedName(words[i]);
7640             }
7641             if (words.size()>1)
7642             {
7643                 std::vector<string> vexcludednames(ConfigurationManager::excludedNames.begin(), ConfigurationManager::excludedNames.end());
7644                 api->setExcludedNames(&vexcludednames);
7645                 if (getFlag(clflags, "restart-syncs"))
7646                 {
7647                     restartsyncs();
7648                 }
7649             }
7650             else
7651             {
7652                 setCurrentOutCode(MCMD_EARGS);
7653                 LOG_err << "      " << getUsageStr("exclude");
7654                 return;
7655             }
7656         }
7657         else if (getFlag(clflags, "d"))
7658         {
7659             for (unsigned int i=1;i<words.size(); i++)
7660             {
7661                 ConfigurationManager::removeExcludedName(words[i]);
7662             }
7663             if (words.size()>1)
7664             {
7665                 std::vector<string> vexcludednames(ConfigurationManager::excludedNames.begin(), ConfigurationManager::excludedNames.end());
7666                 api->setExcludedNames(&vexcludednames);
7667                 if (getFlag(clflags, "restart-syncs"))
7668                 {
7669                     restartsyncs();
7670                 }
7671             }
7672             else
7673             {
7674                 setCurrentOutCode(MCMD_EARGS);
7675                 LOG_err << "      " << getUsageStr("exclude");
7676                 return;
7677             }
7678         }
7679 
7680 
7681         OUTSTREAM << "List of excluded names:" << endl;
7682 
7683         for (set<string>::iterator it=ConfigurationManager::excludedNames.begin(); it!=ConfigurationManager::excludedNames.end(); ++it)
7684         {
7685             OUTSTREAM << *it << endl;
7686         }
7687 
7688         if ( !getFlag(clflags, "restart-syncs") && (getFlag(clflags, "a") || getFlag(clflags, "d")) )
7689         {
7690             OUTSTREAM << endl <<  "Changes will not be applied immediately to operations being performed in active syncs."
7691                       << " See \"exclude --help\" for further info" << endl;
7692         }
7693     }
7694     else if (words[0] == "sync")
7695     {
7696         if (!api->isFilesystemAvailable())
7697         {
7698             setCurrentOutCode(MCMD_NOTLOGGEDIN);
7699             LOG_err << "Not logged in.";
7700             return;
7701         }
7702         if (!api->isLoggedIn())
7703         {
7704             LOG_err << "Not logged in";
7705             setCurrentOutCode(MCMD_NOTLOGGEDIN);
7706             return;
7707         }
7708 
7709         int PATHSIZE = getintOption(cloptions,"path-display-size");
7710         if (!PATHSIZE)
7711         {
7712             // get screen size for output purposes
7713             unsigned int width = getNumberOfCols(75);
7714             PATHSIZE = min(60,int((width-46)/2));
7715         }
7716         PATHSIZE = max(0, PATHSIZE);
7717 
7718         bool headershown = false;
7719         bool modifiedsyncs = false;
7720         mtxSyncMap.lock();
7721         if (words.size() == 3)
7722         {
7723             LocalPath localRelativePath = LocalPath::fromPath(words[1], *fsAccessCMD);
7724             LocalPath localAbsolutePath = localRelativePath;
7725             fsAccessCMD->expanselocalpath(localRelativePath, localAbsolutePath);
7726 
7727             MegaNode* n = nodebypath(words[2].c_str());
7728             if (n)
7729             {
7730                 if (n->getType() == MegaNode::TYPE_FILE)
7731                 {
7732                     LOG_err << words[2] << ": Remote sync root must be folder.";
7733                 }
7734                 else if (api->getAccess(n) >= MegaShare::ACCESS_FULL)
7735                 {
7736                     std::lock_guard<std::recursive_mutex> g(ConfigurationManager::settingsMutex);
7737 
7738                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7739                     api->syncFolder(localAbsolutePath.toPath(*fsAccessCMD).c_str(), n, megaCmdListener);
7740                     megaCmdListener->wait();
7741                     if (checkNoErrors(megaCmdListener->getError(), "sync folder"))
7742                     {
7743                         sync_struct *thesync = new sync_struct;
7744                         thesync->active = true;
7745                         thesync->handle = megaCmdListener->getRequest()->getNodeHandle();
7746                         thesync->localpath = string(megaCmdListener->getRequest()->getFile());
7747                         thesync->fingerprint = megaCmdListener->getRequest()->getNumber();
7748 
7749                         if (ConfigurationManager::configuredSyncs.find(megaCmdListener->getRequest()->getFile()) != ConfigurationManager::configuredSyncs.end())
7750                         {
7751                             delete ConfigurationManager::configuredSyncs[megaCmdListener->getRequest()->getFile()];
7752                         }
7753                         ConfigurationManager::configuredSyncs[megaCmdListener->getRequest()->getFile()] = thesync;
7754 
7755                         char * nodepath = api->getNodePath(n);
7756                         LOG_info << "Added sync: " << megaCmdListener->getRequest()->getFile() << " to " << nodepath;
7757 
7758                         modifiedsyncs=true;
7759                         delete []nodepath;
7760                     }
7761 
7762                     delete megaCmdListener;
7763                 }
7764                 else
7765                 {
7766                     setCurrentOutCode(MCMD_NOTPERMITTED);
7767                     LOG_err << words[2] << ": Syncing requires full access to path, current access: " << api->getAccess(n);
7768                 }
7769                 delete n;
7770             }
7771             else
7772             {
7773                 setCurrentOutCode(MCMD_NOTFOUND);
7774                 LOG_err << "Couldn't find remote folder: " << words[2];
7775             }
7776         }
7777         else if (words.size() == 2)
7778         {
7779             int id = toInteger(words[1].c_str());
7780             map<string, sync_struct *>::iterator itr;
7781             int i = 0;
7782             bool foundsync = false;
7783             std::lock_guard<std::recursive_mutex> g(ConfigurationManager::settingsMutex);
7784             for (itr = ConfigurationManager::configuredSyncs.begin(); itr != ConfigurationManager::configuredSyncs.end(); i++)
7785             {
7786                 string key = ( *itr ).first;
7787                 sync_struct *thesync = ((sync_struct*)( *itr ).second );
7788                 MegaNode * n = api->getNodeByHandle(thesync->handle);
7789                 bool erased = false;
7790 
7791                 if (n)
7792                 {
7793                     char * nodepath = api->getNodePath(n);
7794                     ColumnDisplayer cd(getintOption(cloptions,"path-display-size", 0));
7795 
7796                     if (( id == i ) || (( id == -1 ) && ( words[1] == thesync->localpath )))
7797                     {
7798                         foundsync = true;
7799                         long long nfiles = 0;
7800                         long long nfolders = 0;
7801                         nfolders++; //add the share itself
7802                         getInfoFromFolder(n, api, &nfiles, &nfolders);
7803 
7804                         if (getFlag(clflags, "s") || getFlag(clflags, "r"))
7805                         {
7806                             bool stopping = getFlag(clflags, "s");
7807                             LOG_info << (stopping?"Stopping (disabling) sync ":"Resuming sync ") << key << ": " << nodepath;
7808                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7809                             if (stopping)
7810                             {
7811                                 api->disableSync(n, megaCmdListener);
7812                             }
7813                             else
7814                             {
7815                                 api->syncFolder(thesync->localpath.c_str(), n, megaCmdListener);
7816                             }
7817 
7818                             megaCmdListener->wait();
7819 
7820                             if (checkNoErrors(megaCmdListener->getError(), stopping?"stop sync":"resume sync"))
7821                             {
7822                                 thesync->active = !stopping;
7823                                 thesync->loadedok = true;
7824                                 if (!stopping) //syncFolder
7825                                 {
7826                                     if (megaCmdListener->getRequest()->getNumber())
7827                                     {
7828                                         thesync->fingerprint = megaCmdListener->getRequest()->getNumber();
7829                                     }
7830                                 }
7831                                 modifiedsyncs=true;
7832                             }
7833                             else
7834                             {
7835                                 thesync->active = false;
7836                                 thesync->loadedok = false;
7837                             }
7838                             delete megaCmdListener;
7839                         }
7840                         else if (getFlag(clflags, "d"))
7841                         {
7842                             LOG_debug << "Removing sync " << key << " to " << nodepath;
7843                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7844                             if (thesync->active)  //if not active, removeSync will fail.)
7845                             {
7846                                 api->removeSync(n, megaCmdListener);
7847                                 megaCmdListener->wait();
7848                                 if (checkNoErrors(megaCmdListener->getError(), "remove sync"))
7849                                 {
7850                                     ConfigurationManager::configuredSyncs.erase(itr++); //TODO: should protect with mutex!
7851                                     erased = true;
7852                                     delete ( thesync );
7853                                     OUTSTREAM << "Removed sync " << key << " to " << nodepath << endl;
7854                                     modifiedsyncs=true;
7855                                 }
7856                             }
7857                             else //if !active simply remove
7858                             {
7859                                 //TODO: if the sdk ever provides a way to clean cache, call it
7860                                 ConfigurationManager::configuredSyncs.erase(itr++);
7861                                 erased = true;
7862                                 delete ( thesync );
7863                                 OUTSTREAM << "Removed sync " << key << " to " << nodepath << endl;
7864                                 modifiedsyncs=true;
7865                             }
7866                             delete megaCmdListener;
7867                         }
7868 
7869                         if (!headershown)
7870                         {
7871                             headershown = true;
7872                             printSyncHeader(PATHSIZE, &cd);
7873                         }
7874 
7875                         printSync(i, key, nodepath, thesync, n, nfiles, nfolders, PATHSIZE, &cd);
7876 
7877                     }
7878                     delete n;
7879                     delete []nodepath;
7880                     OUTSTRINGSTREAM oss;
7881                     cd.print(oss, getintOption(cloptions, "client-width", getNumberOfCols(75)));
7882                     OUTSTREAM << oss.str();
7883                 }
7884                 else
7885                 {
7886                     if (( id == i ) && getFlag(clflags, "d")) //simply remove: disabled (not found)
7887                     {
7888                         ConfigurationManager::configuredSyncs.erase(itr++);
7889                         erased = true;
7890                         delete ( thesync );
7891                         OUTSTREAM << "Removed sync " << key << endl;
7892                         modifiedsyncs = true;
7893                     }
7894                     else
7895                     {
7896                         setCurrentOutCode(MCMD_NOTFOUND);
7897                         LOG_err << "Node not found for sync " << key << " into handle: " << thesync->handle;
7898                     }
7899                 }
7900                 if (!erased)
7901                 {
7902                     ++itr;
7903                 }
7904             }
7905             if (!foundsync && !modifiedsyncs)
7906             {
7907                 setCurrentOutCode(MCMD_NOTFOUND);
7908                 LOG_err << "Sync not found: " << words[1] << ". Please provide full path or valid ID";
7909             }
7910         }
7911         else if (words.size() == 1)
7912         {
7913             ColumnDisplayer cd(getintOption(cloptions,"path-display-size", 0));
7914             map<string, sync_struct *>::const_iterator itr;
7915             int i = 0;
7916             for (itr = ConfigurationManager::configuredSyncs.begin(); itr != ConfigurationManager::configuredSyncs.end(); ++itr)
7917             {
7918                 sync_struct *thesync = ((sync_struct*)( *itr ).second );
7919                 MegaNode * n = api->getNodeByHandle(thesync->handle);
7920                 if (!headershown)
7921                 {
7922                     headershown = true;
7923                     printSyncHeader(PATHSIZE, &cd);
7924                 }
7925                 if (n)
7926                 {
7927                     long long nfiles = 0;
7928                     long long nfolders = 0;
7929                     nfolders++; //add the share itself
7930                     getInfoFromFolder(n, api, &nfiles, &nfolders);
7931 
7932                     char * nodepath = api->getNodePath(n);
7933                     printSync(i++, ( *itr ).first, nodepath, thesync, n, nfiles, nfolders, PATHSIZE, &cd);
7934 
7935                     delete n;
7936                     delete []nodepath;
7937                 }
7938                 else
7939                 {
7940                     printSync(i++, ( *itr ).first, "NOT FOUND", thesync, n, -1, -1, PATHSIZE, &cd);
7941                     setCurrentOutCode(MCMD_NOTFOUND);
7942                 }
7943             }
7944             OUTSTRINGSTREAM oss;
7945             cd.print(oss, getintOption(cloptions, "client-width", getNumberOfCols(75)));
7946             OUTSTREAM << oss.str();
7947 
7948         }
7949         else
7950         {
7951             setCurrentOutCode(MCMD_EARGS);
7952             LOG_err << "      " << getUsageStr("sync");
7953             mtxSyncMap.unlock();
7954             return;
7955         }
7956         if (modifiedsyncs)
7957         {
7958             ConfigurationManager::saveSyncs(&ConfigurationManager::configuredSyncs);
7959         }
7960         mtxSyncMap.unlock();
7961         return;
7962     }
7963 #endif
7964     else if (words[0] == "cancel")
7965     {
7966         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
7967         api->cancelAccount(megaCmdListener);
7968         megaCmdListener->wait();
7969         if (checkNoErrors(megaCmdListener->getError(), "cancel account"))
7970         {
7971             OUTSTREAM << "Account pendind cancel confirmation. You will receive a confirmation link. Use \"confirmcancel\" with the provided link to confirm the cancelation" << endl;
7972         }
7973         delete megaCmdListener;
7974     }
7975     else if (words[0] == "confirmcancel")
7976     {
7977         if (words.size() < 2)
7978         {
7979             setCurrentOutCode(MCMD_EARGS);
7980             LOG_err << "      " << getUsageStr("confirmcancel");
7981             return;
7982         }
7983 
7984         const char * confirmlink = words[1].c_str();
7985         if (words.size() > 2)
7986         {
7987             const char * pass = words[2].c_str();
7988             confirmCancel(confirmlink, pass);
7989         }
7990         else if (interactiveThread())
7991         {
7992             link = confirmlink;
7993             confirmingcancel = true;
7994             setprompt(LOGINPASSWORD);
7995         }
7996         else
7997         {
7998             setCurrentOutCode(MCMD_EARGS);
7999             LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("confirmcancel");
8000         }
8001     }
8002     else if (words[0] == "login")
8003     {
8004         LoginGuard loginGuard;
8005         int clientID = getintOption(cloptions, "clientID", -1);
8006 
8007         if (!api->isLoggedIn())
8008         {
8009             if (words.size() > 1)
8010             {
8011                 if (strchr(words[1].c_str(), '@'))
8012                 {
8013                     // full account login
8014                     if (words.size() > 2)
8015                     {
8016                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL,NULL,clientID);
8017                         sandboxCMD->resetSandBox();
8018                         api->login(words[1].c_str(), words[2].c_str(), megaCmdListener);
8019                         if (actUponLogin(megaCmdListener) == MegaError::API_EMFAREQUIRED )
8020                         {
8021                             MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL,NULL,clientID);
8022                             string pin2fa = getOption(cloptions, "auth-code", "");
8023                             if (!pin2fa.size())
8024                             {
8025                                 pin2fa = askforUserResponse("Enter the code generated by your authentication app: ");
8026                             }
8027                             LOG_verbose << " Using confirmation pin: " << pin2fa;
8028                             api->multiFactorAuthLogin(words[1].c_str(), words[2].c_str(), pin2fa.c_str(), megaCmdListener2);
8029                             actUponLogin(megaCmdListener2);
8030                             delete megaCmdListener2;
8031                             return;
8032 
8033                         }
8034                         delete megaCmdListener;
8035                     }
8036                     else
8037                     {
8038                         login = words[1];
8039                         if (interactiveThread())
8040                         {
8041                             setprompt(LOGINPASSWORD);
8042                         }
8043                         else
8044                         {
8045                             setCurrentOutCode(MCMD_EARGS);
8046                             LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("login");
8047                         }
8048                     }
8049                 }
8050                 else
8051                 {
8052                     const char* ptr;
8053                     if (( ptr = strchr(words[1].c_str(), '#')))  // folder link indicator
8054                     {
8055                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8056                         sandboxCMD->resetSandBox();
8057                         api->loginToFolder(words[1].c_str(), megaCmdListener);
8058                         actUponLogin(megaCmdListener);
8059                         delete megaCmdListener;
8060                         return;
8061                     }
8062                     else
8063                     {
8064                         unsigned char session[64];
8065 
8066                         if (words[1].size() < sizeof session * 4 / 3)
8067                         {
8068                             LOG_info << "Resuming session...";
8069                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8070                             sandboxCMD->resetSandBox();
8071                             api->fastLogin(words[1].c_str(), megaCmdListener);
8072                             actUponLogin(megaCmdListener);
8073                             delete megaCmdListener;
8074                             return;
8075                         }
8076                     }
8077                     setCurrentOutCode(MCMD_EARGS);
8078                     LOG_err << "Invalid argument. Please specify a valid e-mail address, "
8079                               << "a folder link containing the folder key "
8080                               << "or a valid session.";
8081                 }
8082             }
8083             else
8084             {
8085                 setCurrentOutCode(MCMD_EARGS);
8086                 LOG_err << "      " << getUsageStr("login");
8087             }
8088         }
8089         else
8090         {
8091             setCurrentOutCode(MCMD_INVALIDSTATE);
8092             LOG_err << "Already logged in. Please log out first.";
8093         }
8094 
8095         return;
8096     }
8097     else if (words[0] == "psa")
8098     {
8099         int psanum = sandboxCMD->lastPSAnumreceived;
8100 
8101 #ifndef NDEBUG
8102         if (words.size() >1)
8103         {
8104             psanum = toInteger(words[1], 0);
8105         }
8106 #endif
8107         bool discard = getFlag(clflags, "discard");
8108 
8109         if (discard)
8110         {
8111             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8112             api->setPSA(psanum, megaCmdListener);
8113             megaCmdListener->wait();
8114             if (checkNoErrors(megaCmdListener->getError(), "set psa " + SSTR(psanum)))
8115             {
8116                 OUTSTREAM << "PSA discarded" << endl;
8117             }
8118             delete megaCmdListener;
8119         }
8120 
8121         if (!checkAndInformPSA(NULL, true) && !discard) // even when discarded: we need to read the next
8122         {
8123             OUTSTREAM << "No PSA available" << endl;
8124             setCurrentOutCode(MCMD_NOTFOUND);
8125         }
8126     }
8127     else if (words[0] == "mount")
8128     {
8129         if (!api->isFilesystemAvailable())
8130         {
8131             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8132             LOG_err << "Not logged in.";
8133             return;
8134         }
8135         listtrees();
8136         return;
8137     }
8138     else if (words[0] == "share")
8139     {
8140         if (!api->isFilesystemAvailable())
8141         {
8142             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8143             LOG_err << "Not logged in.";
8144             return;
8145         }
8146         string with = getOption(cloptions, "with", "");
8147         if (getFlag(clflags, "a") && ( "" == with ))
8148         {
8149             setCurrentOutCode(MCMD_EARGS);
8150             LOG_err << " Required --with=user";
8151             LOG_err <<  "      " << getUsageStr("share");
8152             return;
8153         }
8154 
8155         string slevel = getOption(cloptions, "level", "NONE");
8156 
8157         int level_NOT_present_value = -214;
8158         int level;
8159         if (slevel == "NONE")
8160         {
8161             level = level_NOT_present_value;
8162         }
8163         else
8164         {
8165             level = getShareLevelNum(slevel.c_str());
8166         }
8167         if (( level != level_NOT_present_value ) && (( level < -1 ) || ( level > 3 )))
8168         {
8169             setCurrentOutCode(MCMD_EARGS);
8170             LOG_err << "Invalid level of access";
8171             return;
8172         }
8173         bool listPending = getFlag(clflags, "p");
8174 
8175         if (words.size() <= 1)
8176         {
8177             words.push_back(string(".")); //cwd
8178         }
8179         for (int i = 1; i < (int)words.size(); i++)
8180         {
8181             unescapeifRequired(words[i]);
8182             if (isRegExp(words[i]))
8183             {
8184                 vector<MegaNode *> *nodes = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
8185                 if (nodes)
8186                 {
8187                     if (!nodes->size())
8188                     {
8189                         setCurrentOutCode(MCMD_NOTFOUND);
8190                         if (words[i].find("@") != string::npos)
8191                         {
8192                             LOG_err << "Could not find " << words[i] << ". Use --with=" << words[i] << " to specify the user to share with";
8193                         }
8194                         else
8195                         {
8196                             LOG_err << "Node not found: " << words[i];
8197                         }
8198                     }
8199                     for (std::vector< MegaNode * >::iterator it = nodes->begin(); it != nodes->end(); ++it)
8200                     {
8201                         MegaNode * n = *it;
8202                         if (n)
8203                         {
8204                             if (getFlag(clflags, "a"))
8205                             {
8206                                 LOG_debug << " sharing ... " << n->getName() << " with " << with;
8207                                 if (level == level_NOT_present_value)
8208                                 {
8209                                     level = MegaShare::ACCESS_READ;
8210                                 }
8211 
8212                                 if (n->getType() == MegaNode::TYPE_FILE)
8213                                 {
8214                                     setCurrentOutCode(MCMD_INVALIDTYPE);
8215                                     LOG_err << "Cannot share file: " << n->getName() << ". Only folders allowed. You can send file to user's inbox with cp (see \"cp --help\")";
8216                                 }
8217                                 else
8218                                 {
8219                                     shareNode(n, with, level);
8220                                 }
8221                             }
8222                             else if (getFlag(clflags, "d"))
8223                             {
8224                                 if ("" != with)
8225                                 {
8226                                     LOG_debug << " deleting share ... " << n->getName() << " with " << with;
8227                                     disableShare(n, with);
8228                                 }
8229                                 else
8230                                 {
8231                                     MegaShareList* outShares = api->getOutShares(n);
8232                                     if (outShares)
8233                                     {
8234                                         for (int i = 0; i < outShares->size(); i++)
8235                                         {
8236                                             if (outShares->get(i)->getNodeHandle() == n->getHandle())
8237                                             {
8238                                                 LOG_debug << " deleting share ... " << n->getName() << " with " << outShares->get(i)->getUser();
8239                                                 disableShare(n, outShares->get(i)->getUser());
8240                                             }
8241                                         }
8242 
8243                                         delete outShares;
8244                                     }
8245                                 }
8246                             }
8247                             else
8248                             {
8249                                 if (( level != level_NOT_present_value ) || ( with != "" ))
8250                                 {
8251                                     setCurrentOutCode(MCMD_EARGS);
8252                                     LOG_err << "Unexpected option received. To create/modify a share use -a";
8253                                 }
8254                                 else if (listPending)
8255                                 {
8256                                     dumpListOfPendingShares(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i]);
8257                                 }
8258                                 else
8259                                 {
8260                                     dumpListOfShared(n, words[i]);
8261                                 }
8262                             }
8263                             delete n;
8264                         }
8265                     }
8266 
8267                     nodes->clear();
8268                     delete nodes;
8269                 }
8270                 else
8271                 {
8272                     setCurrentOutCode(MCMD_NOTFOUND);
8273                     LOG_err << "Node not found: " << words[i];
8274                 }
8275             }
8276             else // non-regexp
8277             {
8278                 MegaNode *n = nodebypath(words[i].c_str());
8279                 if (n)
8280                 {
8281                     if (getFlag(clflags, "a"))
8282                     {
8283                         LOG_debug << " sharing ... " << n->getName() << " with " << with;
8284                         if (level == level_NOT_present_value)
8285                         {
8286                             level = MegaShare::ACCESS_READ;
8287                         }
8288                         shareNode(n, with, level);
8289                     }
8290                     else if (getFlag(clflags, "d"))
8291                     {
8292                         if ("" != with)
8293                         {
8294                             LOG_debug << " deleting share ... " << n->getName() << " with " << with;
8295                             disableShare(n, with);
8296                         }
8297                         else
8298                         {
8299                             MegaShareList* outShares = api->getOutShares(n);
8300                             if (outShares)
8301                             {
8302                                 for (int i = 0; i < outShares->size(); i++)
8303                                 {
8304                                     if (outShares->get(i)->getNodeHandle() == n->getHandle())
8305                                     {
8306                                         LOG_debug << " deleting share ... " << n->getName() << " with " << outShares->get(i)->getUser();
8307                                         disableShare(n, outShares->get(i)->getUser());
8308                                     }
8309                                 }
8310 
8311                                 delete outShares;
8312                             }
8313                         }
8314                     }
8315                     else
8316                     {
8317                         if (( level != level_NOT_present_value ) || ( with != "" ))
8318                         {
8319                             setCurrentOutCode(MCMD_EARGS);
8320                             LOG_err << "Unexpected option received. To create/modify a share use -a";
8321                         }
8322                         else if (listPending)
8323                         {
8324                             dumpListOfPendingShares(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i]);
8325                         }
8326                         else
8327                         {
8328                             dumpListOfShared(n, words[i]);
8329                         }
8330                     }
8331                     delete n;
8332                 }
8333                 else
8334                 {
8335                     setCurrentOutCode(MCMD_NOTFOUND);
8336                     LOG_err << "Node not found: " << words[i];
8337                 }
8338             }
8339         }
8340 
8341         return;
8342     }
8343     else if (words[0] == "users")
8344     {
8345         if (!api->isFilesystemAvailable())
8346         {
8347             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8348             LOG_err << "Not logged in.";
8349             return;
8350         }
8351         if (getFlag(clflags, "d") && ( words.size() <= 1 ))
8352         {
8353             setCurrentOutCode(MCMD_EARGS);
8354             LOG_err << "Contact to delete not specified";
8355             return;
8356         }
8357         MegaUserList* usersList = api->getContacts();
8358         if (usersList)
8359         {
8360             for (int i = 0; i < usersList->size(); i++)
8361             {
8362                 MegaUser *user = usersList->get(i);
8363 
8364                 if (getFlag(clflags, "d") && ( words.size() > 1 ) && ( words[1] == user->getEmail()))
8365                 {
8366                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8367                     api->removeContact(user, megaCmdListener);
8368                     megaCmdListener->wait();
8369                     if (checkNoErrors(megaCmdListener->getError(), "delete contact"))
8370                     {
8371                         OUTSTREAM << "Contact " << words[1] << " removed succesfully" << endl;
8372                     }
8373                     delete megaCmdListener;
8374                 }
8375                 else
8376                 {
8377                     if (!(( user->getVisibility() != MegaUser::VISIBILITY_VISIBLE ) && !getFlag(clflags, "h")))
8378                     {
8379                         if (getFlag(clflags,"n"))
8380                         {
8381                             string name;
8382                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8383                             api->getUserAttribute(user, ATTR_FIRSTNAME, megaCmdListener);
8384                             megaCmdListener->wait();
8385                             if (megaCmdListener->getError()->getErrorCode() == MegaError::API_OK)
8386                             {
8387                                 if (megaCmdListener->getRequest()->getText() && strlen(megaCmdListener->getRequest()->getText()))
8388                                 {
8389                                     name += megaCmdListener->getRequest()->getText();
8390                                 }
8391                             }
8392                             delete megaCmdListener;
8393 
8394                             megaCmdListener = new MegaCmdListener(NULL);
8395                             api->getUserAttribute(user, ATTR_LASTNAME, megaCmdListener);
8396                             megaCmdListener->wait();
8397                             if (megaCmdListener->getError()->getErrorCode() == MegaError::API_OK)
8398                             {
8399                                 if (megaCmdListener->getRequest()->getText() && strlen(megaCmdListener->getRequest()->getText()))
8400                                 {
8401                                     if (name.size())
8402                                     {
8403                                         name+=" ";
8404                                     }
8405                                     name+=megaCmdListener->getRequest()->getText();
8406                                 }
8407                             }
8408                             if (name.size())
8409                             {
8410                                 OUTSTREAM << name << ": ";
8411                             }
8412 
8413                             delete megaCmdListener;
8414                         }
8415 
8416 
8417                         OUTSTREAM << user->getEmail() << ", " << visibilityToString(user->getVisibility());
8418                         if (user->getTimestamp())
8419                         {
8420                             OUTSTREAM << " since " << getReadableTime(user->getTimestamp(), getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")));
8421                         }
8422                         OUTSTREAM << endl;
8423 
8424                         if (getFlag(clflags, "s"))
8425                         {
8426                             MegaShareList *shares = api->getOutShares();
8427                             if (shares)
8428                             {
8429                                 bool first_share = true;
8430                                 for (int j = 0; j < shares->size(); j++)
8431                                 {
8432                                     if (!strcmp(shares->get(j)->getUser(), user->getEmail()))
8433                                     {
8434                                         MegaNode * n = api->getNodeByHandle(shares->get(j)->getNodeHandle());
8435                                         if (n)
8436                                         {
8437                                             if (first_share)
8438                                             {
8439                                                 OUTSTREAM << "\tSharing:" << endl;
8440                                                 first_share = false;
8441                                             }
8442 
8443                                             OUTSTREAM << "\t";
8444                                             dumpNode(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, 2, false, 0, getDisplayPath("/", n).c_str());
8445                                             delete n;
8446                                         }
8447                                     }
8448                                 }
8449 
8450                                 delete shares;
8451                             }
8452                         }
8453                     }
8454                 }
8455             }
8456 
8457             delete usersList;
8458         }
8459 
8460         return;
8461     }
8462     else if (words[0] == "mkdir")
8463     {
8464         if (!api->isFilesystemAvailable())
8465         {
8466             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8467             LOG_err << "Not logged in.";
8468             return;
8469         }
8470         int globalstatus = MCMD_OK;
8471         if (words.size()<2)
8472         {
8473             globalstatus = MCMD_EARGS;
8474         }
8475         bool printusage = false;
8476         for (unsigned int i = 1; i < words.size(); i++)
8477         {
8478             unescapeifRequired(words[i]);
8479             //git first existing node in the asked path:
8480             MegaNode *baseNode;
8481 
8482             string rest = words[i];
8483             if (rest.find("//bin/") == 0)
8484             {
8485                 baseNode = api->getRubbishNode();
8486                 rest = rest.substr(6);
8487             }//elseif //in/
8488             else if(rest.find("/") == 0)
8489             {
8490                 baseNode = api->getRootNode();
8491                 rest = rest.substr(1);
8492             }
8493             else
8494             {
8495                 baseNode = api->getNodeByHandle(cwd);
8496             }
8497 
8498             while (baseNode && rest.length())
8499             {
8500                 size_t possep = rest.find_first_of("/");
8501                 if (possep == string::npos)
8502                 {
8503                     break;
8504                 }
8505 
8506                 string next = rest.substr(0, possep);
8507                 if (next == ".")
8508                 {
8509                     rest = rest.substr(possep + 1);
8510                     continue;
8511                 }
8512                 else if(next == "..")
8513                 {
8514                     MegaNode *aux = baseNode;
8515                     baseNode = api->getNodeByHandle(baseNode->getParentHandle());
8516 
8517                     if (aux!=baseNode) // let's be paranoid
8518                     {
8519                         delete aux;
8520                     }
8521                 }
8522                 else
8523                 {
8524                     MegaNodeList *children = api->getChildren(baseNode);
8525                     if (children)
8526                     {
8527                         bool found = false;
8528                         for (int i = 0; i < children->size(); i++)
8529                         {
8530                             MegaNode *child = children->get(i);
8531                             if (next == child->getName())
8532                             {
8533                                 MegaNode *aux = baseNode;
8534                                 baseNode = child->copy();
8535                                 found = true;
8536                                 if (aux!=baseNode) // let's be paranoid
8537                                 {
8538                                     delete aux;
8539                                 }
8540                                 break;
8541                             }
8542                         }
8543                         delete children;
8544                         if (!found)
8545                         {
8546                             break;
8547                         }
8548                     }
8549                 }
8550 
8551                 rest = rest.substr(possep + 1);
8552             }
8553             if (baseNode)
8554             {
8555                 int status = makedir(rest,getFlag(clflags, "p"),baseNode);
8556                 if (status != MCMD_OK)
8557                 {
8558                     globalstatus = status;
8559                 }
8560                 if (status == MCMD_EARGS)
8561                 {
8562                     printusage = true;
8563                 }
8564                 delete baseNode;
8565             }
8566             else
8567             {
8568                 setCurrentOutCode(MCMD_INVALIDSTATE);
8569                 LOG_err << "Folder navigation failed";
8570                 return;
8571             }
8572 
8573         }
8574 
8575         setCurrentOutCode(globalstatus);
8576         if (printusage)
8577         {
8578             LOG_err << "      " << getUsageStr("mkdir");
8579         }
8580 
8581         return;
8582     }
8583     else if (words[0] == "attr")
8584     {
8585         if (!api->isFilesystemAvailable())
8586         {
8587             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8588             LOG_err << "Not logged in.";
8589             return;
8590         }
8591         if (words.size() > 1)
8592         {
8593             int cancel = getFlag(clflags, "d");
8594             bool settingattr = getFlag(clflags, "s");
8595 
8596             string nodePath = words.size() > 1 ? words[1] : "";
8597             string attribute = words.size() > 2 ? words[2] : "";
8598             string attrValue = words.size() > 3 ? words[3] : "";
8599             n = nodebypath(nodePath.c_str());
8600 
8601             if (n)
8602             {
8603                 if (settingattr || cancel)
8604                 {
8605                     if (attribute.size())
8606                     {
8607                         const char *cattrValue = cancel ? NULL : attrValue.c_str();
8608                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8609                         api->setCustomNodeAttribute(n, attribute.c_str(), cattrValue, megaCmdListener);
8610                         megaCmdListener->wait();
8611                         if (checkNoErrors(megaCmdListener->getError(), "set node attribute: " + attribute))
8612                         {
8613                             OUTSTREAM << "Node attribute " << attribute << ( cancel ? " removed" : " updated" ) << " correctly" << endl;
8614                             delete n;
8615                             n = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
8616                         }
8617                         delete megaCmdListener;
8618                     }
8619                     else
8620                     {
8621                         setCurrentOutCode(MCMD_EARGS);
8622                         LOG_err << "Attribute not specified";
8623                         LOG_err << "      " << getUsageStr("attr");
8624                         return;
8625                     }
8626                 }
8627 
8628                 //List node custom attributes
8629                 MegaStringList *attrlist = n->getCustomAttrNames();
8630                 if (attrlist)
8631                 {
8632                     if (!attribute.size())
8633                     {
8634                         OUTSTREAM << "The node has " << attrlist->size() << " attributes" << endl;
8635                     }
8636                     for (int a = 0; a < attrlist->size(); a++)
8637                     {
8638                         string iattr = attrlist->get(a);
8639                         if (!attribute.size() || ( attribute == iattr ))
8640                         {
8641                             const char* iattrval = n->getCustomAttr(iattr.c_str());
8642                             OUTSTREAM << "\t" << iattr << " = " << ( iattrval ? iattrval : "NULL" ) << endl;
8643                         }
8644                     }
8645 
8646                     delete attrlist;
8647                 }
8648 
8649                 delete n;
8650             }
8651             else
8652             {
8653                 setCurrentOutCode(MCMD_NOTFOUND);
8654                 LOG_err << "Couldn't find node: " << nodePath;
8655                 return;
8656             }
8657         }
8658         else
8659         {
8660             setCurrentOutCode(MCMD_EARGS);
8661             LOG_err << "      " << getUsageStr("attr");
8662             return;
8663         }
8664 
8665 
8666         return;
8667     }
8668     else if (words[0] == "userattr")
8669     {
8670         if (!api->isFilesystemAvailable())
8671         {
8672             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8673             LOG_err << "Not logged in.";
8674             return;
8675         }
8676         bool settingattr = getFlag(clflags, "s");
8677         bool listattrs = getFlag(clflags, "list");
8678         bool listall = words.size() == 1;
8679 
8680         int attribute = api->userAttributeFromString(words.size() > 1 ? words[1].c_str() : "-1");
8681         string attrValue = words.size() > 2 ? words[2] : "";
8682         string user = getOption(cloptions, "user", "");
8683         if (settingattr && user.size())
8684         {
8685             LOG_err << "Can't change other user attributes";
8686             return;
8687         }
8688 
8689 
8690         if (settingattr)
8691         {
8692             if (attribute != -1)
8693             {
8694                 const char *catrn = api->userAttributeToString(attribute);
8695                 string attrname = catrn;
8696                 delete [] catrn;
8697 
8698                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8699                 api->setUserAttribute(attribute, attrValue.c_str(), megaCmdListener);
8700                 megaCmdListener->wait();
8701                 if (checkNoErrors(megaCmdListener->getError(), string("set user attribute ") + attrname))
8702                 {
8703                     OUTSTREAM << "User attribute " << attrname << " updated correctly" << endl;
8704                     printUserAttribute(attribute, user, listattrs);
8705                 }
8706                 delete megaCmdListener;
8707             }
8708             else
8709             {
8710                 setCurrentOutCode(MCMD_EARGS);
8711                 LOG_err << "Attribute not specified";
8712                 LOG_err << "      " << getUsageStr("userattr");
8713                 return;
8714             }
8715         }
8716         else // list
8717         {
8718             if (listall)
8719             {
8720                 int a = 0;
8721                 bool found = false;
8722                 do
8723                 {
8724                     found = printUserAttribute(a, user, listattrs);
8725                     a++;
8726                 } while (found && listall);
8727             }
8728             else
8729             {
8730                 if (!printUserAttribute(attribute, user, listattrs))
8731                 {
8732                     setCurrentOutCode(MCMD_NOTFOUND);
8733                     LOG_err << "Attribute not found: " << words[1];
8734                 }
8735             }
8736         }
8737 
8738         return;
8739     }
8740     else if (words[0] == "thumbnail")
8741     {
8742         if (!api->isFilesystemAvailable())
8743         {
8744             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8745             LOG_err << "Not logged in.";
8746             return;
8747         }
8748         if (words.size() > 1)
8749         {
8750             string nodepath = words[1];
8751             string path = words.size() > 2 ? words[2] : "./";
8752             n = nodebypath(nodepath.c_str());
8753             if (n)
8754             {
8755                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8756                 bool setting = getFlag(clflags, "s");
8757                 if (setting)
8758                 {
8759                     api->setThumbnail(n, path.c_str(), megaCmdListener);
8760                 }
8761                 else
8762                 {
8763                     api->getThumbnail(n, path.c_str(), megaCmdListener);
8764                 }
8765                 megaCmdListener->wait();
8766                 if (checkNoErrors(megaCmdListener->getError(), ( setting ? "set thumbnail " : "get thumbnail " ) + nodepath + " to " + path))
8767                 {
8768                     OUTSTREAM << "Thumbnail for " << nodepath << ( setting ? " loaded from " : " saved in " ) << megaCmdListener->getRequest()->getFile() << endl;
8769                 }
8770                 delete megaCmdListener;
8771                 delete n;
8772             }
8773         }
8774         else
8775         {
8776             setCurrentOutCode(MCMD_EARGS);
8777             LOG_err << "      " << getUsageStr("attr");
8778             return;
8779         }
8780         return;
8781     }
8782     else if (words[0] == "preview")
8783     {
8784         if (!api->isFilesystemAvailable())
8785         {
8786             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8787             LOG_err << "Not logged in.";
8788             return;
8789         }
8790         if (words.size() > 1)
8791         {
8792             string nodepath = words[1];
8793             string path = words.size() > 2 ? words[2] : "./";
8794             n = nodebypath(nodepath.c_str());
8795             if (n)
8796             {
8797                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8798                 bool setting = getFlag(clflags, "s");
8799                 if (setting)
8800                 {
8801                     api->setPreview(n, path.c_str(), megaCmdListener);
8802                 }
8803                 else
8804                 {
8805                     api->getPreview(n, path.c_str(), megaCmdListener);
8806                 }
8807                 megaCmdListener->wait();
8808                 if (checkNoErrors(megaCmdListener->getError(), ( setting ? "set preview " : "get preview " ) + nodepath + " to " + path))
8809                 {
8810                     OUTSTREAM << "Preview for " << nodepath << ( setting ? " loaded from " : " saved in " ) << megaCmdListener->getRequest()->getFile() << endl;
8811                 }
8812                 delete megaCmdListener;
8813                 delete n;
8814             }
8815         }
8816         else
8817         {
8818             setCurrentOutCode(MCMD_EARGS);
8819             LOG_err << "      " << getUsageStr("attr");
8820             return;
8821         }
8822         return;
8823     }
8824     else if (words[0] == "tree")
8825     {
8826         vector<string> newcom;
8827         newcom.push_back("ls");
8828         (*clflags)["tree"] = 1;
8829         if (words.size()>1)
8830         {
8831             for (vector<string>::iterator it = ++words.begin(); it != words.end();it++)
8832             {
8833                 newcom.push_back(*it);
8834             }
8835         }
8836 
8837         return executecommand(newcom, clflags, cloptions);
8838     }
8839     else if (words[0] == "debug")
8840     {
8841         vector<string> newcom;
8842         newcom.push_back("log");
8843         newcom.push_back("5");
8844 
8845         return executecommand(newcom, clflags, cloptions);
8846     }
8847     else if (words[0] == "passwd")
8848     {
8849         string confirmationQuery("Changing password will close all your sessions. Are you sure?(Yes/No): ");
8850         bool force = getFlag(clflags, "f");
8851         int confirmationResponse = force?MCMDCONFIRM_ALL:askforConfirmation(confirmationQuery);
8852         if (confirmationResponse != MCMDCONFIRM_YES && confirmationResponse != MCMDCONFIRM_ALL)
8853         {
8854             return;
8855         }
8856 
8857         if (api->isLoggedIn())
8858         {
8859             if (words.size() == 1)
8860             {
8861                 if (interactiveThread())
8862                 {
8863                     setprompt(NEWPASSWORD);
8864                 }
8865                 else
8866                 {
8867                     setCurrentOutCode(MCMD_EARGS);
8868                     LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("passwd");
8869                 }
8870             }
8871             else if (words.size() == 2)
8872             {
8873                 string pin2fa = getOption(cloptions, "auth-code", "");
8874                 changePassword(words[1].c_str(), pin2fa);
8875             }
8876             else
8877             {
8878                 setCurrentOutCode(MCMD_EARGS);
8879                 LOG_err << "      " << getUsageStr("passwd");
8880             }
8881         }
8882         else
8883         {
8884             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8885             LOG_err << "Not logged in.";
8886         }
8887 
8888         return;
8889     }
8890     else if (words[0] == "speedlimit")
8891     {
8892         if (words.size() > 2)
8893         {
8894             setCurrentOutCode(MCMD_EARGS);
8895             LOG_err << "      " << getUsageStr("speedlimit");
8896             return;
8897         }
8898         if (words.size() > 1)
8899         {
8900             long long maxspeed = textToSize(words[1].c_str());
8901             if (maxspeed == -1)
8902             {
8903                 string s = words[1] + "B";
8904                 maxspeed = textToSize(s.c_str());
8905             }
8906             if (!getFlag(clflags, "u") && !getFlag(clflags, "d"))
8907             {
8908                 api->setMaxDownloadSpeed(maxspeed);
8909                 api->setMaxUploadSpeed(maxspeed);
8910                 ConfigurationManager::savePropertyValue("maxspeedupload", maxspeed);
8911                 ConfigurationManager::savePropertyValue("maxspeeddownload", maxspeed);
8912             }
8913             else if (getFlag(clflags, "u"))
8914             {
8915                 api->setMaxUploadSpeed(maxspeed);
8916                 ConfigurationManager::savePropertyValue("maxspeedupload", maxspeed);
8917             }
8918             else if (getFlag(clflags, "d"))
8919             {
8920                 api->setMaxDownloadSpeed(maxspeed);
8921                 ConfigurationManager::savePropertyValue("maxspeeddownload", maxspeed);
8922             }
8923         }
8924 
8925         bool hr = getFlag(clflags,"h");
8926 
8927         if (!getFlag(clflags, "u") && !getFlag(clflags, "d"))
8928         {
8929             long long us = api->getMaxUploadSpeed();
8930             long long ds = api->getMaxDownloadSpeed();
8931             OUTSTREAM << "Upload speed limit = " << (us?sizeToText(us,false,hr):"unlimited") << ((us && hr)?"/s":(us?" B/s":""))  << endl;
8932             OUTSTREAM << "Download speed limit = " << (ds?sizeToText(ds,false,hr):"unlimited") << ((ds && hr)?"/s":(us?" B/s":"")) << endl;
8933         }
8934         else if (getFlag(clflags, "u"))
8935         {
8936             long long us = api->getMaxUploadSpeed();
8937             OUTSTREAM << "Upload speed limit = " << (us?sizeToText(us,false,hr):"unlimited") << ((us && hr)?"/s":(us?" B/s":"")) << endl;
8938         }
8939         else if (getFlag(clflags, "d"))
8940         {
8941             long long ds = api->getMaxDownloadSpeed();
8942             OUTSTREAM << "Download speed limit = " << (ds?sizeToText(ds,false,hr):"unlimited") << ((ds && hr)?"/s":(ds?" B/s":"")) << endl;
8943         }
8944 
8945         return;
8946     }
8947     else if (words[0] == "invite")
8948     {
8949         if (!api->isFilesystemAvailable())
8950         {
8951             setCurrentOutCode(MCMD_NOTLOGGEDIN);
8952             LOG_err << "Not logged in.";
8953             return;
8954         }
8955         if (words.size() > 1)
8956         {
8957             string email = words[1];
8958             if (!isValidEmail(email))
8959             {
8960                 setCurrentOutCode(MCMD_INVALIDEMAIL);
8961                 LOG_err << "No valid email provided";
8962                 LOG_err << "      " << getUsageStr("invite");
8963             }
8964             else
8965             {
8966                 int action = MegaContactRequest::INVITE_ACTION_ADD;
8967                 if (getFlag(clflags, "d"))
8968                 {
8969                     action = MegaContactRequest::INVITE_ACTION_DELETE;
8970                 }
8971                 if (getFlag(clflags, "r"))
8972                 {
8973                     action = MegaContactRequest::INVITE_ACTION_REMIND;
8974                 }
8975 
8976                 string message = getOption(cloptions, "message", "");
8977                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
8978                 api->inviteContact(email.c_str(), message.c_str(), action, megaCmdListener);
8979                 megaCmdListener->wait();
8980                 if (checkNoErrors(megaCmdListener->getError(), action==MegaContactRequest::INVITE_ACTION_DELETE?"remove invitation":"(re)invite user"))
8981                 {
8982                     OUTSTREAM << "Invitation to user: " << email << " " << (action==MegaContactRequest::INVITE_ACTION_DELETE?"removed":"sent") << endl;
8983                 }
8984                 else if (megaCmdListener->getError()->getErrorCode() == MegaError::API_EACCESS)
8985                 {
8986                     ostringstream os;
8987                     os << "Reminder not yet available: " << " available after 15 days";
8988                     MegaContactRequestList *ocrl = api->getOutgoingContactRequests();
8989                     if (ocrl)
8990                     {
8991                         for (int i = 0; i < ocrl->size(); i++)
8992                         {
8993                             if (ocrl->get(i)->getTargetEmail() && megaCmdListener->getRequest()->getEmail() && !strcmp(ocrl->get(i)->getTargetEmail(), megaCmdListener->getRequest()->getEmail()))
8994                             {
8995                                 os << " (" << getReadableTime(getTimeStampAfter(ocrl->get(i)->getModificationTime(), "15d")) << ")";
8996                             }
8997                         }
8998 
8999                         delete ocrl;
9000                     }
9001                     LOG_err << os.str();
9002                 }
9003                 delete megaCmdListener;
9004             }
9005         }
9006 
9007         return;
9008     }
9009     else if (words[0] == "errorcode")
9010     {
9011         if (words.size() != 2)
9012         {
9013             setCurrentOutCode(MCMD_EARGS);
9014             LOG_err << "      " << getUsageStr("errorcode");
9015             return;
9016         }
9017 
9018         int errCode = -1999;
9019         istringstream is(words[1]);
9020         is >> errCode;
9021         if (errCode == -1999)
9022         {
9023             setCurrentOutCode(MCMD_EARGS);
9024             LOG_err << "Invalid error code: " << words[1];
9025             return;
9026         }
9027 
9028         if (errCode > 0)
9029         {
9030             errCode = -errCode;
9031         }
9032 
9033         string errString = getMCMDErrorString(errCode);
9034         if (errString == "UNKNOWN")
9035         {
9036             errString = MegaError::getErrorString(errCode);
9037         }
9038 
9039         OUTSTREAM << errString << endl;
9040 
9041     }
9042     else if (words[0] == "signup")
9043     {
9044         if (api->isLoggedIn())
9045         {
9046             setCurrentOutCode(MCMD_INVALIDSTATE);
9047             LOG_err << "Please loggout first ";
9048         }
9049         else if (words.size() > 1)
9050         {
9051             string email = words[1];
9052             if (words.size() > 2)
9053             {
9054                 string name = getOption(cloptions, "name", email);
9055                 string passwd = words[2];
9056                 signup(name, passwd, email);
9057             }
9058             else
9059             {
9060                 login = words[1];
9061                 name = getOption(cloptions, "name", email);
9062                 signingup = true;
9063                 if (interactiveThread())
9064                 {
9065                     setprompt(NEWPASSWORD);
9066                 }
9067                 else
9068                 {
9069                     setCurrentOutCode(MCMD_EARGS);
9070                     LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("signup");
9071                 }
9072             }
9073         }
9074         else
9075         {
9076             setCurrentOutCode(MCMD_EARGS);
9077             LOG_err << "      " << getUsageStr("signup");
9078         }
9079 
9080         return;
9081     }
9082     else if (words[0] == "whoami")
9083     {
9084         MegaUser *u = api->getMyUser();
9085         if (u)
9086         {
9087             OUTSTREAM << "Account e-mail: " << u->getEmail() << endl;
9088             if (getFlag(clflags, "l"))
9089             {
9090                 MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9091                 api->getExtendedAccountDetails(true, true, true, megaCmdListener);
9092                 actUponGetExtendedAccountDetails(megaCmdListener);
9093                 delete megaCmdListener;
9094             }
9095             delete u;
9096         }
9097         else
9098         {
9099             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9100             LOG_err << "Not logged in.";
9101         }
9102 
9103         return;
9104     }
9105     else if (words[0] == "df")
9106     {
9107         bool humanreadable = getFlag(clflags, "h");
9108         MegaUser *u = api->getMyUser();
9109         if (u)
9110         {
9111             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9112             api->getSpecificAccountDetails(true, false, false, -1, megaCmdListener);
9113             megaCmdListener->wait();
9114             if (checkNoErrors(megaCmdListener->getError(), "failed to get used storage"))
9115             {
9116                 unique_ptr<MegaAccountDetails> details(megaCmdListener->getRequest()->getMegaAccountDetails());
9117                 if (details)
9118                 {
9119                     long long usedTotal = 0;
9120                     long long rootStorage = 0;
9121                     long long inboxStorage = 0;
9122                     long long rubbishStorage = 0;
9123                     long long insharesStorage = 0;
9124 
9125                     long long storageMax = details->getStorageMax();
9126 
9127                     unique_ptr<MegaNode> root(api->getRootNode());
9128                     unique_ptr<MegaNode> inbox(api->getInboxNode());
9129                     unique_ptr<MegaNode> rubbish(api->getRubbishNode());
9130                     unique_ptr<MegaNodeList> inShares(api->getInShares());
9131 
9132                     if (!root || !inbox || !rubbish)
9133                     {
9134                         LOG_err << " Error retrieving storage details. Root node missing";
9135                         return;
9136                     }
9137 
9138                     MegaHandle rootHandle = root->getHandle();
9139                     MegaHandle inboxHandle = inbox->getHandle();
9140                     MegaHandle rubbishHandle = rubbish->getHandle();
9141 
9142                     rootStorage = details->getStorageUsed(rootHandle);
9143                     OUTSTREAM << "Cloud drive:          "
9144                               << getFixLengthString(sizeToText(rootStorage, true, humanreadable), 12, ' ', true) << " in "
9145                               << getFixLengthString(SSTR(details->getNumFiles(rootHandle)),7,' ',true) << " file(s) and "
9146                               << getFixLengthString(SSTR(details->getNumFolders(rootHandle)),7,' ',true) << " folder(s)" << endl;
9147 
9148 
9149                     inboxStorage = details->getStorageUsed(inboxHandle);
9150                     OUTSTREAM << "Inbox:                "
9151                               << getFixLengthString(sizeToText(inboxStorage, true, humanreadable), 12, ' ', true ) << " in "
9152                               << getFixLengthString(SSTR(details->getNumFiles(inboxHandle)),7,' ',true) << " file(s) and "
9153                               << getFixLengthString(SSTR(details->getNumFolders(inboxHandle)),7,' ',true) << " folder(s)" << endl;
9154 
9155                     rubbishStorage = details->getStorageUsed(rubbishHandle);
9156                     OUTSTREAM << "Rubbish bin:          "
9157                               << getFixLengthString(sizeToText(rubbishStorage, true, humanreadable), 12, ' ', true) << " in "
9158                               << getFixLengthString(SSTR(details->getNumFiles(rubbishHandle)),7,' ',true) << " file(s) and "
9159                               << getFixLengthString(SSTR(details->getNumFolders(rubbishHandle)),7,' ',true) << " folder(s)" << endl;
9160 
9161 
9162                     if (inShares)
9163                     {
9164                         for (int i = 0; i < inShares->size(); i++)
9165                         {
9166                             n = inShares->get(i);
9167                             long long thisinshareStorage = details->getStorageUsed(n->getHandle());
9168                             insharesStorage += thisinshareStorage;
9169                             if (i == 0)
9170                             {
9171                                 OUTSTREAM << "Incoming shares:" << endl;
9172                             }
9173 
9174                             string name = n->getName();
9175                             name += ": ";
9176                             name.append(max(0, int (21 - name.size())), ' ');
9177 
9178                             OUTSTREAM << " " << name
9179                                       << getFixLengthString(sizeToText(thisinshareStorage, true, humanreadable), 12, ' ', true) << " in "
9180                                       << getFixLengthString(SSTR(details->getNumFiles(n->getHandle())),7,' ',true) << " file(s) and "
9181                                       << getFixLengthString(SSTR(details->getNumFolders(n->getHandle())),7,' ',true) << " folder(s)" << endl;
9182                         }
9183                     }
9184 
9185                     usedTotal = sandboxCMD->receivedStorageSum;
9186 
9187                     float percent = float(usedTotal * 1.0 / storageMax);
9188                     if (percent < 0 ) percent = 0;
9189 
9190                     string sof= percentageToText(percent);
9191                     sof +=  " of ";
9192                     sof +=  sizeToText(storageMax, true, humanreadable);
9193 
9194 
9195                     for (int i = 0; i < 75 ; i++)
9196                     {
9197                         OUTSTREAM << "-";
9198                     }
9199                     OUTSTREAM << endl;
9200 
9201                     OUTSTREAM << "USED STORAGE:         " << getFixLengthString(sizeToText(usedTotal, true, humanreadable), 12, ' ', true)
9202                               << "  " << getFixLengthString(sof, 39, ' ', true) << endl;
9203 
9204                     for (int i = 0; i < 75 ; i++)
9205                     {
9206                         OUTSTREAM << "-";
9207                     }
9208                     OUTSTREAM << endl;
9209 
9210                     long long usedinVersions = details->getVersionStorageUsed(rootHandle)
9211                             + details->getVersionStorageUsed(inboxHandle)
9212                             + details->getVersionStorageUsed(rubbishHandle);
9213 
9214 
9215                     OUTSTREAM << "Total size taken up by file versions: "
9216                               << getFixLengthString(sizeToText(usedinVersions, true, humanreadable), 12, ' ', true) << endl;
9217                 }
9218             }
9219             delete megaCmdListener;
9220             delete u;
9221         }
9222         else
9223         {
9224             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9225             LOG_err << "Not logged in.";
9226         }
9227 
9228         return;
9229     }
9230     else if (words[0] == "export")
9231     {
9232         if (!api->isFilesystemAvailable())
9233         {
9234             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9235             LOG_err << "Not logged in.";
9236             return;
9237         }
9238         ::mega::m_time_t expireTime = 0;
9239         string sexpireTime = getOption(cloptions, "expire", "");
9240         if ("" != sexpireTime)
9241         {
9242             expireTime = getTimeStampAfter(sexpireTime);
9243         }
9244         if (expireTime < 0)
9245         {
9246             setCurrentOutCode(MCMD_EARGS);
9247             LOG_err << "Invalid time " << sexpireTime;
9248             return;
9249         }
9250 
9251         string linkPass = getOption(cloptions, "password", "");
9252         bool add = getFlag(clflags, "a");
9253 
9254         if (linkPass.size() && !add)
9255         {
9256             setCurrentOutCode(MCMD_EARGS);
9257             LOG_err << "You need to use -a to add an export. Usage: " << getUsageStr("export");
9258             return;
9259         }
9260 
9261         if (words.size() <= 1)
9262         {
9263             words.push_back(string(".")); //cwd
9264         }
9265 
9266         for (int i = 1; i < (int)words.size(); i++)
9267         {
9268             unescapeifRequired(words[i]);
9269             if (isRegExp(words[i]))
9270             {
9271                 vector<MegaNode *> *nodes = nodesbypath(words[i].c_str(), getFlag(clflags,"use-pcre"));
9272                 if (nodes)
9273                 {
9274                     if (!nodes->size())
9275                     {
9276                         setCurrentOutCode(MCMD_NOTFOUND);
9277                         LOG_err << "Nodes not found: " << words[i];
9278                     }
9279                     for (std::vector< MegaNode * >::iterator it = nodes->begin(); it != nodes->end(); ++it)
9280                     {
9281                         MegaNode * n = *it;
9282                         if (n)
9283                         {
9284                             if (add)
9285                             {
9286                                 LOG_debug << " exporting ... " << n->getName() << " expireTime=" << expireTime;
9287                                 exportNode(n, expireTime, linkPass, getFlag(clflags,"f"));
9288                             }
9289                             else if (getFlag(clflags, "d"))
9290                             {
9291                                 LOG_debug << " deleting export ... " << n->getName();
9292                                 disableExport(n);
9293                             }
9294                             else
9295                             {
9296                                 if (dumpListOfExported(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i]) == 0 )
9297                                 {
9298                                     OUTSTREAM << words[i] << " is not exported. Use -a to export it" << endl;
9299                                 }
9300                             }
9301                             delete n;
9302                         }
9303                     }
9304 
9305                     nodes->clear();
9306                     delete nodes;
9307                 }
9308                 else
9309                 {
9310                     setCurrentOutCode(MCMD_NOTFOUND);
9311                     LOG_err << "Node not found: " << words[i];
9312                 }
9313             }
9314             else
9315             {
9316                 MegaNode *n = nodebypath(words[i].c_str());
9317                 if (n)
9318                 {
9319                     if (add)
9320                     {
9321                         LOG_debug << " exporting ... " << n->getName();
9322                         exportNode(n, expireTime, linkPass, getFlag(clflags,"f"));
9323                     }
9324                     else if (getFlag(clflags, "d"))
9325                     {
9326                         LOG_debug << " deleting export ... " << n->getName();
9327                         disableExport(n);
9328                     }
9329                     else
9330                     {
9331                         if (dumpListOfExported(n, getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")), clflags, cloptions, words[i]) == 0 )
9332                         {
9333                             OUTSTREAM << "Couldn't find anything exported below ";
9334                             if (words[i] == ".")
9335                             {
9336                                 OUTSTREAM << "current folder";
9337                             }
9338                             else
9339                             {
9340                                 OUTSTREAM << "<";
9341                                 OUTSTREAM << words[i];
9342                                 OUTSTREAM << ">";
9343                             }
9344                             OUTSTREAM << ". Use -a to export " << (words[i].size()?"it":"something") << endl;
9345                         }
9346                     }
9347                     delete n;
9348                 }
9349                 else
9350                 {
9351                     setCurrentOutCode(MCMD_NOTFOUND);
9352                     LOG_err << "Node not found: " << words[i];
9353                 }
9354             }
9355         }
9356 
9357         return;
9358     }
9359     else if (words[0] == "import")
9360     {
9361         if (!api->isFilesystemAvailable())
9362         {
9363             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9364             LOG_err << "Not logged in.";
9365             return;
9366         }
9367         string remotePath = "";
9368         MegaNode *dstFolder = NULL;
9369         if (words.size() > 1) //link
9370         {
9371             if (isPublicLink(words[1]))
9372             {
9373                 string publicLink = words[1];
9374                 if (isEncryptedLink(publicLink))
9375                 {
9376                     string linkPass = getOption(cloptions, "password", "");
9377                     if (!linkPass.size())
9378                     {
9379                         linkPass = askforUserResponse("Enter password: ");
9380                     }
9381 
9382                     if (linkPass.size())
9383                     {
9384                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9385                         api->decryptPasswordProtectedLink(publicLink.c_str(), linkPass.c_str(), megaCmdListener);
9386                         megaCmdListener->wait();
9387                         if (checkNoErrors(megaCmdListener->getError(), "decrypt password protected link"))
9388                         {
9389                             publicLink = megaCmdListener->getRequest()->getText();
9390                             delete megaCmdListener;
9391                         }
9392                         else
9393                         {
9394                             setCurrentOutCode(MCMD_NOTPERMITTED);
9395                             LOG_err << "Invalid password";
9396                             delete megaCmdListener;
9397                             return;
9398                         }
9399                     }
9400                     else
9401                     {
9402                         setCurrentOutCode(MCMD_EARGS);
9403                         LOG_err << "Need a password to decrypt provided link (--password=PASSWORD)";
9404                         return;
9405                     }
9406                 }
9407 
9408                 if (words.size() > 2)
9409                 {
9410                     remotePath = words[2];
9411                     dstFolder = nodebypath(remotePath.c_str());
9412                 }
9413                 else
9414                 {
9415                     dstFolder = api->getNodeByHandle(cwd);
9416                     remotePath = "."; //just to inform (alt: getpathbynode)
9417                 }
9418                 if (dstFolder && ( !dstFolder->getType() == MegaNode::TYPE_FILE ))
9419                 {
9420                     if (getLinkType(publicLink) == MegaNode::TYPE_FILE)
9421                     {
9422                         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9423 
9424                         api->importFileLink(publicLink.c_str(), dstFolder, megaCmdListener);
9425                         megaCmdListener->wait();
9426                         if (checkNoErrors(megaCmdListener->getError(), "import node"))
9427                         {
9428                             MegaNode *imported = api->getNodeByHandle(megaCmdListener->getRequest()->getNodeHandle());
9429                             if (imported)
9430                             {
9431                                 char *importedPath = api->getNodePath(imported);
9432                                 OUTSTREAM << "Import file complete: " << importedPath << endl;
9433                                 delete []importedPath;
9434                             }
9435                             else
9436                             {
9437                                 LOG_warn << "Import file complete: Couldn't get path of imported file";
9438                             }
9439                             delete imported;
9440                         }
9441 
9442                         delete megaCmdListener;
9443                     }
9444                     else if (getLinkType(publicLink) == MegaNode::TYPE_FOLDER)
9445                     {
9446                         MegaApi* apiFolder = getFreeApiFolder();
9447                         char *accountAuth = api->getAccountAuth();
9448                         apiFolder->setAccountAuth(accountAuth);
9449                         delete []accountAuth;
9450 
9451                         MegaCmdListener *megaCmdListener = new MegaCmdListener(apiFolder, NULL);
9452                         apiFolder->loginToFolder(publicLink.c_str(), megaCmdListener);
9453                         megaCmdListener->wait();
9454                         if (checkNoErrors(megaCmdListener->getError(), "login to folder"))
9455                         {
9456                             MegaCmdListener *megaCmdListener2 = new MegaCmdListener(apiFolder, NULL);
9457                             apiFolder->fetchNodes(megaCmdListener2);
9458                             megaCmdListener2->wait();
9459                             if (checkNoErrors(megaCmdListener2->getError(), "access folder link " + publicLink))
9460                             {
9461                                 MegaNode *nodeToImport = NULL;
9462                                 bool usedRoot = false;
9463                                 string shandle = getPublicLinkHandle(publicLink);
9464                                 if (shandle.size())
9465                                 {
9466                                     handle thehandle = apiFolder->base64ToHandle(shandle.c_str());
9467                                     nodeToImport = apiFolder->getNodeByHandle(thehandle);
9468                                 }
9469                                 else
9470                                 {
9471                                     nodeToImport = apiFolder->getRootNode();
9472                                     usedRoot = true;
9473                                 }
9474 
9475                                 if (nodeToImport)
9476                                 {
9477                                     MegaNode *authorizedNode = apiFolder->authorizeNode(nodeToImport);
9478                                     if (authorizedNode != NULL)
9479                                     {
9480                                         MegaCmdListener *megaCmdListener3 = new MegaCmdListener(apiFolder, NULL);
9481                                         api->copyNode(authorizedNode, dstFolder, megaCmdListener3);
9482                                         megaCmdListener3->wait();
9483                                         if (checkNoErrors(megaCmdListener->getError(), "import folder node"))
9484                                         {
9485                                             MegaNode *importedFolderNode = api->getNodeByHandle(megaCmdListener3->getRequest()->getNodeHandle());
9486                                             char *pathnewFolder = api->getNodePath(importedFolderNode);
9487                                             if (pathnewFolder)
9488                                             {
9489                                                 OUTSTREAM << "Imported folder complete: " << pathnewFolder << endl;
9490                                                 delete []pathnewFolder;
9491                                             }
9492                                             delete importedFolderNode;
9493                                         }
9494                                         delete megaCmdListener3;
9495                                         delete authorizedNode;
9496                                     }
9497                                     else
9498                                     {
9499                                         setCurrentOutCode(MCMD_EUNEXPECTED);
9500                                         LOG_debug << "Node couldn't be authorized: " << publicLink;
9501                                     }
9502                                     delete nodeToImport;
9503                                 }
9504                                 else
9505                                 {
9506                                     setCurrentOutCode(MCMD_INVALIDSTATE);
9507                                     if (usedRoot)
9508                                     {
9509                                         LOG_err << "Couldn't get root folder for folder link";
9510                                     }
9511                                     else
9512                                     {
9513                                         LOG_err << "Failed to get node corresponding to handle within public link " << shandle;
9514                                     }
9515                                 }
9516                             }
9517                             delete megaCmdListener2;
9518                         }
9519                         delete megaCmdListener;
9520                         freeApiFolder(apiFolder);
9521                     }
9522                     else
9523                     {
9524                         setCurrentOutCode(MCMD_EARGS);
9525                         LOG_err << "Invalid link: " << publicLink;
9526                         LOG_err << "      " << getUsageStr("import");
9527                     }
9528                 }
9529                 else
9530                 {
9531                     setCurrentOutCode(MCMD_INVALIDTYPE);
9532                     LOG_err << "Invalid destiny: " << remotePath;
9533                 }
9534                 delete dstFolder;
9535             }
9536             else
9537             {
9538                 setCurrentOutCode(MCMD_INVALIDTYPE);
9539                 LOG_err << "Invalid link: " << words[1];
9540             }
9541         }
9542         else
9543         {
9544             setCurrentOutCode(MCMD_EARGS);
9545             LOG_err << "      " << getUsageStr("import");
9546         }
9547 
9548         return;
9549     }
9550     else if (words[0] == "reload")
9551     {
9552         int clientID = getintOption(cloptions, "clientID", -1);
9553 
9554         OUTSTREAM << "Reloading account..." << endl;
9555         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL, NULL, clientID);
9556         api->fetchNodes(megaCmdListener);
9557         actUponFetchNodes(api, megaCmdListener);
9558         delete megaCmdListener;
9559         return;
9560     }
9561     else if (words[0] == "logout")
9562     {
9563         OUTSTREAM << "Logging out..." << endl;
9564         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9565         bool keepSession = getFlag(clflags, "keep-session");
9566         char * dumpSession = NULL;
9567 
9568         if (keepSession) //local logout
9569         {
9570             dumpSession = api->dumpSession();
9571             api->localLogout(megaCmdListener);
9572         }
9573         else
9574         {
9575             api->logout(megaCmdListener);
9576         }
9577         actUponLogout(megaCmdListener, keepSession);
9578         if (keepSession)
9579         {
9580             OUTSTREAM << "Session closed but not deleted. Warning: it will be restored the next time you execute the application. Execute \"logout\" to delete the session permanently." << endl;
9581 
9582             if (dumpSession)
9583             {
9584                 OUTSTREAM << "You can also login with the session id: " << dumpSession << endl;
9585                 delete []dumpSession;
9586             }
9587         }
9588         delete megaCmdListener;
9589 
9590         return;
9591     }
9592     else if (words[0] == "confirm")
9593     {
9594         if (words.size() > 2)
9595         {
9596             string link = words[1];
9597             string email = words[2];
9598             // check email corresponds with link:
9599             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9600             api->querySignupLink(link.c_str(), megaCmdListener);
9601             megaCmdListener->wait();
9602             if (checkNoErrors(megaCmdListener->getError(), "check email corresponds to link"))
9603             {
9604                 if (megaCmdListener->getRequest()->getFlag())
9605                 {
9606                     OUTSTREAM << "Account " << email << " confirmed succesfully. You can login with it now" << endl;
9607                 }
9608                 else if (megaCmdListener->getRequest()->getEmail() && email == megaCmdListener->getRequest()->getEmail())
9609                 {
9610                     string passwd;
9611                     if (words.size() > 3)
9612                     {
9613                         passwd = words[3];
9614                         confirm(passwd, email, link);
9615                     }
9616                     else
9617                     {
9618                         this->login = email;
9619                         this->link = link;
9620                         confirming = true;
9621                         if (interactiveThread() && !getCurrentThreadIsCmdShell())
9622                         {
9623                             setprompt(LOGINPASSWORD);
9624                         }
9625                         else
9626                         {
9627                             setCurrentOutCode(MCMD_EARGS);
9628                             LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("confirm");
9629                         }
9630                     }
9631                 }
9632                 else
9633                 {
9634                     setCurrentOutCode(MCMD_INVALIDEMAIL);
9635                     LOG_err << email << " doesn't correspond to the confirmation link: " << link;
9636                 }
9637             }
9638 
9639             delete megaCmdListener;
9640         }
9641         else
9642         {
9643             setCurrentOutCode(MCMD_EARGS);
9644             LOG_err << "      " << getUsageStr("confirm");
9645         }
9646 
9647         return;
9648     }
9649     else if (words[0] == "session")
9650     {
9651         char * dumpSession = api->dumpSession();
9652         if (dumpSession)
9653         {
9654             OUTSTREAM << "Your (secret) session is: " << dumpSession << endl;
9655             delete []dumpSession;
9656         }
9657         else
9658         {
9659             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9660             LOG_err << "Not logged in.";
9661         }
9662         return;
9663     }
9664     else if (words[0] == "history")
9665     {
9666         return;
9667     }
9668     else if (words[0] == "version")
9669     {
9670         OUTSTREAM << "MEGAcmd version: " << MEGACMD_MAJOR_VERSION << "." << MEGACMD_MINOR_VERSION << "." << MEGACMD_MICRO_VERSION << "." << MEGACMD_BUILD_ID << ": code " << MEGACMD_CODE_VERSION
9671 #ifdef _WIN64
9672                   << " (64 bits)"
9673 #endif
9674                   << endl;
9675 
9676         MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9677         api->getLastAvailableVersion("BdARkQSQ",megaCmdListener);
9678         if (!megaCmdListener->trywait(2000))
9679         {
9680             if (!megaCmdListener->getError())
9681             {
9682                 LOG_fatal << "No MegaError at getLastAvailableVersion: ";
9683             }
9684             else if (megaCmdListener->getError()->getErrorCode() != MegaError::API_OK)
9685             {
9686                 LOG_debug << "Couldn't get latests available version: " << megaCmdListener->getError()->getErrorString();
9687             }
9688             else
9689             {
9690                 if (megaCmdListener->getRequest()->getNumber() != MEGACMD_CODE_VERSION)
9691                 {
9692                     OUTSTREAM << "---------------------------------------------------------------------" << endl;
9693                     OUTSTREAM << "--        There is a new version available of megacmd: " << getLeftAlignedStr(megaCmdListener->getRequest()->getName(), 12) << "--" << endl;
9694                     OUTSTREAM << "--        Please, download it from https://mega.nz/cmd             --" << endl;
9695 #if defined(__APPLE__)
9696                     OUTSTREAM << "--        Before installing enter \"exit\" to close MEGAcmd          --" << endl;
9697 #endif
9698                     OUTSTREAM << "---------------------------------------------------------------------" << endl;
9699                 }
9700             }
9701             delete megaCmdListener;
9702         }
9703         else
9704         {
9705             LOG_debug << "Couldn't get latests available version (petition timed out)";
9706 
9707             api->removeRequestListener(megaCmdListener);
9708             delete megaCmdListener;
9709         }
9710 
9711 
9712 
9713 
9714 
9715         if (getFlag(clflags,"c"))
9716         {
9717             OUTSTREAM << "Changes in the current version:" << endl;
9718             string thechangelog = megacmdchangelog;
9719             if (thechangelog.size())
9720             {
9721                 replaceAll(thechangelog,"\n","\n * ");
9722                 OUTSTREAM << " * " << thechangelog << endl << endl;
9723             }
9724         }
9725         if (getFlag(clflags,"l"))
9726         {
9727             OUTSTREAM << "MEGA SDK version: " << MEGA_MAJOR_VERSION << "." << MEGA_MINOR_VERSION << "." << MEGA_MICRO_VERSION << endl;
9728 
9729             OUTSTREAM << "MEGA SDK Credits: https://github.com/meganz/sdk/blob/master/CREDITS.md" << endl;
9730             OUTSTREAM << "MEGA SDK License: https://github.com/meganz/sdk/blob/master/LICENSE" << endl;
9731             OUTSTREAM << "MEGAcmd License: https://github.com/meganz/megacmd/blob/master/LICENSE" << endl;
9732             OUTSTREAM << "MEGA Terms: https://mega.nz/terms" << endl;
9733             OUTSTREAM << "MEGA General Data Protection Regulation Disclosure: https://mega.nz/gdpr" << endl;
9734 
9735             OUTSTREAM << "Features enabled:" << endl;
9736 
9737 #ifdef USE_CRYPTOPP
9738             OUTSTREAM << "* CryptoPP" << endl;
9739 #endif
9740 
9741 #ifdef USE_SQLITE
9742           OUTSTREAM << "* SQLite" << endl;
9743 #endif
9744 
9745 #ifdef USE_BDB
9746             OUTSTREAM << "* Berkeley DB" << endl;
9747 #endif
9748 
9749 #ifdef USE_INOTIFY
9750            OUTSTREAM << "* inotify" << endl;
9751 #endif
9752 
9753 #ifdef HAVE_FDOPENDIR
9754            OUTSTREAM << "* fdopendir" << endl;
9755 #endif
9756 
9757 #ifdef HAVE_SENDFILE
9758             OUTSTREAM << "* sendfile" << endl;
9759 #endif
9760 
9761 #ifdef _LARGE_FILES
9762            OUTSTREAM << "* _LARGE_FILES" << endl;
9763 #endif
9764 
9765 #ifdef USE_FREEIMAGE
9766             OUTSTREAM << "* FreeImage" << endl;
9767 #endif
9768 
9769 #ifdef USE_PCRE
9770             OUTSTREAM << "* PCRE" << endl;
9771 #endif
9772 
9773 #ifdef ENABLE_SYNC
9774            OUTSTREAM << "* sync subsystem" << endl;
9775 #endif
9776         }
9777         return;
9778     }
9779     else if (words[0] == "masterkey")
9780     {
9781         if (!api->isFilesystemAvailable())
9782         {
9783             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9784             LOG_err << "Not logged in.";
9785             return;
9786         }
9787         OUTSTREAM << api->exportMasterKey() << endl;
9788         api->masterKeyExported(); //we do not wait for this to end
9789     }
9790     else if (words[0] == "showpcr")
9791     {
9792         if (!api->isFilesystemAvailable())
9793         {
9794             setCurrentOutCode(MCMD_NOTLOGGEDIN);
9795             LOG_err << "Not logged in.";
9796             return;
9797         }
9798         bool incoming = getFlag(clflags, "in");
9799         bool outgoing = getFlag(clflags, "out");
9800 
9801         if (!incoming && !outgoing)
9802         {
9803             incoming = true;
9804             outgoing = true;
9805         }
9806 
9807         if (outgoing)
9808         {
9809             MegaContactRequestList *ocrl = api->getOutgoingContactRequests();
9810             if (ocrl)
9811             {
9812                 if (ocrl->size())
9813                 {
9814                     OUTSTREAM << "Outgoing PCRs:" << endl;
9815                 }
9816                 for (int i = 0; i < ocrl->size(); i++)
9817                 {
9818                     MegaContactRequest * cr = ocrl->get(i);
9819                     OUTSTREAM << " " << getLeftAlignedStr(cr->getTargetEmail(),22);
9820 
9821                     char * sid = api->userHandleToBase64(cr->getHandle());
9822 
9823                     OUTSTREAM << "\t (id: " << sid << ", creation: " << getReadableTime(cr->getCreationTime(), getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")))
9824                               << ", modification: " << getReadableTime(cr->getModificationTime(), getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822"))) << ")";
9825 
9826                     delete[] sid;
9827                     OUTSTREAM << endl;
9828                 }
9829 
9830                 delete ocrl;
9831             }
9832         }
9833 
9834         if (incoming)
9835         {
9836             MegaContactRequestList *icrl = api->getIncomingContactRequests();
9837             if (icrl)
9838             {
9839                 if (icrl->size())
9840                 {
9841                     OUTSTREAM << "Incoming PCRs:" << endl;
9842                 }
9843 
9844                 for (int i = 0; i < icrl->size(); i++)
9845                 {
9846                     MegaContactRequest * cr = icrl->get(i);
9847                     OUTSTREAM << " " << getLeftAlignedStr(cr->getSourceEmail(), 22);
9848 
9849                     MegaHandle id = cr->getHandle();
9850                     char sid[12];
9851                     Base64::btoa((unsigned char*)&( id ), sizeof( id ), sid);
9852 
9853                     OUTSTREAM << "\t (id: " << sid << ", creation: " << getReadableTime(cr->getCreationTime(), getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822")))
9854                               << ", modification: " << getReadableTime(cr->getModificationTime(), getTimeFormatFromSTR(getOption(cloptions, "time-format","RFC2822"))) << ")";
9855                     if (cr->getSourceMessage())
9856                     {
9857                         OUTSTREAM << endl << "\t" << "Invitation message: " << cr->getSourceMessage();
9858                     }
9859 
9860                     OUTSTREAM << endl;
9861                 }
9862 
9863                 delete icrl;
9864             }
9865         }
9866         return;
9867     }
9868     else if (words[0] == "killsession")
9869     {
9870         bool all = getFlag(clflags, "a");
9871 
9872         if ((words.size() <= 1 && !all) || (all && words.size() > 1))
9873         {
9874             setCurrentOutCode(MCMD_EARGS);
9875             LOG_err << "      " << getUsageStr("killsession");
9876             return;
9877         }
9878         string thesession;
9879         MegaHandle thehandle = UNDEF;
9880 
9881         if (all)
9882         {
9883             // Kill all sessions (except current)
9884             words.push_back("all");
9885         }
9886 
9887         for (unsigned int i = 1; i < words.size() ; i++)
9888         {
9889             thesession = words[i];
9890             if (thesession == "all")
9891             {
9892                 thehandle = INVALID_HANDLE;
9893             }
9894             else
9895             {
9896                 thehandle = api->base64ToUserHandle(thesession.c_str());
9897             }
9898 
9899 
9900             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9901             api->killSession(thehandle, megaCmdListener);
9902             megaCmdListener->wait();
9903             if (checkNoErrors(megaCmdListener->getError(), "kill session " + thesession + ". Maybe the session was not valid."))
9904             {
9905                 if (thesession != "all")
9906                 {
9907                     OUTSTREAM << "Session " << thesession << " killed successfully" << endl;
9908                 }
9909                 else
9910                 {
9911                     OUTSTREAM << "All sessions killed successfully" << endl;
9912                 }
9913             }
9914 
9915             delete megaCmdListener;
9916         }
9917 
9918         return;
9919     }
9920     else if (words[0] == "transfers")
9921     {
9922         bool showcompleted = getFlag(clflags, "show-completed");
9923         bool onlycompleted = getFlag(clflags, "only-completed");
9924         bool onlyuploads = getFlag(clflags, "only-uploads");
9925         bool onlydownloads = getFlag(clflags, "only-downloads");
9926         bool showsyncs = getFlag(clflags, "show-syncs");
9927         bool printsummary = getFlag(clflags, "summary");
9928 
9929         int PATHSIZE = getintOption(cloptions,"path-display-size");
9930         if (!PATHSIZE)
9931         {
9932             // get screen size for output purposes
9933             unsigned int width = getNumberOfCols(75);
9934             PATHSIZE = min(60,int((width-46)/2));
9935         }
9936         PATHSIZE = max(0, PATHSIZE);
9937 
9938         if (getFlag(clflags,"c"))
9939         {
9940             if (getFlag(clflags,"a"))
9941             {
9942                 if (onlydownloads || (!onlyuploads && !onlydownloads) )
9943                 {
9944                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9945                     api->cancelTransfers(MegaTransfer::TYPE_DOWNLOAD, megaCmdListener);
9946                     megaCmdListener->wait();
9947                     if (checkNoErrors(megaCmdListener->getError(), "cancel all download transfers"))
9948                     {
9949                         OUTSTREAM << "Download transfers cancelled successfully." << endl;
9950                     }
9951                     delete megaCmdListener;
9952                 }
9953                 if (onlyuploads || (!onlyuploads && !onlydownloads) )
9954                 {
9955                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9956                     api->cancelTransfers(MegaTransfer::TYPE_UPLOAD, megaCmdListener);
9957                     megaCmdListener->wait();
9958                     if (checkNoErrors(megaCmdListener->getError(), "cancel all upload transfers"))
9959                     {
9960                         OUTSTREAM << "Upload transfers cancelled successfully." << endl;
9961                     }
9962                     delete megaCmdListener;
9963                 }
9964 
9965             }
9966             else
9967             {
9968                 if (words.size() < 2)
9969                 {
9970                     setCurrentOutCode(MCMD_EARGS);
9971                     LOG_err << "      " << getUsageStr("transfers");
9972                     return;
9973                 }
9974                 for (unsigned int i = 1; i < words.size(); i++)
9975                 {
9976                     MegaTransfer *transfer = api->getTransferByTag(toInteger(words[i],-1));
9977                     if (transfer)
9978                     {
9979                         if (transfer->isSyncTransfer())
9980                         {
9981                             LOG_err << "Unable to cancel transfer with tag " << words[i] << ". Sync transfers cannot be cancelled";
9982                             setCurrentOutCode(MCMD_INVALIDTYPE);
9983                         }
9984                         else
9985                         {
9986                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
9987                             api->cancelTransfer(transfer, megaCmdListener);
9988                             megaCmdListener->wait();
9989                             if (checkNoErrors(megaCmdListener->getError(), "cancel transfer with tag " + words[i] + "."))
9990                             {
9991                                 OUTSTREAM << "Transfer " << words[i]<< " cancelled successfully." << endl;
9992                             }
9993                             delete megaCmdListener;
9994                         }
9995                     }
9996                     else
9997                     {
9998                         LOG_err << "Coul not find transfer with tag: " << words[i];
9999                         setCurrentOutCode(MCMD_NOTFOUND);
10000                     }
10001                 }
10002             }
10003 
10004             return;
10005         }
10006 
10007         if (getFlag(clflags,"p") || getFlag(clflags,"r"))
10008         {
10009             if (getFlag(clflags,"a"))
10010             {
10011                 if (onlydownloads || (!onlyuploads && !onlydownloads) )
10012                 {
10013                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
10014                     api->pauseTransfers(getFlag(clflags,"p"), MegaTransfer::TYPE_DOWNLOAD, megaCmdListener);
10015                     megaCmdListener->wait();
10016                     if (checkNoErrors(megaCmdListener->getError(), (getFlag(clflags,"p")?"pause all download transfers":"resume all download transfers")))
10017                     {
10018                         OUTSTREAM << "Download transfers "<< (getFlag(clflags,"p")?"pause":"resume") << "d successfully." << endl;
10019                     }
10020                     delete megaCmdListener;
10021                 }
10022                 if (onlyuploads || (!onlyuploads && !onlydownloads) )
10023                 {
10024                     MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
10025                     api->pauseTransfers(getFlag(clflags,"p"), MegaTransfer::TYPE_UPLOAD, megaCmdListener);
10026                     megaCmdListener->wait();
10027                     if (checkNoErrors(megaCmdListener->getError(), (getFlag(clflags,"p")?"pause all download transfers":"resume all download transfers")))
10028                     {
10029                         OUTSTREAM << "Upload transfers "<< (getFlag(clflags,"p")?"pause":"resume") << "d successfully." << endl;
10030                     }
10031                     delete megaCmdListener;
10032                 }
10033 
10034             }
10035             else
10036             {
10037                 if (words.size() < 2)
10038                 {
10039                     setCurrentOutCode(MCMD_EARGS);
10040                     LOG_err << "      " << getUsageStr("transfers");
10041                     return;
10042                 }
10043                 for (unsigned int i = 1; i < words.size(); i++)
10044                 {
10045                     MegaTransfer *transfer = api->getTransferByTag(toInteger(words[i],-1));
10046                     if (transfer)
10047                     {
10048                         if (transfer->isSyncTransfer())
10049                         {
10050                             LOG_err << "Unable to "<< (getFlag(clflags,"p")?"pause":"resume") << " transfer with tag " << words[i] << ". Sync transfers cannot be "<< (getFlag(clflags,"p")?"pause":"resume") << "d";
10051                             setCurrentOutCode(MCMD_INVALIDTYPE);
10052                         }
10053                         else
10054                         {
10055                             MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
10056                             api->pauseTransfer(transfer, getFlag(clflags,"p"), megaCmdListener);
10057                             megaCmdListener->wait();
10058                             if (checkNoErrors(megaCmdListener->getError(), (getFlag(clflags,"p")?"pause transfer with tag ":"resume transfer with tag ") + words[i] + "."))
10059                             {
10060                                 OUTSTREAM << "Transfer " << words[i]<< " "<< (getFlag(clflags,"p")?"pause":"resume") << "d successfully." << endl;
10061                             }
10062                             delete megaCmdListener;
10063                         }
10064                     }
10065                     else
10066                     {
10067                         LOG_err << "Coul not find transfer with tag: " << words[i];
10068                         setCurrentOutCode(MCMD_NOTFOUND);
10069                     }
10070                 }
10071             }
10072 
10073             return;
10074         }
10075 
10076         //show transfers
10077         MegaTransferData* transferdata = api->getTransferData();
10078 
10079         int ndownloads = transferdata->getNumDownloads();
10080         int nuploads = transferdata->getNumUploads();
10081 
10082         if (printsummary)
10083         {
10084             long long transferredDownload = 0, transferredUpload = 0, totalDownload = 0, totalUpload = 0;
10085             for (int i = 0; i< ndownloads; i++)
10086             {
10087                 MegaTransfer *t = api->getTransferByTag(transferdata->getDownloadTag(i));
10088                 {
10089                     if (t)
10090                     {
10091                         transferredDownload += t->getTransferredBytes();
10092                         totalDownload += t->getTotalBytes();
10093                         delete t;
10094                     }
10095                 }
10096             }
10097             for (int i = 0; i< nuploads; i++)
10098             {
10099                 MegaTransfer *t = api->getTransferByTag(transferdata->getUploadTag(i));
10100                 {
10101                     if (t)
10102                     {
10103                         transferredUpload += t->getTransferredBytes();
10104                         totalUpload += t->getTotalBytes();
10105                         delete t;
10106                     }
10107                 }
10108             }
10109 
10110             float percentDownload = !totalDownload?0:float(transferredDownload*1.0/totalDownload);
10111             float percentUpload = !totalUpload?0:float(transferredUpload*1.0/totalUpload);
10112 
10113             OUTSTREAM << getFixLengthString("NUM DOWNLOADS", 16, ' ', true);
10114             OUTSTREAM << getFixLengthString("DOWNLOADED", 12, ' ', true);
10115             OUTSTREAM << getFixLengthString("TOTAL", 12, ' ', true);
10116             OUTSTREAM << getFixLengthString("%   ", 8, ' ', true);
10117             OUTSTREAM << "     ";
10118             OUTSTREAM << getFixLengthString("NUM UPLOADS", 16, ' ', true);
10119             OUTSTREAM << getFixLengthString("UPLOADED", 12, ' ', true);
10120             OUTSTREAM << getFixLengthString("TOTAL", 12, ' ', true);
10121             OUTSTREAM << getFixLengthString("%   ", 8, ' ', true);
10122             OUTSTREAM << endl;
10123 
10124             OUTSTREAM << getFixLengthString(SSTR(ndownloads), 16, ' ', true);
10125             OUTSTREAM << getFixLengthString(sizeToText(transferredDownload), 12, ' ', true);
10126             OUTSTREAM << getFixLengthString(sizeToText(totalDownload), 12, ' ', true);
10127             OUTSTREAM << getFixLengthString(percentageToText(percentDownload),8,' ',true);
10128 
10129             OUTSTREAM << "     ";
10130             OUTSTREAM << getFixLengthString(SSTR(nuploads), 16, ' ', true);
10131             OUTSTREAM << getFixLengthString(sizeToText(transferredUpload), 12, ' ', true);
10132             OUTSTREAM << getFixLengthString(sizeToText(totalUpload), 12, ' ', true);
10133             OUTSTREAM << getFixLengthString(percentageToText(percentUpload),8,' ',true);
10134             OUTSTREAM << endl;
10135             delete transferdata;
10136             return;
10137         }
10138 
10139 
10140 
10141         int limit = getintOption(cloptions, "limit", min(10,ndownloads+nuploads+(int)globalTransferListener->completedTransfers.size()));
10142 
10143         if (!transferdata)
10144         {
10145             setCurrentOutCode(MCMD_EUNEXPECTED);
10146             LOG_err << "No transferdata.";
10147             return;
10148         }
10149 
10150         bool downloadpaused = api->areTransfersPaused(MegaTransfer::TYPE_DOWNLOAD);
10151         bool uploadpaused = api->areTransfersPaused(MegaTransfer::TYPE_UPLOAD);
10152 
10153         int indexUpload = 0;
10154         int indexDownload = 0;
10155         int shown = 0;
10156 
10157         int showndl = 0;
10158         int shownup = 0;
10159         unsigned int shownCompleted = 0;
10160 
10161         vector<MegaTransfer *> transfersDLToShow;
10162         vector<MegaTransfer *> transfersUPToShow;
10163         vector<MegaTransfer *> transfersCompletedToShow;
10164 
10165         if (showcompleted)
10166         {
10167             globalTransferListener->completedTransfersMutex.lock();
10168             size_t totalcompleted = globalTransferListener->completedTransfers.size();
10169             for (size_t i = 0;(i < totalcompleted)
10170                  && (shownCompleted < totalcompleted)
10171                  && (shownCompleted < (size_t)(limit+1)); //Note limit+1 to seek for one more to show if there are more to show!
10172                  i++)
10173             {
10174                 MegaTransfer *transfer = globalTransferListener->completedTransfers.at(i);
10175                 if (
10176                     (
10177                             (transfer->getType() == MegaTransfer::TYPE_UPLOAD && (onlyuploads || (!onlyuploads && !onlydownloads) ))
10178                         ||  (transfer->getType() == MegaTransfer::TYPE_DOWNLOAD && (onlydownloads || (!onlyuploads && !onlydownloads) ) )
10179                     )
10180                     &&  !(!showsyncs && transfer->isSyncTransfer())
10181                     )
10182                 {
10183 
10184                     transfersCompletedToShow.push_back(transfer);
10185                     shownCompleted++;
10186                 }
10187             }
10188             globalTransferListener->completedTransfersMutex.unlock();
10189         }
10190 
10191         shown += shownCompleted;
10192 
10193         while (!onlycompleted)
10194         {
10195             //MegaTransfer *transfer = transferdata->get(i);
10196             MegaTransfer *transfer = NULL;
10197             //Next transfer to show
10198             if (onlyuploads && !onlydownloads && indexUpload < transferdata->getNumUploads()) //Only uploads
10199             {
10200                 transfer = api->getTransferByTag(transferdata->getUploadTag(indexUpload++));
10201             }
10202             else
10203             {
10204                 if ( (!onlydownloads || (onlydownloads && onlyuploads)) //both
10205                      && ( (shown >= (limit/2) ) || indexDownload == ndownloads ) // /already chosen half slots for dls or no more dls
10206                      && indexUpload < transferdata->getNumUploads()
10207                      )
10208                     //This is not 100 % perfect, it could show with a limit of 10 5 downloads and 3 uploads with more downloads on the queue.
10209                 {
10210                     transfer = api->getTransferByTag(transferdata->getUploadTag(indexUpload++));
10211 
10212                 }
10213                 else if(indexDownload < ndownloads)
10214                 {
10215                     transfer =  api->getTransferByTag(transferdata->getDownloadTag(indexDownload++));
10216                 }
10217             }
10218 
10219             if (!transfer) break; //finish
10220 
10221             if (
10222                     (showcompleted || transfer->getState() != MegaTransfer::STATE_COMPLETED)
10223                     &&  !(onlyuploads && transfer->getType() != MegaTransfer::TYPE_UPLOAD && !onlydownloads )
10224                     &&  !(onlydownloads && transfer->getType() != MegaTransfer::TYPE_DOWNLOAD && !onlyuploads )
10225                     &&  !(!showsyncs && transfer->isSyncTransfer())
10226                     &&  (shown < (limit+1)) //Note limit+1 to seek for one more to show if there are more to show!
10227                     )
10228             {
10229                 shown++;
10230                 if (transfer->getType() == MegaTransfer::TYPE_DOWNLOAD)
10231                 {
10232                     transfersDLToShow.push_back(transfer);
10233                     showndl++;
10234                 }
10235                 else
10236                 {
10237                     transfersUPToShow.push_back(transfer);
10238                     shownup++;
10239                 }
10240             }
10241             else
10242             {
10243                 delete transfer;
10244             }
10245             if (shown>limit || transfer == NULL) //we-re done
10246             {
10247                 break;
10248             }
10249         }
10250 
10251         delete transferdata;
10252 
10253         vector<MegaTransfer *>::iterator itCompleted = transfersCompletedToShow.begin();
10254         vector<MegaTransfer *>::iterator itDLs = transfersDLToShow.begin();
10255         vector<MegaTransfer *>::iterator itUPs = transfersUPToShow.begin();
10256 
10257         ColumnDisplayer cd(getintOption(cloptions,"path-display-size", 0));
10258         cd.addHeader("SOURCEPATH", false);
10259         cd.addHeader("DESTINYPATH", false);
10260 
10261         for (unsigned int i=0;i<showndl+shownup+shownCompleted; i++)
10262         {
10263             MegaTransfer *transfer = NULL;
10264             bool deleteTransfer = true;
10265             if (itDLs == transfersDLToShow.end() && itCompleted == transfersCompletedToShow.end())
10266             {
10267                 transfer = (MegaTransfer *) *itUPs;
10268                 itUPs++;
10269             }
10270             else if (itCompleted == transfersCompletedToShow.end())
10271             {
10272                 transfer = (MegaTransfer *) *itDLs;
10273                 itDLs++;
10274             }
10275             else
10276             {
10277                 transfer = (MegaTransfer *) *itCompleted;
10278                 itCompleted++;
10279                 deleteTransfer=false;
10280             }
10281             if (i == 0) //first
10282             {
10283                 if (uploadpaused || downloadpaused)
10284                 {
10285                     OUTSTREAM << "            " << (downloadpaused?"DOWNLOADS":"") << ((uploadpaused && downloadpaused)?" AND ":"")
10286                               << (uploadpaused?"UPLOADS":"") << " ARE PAUSED " << endl;
10287                 }
10288             }
10289             if (i==(unsigned int)limit) //we are in the extra one (not to be shown)
10290             {
10291                 OUTSTREAM << " ...  Showing first " << limit << " transfers ..." << endl;
10292                 if (deleteTransfer)
10293                 {
10294                     delete transfer;
10295                 }
10296                 break;
10297             }
10298 
10299             printTransferColumnDisplayer(&cd, transfer);
10300 
10301             if (deleteTransfer)
10302             {
10303                 delete transfer;
10304             }
10305         }
10306         OUTSTRINGSTREAM oss;
10307         cd.print(oss, getintOption(cloptions, "client-width", getNumberOfCols(75)));
10308         OUTSTREAM << oss.str();
10309     }
10310     else if (words[0] == "locallogout")
10311     {
10312         OUTSTREAM << "Logging out locally..." << endl;
10313         cwd = UNDEF;
10314         return;
10315     }
10316     else if (words[0] == "proxy")
10317     {
10318         bool autoProxy = getFlag(clflags, "auto");
10319         bool noneProxy = getFlag(clflags, "none");
10320         int proxyType = -1;
10321 
10322 
10323         string username = getOption(cloptions, "username", "");
10324         string password;
10325         if (username.size())
10326         {
10327             password = getOption(cloptions, "password", "");
10328             if (!password.size())
10329             {
10330                 password = askforUserResponse("Enter password: ");
10331             }
10332         }
10333 
10334         string urlProxy;
10335         if (words.size() > 1)
10336         {
10337             proxyType = MegaProxy::PROXY_CUSTOM;
10338             urlProxy = words[1];
10339         }
10340         else if (autoProxy)
10341         {
10342             proxyType = MegaProxy::PROXY_AUTO;
10343         }
10344         else if (noneProxy)
10345         {
10346             proxyType = MegaProxy::PROXY_NONE;
10347         }
10348 
10349         if (proxyType == -1)
10350         {
10351             int configuredProxyType = ConfigurationManager::getConfigurationValue("proxy_type", -1);
10352             auto configuredProxyUrl = ConfigurationManager::getConfigurationSValue("proxy_url");
10353 
10354             auto configuredProxyUsername = ConfigurationManager::getConfigurationSValue("proxy_username");
10355             auto configuredProxyPassword = ConfigurationManager::getConfigurationSValue("proxy_password");
10356 
10357             if (configuredProxyType == -1)
10358             {
10359                 OUTSTREAM << "No proxy configuration found" << endl;
10360                 return;
10361             }
10362             if (configuredProxyType == MegaProxy::PROXY_NONE)
10363             {
10364                 OUTSTREAM << "Proxy disabled" << endl;
10365                 return;
10366             }
10367 
10368 
10369             OUTSTREAM << "Proxy configured. ";
10370 
10371             if (configuredProxyType == MegaProxy::PROXY_AUTO)
10372             {
10373                 OUTSTREAM << endl << " Type = AUTO";
10374             }
10375             else
10376             {
10377                 OUTSTREAM << endl << " Type = CUSTOM";
10378             }
10379 
10380             if (configuredProxyUrl.size())
10381             {
10382                 OUTSTREAM << endl << " URL = " << configuredProxyUrl;
10383             }
10384             if (configuredProxyUsername.size())
10385             {
10386                 OUTSTREAM << endl << " username = " << configuredProxyUsername;
10387             }
10388             if (configuredProxyPassword.size())
10389             {
10390                 OUTSTREAM << endl << " password = " << configuredProxyPassword;
10391             }
10392 
10393             OUTSTREAM << endl;
10394         }
10395         else
10396         {
10397             setProxy(urlProxy, username, password, proxyType);
10398         }
10399 
10400     }
10401     else
10402     {
10403         setCurrentOutCode(MCMD_EARGS);
10404         LOG_err << "Invalid command: " << words[0];
10405     }
10406 }
10407 
10408 }//end namespace
10409