diff --git a/include/api.h b/include/api.h index 97abe45..93cfb47 100644 --- a/include/api.h +++ b/include/api.h @@ -55,6 +55,7 @@ class API API(const std::string& token,const std::string& secret); int init(); + bool isLoggedIn(); int login(const std::string& email, const std::string& password); int getAPIConfig(); std::string getResponse(const std::string& url); diff --git a/include/downloader.h b/include/downloader.h index 0aa1d45..cb0fa6b 100644 --- a/include/downloader.h +++ b/include/downloader.h @@ -63,6 +63,7 @@ class Downloader public: Downloader(Config &conf); virtual ~Downloader(); + bool isLoggedIn(); int init(); int login(); int listGames(); diff --git a/main.cpp b/main.cpp index 5a761fd..23b13c5 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,7 @@ #include "config.h" #include "util.h" #include "globalconstants.h" +#include "ssl_thread_setup.h" #include #include @@ -475,17 +476,43 @@ int main(int argc, char *argv[]) std::cerr << std::endl; } - Downloader downloader(config); - int initResult = downloader.init(); + // Init curl globally + ssl_thread_setup(); + curl_global_init(CURL_GLOBAL_ALL); - int iLoginResult = 0; - if (config.bLoginAPI || config.bLoginHTTP || initResult == 1) + if (config.bLoginAPI) { - if (!config.bLoginAPI && !config.bLoginHTTP) - downloader.config.bLoginAPI = true; - iLoginResult = downloader.login(); - if (iLoginResult == 0) - return 1; + config.sToken = ""; + config.sSecret = ""; + } + + Downloader downloader(config); + + int iLoginTries = 0; + bool bLoginOK = false; + + // Login because --login, --login-api or --login-website was used + if (config.bLoginAPI || config.bLoginHTTP) + bLoginOK = downloader.login(); + + bool bIsLoggedin = downloader.isLoggedIn(); + + // Login because we are not logged in + while (iLoginTries++ < config.iRetries && !bIsLoggedin) + { + bLoginOK = downloader.login(); + if (bLoginOK) + { + bIsLoggedin = downloader.isLoggedIn(); + } + } + + // Login failed, cleanup + if (!bLoginOK && !bIsLoggedin) + { + curl_global_cleanup(); + ssl_thread_cleanup(); + return 1; } // Make sure that config file and cookie file are only readable/writable by owner @@ -495,9 +522,9 @@ int main(int argc, char *argv[]) Util::setFilePermissions(config.sCookiePath, boost::filesystem::owner_read | boost::filesystem::owner_write); } - if (config.bSaveConfig || iLoginResult == 1) + if (config.bSaveConfig || bLoginOK) { - if (iLoginResult == 1) + if (bLoginOK) { set_vm_value(vm, "token", downloader.config.sToken); set_vm_value(vm, "secret", downloader.config.sSecret); @@ -553,11 +580,17 @@ int main(int argc, char *argv[]) if (!config.bRespectUmask) Util::setFilePermissions(config.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write); if (config.bSaveConfig) + { + curl_global_cleanup(); + ssl_thread_cleanup(); return 0; + } } else { std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; + curl_global_cleanup(); + ssl_thread_cleanup(); return 1; } } @@ -574,15 +607,28 @@ int main(int argc, char *argv[]) ofs.close(); if (!config.bRespectUmask) Util::setFilePermissions(config.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write); + + curl_global_cleanup(); + ssl_thread_cleanup(); return 0; } else { std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; + curl_global_cleanup(); + ssl_thread_cleanup(); return 1; } } + bool bInitOK = downloader.init(); + if (!bInitOK) + { + curl_global_cleanup(); + ssl_thread_cleanup(); + return 1; + } + int res = 0; if (config.bShowWishlist) @@ -622,5 +668,8 @@ int main(int argc, char *argv[]) if (!config.sOrphanRegex.empty() && config.bDownload) downloader.checkOrphans(); + curl_global_cleanup(); + ssl_thread_cleanup(); + return res; } diff --git a/src/api.cpp b/src/api.cpp index 8e7712e..f465ce1 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -41,11 +41,32 @@ API::API(const std::string& token, const std::string& secret) this->config.oauth_secret = secret; } +/* Initialize the API + returns 0 if failed + returns 1 if successful +*/ int API::init() { int res = 0; + this->getAPIConfig(); + if (!this->getError()) + res = 1; + else + this->clearError(); + + return res; +} + +/* Login check + returns false if not logged in + returns true if logged in +*/ +bool API::isLoggedIn() +{ + int res = 0; + // Check if we already have token and secret if (!this->config.oauth_token.empty() && !this->config.oauth_secret.empty()) { @@ -56,7 +77,6 @@ int API::init() return res; } - int API::getAPIConfig() { std::string url = "https://api.gog.com/downloader2/status/stable/"; // Stable API diff --git a/src/downloader.cpp b/src/downloader.cpp index a3bc74a..f359ea2 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -7,7 +7,6 @@ #include "downloader.h" #include "util.h" #include "globalconstants.h" -#include "ssl_thread_setup.h" #include "downloadinfo.h" #include "message.h" @@ -28,6 +27,8 @@ #include #include #include +#include +#include namespace bptime = boost::posix_time; @@ -41,41 +42,15 @@ std::mutex mtx_create_directories; // Mutex for creating directories in Download Downloader::Downloader(Config &conf) { - ssl_thread_setup(); this->config = conf; if (config.bLoginHTTP && boost::filesystem::exists(config.sCookiePath)) if (!boost::filesystem::remove(config.sCookiePath)) std::cerr << "Failed to delete " << config.sCookiePath << std::endl; -} -Downloader::~Downloader() -{ - if (config.bReport) - if (this->report_ofs) - this->report_ofs.close(); - delete progressbar; - delete gogAPI; - delete gogWebsite; - curl_easy_cleanup(curlhandle); - curl_global_cleanup(); - ssl_thread_cleanup(); - // Make sure that cookie file is only readable/writable by owner - if (!config.bRespectUmask) - Util::setFilePermissions(config.sCookiePath, boost::filesystem::owner_read | boost::filesystem::owner_write); -} - - -/* Initialize the downloader - returns 0 if successful - returns 1 if failed -*/ -int Downloader::init() -{ this->resume_position = 0; this->retries = 0; // Initialize curl and set curl options - curl_global_init(CURL_GLOBAL_ALL); curlhandle = curl_easy_init(); curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curlhandle, CURLOPT_USERAGENT, config.sVersionString.c_str()); @@ -100,7 +75,6 @@ int Downloader::init() // Create new GOG website handle gogWebsite = new Website(config); - bool bWebsiteIsLoggedIn = gogWebsite->IsLoggedIn(); // Create new API handle and set curl options for the API gogAPI = new API(config.sToken, config.sSecret); @@ -110,12 +84,55 @@ int Downloader::init() if (!config.sCACertPath.empty()) gogAPI->curlSetOpt(CURLOPT_CAINFO, config.sCACertPath.c_str()); + gogAPI->init(); + progressbar = new ProgressBar(config.bUnicode, config.bColor); +} - bool bInitOK = gogAPI->init(); // Initialize the API - if (!bInitOK || !bWebsiteIsLoggedIn || config.bLoginHTTP || config.bLoginAPI) - return 1; +Downloader::~Downloader() +{ + if (config.bReport) + if (this->report_ofs) + this->report_ofs.close(); + delete progressbar; + delete gogAPI; + delete gogWebsite; + curl_easy_cleanup(curlhandle); + // Make sure that cookie file is only readable/writable by owner + if (!config.bRespectUmask) + Util::setFilePermissions(config.sCookiePath, boost::filesystem::owner_read | boost::filesystem::owner_write); +} +/* Login check + returns false if not logged in + returns true if logged in +*/ +bool Downloader::isLoggedIn() +{ + bool bIsLoggedIn = false; + config.bLoginAPI = false; + config.bLoginHTTP = false; + + bool bWebsiteIsLoggedIn = gogWebsite->IsLoggedIn(); + if (!bWebsiteIsLoggedIn) + config.bLoginHTTP = true; + + bool bIsLoggedInAPI = gogAPI->isLoggedIn(); + if (!bIsLoggedInAPI) + config.bLoginAPI = true; + + if (bIsLoggedInAPI && bWebsiteIsLoggedIn) + bIsLoggedIn = true; + + return bIsLoggedIn; +} + +/* Initialize the downloader + returns 0 if failed + returns 1 if successful +*/ +int Downloader::init() +{ if (!config.sGameHasDLCList.empty()) { if (config.gamehasdlc.empty()) @@ -127,25 +144,18 @@ int Downloader::init() } gogWebsite->setConfig(config); // Update config for website handle - if (config.bCover && config.bDownload && !config.bUpdateCheck) - coverXML = this->getResponse(config.sCoverList); - - // updateCheck() calls getGameList() if needed - // getGameList() is not needed when using cache unless we want to list games from account - if ( !config.bUpdateCheck && (!config.bUseCache || (config.bUseCache && config.bList)) && config.sFileIdString.empty() && !config.bShowWishlist ) - this->getGameList(); - if (config.bReport && (config.bDownload || config.bRepair)) { this->report_ofs.open(config.sReportFilePath); if (!this->report_ofs) { + config.bReport = false; std::cerr << "Failed to create " << config.sReportFilePath << std::endl; - return 1; + return 0; } } - return 0; + return 1; } /* Login @@ -183,6 +193,11 @@ int Downloader::login() // Login to website if (config.bLoginHTTP) { + // Delete old cookies + if (boost::filesystem::exists(config.sCookiePath)) + if (!boost::filesystem::remove(config.sCookiePath)) + std::cerr << "Failed to delete " << config.sCookiePath << std::endl; + if (!gogWebsite->Login(email, password)) { std::cerr << "HTTP: Login failed" << std::endl; @@ -308,6 +323,9 @@ int Downloader::getGameDetails() } } + if (gameItems.empty()) + this->getGameList(); + if (!gameItems.empty()) { for (unsigned int i = 0; i < gameItems.size(); ++i) @@ -578,6 +596,9 @@ int Downloader::listGames() } else { // List game names + if (gameItems.empty()) + this->getGameList(); + for (unsigned int i = 0; i < gameItems.size(); ++i) { std::cout << gameItems[i].name << std::endl; @@ -878,6 +899,9 @@ void Downloader::download() if (this->games.empty()) this->getGameDetails(); + if (config.bCover && !config.bUpdateCheck) + coverXML = this->getResponse(config.sCoverList); + for (unsigned int i = 0; i < games.size(); ++i) { if (config.bSaveSerials && !games[i].serials.empty())