2
0
mirror of https://github.com/meganz/MEGAcmd synced 2025-08-30 13:27:44 +00:00

Merge branch 'task/CMD-620_folder_link_implicit_resume' into 'develop'

CMD-620. Allow explicit --resume for folder links logins

Closes CMD-620

See merge request apps/MEGAcmd!873
This commit is contained in:
Pablo M
2025-03-20 02:19:45 +13:00
6 changed files with 155 additions and 94 deletions

View File

@@ -20,6 +20,10 @@
#include "configurationmanager.h" #include "configurationmanager.h"
#include "megacmdutils.h" #include "megacmdutils.h"
#ifdef MEGACMD_TESTING_CODE
#include "../tests/common/Instruments.h"
#endif
#include <utility> #include <utility>
using namespace mega; using namespace mega;
@@ -642,6 +646,9 @@ void MegaCmdListener::onRequestUpdate(MegaApi* api, MegaRequest *request)
{ {
case MegaRequest::TYPE_FETCH_NODES: case MegaRequest::TYPE_FETCH_NODES:
{ {
#ifdef MEGACMD_TESTING_CODE
TestInstruments::Instance().fireEvent(TestInstruments::Event::FETCH_NODES_REQ_UPDATE);
#endif
unsigned int cols = getNumberOfCols(80); unsigned int cols = getNumberOfCols(80);
string outputString; string outputString;
outputString.resize(cols+1); outputString.resize(cols+1);

View File

@@ -777,6 +777,7 @@ void insertValidParamsPerCommand(set<string> *validParams, string thecommand, se
validOptValues->insert("auth-code"); validOptValues->insert("auth-code");
validOptValues->insert("auth-key"); validOptValues->insert("auth-key");
validOptValues->insert("password"); validOptValues->insert("password");
validOptValues->insert("resume");
} }
else if ("psa" == thecommand) else if ("psa" == thecommand)
{ {
@@ -1541,13 +1542,13 @@ const char * getUsageStr(const char *command, const HelpFlags& flags)
if (isCurrentThreadInteractive()) if (isCurrentThreadInteractive())
{ {
return "login [--auth-code=XXXX] [email [password]] | exportedfolderurl#key" return "login [--auth-code=XXXX] [email [password]] | exportedfolderurl#key"
" [--auth-key=XXXX] | passwordprotectedlink [--password=PASSWORD]" " [--auth-key=XXXX] [--resume] | passwordprotectedlink [--password=PASSWORD]"
" | session"; " | session";
} }
else else
{ {
return "login [--auth-code=XXXX] email password | exportedfolderurl#key" return "login [--auth-code=XXXX] email password | exportedfolderurl#key"
" [--auth-key=XXXX] | passwordprotectedlink [--password=PASSWORD]" " [--auth-key=XXXX] [--resume] | passwordprotectedlink [--password=PASSWORD]"
" | session"; " | session";
} }
} }
@@ -2072,7 +2073,7 @@ string getHelpStr(const char *command, const HelpFlags& flags = {})
os << "Usage: " << getUsageStr(command, flags) << endl; os << "Usage: " << getUsageStr(command, flags) << endl;
if (!strcmp(command, "login")) if (!strcmp(command, "login"))
{ {
os << "Logs into a MEGA account or folder link. You can only log into one entity at a time." << endl; os << "Logs into a MEGA account, folder link or a previous session. You can only log into one entity at a time." << endl;
os << "Logging into a MEGA account:" << endl; os << "Logging into a MEGA account:" << endl;
os << "\tYou can log into a MEGA account by providing either a session ID or a username and password. A session " os << "\tYou can log into a MEGA account by providing either a session ID or a username and password. A session "
"ID simply identifies a session that you have previously logged in with using a username and password; " "ID simply identifies a session that you have previously logged in with using a username and password; "
@@ -2091,6 +2092,10 @@ string getHelpStr(const char *command, const HelpFlags& flags = {})
"the password for that link." << endl; "the password for that link." << endl;
os << "\t--auth-key=AUTHKEY: If the link is a writable folder link, then this option allows you to log in with " os << "\t--auth-key=AUTHKEY: If the link is a writable folder link, then this option allows you to log in with "
"write privileges. Without this option, you will log into the link with read access only." << endl; "write privileges. Without this option, you will log into the link with read access only." << endl;
os << "\t--resume: A convenience option to try to resume from cache. When login into a folder, contrary to what occurs with login into a user account,"
" MEGAcmd will not try to load anything from cache: loading everything from scratch. This option changes that. Note, "
"login using a session string, will of course, try to load from cache. This option may be convinient, for instance, if you previously "
"logged out using --keep-session." << endl;
os << endl; os << endl;
os << "For more information about MEGA folder links, see \"" << getCommandPrefixBasedOnMode() << "export --help\"." << endl; os << "For more information about MEGA folder links, see \"" << getCommandPrefixBasedOnMode() << "export --help\"." << endl;
} }
@@ -2330,7 +2335,8 @@ string getHelpStr(const char *command, const HelpFlags& flags = {})
os << "Logs out" << endl; os << "Logs out" << endl;
os << endl; os << endl;
os << "Options:" << endl; os << "Options:" << endl;
os << " --keep-session" << "\t" << "Keeps the current session." << endl; os << " --keep-session" << "\t" << "Keeps the current session. This will also prevent the deletion of cached data associated "
"with current session." << endl;
} }
else if (!strcmp(command, "import")) else if (!strcmp(command, "import"))
{ {

View File

@@ -8012,13 +8012,43 @@ void MegaCmdExecuter::executecommand(vector<string> words, map<string, int> *clf
LoginGuard loginGuard; LoginGuard loginGuard;
int clientID = getintOption(cloptions, "clientID", -1); int clientID = getintOption(cloptions, "clientID", -1);
if (!api->isLoggedIn()) if (api->isLoggedIn())
{ {
if (words.size() > 1) setCurrentThreadOutCode(MCMD_INVALIDSTATE);
LOG_err << "Already logged in. Please log out first.";
return;
}
if (words.size() < 2)
{ {
if (strchr(words[1].c_str(), '@')) setCurrentThreadOutCode(MCMD_EARGS);
LOG_err << " " << getUsageStr("login");
return;
}
bool accountLogin = words[1].find('@') != std::string::npos;
bool folderLinkLogin = !accountLogin && words[1].find('#') != std::string::npos;
bool resumeFolderLink = getFlag(clflags, "resume");
string authKey = getOption(cloptions, "auth-key", "");
if (!folderLinkLogin)
{
if (resumeFolderLink)
{
setCurrentThreadOutCode(MCMD_EARGS);
LOG_err << "Explicit resumption only required for folder links logins.";
return;
}
if (!authKey.empty())
{
setCurrentThreadOutCode(MCMD_EARGS);
LOG_err << "Auth key only required for login in writable folder links.";
return;
}
}
if (accountLogin)
{ {
// full account login
if (words.size() > 2) if (words.size() > 2)
{ {
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL,NULL,clientID); MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL,NULL,clientID);
@@ -8055,10 +8085,7 @@ void MegaCmdExecuter::executecommand(vector<string> words, map<string, int> *clf
} }
} }
} }
else else if (folderLinkLogin) // folder link indicator
{
const char* ptr;
if (( ptr = strchr(words[1].c_str(), '#'))) // folder link indicator
{ {
string publicLink = words[1]; string publicLink = words[1];
if (!decryptLinkIfEncrypted(api, publicLink, cloptions)) if (!decryptLinkIfEncrypted(api, publicLink, cloptions))
@@ -8066,24 +8093,15 @@ void MegaCmdExecuter::executecommand(vector<string> words, map<string, int> *clf
return; return;
} }
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL); std::unique_ptr<MegaCmdListener>megaCmdListener = std::make_unique<MegaCmdListener>(nullptr);
sandboxCMD->resetSandBox(); sandboxCMD->resetSandBox();
api->loginToFolder(publicLink.c_str(), authKey.empty() ? nullptr : authKey.c_str(),
resumeFolderLink, megaCmdListener.get());
string authKey = getOption(cloptions, "auth-key", ""); actUponLogin(megaCmdListener.get());
if (authKey.empty())
{
api->loginToFolder(publicLink.c_str(), megaCmdListener);
}
else
{
api->loginToFolder(publicLink.c_str(), authKey.c_str(), megaCmdListener);
}
actUponLogin(megaCmdListener);
delete megaCmdListener;
return; return;
} }
else else // session resumption
{ {
LOG_info << "Resuming session..."; LOG_info << "Resuming session...";
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL); MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
@@ -8093,19 +8111,6 @@ void MegaCmdExecuter::executecommand(vector<string> words, map<string, int> *clf
delete megaCmdListener; delete megaCmdListener;
return; return;
} }
}
}
else
{
setCurrentThreadOutCode(MCMD_EARGS);
LOG_err << " " << getUsageStr("login");
}
}
else
{
setCurrentThreadOutCode(MCMD_INVALIDSTATE);
LOG_err << "Already logged in. Please log out first.";
}
return; return;
} }

