mirror of
https://github.com/meganz/MEGAcmd
synced 2025-08-30 05:17:41 +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:
commit
e85a3e42db
@ -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);
|
||||||
|
@ -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"))
|
||||||
{
|
{
|
||||||
|
@ -8012,99 +8012,104 @@ 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)
|
|
||||||
{
|
|
||||||
if (strchr(words[1].c_str(), '@'))
|
|
||||||
{
|
|
||||||
// full account login
|
|
||||||
if (words.size() > 2)
|
|
||||||
{
|
|
||||||
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL,NULL,clientID);
|
|
||||||
sandboxCMD->resetSandBox();
|
|
||||||
api->login(words[1].c_str(), words[2].c_str(), megaCmdListener);
|
|
||||||
if (actUponLogin(megaCmdListener) == MegaError::API_EMFAREQUIRED )
|
|
||||||
{
|
|
||||||
MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL,NULL,clientID);
|
|
||||||
string pin2fa = getOption(cloptions, "auth-code", "");
|
|
||||||
if (!pin2fa.size())
|
|
||||||
{
|
|
||||||
pin2fa = askforUserResponse("Enter the code generated by your authentication app: ");
|
|
||||||
}
|
|
||||||
LOG_verbose << " Using confirmation pin: " << pin2fa;
|
|
||||||
api->multiFactorAuthLogin(words[1].c_str(), words[2].c_str(), pin2fa.c_str(), megaCmdListener2);
|
|
||||||
actUponLogin(megaCmdListener2);
|
|
||||||
delete megaCmdListener2;
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
delete megaCmdListener;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
login = words[1];
|
|
||||||
if (isCurrentThreadInteractive())
|
|
||||||
{
|
|
||||||
setprompt(LOGINPASSWORD);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setCurrentThreadOutCode(MCMD_EARGS);
|
|
||||||
LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("login");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char* ptr;
|
|
||||||
if (( ptr = strchr(words[1].c_str(), '#'))) // folder link indicator
|
|
||||||
{
|
|
||||||
string publicLink = words[1];
|
|
||||||
if (!decryptLinkIfEncrypted(api, publicLink, cloptions))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
|
|
||||||
sandboxCMD->resetSandBox();
|
|
||||||
|
|
||||||
string authKey = getOption(cloptions, "auth-key", "");
|
|
||||||
if (authKey.empty())
|
|
||||||
{
|
|
||||||
api->loginToFolder(publicLink.c_str(), megaCmdListener);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
api->loginToFolder(publicLink.c_str(), authKey.c_str(), megaCmdListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
actUponLogin(megaCmdListener);
|
|
||||||
delete megaCmdListener;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_info << "Resuming session...";
|
|
||||||
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
|
|
||||||
sandboxCMD->resetSandBox();
|
|
||||||
api->fastLogin(words[1].c_str(), megaCmdListener);
|
|
||||||
actUponLogin(megaCmdListener);
|
|
||||||
delete megaCmdListener;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setCurrentThreadOutCode(MCMD_EARGS);
|
|
||||||
LOG_err << " " << getUsageStr("login");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
setCurrentThreadOutCode(MCMD_INVALIDSTATE);
|
setCurrentThreadOutCode(MCMD_INVALIDSTATE);
|
||||||
LOG_err << "Already logged in. Please log out first.";
|
LOG_err << "Already logged in. Please log out first.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (words.size() < 2)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (words.size() > 2)
|
||||||
|
{
|
||||||
|
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL,NULL,clientID);
|
||||||
|
sandboxCMD->resetSandBox();
|
||||||
|
api->login(words[1].c_str(), words[2].c_str(), megaCmdListener);
|
||||||
|
if (actUponLogin(megaCmdListener) == MegaError::API_EMFAREQUIRED )
|
||||||
|
{
|
||||||
|
MegaCmdListener *megaCmdListener2 = new MegaCmdListener(NULL,NULL,clientID);
|
||||||
|
string pin2fa = getOption(cloptions, "auth-code", "");
|
||||||
|
if (!pin2fa.size())
|
||||||
|
{
|
||||||
|
pin2fa = askforUserResponse("Enter the code generated by your authentication app: ");
|
||||||
|
}
|
||||||
|
LOG_verbose << " Using confirmation pin: " << pin2fa;
|
||||||
|
api->multiFactorAuthLogin(words[1].c_str(), words[2].c_str(), pin2fa.c_str(), megaCmdListener2);
|
||||||
|
actUponLogin(megaCmdListener2);
|
||||||
|
delete megaCmdListener2;
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
delete megaCmdListener;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
login = words[1];
|
||||||
|
if (isCurrentThreadInteractive())
|
||||||
|
{
|
||||||
|
setprompt(LOGINPASSWORD);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setCurrentThreadOutCode(MCMD_EARGS);
|
||||||
|
LOG_err << "Extra args required in non-interactive mode. Usage: " << getUsageStr("login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (folderLinkLogin) // folder link indicator
|
||||||
|
{
|
||||||
|
string publicLink = words[1];
|
||||||
|
if (!decryptLinkIfEncrypted(api, publicLink, cloptions))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MegaCmdListener>megaCmdListener = std::make_unique<MegaCmdListener>(nullptr);
|
||||||
|
sandboxCMD->resetSandBox();
|
||||||
|
api->loginToFolder(publicLink.c_str(), authKey.empty() ? nullptr : authKey.c_str(),
|
||||||
|
resumeFolderLink, megaCmdListener.get());
|
||||||
|
|
||||||
|
actUponLogin(megaCmdListener.get());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else // session resumption
|
||||||
|
{
|
||||||
|
LOG_info << "Resuming session...";
|
||||||
|
MegaCmdListener *megaCmdListener = new MegaCmdListener(NULL);
|
||||||
|
sandboxCMD->resetSandBox();
|
||||||
|
api->fastLogin(words[1].c_str(), megaCmdListener);
|
||||||
|
actUponLogin(megaCmdListener);
|
||||||
|
delete megaCmdListener;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -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;
|
||||||
|
@ -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"});
|
||||||
|
@ -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{};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user