View File

@@ -66,6 +66,7 @@ public:
{ {
SERVER_ABOUT_TO_START_WAITING_FOR_PETITIONS, SERVER_ABOUT_TO_START_WAITING_FOR_PETITIONS,
SYNC_ISSUES_LIST_UPDATED, SYNC_ISSUES_LIST_UPDATED,
FETCH_NODES_REQ_UPDATE,
}; };
typedef std::function<void()> EventCallback; typedef std::function<void()> EventCallback;

View File

@@ -32,6 +32,28 @@ TEST_F(NOINTERACTIVEBasicTest, Help)
executeInClient({"help"}); executeInClient({"help"});
} }
TEST_F(NOINTERACTIVENotLoggedTest, Folderlogin)
{
{
auto rLoging = executeInClient({"login", LINK_TESTEXPORTFOLDER});
ASSERT_TRUE(rLoging.ok());
}
{
auto rLogout = executeInClient({"logout", "--keep-session"});
ASSERT_TRUE(rLogout.ok());
}
{
EventTracker evTracker({TestInstruments::Event::FETCH_NODES_REQ_UPDATE});
auto rLoging = executeInClient({"login", "--resume", LINK_TESTEXPORTFOLDER});
ASSERT_TRUE(rLoging.ok());
ASSERT_STREQ(rLoging.out().c_str(), "");
ASSERT_STREQ(rLoging.err().c_str(), "");
ASSERT_FALSE(evTracker.eventHappened(TestInstruments::Event::FETCH_NODES_REQ_UPDATE));
}
}
TEST_F(NOINTERACTIVEReadTest, Find) TEST_F(NOINTERACTIVEReadTest, Find)
{ {
auto r = executeInClient({"find"}); auto r = executeInClient({"find"});

View File

@@ -88,6 +88,25 @@ class BasicGenericTest : public ::testing::Test
{ {
}; };
class NotLoggedTest : public BasicGenericTest
{
protected:
void SetUp() override
{
BasicGenericTest::SetUp();
auto result = executeInClient({"logout"}).ok();
ASSERT_TRUE(result);
}
void TearDown() override
{
BasicGenericTest::SetUp();
auto result = executeInClient({"logout"}).ok();
ASSERT_TRUE(result);
}
};
class LoggedInTest : public BasicGenericTest class LoggedInTest : public BasicGenericTest
{ {
protected: protected:
@@ -133,4 +152,5 @@ protected:
class NOINTERACTIVEBasicTest : public BasicGenericTest{}; class NOINTERACTIVEBasicTest : public BasicGenericTest{};
class NOINTERACTIVELoggedInTest : public LoggedInTest{}; class NOINTERACTIVELoggedInTest : public LoggedInTest{};
class NOINTERACTIVENotLoggedTest : public NotLoggedTest{};
class NOINTERACTIVEReadTest : public ReadTest{}; class NOINTERACTIVEReadTest : public ReadTest{};