Some initial Galaxy code

Add some code for initial Galaxy support.
Rewrite and move code around in preparation for GOG Galaxy API support.
This commit is contained in:
Sude 2017-02-17 11:14:28 +02:00
parent 22f47de4fc
commit f2e8dde934
18 changed files with 1495 additions and 706 deletions

View File

@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project (lgogdownloader LANGUAGES C CXX VERSION 3.1) project (lgogdownloader LANGUAGES C CXX VERSION 3.1)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG=1")
set(LINK_LIBCRYPTO 0) set(LINK_LIBCRYPTO 0)
find_program(READELF readelf DOC "Location of the readelf program") find_program(READELF readelf DOC "Location of the readelf program")
@ -14,6 +13,7 @@ find_package(Boost
regex regex
program_options program_options
date_time date_time
iostreams
) )
find_package(CURL 7.32.0 REQUIRED) find_package(CURL 7.32.0 REQUIRED)
if(CURL_FOUND) if(CURL_FOUND)
@ -46,6 +46,7 @@ file(GLOB SRC_FILES
src/blacklist.cpp src/blacklist.cpp
src/gamefile.cpp src/gamefile.cpp
src/gamedetails.cpp src/gamedetails.cpp
src/galaxyapi.cpp
) )
set(GIT_CHECKOUT FALSE) set(GIT_CHECKOUT FALSE)

View File

@ -9,6 +9,7 @@
#include "globalconstants.h" #include "globalconstants.h"
#include "gamedetails.h" #include "gamedetails.h"
#include "globals.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>

View File

@ -9,93 +9,262 @@
#include <iostream> #include <iostream>
#include <curl/curl.h> #include <curl/curl.h>
#include <json/json.h>
#include <mutex>
#include <ctime>
#include "blacklist.h" #include "blacklist.h"
struct DirectoryConfig
{
bool bSubDirectories;
std::string sDirectory;
std::string sGameSubdir;
std::string sInstallersSubdir;
std::string sExtrasSubdir;
std::string sPatchesSubdir;
std::string sLanguagePackSubdir;
std::string sDLCSubdir;
};
struct DownloadConfig
{
unsigned int iInstallerPlatform;
unsigned int iInstallerLanguage;
std::vector<unsigned int> vPlatformPriority;
std::vector<unsigned int> vLanguagePriority;
unsigned int iInclude;
bool bRemoteXML;
bool bCover;
bool bSaveChangelogs;
bool bSaveSerials;
bool bAutomaticXMLCreation;
bool bInstallers;
bool bExtras;
bool bPatches;
bool bLanguagePacks;
bool bDLC;
bool bIgnoreDLCCount;
bool bDuplicateHandler;
};
struct gameSpecificConfig
{
DownloadConfig dlConf;
DirectoryConfig dirConf;
};
class GalaxyConfig
{
public:
bool isExpired()
{
std::unique_lock<std::mutex> lock(m);
bool bExpired = false;
if (this->token_json.isMember("expires_at"))
bExpired = (time(NULL) > this->token_json["expires_at"].asUInt());
return bExpired;
}
std::string getAccessToken()
{
std::unique_lock<std::mutex> lock(m);
return this->token_json["access_token"].asString();
}
std::string getRefreshToken()
{
std::unique_lock<std::mutex> lock(m);
return this->token_json["refresh_token"].asString();
}
Json::Value getJSON()
{
std::unique_lock<std::mutex> lock(m);
return this->token_json;
}
void setJSON(Json::Value json)
{
std::unique_lock<std::mutex> lock(m);
if (!json.isMember("expires_at"))
json["expires_at"] = json["expires_in"].asUInt() + time(NULL);
this->token_json = json;
}
void setFilepath(const std::string& path)
{
std::unique_lock<std::mutex> lock(m);
this->filepath = path;
}
std::string getFilepath()
{
std::unique_lock<std::mutex> lock(m);
return this->filepath;
}
std::string getClientId()
{
std::unique_lock<std::mutex> lock(m);
return this->client_id;
}
std::string getClientSecret()
{
std::unique_lock<std::mutex> lock(m);
return this->client_secret;
}
std::string getRedirectUri()
{
std::unique_lock<std::mutex> lock(m);
return this->redirect_uri;
}
GalaxyConfig() = default;
GalaxyConfig(const GalaxyConfig& other)
{
std::lock_guard<std::mutex> guard(other.m);
client_id = other.client_id;
client_secret = other.client_secret;
redirect_uri = other.redirect_uri;
filepath = other.filepath;
token_json = other.token_json;
}
GalaxyConfig& operator= (GalaxyConfig& other)
{
if(&other == this)
return *this;
std::unique_lock<std::mutex> lock1(m, std::defer_lock);
std::unique_lock<std::mutex> lock2(other.m, std::defer_lock);
std::lock(lock1, lock2);
client_id = other.client_id;
client_secret = other.client_secret;
redirect_uri = other.redirect_uri;
filepath = other.filepath;
token_json = other.token_json;
return *this;
}
protected:
private:
std::string client_id = "46899977096215655";
std::string client_secret = "9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9";
std::string redirect_uri = "https://embed.gog.com/on_login_success?origin=client";
std::string filepath;
Json::Value token_json;
mutable std::mutex m;
};
struct CurlConfig
{
bool bVerifyPeer;
bool bVerbose;
std::string sCACertPath;
std::string sCookiePath;
long int iTimeout;
curl_off_t iDownloadRate;
};
struct GogAPIConfig
{
std::string sToken;
std::string sSecret;
};
class Config class Config
{ {
public: public:
Config() {}; Config() {};
virtual ~Config() {}; virtual ~Config() {};
bool bVerbose;
bool bRemoteXML; // Booleans
bool bCover;
bool bUpdateCheck;
bool bDownload;
bool bList;
bool bListDetails;
bool bLoginHTTP; bool bLoginHTTP;
bool bLoginAPI; bool bLoginAPI;
bool bRepair;
bool bInstallers;
bool bExtras;
bool bPatches;
bool bLanguagePacks;
bool bDLC;
bool bUnicode; // use Unicode in console output
bool bColor; // use colors
bool bVerifyPeer;
bool bCheckStatus;
bool bDuplicateHandler;
bool bSaveConfig; bool bSaveConfig;
bool bResetConfig; bool bResetConfig;
bool bDownload;
bool bRepair;
bool bUpdateCheck;
bool bList;
bool bListDetails;
bool bCheckStatus;
bool bShowWishlist;
bool bVerbose;
bool bUnicode; // use Unicode in console output
bool bColor; // use colors
bool bReport; bool bReport;
bool bSubDirectories; bool bRespectUmask;
bool bPlatformDetection;
// Cache
bool bUseCache; bool bUseCache;
bool bUpdateCache; bool bUpdateCache;
bool bSaveSerials; int iCacheValid;
bool bPlatformDetection;
bool bShowWishlist; // Download with file id options
bool bAutomaticXMLCreation; std::string sFileIdString;
bool bSaveChangelogs; std::string sOutputFilename;
bool bRespectUmask;
std::string sGameRegex; // Curl
std::string sDirectory; CurlConfig curlConf;
// Download
DownloadConfig dlConf;
// Galaxy
//GalaxyConfig galaxyConf;
// Directories
DirectoryConfig dirConf;
std::string sCacheDirectory; std::string sCacheDirectory;
std::string sXMLFile;
std::string sXMLDirectory; std::string sXMLDirectory;
std::string sToken;
std::string sSecret;
std::string sVersionString;
std::string sVersionNumber;
std::string sConfigDirectory; std::string sConfigDirectory;
std::string sCookiePath;
// File paths
std::string sConfigFilePath; std::string sConfigFilePath;
std::string sBlacklistFilePath; std::string sBlacklistFilePath;
std::string sIgnorelistFilePath; std::string sIgnorelistFilePath;
std::string sGameHasDLCListFilePath; std::string sGameHasDLCListFilePath;
std::string sOrphanRegex;
std::string sCoverList;
std::string sGameHasDLCList;
std::string sReportFilePath; std::string sReportFilePath;
std::string sInstallersSubdir;
std::string sExtrasSubdir;
std::string sPatchesSubdir;
std::string sLanguagePackSubdir;
std::string sDLCSubdir;
std::string sGameSubdir;
std::string sFileIdString;
std::string sOutputFilename;
std::string sLanguagePriority;
std::string sPlatformPriority;
std::string sIgnoreDLCCountRegex;
std::string sCACertPath;
std::vector<unsigned int> vLanguagePriority;
std::vector<unsigned int> vPlatformPriority;
unsigned int iInstallerPlatform; std::string sXMLFile;
unsigned int iInstallerLanguage;
unsigned int iInclude; // Regex
unsigned int iThreads; std::string sGameRegex;
int iRetries; std::string sOrphanRegex;
int iWait; std::string sIgnoreDLCCountRegex;
int iCacheValid;
size_t iChunkSize; // Priorities
curl_off_t iDownloadRate; std::string sPlatformPriority;
long int iTimeout; std::string sLanguagePriority;
// General strings
std::string sVersionString;
std::string sVersionNumber;
GogAPIConfig apiConf;
// Lists
Blacklist blacklist; Blacklist blacklist;
Blacklist ignorelist; Blacklist ignorelist;
Blacklist gamehasdlc; Blacklist gamehasdlc;
std::string sCoverList;
std::string sGameHasDLCList;
// Integers
int iRetries;
unsigned int iThreads;
int iWait;
size_t iChunkSize;
}; };
#endif // CONFIG_H__ #endif // CONFIG_H__

View File

@ -26,6 +26,9 @@
#include "progressbar.h" #include "progressbar.h"
#include "website.h" #include "website.h"
#include "threadsafequeue.h" #include "threadsafequeue.h"
#include "galaxyapi.h"
#include "globals.h"
#include <curl/curl.h> #include <curl/curl.h>
#include <json/json.h> #include <json/json.h>
#include <ctime> #include <ctime>
@ -58,10 +61,16 @@ struct xferInfo
curl_off_t offset; curl_off_t offset;
}; };
struct ChunkMemoryStruct
{
char *memory;
curl_off_t size;
};
class Downloader class Downloader
{ {
public: public:
Downloader(Config &conf); Downloader();
virtual ~Downloader(); virtual ~Downloader();
bool isLoggedIn(); bool isLoggedIn();
int init(); int init();
@ -77,9 +86,12 @@ class Downloader
void showWishlist(); void showWishlist();
CURL* curlhandle; CURL* curlhandle;
Timer timer; Timer timer;
Config config;
ProgressBar* progressbar; ProgressBar* progressbar;
std::deque< std::pair<time_t, uintmax_t> > TimeAndSize; std::deque< std::pair<time_t, uintmax_t> > TimeAndSize;
void saveGalaxyJSON();
void galaxyInstallGame(const std::string& product_id, int build_index = -1);
void galaxyShowBuilds(const std::string& product_id, int build_index = -1);
protected: protected:
private: private:
CURLcode downloadFile(const std::string& url, const std::string& filepath, const std::string& xml_data = std::string(), const std::string& gamename = std::string()); CURLcode downloadFile(const std::string& url, const std::string& filepath, const std::string& xml_data = std::string(), const std::string& gamename = std::string());
@ -112,6 +124,7 @@ class Downloader
Website *gogWebsite; Website *gogWebsite;
API *gogAPI; API *gogAPI;
galaxyAPI *gogGalaxy;
std::vector<gameItem> gameItems; std::vector<gameItem> gameItems;
std::vector<gameDetails> games; std::vector<gameDetails> games;
std::string coverXML; std::string coverXML;

61
include/galaxyapi.h Normal file
View File

@ -0,0 +1,61 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details. */
#ifndef GALAXYAPI_H
#define GALAXYAPI_H
#include "globalconstants.h"
#include "globals.h"
#include "config.h"
#include "util.h"
#include <iostream>
#include <vector>
#include <cstring>
#include <curl/curl.h>
#include <sys/time.h>
struct galaxyDepotItemChunk
{
std::string md5_compressed;
std::string md5_uncompressed;
off_t size_compressed;
off_t size_uncompressed;
};
struct galaxyDepotItem
{
std::string path;
std::vector<galaxyDepotItemChunk> chunks;
off_t totalSizeCompressed;
off_t totalSizeUncompressed;
std::string md5;
};
class galaxyAPI
{
public:
galaxyAPI(CurlConfig& conf);
virtual ~galaxyAPI();
int init();
bool isTokenExpired();
bool refreshLogin();
Json::Value getProductBuilds(const std::string& product_id, const std::string& platform = "windows", const std::string& generation = "2");
Json::Value getManifestV1(const std::string& product_id, const std::string& build_id, const std::string& manifest_id = "repository", const std::string& platform = "windows");
Json::Value getManifestV2(std::string manifest_hash);
Json::Value getSecureLink(const std::string& product_id, const std::string& path);
std::string getResponse(const std::string& url, const bool& zlib_decompress = false);
std::string hashToGalaxyPath(const std::string& hash);
std::vector<galaxyDepotItem> getDepotItemsVector(const std::string& hash);
protected:
private:
CurlConfig curlConf;
static size_t writeMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userp);
CURL* curlhandle;
};
#endif // GALAXYAPI_H

View File

@ -8,6 +8,7 @@
#define GAMEDETAILS_H #define GAMEDETAILS_H
#include "globalconstants.h" #include "globalconstants.h"
#include "globals.h"
#include "gamefile.h" #include "gamefile.h"
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
@ -26,12 +27,13 @@ class gameDetails
std::vector<gameFile> languagepacks; std::vector<gameFile> languagepacks;
std::vector<gameDetails> dlcs; std::vector<gameDetails> dlcs;
std::string gamename; std::string gamename;
std::string product_id;
std::string title; std::string title;
std::string icon; std::string icon;
std::string serials; std::string serials;
std::string changelog; std::string changelog;
void filterWithPriorities(const gameSpecificConfig& config); void filterWithPriorities(const gameSpecificConfig& config);
void makeFilepaths(const gameSpecificDirectoryConfig& config); void makeFilepaths(const DirectoryConfig& config);
std::string getSerialsFilepath(); std::string getSerialsFilepath();
std::string getChangelogFilepath(); std::string getChangelogFilepath();
Json::Value getDetailsAsJson(); Json::Value getDetailsAsJson();

View File

@ -8,6 +8,7 @@
#define GAMEFILE_H #define GAMEFILE_H
#include "globalconstants.h" #include "globalconstants.h"
#include "globals.h"
#include <iostream> #include <iostream>
#include <vector> #include <vector>

View File

@ -12,7 +12,8 @@
namespace GlobalConstants namespace GlobalConstants
{ {
const int GAMEDETAILS_CACHE_VERSION = 1; const int GAMEDETAILS_CACHE_VERSION = 2;
const int ZLIB_WINDOW_SIZE = 15;
struct optionsStruct {const unsigned int id; const std::string code; const std::string str; const std::string regexp;}; struct optionsStruct {const unsigned int id; const std::string code; const std::string str; const std::string regexp;};
const std::string PROTOCOL_PREFIX = "gogdownloader://"; const std::string PROTOCOL_PREFIX = "gogdownloader://";

21
include/globals.h Normal file
View File

@ -0,0 +1,21 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details. */
#ifndef GLOBALS_H_INCLUDED
#define GLOBALS_H_INCLUDED
#include "config.h"
#include <iostream>
#include <vector>
namespace Globals
{
extern GalaxyConfig galaxyConf;
extern Config globalConfig;
}
#endif // GLOBALS_H_INCLUDED

View File

@ -8,6 +8,8 @@
#define UTIL_H #define UTIL_H
#include "globalconstants.h" #include "globalconstants.h"
#include "config.h"
#include "globals.h"
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@ -21,29 +23,6 @@
#include <boost/regex.hpp> #include <boost/regex.hpp>
#include <json/json.h> #include <json/json.h>
struct gameSpecificDirectoryConfig
{
bool bSubDirectories;
std::string sDirectory;
std::string sGameSubdir;
std::string sInstallersSubdir;
std::string sExtrasSubdir;
std::string sPatchesSubdir;
std::string sLanguagePackSubdir;
std::string sDLCSubdir;
};
struct gameSpecificConfig
{
unsigned int iInstallerPlatform;
unsigned int iInstallerLanguage;
bool bDLC;
bool bIgnoreDLCCount;
gameSpecificDirectoryConfig dirConf;
std::vector<unsigned int> vLanguagePriority;
std::vector<unsigned int> vPlatformPriority;
};
struct gameItem struct gameItem
{ {
std::string name; std::string name;

View File

@ -9,6 +9,7 @@
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
#include "globals.h"
#include <curl/curl.h> #include <curl/curl.h>
#include <json/json.h> #include <json/json.h>
#include <fstream> #include <fstream>
@ -16,7 +17,7 @@
class Website class Website
{ {
public: public:
Website(Config &conf); Website();
int Login(const std::string& email, const std::string& password); int Login(const std::string& email, const std::string& password);
std::string getResponse(const std::string& url); std::string getResponse(const std::string& url);
Json::Value getGameDetailsJSON(const std::string& gameid); Json::Value getGameDetailsJSON(const std::string& gameid);
@ -24,7 +25,6 @@ class Website
std::vector<gameItem> getFreeGames(); std::vector<gameItem> getFreeGames();
std::vector<wishlistItem> getWishlistItems(); std::vector<wishlistItem> getWishlistItems();
bool IsLoggedIn(); bool IsLoggedIn();
void setConfig(Config &conf);
virtual ~Website(); virtual ~Website();
protected: protected:
private: private:

373
main.cpp
View File

@ -9,12 +9,15 @@
#include "util.h" #include "util.h"
#include "globalconstants.h" #include "globalconstants.h"
#include "ssl_thread_setup.h" #include "ssl_thread_setup.h"
#include "galaxyapi.h"
#include "globals.h"
#include <fstream> #include <fstream>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
namespace bpo = boost::program_options; namespace bpo = boost::program_options;
Config Globals::globalConfig;
template<typename T> void set_vm_value(std::map<std::string, bpo::variable_value>& vm, const std::string& option, const T& value) template<typename T> void set_vm_value(std::map<std::string, bpo::variable_value>& vm, const std::string& option, const T& value)
{ {
@ -44,19 +47,20 @@ int main(int argc, char *argv[])
{ OPTION_DLCS, "d", "DLCs", "d|dlc|dlcs" } { OPTION_DLCS, "d", "DLCs", "d|dlc|dlcs" }
}; };
Config config; Globals::globalConfig.sVersionString = VERSION_STRING;
config.sVersionString = VERSION_STRING; Globals::globalConfig.sVersionNumber = VERSION_NUMBER;
config.sVersionNumber = VERSION_NUMBER;
config.sCacheDirectory = Util::getCacheHome() + "/lgogdownloader"; Globals::globalConfig.sCacheDirectory = Util::getCacheHome() + "/lgogdownloader";
config.sXMLDirectory = config.sCacheDirectory + "/xml"; Globals::globalConfig.sXMLDirectory = Globals::globalConfig.sCacheDirectory + "/xml";
config.sConfigDirectory = Util::getConfigHome() + "/lgogdownloader"; Globals::globalConfig.sConfigDirectory = Util::getConfigHome() + "/lgogdownloader";
config.sCookiePath = config.sConfigDirectory + "/cookies.txt"; Globals::globalConfig.curlConf.sCookiePath = Globals::globalConfig.sConfigDirectory + "/cookies.txt";
config.sConfigFilePath = config.sConfigDirectory + "/config.cfg"; Globals::globalConfig.sConfigFilePath = Globals::globalConfig.sConfigDirectory + "/config.cfg";
config.sBlacklistFilePath = config.sConfigDirectory + "/blacklist.txt"; Globals::globalConfig.sBlacklistFilePath = Globals::globalConfig.sConfigDirectory + "/blacklist.txt";
config.sIgnorelistFilePath = config.sConfigDirectory + "/ignorelist.txt"; Globals::globalConfig.sIgnorelistFilePath = Globals::globalConfig.sConfigDirectory + "/ignorelist.txt";
config.sGameHasDLCListFilePath = config.sConfigDirectory + "/game_has_dlc.txt"; Globals::globalConfig.sGameHasDLCListFilePath = Globals::globalConfig.sConfigDirectory + "/game_has_dlc.txt";
Globals::galaxyConf.setFilepath(Globals::globalConfig.sConfigDirectory + "/galaxy_tokens.json");
std::string priority_help_text = "Set priority by separating values with \",\"\nCombine values by separating with \"+\""; std::string priority_help_text = "Set priority by separating values with \",\"\nCombine values by separating with \"+\"";
// Create help text for --platform option // Create help text for --platform option
@ -97,12 +101,17 @@ int main(int argc, char *argv[])
} }
include_options_text += "Separate with \",\" to use multiple values"; include_options_text += "Separate with \",\" to use multiple values";
std::string galaxy_product_id_install;
std::string galaxy_product_id_show_builds;
std::vector<std::string> vFileIdStrings; std::vector<std::string> vFileIdStrings;
std::vector<std::string> unrecognized_options_cfg; std::vector<std::string> unrecognized_options_cfg;
std::vector<std::string> unrecognized_options_cli; std::vector<std::string> unrecognized_options_cli;
bpo::variables_map vm; bpo::variables_map vm;
bpo::options_description options_cli_all("Options"); bpo::options_description options_cli_all("Options");
bpo::options_description options_cli_no_cfg; bpo::options_description options_cli_no_cfg;
bpo::options_description options_cli_no_cfg_hidden;
bpo::options_description options_cli_all_include_hidden;
bpo::options_description options_cli_cfg; bpo::options_description options_cli_cfg;
bpo::options_description options_cfg_only; bpo::options_description options_cfg_only;
bpo::options_description options_cfg_all("Configuration"); bpo::options_description options_cfg_all("Configuration");
@ -120,40 +129,40 @@ int main(int argc, char *argv[])
std::string sInstallerLanguage; std::string sInstallerLanguage;
std::string sIncludeOptions; std::string sIncludeOptions;
std::string sExcludeOptions; std::string sExcludeOptions;
config.bReport = false; Globals::globalConfig.bReport = false;
// Commandline options (no config file) // Commandline options (no config file)
options_cli_no_cfg.add_options() options_cli_no_cfg.add_options()
("help,h", "Print help message") ("help,h", "Print help message")
("version", "Print version information") ("version", "Print version information")
("login", bpo::value<bool>(&bLogin)->zero_tokens()->default_value(false), "Login") ("login", bpo::value<bool>(&bLogin)->zero_tokens()->default_value(false), "Login")
("list", bpo::value<bool>(&config.bList)->zero_tokens()->default_value(false), "List games") ("list", bpo::value<bool>(&Globals::globalConfig.bList)->zero_tokens()->default_value(false), "List games")
("list-details", bpo::value<bool>(&config.bListDetails)->zero_tokens()->default_value(false), "List games with detailed info") ("list-details", bpo::value<bool>(&Globals::globalConfig.bListDetails)->zero_tokens()->default_value(false), "List games with detailed info")
("download", bpo::value<bool>(&config.bDownload)->zero_tokens()->default_value(false), "Download") ("download", bpo::value<bool>(&Globals::globalConfig.bDownload)->zero_tokens()->default_value(false), "Download")
("repair", bpo::value<bool>(&config.bRepair)->zero_tokens()->default_value(false), "Repair downloaded files\nUse --repair --download to redownload files when filesizes don't match (possibly different version). Redownload will rename the old file (appends .old to filename)") ("repair", bpo::value<bool>(&Globals::globalConfig.bRepair)->zero_tokens()->default_value(false), "Repair downloaded files\nUse --repair --download to redownload files when filesizes don't match (possibly different version). Redownload will rename the old file (appends .old to filename)")
("game", bpo::value<std::string>(&config.sGameRegex)->default_value(""), "Set regular expression filter\nfor download/list/repair (Perl syntax)\nAliases: \"all\", \"free\"\nAlias \"free\" doesn't work with cached details") ("game", bpo::value<std::string>(&Globals::globalConfig.sGameRegex)->default_value(""), "Set regular expression filter\nfor download/list/repair (Perl syntax)\nAliases: \"all\", \"free\"\nAlias \"free\" doesn't work with cached details")
("create-xml", bpo::value<std::string>(&config.sXMLFile)->implicit_value("automatic"), "Create GOG XML for file\n\"automatic\" to enable automatic XML creation") ("create-xml", bpo::value<std::string>(&Globals::globalConfig.sXMLFile)->implicit_value("automatic"), "Create GOG XML for file\n\"automatic\" to enable automatic XML creation")
("update-check", bpo::value<bool>(&config.bUpdateCheck)->zero_tokens()->default_value(false), "Check for update notifications") ("update-check", bpo::value<bool>(&Globals::globalConfig.bUpdateCheck)->zero_tokens()->default_value(false), "Check for update notifications")
("check-orphans", bpo::value<std::string>(&config.sOrphanRegex)->implicit_value(""), check_orphans_text.c_str()) ("check-orphans", bpo::value<std::string>(&Globals::globalConfig.sOrphanRegex)->implicit_value(""), check_orphans_text.c_str())
("status", bpo::value<bool>(&config.bCheckStatus)->zero_tokens()->default_value(false), "Show status of files\n\nOutput format:\nstatuscode gamename filename filesize filehash\n\nStatus codes:\nOK - File is OK\nND - File is not downloaded\nMD5 - MD5 mismatch, different version\nFS - File size mismatch, incomplete download") ("status", bpo::value<bool>(&Globals::globalConfig.bCheckStatus)->zero_tokens()->default_value(false), "Show status of files\n\nOutput format:\nstatuscode gamename filename filesize filehash\n\nStatus codes:\nOK - File is OK\nND - File is not downloaded\nMD5 - MD5 mismatch, different version\nFS - File size mismatch, incomplete download")
("save-config", bpo::value<bool>(&config.bSaveConfig)->zero_tokens()->default_value(false), "Create config file with current settings") ("save-config", bpo::value<bool>(&Globals::globalConfig.bSaveConfig)->zero_tokens()->default_value(false), "Create config file with current settings")
("reset-config", bpo::value<bool>(&config.bResetConfig)->zero_tokens()->default_value(false), "Reset config settings to default") ("reset-config", bpo::value<bool>(&Globals::globalConfig.bResetConfig)->zero_tokens()->default_value(false), "Reset config settings to default")
("report", bpo::value<std::string>(&config.sReportFilePath)->implicit_value("lgogdownloader-report.log"), "Save report of downloaded/repaired files to specified file\nDefault filename: lgogdownloader-report.log") ("report", bpo::value<std::string>(&Globals::globalConfig.sReportFilePath)->implicit_value("lgogdownloader-report.log"), "Save report of downloaded/repaired files to specified file\nDefault filename: lgogdownloader-report.log")
("update-cache", bpo::value<bool>(&config.bUpdateCache)->zero_tokens()->default_value(false), "Update game details cache") ("update-cache", bpo::value<bool>(&Globals::globalConfig.bUpdateCache)->zero_tokens()->default_value(false), "Update game details cache")
("no-platform-detection", bpo::value<bool>(&bNoPlatformDetection)->zero_tokens()->default_value(false), "Don't try to detect supported platforms from game shelf.\nSkips the initial fast platform detection and detects the supported platforms from game details which is slower but more accurate.\nUseful in case platform identifier is missing for some games in the game shelf.\nUsing --platform with --list doesn't work with this option.") ("no-platform-detection", bpo::value<bool>(&bNoPlatformDetection)->zero_tokens()->default_value(false), "Don't try to detect supported platforms from game shelf.\nSkips the initial fast platform detection and detects the supported platforms from game details which is slower but more accurate.\nUseful in case platform identifier is missing for some games in the game shelf.\nUsing --platform with --list doesn't work with this option.")
("download-file", bpo::value<std::string>(&config.sFileIdString)->default_value(""), "Download files using fileid\n\nFormat:\n\"gamename/fileid\"\nor: \"gogdownloader://gamename/fileid\"\n\nMultiple files:\n\"gamename1/fileid1,gamename2/fileid2\"\nor: \"gogdownloader://gamename1/fileid1,gamename2/fileid2\"\n\nThis option ignores all subdir options. The files are downloaded to directory specified with --directory option.") ("download-file", bpo::value<std::string>(&Globals::globalConfig.sFileIdString)->default_value(""), "Download files using fileid\n\nFormat:\n\"gamename/fileid\"\nor: \"gogdownloader://gamename/fileid\"\n\nMultiple files:\n\"gamename1/fileid1,gamename2/fileid2\"\nor: \"gogdownloader://gamename1/fileid1,gamename2/fileid2\"\n\nThis option ignores all subdir options. The files are downloaded to directory specified with --directory option.")
("output-file,o", bpo::value<std::string>(&config.sOutputFilename)->default_value(""), "Set filename of file downloaded with --download-file.") ("output-file,o", bpo::value<std::string>(&Globals::globalConfig.sOutputFilename)->default_value(""), "Set filename of file downloaded with --download-file.")
("wishlist", bpo::value<bool>(&config.bShowWishlist)->zero_tokens()->default_value(false), "Show wishlist") ("wishlist", bpo::value<bool>(&Globals::globalConfig.bShowWishlist)->zero_tokens()->default_value(false), "Show wishlist")
("login-api", bpo::value<bool>(&config.bLoginAPI)->zero_tokens()->default_value(false), "Login (API only)") ("login-api", bpo::value<bool>(&Globals::globalConfig.bLoginAPI)->zero_tokens()->default_value(false), "Login (API only)")
("login-website", bpo::value<bool>(&config.bLoginHTTP)->zero_tokens()->default_value(false), "Login (website only)") ("login-website", bpo::value<bool>(&Globals::globalConfig.bLoginHTTP)->zero_tokens()->default_value(false), "Login (website only)")
("cacert", bpo::value<std::string>(&config.sCACertPath)->default_value(""), "Path to CA certificate bundle in PEM format") ("cacert", bpo::value<std::string>(&Globals::globalConfig.curlConf.sCACertPath)->default_value(""), "Path to CA certificate bundle in PEM format")
("respect-umask", bpo::value<bool>(&config.bRespectUmask)->zero_tokens()->default_value(false), "Do not adjust permissions of sensitive files") ("respect-umask", bpo::value<bool>(&Globals::globalConfig.bRespectUmask)->zero_tokens()->default_value(false), "Do not adjust permissions of sensitive files")
; ;
// Commandline options (config file) // Commandline options (config file)
options_cli_cfg.add_options() options_cli_cfg.add_options()
("directory", bpo::value<std::string>(&config.sDirectory)->default_value("."), "Set download directory") ("directory", bpo::value<std::string>(&Globals::globalConfig.dirConf.sDirectory)->default_value("."), "Set download directory")
("limit-rate", bpo::value<curl_off_t>(&config.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited") ("limit-rate", bpo::value<curl_off_t>(&Globals::globalConfig.curlConf.iDownloadRate)->default_value(0), "Limit download rate to value in kB\n0 = unlimited")
("xml-directory", bpo::value<std::string>(&config.sXMLDirectory), "Set directory for GOG XML files") ("xml-directory", bpo::value<std::string>(&Globals::globalConfig.sXMLDirectory), "Set directory for GOG XML files")
("chunk-size", bpo::value<size_t>(&config.iChunkSize)->default_value(10), "Chunk size (in MB) when creating XML") ("chunk-size", bpo::value<size_t>(&Globals::globalConfig.iChunkSize)->default_value(10), "Chunk size (in MB) when creating XML")
("platform", bpo::value<std::string>(&sInstallerPlatform)->default_value("w+l"), platform_text.c_str()) ("platform", bpo::value<std::string>(&sInstallerPlatform)->default_value("w+l"), platform_text.c_str())
("language", bpo::value<std::string>(&sInstallerLanguage)->default_value("en"), language_text.c_str()) ("language", bpo::value<std::string>(&sInstallerLanguage)->default_value("en"), language_text.c_str())
("no-remote-xml", bpo::value<bool>(&bNoRemoteXML)->zero_tokens()->default_value(false), "Don't use remote XML for repair") ("no-remote-xml", bpo::value<bool>(&bNoRemoteXML)->zero_tokens()->default_value(false), "Don't use remote XML for repair")
@ -161,46 +170,52 @@ int main(int argc, char *argv[])
("no-color", bpo::value<bool>(&bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar or status messages") ("no-color", bpo::value<bool>(&bNoColor)->zero_tokens()->default_value(false), "Don't use coloring in the progress bar or status messages")
("no-duplicate-handling", bpo::value<bool>(&bNoDuplicateHandler)->zero_tokens()->default_value(false), "Don't use duplicate handler for installers\nDuplicate installers from different languages are handled separately") ("no-duplicate-handling", bpo::value<bool>(&bNoDuplicateHandler)->zero_tokens()->default_value(false), "Don't use duplicate handler for installers\nDuplicate installers from different languages are handled separately")
("no-subdirectories", bpo::value<bool>(&bNoSubDirectories)->zero_tokens()->default_value(false), "Don't create subdirectories for extras, patches and language packs") ("no-subdirectories", bpo::value<bool>(&bNoSubDirectories)->zero_tokens()->default_value(false), "Don't create subdirectories for extras, patches and language packs")
("verbose", bpo::value<bool>(&config.bVerbose)->zero_tokens()->default_value(false), "Print lots of information") ("verbose", bpo::value<bool>(&Globals::globalConfig.bVerbose)->zero_tokens()->default_value(false), "Print lots of information")
("insecure", bpo::value<bool>(&bInsecure)->zero_tokens()->default_value(false), "Don't verify authenticity of SSL certificates") ("insecure", bpo::value<bool>(&bInsecure)->zero_tokens()->default_value(false), "Don't verify authenticity of SSL certificates")
("timeout", bpo::value<long int>(&config.iTimeout)->default_value(10), "Set timeout for connection\nMaximum time in seconds that connection phase is allowed to take") ("timeout", bpo::value<long int>(&Globals::globalConfig.curlConf.iTimeout)->default_value(10), "Set timeout for connection\nMaximum time in seconds that connection phase is allowed to take")
("retries", bpo::value<int>(&config.iRetries)->default_value(3), "Set maximum number of retries on failed download") ("retries", bpo::value<int>(&Globals::globalConfig.iRetries)->default_value(3), "Set maximum number of retries on failed download")
("wait", bpo::value<int>(&config.iWait)->default_value(0), "Time to wait between requests (milliseconds)") ("wait", bpo::value<int>(&Globals::globalConfig.iWait)->default_value(0), "Time to wait between requests (milliseconds)")
("cover-list", bpo::value<std::string>(&config.sCoverList)->default_value("https://raw.githubusercontent.com/Sude-/lgogdownloader-lists/master/covers.xml"), "Set URL for cover list") ("cover-list", bpo::value<std::string>(&Globals::globalConfig.sCoverList)->default_value("https://raw.githubusercontent.com/Sude-/lgogdownloader-lists/master/covers.xml"), "Set URL for cover list")
("subdir-installers", bpo::value<std::string>(&config.sInstallersSubdir)->default_value(""), ("Set subdirectory for extras" + subdir_help_text).c_str()) ("subdir-installers", bpo::value<std::string>(&Globals::globalConfig.dirConf.sInstallersSubdir)->default_value(""), ("Set subdirectory for extras" + subdir_help_text).c_str())
("subdir-extras", bpo::value<std::string>(&config.sExtrasSubdir)->default_value("extras"), ("Set subdirectory for extras" + subdir_help_text).c_str()) ("subdir-extras", bpo::value<std::string>(&Globals::globalConfig.dirConf.sExtrasSubdir)->default_value("extras"), ("Set subdirectory for extras" + subdir_help_text).c_str())
("subdir-patches", bpo::value<std::string>(&config.sPatchesSubdir)->default_value("patches"), ("Set subdirectory for patches" + subdir_help_text).c_str()) ("subdir-patches", bpo::value<std::string>(&Globals::globalConfig.dirConf.sPatchesSubdir)->default_value("patches"), ("Set subdirectory for patches" + subdir_help_text).c_str())
("subdir-language-packs", bpo::value<std::string>(&config.sLanguagePackSubdir)->default_value("languagepacks"), ("Set subdirectory for language packs" + subdir_help_text).c_str()) ("subdir-language-packs", bpo::value<std::string>(&Globals::globalConfig.dirConf.sLanguagePackSubdir)->default_value("languagepacks"), ("Set subdirectory for language packs" + subdir_help_text).c_str())
("subdir-dlc", bpo::value<std::string>(&config.sDLCSubdir)->default_value("dlc/%dlcname%"), ("Set subdirectory for dlc" + subdir_help_text).c_str()) ("subdir-dlc", bpo::value<std::string>(&Globals::globalConfig.dirConf.sDLCSubdir)->default_value("dlc/%dlcname%"), ("Set subdirectory for dlc" + subdir_help_text).c_str())
("subdir-game", bpo::value<std::string>(&config.sGameSubdir)->default_value("%gamename%"), ("Set subdirectory for game" + subdir_help_text).c_str()) ("subdir-game", bpo::value<std::string>(&Globals::globalConfig.dirConf.sGameSubdir)->default_value("%gamename%"), ("Set subdirectory for game" + subdir_help_text).c_str())
("use-cache", bpo::value<bool>(&config.bUseCache)->zero_tokens()->default_value(false), ("Use game details cache")) ("use-cache", bpo::value<bool>(&Globals::globalConfig.bUseCache)->zero_tokens()->default_value(false), ("Use game details cache"))
("cache-valid", bpo::value<int>(&config.iCacheValid)->default_value(2880), ("Set how long cached game details are valid (in minutes)\nDefault: 2880 minutes (48 hours)")) ("cache-valid", bpo::value<int>(&Globals::globalConfig.iCacheValid)->default_value(2880), ("Set how long cached game details are valid (in minutes)\nDefault: 2880 minutes (48 hours)"))
("save-serials", bpo::value<bool>(&config.bSaveSerials)->zero_tokens()->default_value(false), "Save serial numbers when downloading") ("save-serials", bpo::value<bool>(&Globals::globalConfig.dlConf.bSaveSerials)->zero_tokens()->default_value(false), "Save serial numbers when downloading")
("ignore-dlc-count", bpo::value<std::string>(&config.sIgnoreDLCCountRegex)->implicit_value(".*"), "Set regular expression filter for games to ignore DLC count information\nIgnoring DLC count information helps in situations where the account page doesn't provide accurate information about DLCs") ("ignore-dlc-count", bpo::value<std::string>(&Globals::globalConfig.sIgnoreDLCCountRegex)->implicit_value(".*"), "Set regular expression filter for games to ignore DLC count information\nIgnoring DLC count information helps in situations where the account page doesn't provide accurate information about DLCs")
("include", bpo::value<std::string>(&sIncludeOptions)->default_value("all"), ("Select what to download/list/repair\n" + include_options_text).c_str()) ("include", bpo::value<std::string>(&sIncludeOptions)->default_value("all"), ("Select what to download/list/repair\n" + include_options_text).c_str())
("exclude", bpo::value<std::string>(&sExcludeOptions)->default_value("covers"), ("Select what not to download/list/repair\n" + include_options_text).c_str()) ("exclude", bpo::value<std::string>(&sExcludeOptions)->default_value("covers"), ("Select what not to download/list/repair\n" + include_options_text).c_str())
("automatic-xml-creation", bpo::value<bool>(&config.bAutomaticXMLCreation)->zero_tokens()->default_value(false), "Automatically create XML data after download has completed") ("automatic-xml-creation", bpo::value<bool>(&Globals::globalConfig.dlConf.bAutomaticXMLCreation)->zero_tokens()->default_value(false), "Automatically create XML data after download has completed")
("save-changelogs", bpo::value<bool>(&config.bSaveChangelogs)->zero_tokens()->default_value(false), "Save changelogs when downloading") ("save-changelogs", bpo::value<bool>(&Globals::globalConfig.dlConf.bSaveChangelogs)->zero_tokens()->default_value(false), "Save changelogs when downloading")
("threads", bpo::value<unsigned int>(&config.iThreads)->default_value(4), "Number of download threads") ("threads", bpo::value<unsigned int>(&Globals::globalConfig.iThreads)->default_value(4), "Number of download threads")
("dlc-list", bpo::value<std::string>(&config.sGameHasDLCList)->default_value("https://raw.githubusercontent.com/Sude-/lgogdownloader-lists/master/game_has_dlc.txt"), "Set URL for list of games that have DLC") ("dlc-list", bpo::value<std::string>(&Globals::globalConfig.sGameHasDLCList)->default_value("https://raw.githubusercontent.com/Sude-/lgogdownloader-lists/master/game_has_dlc.txt"), "Set URL for list of games that have DLC")
; ;
// Options read from config file // Options read from config file
options_cfg_only.add_options() options_cfg_only.add_options()
("token", bpo::value<std::string>(&config.sToken)->default_value(""), "oauth token") ("token", bpo::value<std::string>(&Globals::globalConfig.apiConf.sToken)->default_value(""), "oauth token")
("secret", bpo::value<std::string>(&config.sSecret)->default_value(""), "oauth secret") ("secret", bpo::value<std::string>(&Globals::globalConfig.apiConf.sSecret)->default_value(""), "oauth secret")
;
options_cli_no_cfg_hidden.add_options()
("galaxy-install", bpo::value<std::string>(&galaxy_product_id_install)->default_value(""), "Install game using product id")
("galaxy-show-builds", bpo::value<std::string>(&galaxy_product_id_show_builds)->default_value(""), "Show game builds using product id")
; ;
options_cli_all.add(options_cli_no_cfg).add(options_cli_cfg); options_cli_all.add(options_cli_no_cfg).add(options_cli_cfg);
options_cfg_all.add(options_cfg_only).add(options_cli_cfg); options_cfg_all.add(options_cfg_only).add(options_cli_cfg);
options_cli_all_include_hidden.add(options_cli_all).add(options_cli_no_cfg_hidden);
bpo::parsed_options parsed = bpo::parse_command_line(argc, argv, options_cli_all); bpo::parsed_options parsed = bpo::parse_command_line(argc, argv, options_cli_all_include_hidden);
bpo::store(parsed, vm); bpo::store(parsed, vm);
unrecognized_options_cli = bpo::collect_unrecognized(parsed.options, bpo::include_positional); unrecognized_options_cli = bpo::collect_unrecognized(parsed.options, bpo::include_positional);
bpo::notify(vm); bpo::notify(vm);
if (vm.count("help")) if (vm.count("help"))
{ {
std::cout << config.sVersionString << std::endl std::cout << Globals::globalConfig.sVersionString << std::endl
<< options_cli_all << std::endl; << options_cli_all << std::endl;
return 0; return 0;
} }
@ -212,7 +227,7 @@ int main(int argc, char *argv[])
} }
// Create lgogdownloader directories // Create lgogdownloader directories
boost::filesystem::path path = config.sXMLDirectory; boost::filesystem::path path = Globals::globalConfig.sXMLDirectory;
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
{ {
if (!boost::filesystem::create_directories(path)) if (!boost::filesystem::create_directories(path))
@ -222,7 +237,7 @@ int main(int argc, char *argv[])
} }
} }
path = config.sConfigDirectory; path = Globals::globalConfig.sConfigDirectory;
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
{ {
if (!boost::filesystem::create_directories(path)) if (!boost::filesystem::create_directories(path))
@ -232,7 +247,7 @@ int main(int argc, char *argv[])
} }
} }
path = config.sCacheDirectory; path = Globals::globalConfig.sCacheDirectory;
if (!boost::filesystem::exists(path)) if (!boost::filesystem::exists(path))
{ {
if (!boost::filesystem::create_directories(path)) if (!boost::filesystem::create_directories(path))
@ -242,12 +257,12 @@ int main(int argc, char *argv[])
} }
} }
if (boost::filesystem::exists(config.sConfigFilePath)) if (boost::filesystem::exists(Globals::globalConfig.sConfigFilePath))
{ {
std::ifstream ifs(config.sConfigFilePath.c_str()); std::ifstream ifs(Globals::globalConfig.sConfigFilePath.c_str());
if (!ifs) if (!ifs)
{ {
std::cerr << "Could not open config file: " << config.sConfigFilePath << std::endl; std::cerr << "Could not open config file: " << Globals::globalConfig.sConfigFilePath << std::endl;
return 1; return 1;
} }
else else
@ -259,12 +274,12 @@ int main(int argc, char *argv[])
unrecognized_options_cfg = bpo::collect_unrecognized(parsed.options, bpo::include_positional); unrecognized_options_cfg = bpo::collect_unrecognized(parsed.options, bpo::include_positional);
} }
} }
if (boost::filesystem::exists(config.sBlacklistFilePath)) if (boost::filesystem::exists(Globals::globalConfig.sBlacklistFilePath))
{ {
std::ifstream ifs(config.sBlacklistFilePath.c_str()); std::ifstream ifs(Globals::globalConfig.sBlacklistFilePath.c_str());
if (!ifs) if (!ifs)
{ {
std::cerr << "Could not open blacklist file: " << config.sBlacklistFilePath << std::endl; std::cerr << "Could not open blacklist file: " << Globals::globalConfig.sBlacklistFilePath << std::endl;
return 1; return 1;
} }
else else
@ -276,16 +291,16 @@ int main(int argc, char *argv[])
std::getline(ifs, line); std::getline(ifs, line);
lines.push_back(std::move(line)); lines.push_back(std::move(line));
} }
config.blacklist.initialize(lines); Globals::globalConfig.blacklist.initialize(lines);
} }
} }
if (boost::filesystem::exists(config.sIgnorelistFilePath)) if (boost::filesystem::exists(Globals::globalConfig.sIgnorelistFilePath))
{ {
std::ifstream ifs(config.sIgnorelistFilePath.c_str()); std::ifstream ifs(Globals::globalConfig.sIgnorelistFilePath.c_str());
if (!ifs) if (!ifs)
{ {
std::cerr << "Could not open ignorelist file: " << config.sIgnorelistFilePath << std::endl; std::cerr << "Could not open ignorelist file: " << Globals::globalConfig.sIgnorelistFilePath << std::endl;
return 1; return 1;
} }
else else
@ -297,18 +312,18 @@ int main(int argc, char *argv[])
std::getline(ifs, line); std::getline(ifs, line);
lines.push_back(std::move(line)); lines.push_back(std::move(line));
} }
config.ignorelist.initialize(lines); Globals::globalConfig.ignorelist.initialize(lines);
} }
} }
if (config.sIgnoreDLCCountRegex.empty()) if (Globals::globalConfig.sIgnoreDLCCountRegex.empty())
{ {
if (boost::filesystem::exists(config.sGameHasDLCListFilePath)) if (boost::filesystem::exists(Globals::globalConfig.sGameHasDLCListFilePath))
{ {
std::ifstream ifs(config.sGameHasDLCListFilePath.c_str()); std::ifstream ifs(Globals::globalConfig.sGameHasDLCListFilePath.c_str());
if (!ifs) if (!ifs)
{ {
std::cerr << "Could not open list of games that have dlc: " << config.sGameHasDLCListFilePath << std::endl; std::cerr << "Could not open list of games that have dlc: " << Globals::globalConfig.sGameHasDLCListFilePath << std::endl;
return 1; return 1;
} }
else else
@ -320,55 +335,56 @@ int main(int argc, char *argv[])
std::getline(ifs, line); std::getline(ifs, line);
lines.push_back(std::move(line)); lines.push_back(std::move(line));
} }
config.gamehasdlc.initialize(lines); Globals::globalConfig.gamehasdlc.initialize(lines);
} }
} }
} }
if (vm.count("chunk-size")) if (vm.count("chunk-size"))
config.iChunkSize <<= 20; // Convert chunk size from bytes to megabytes Globals::globalConfig.iChunkSize <<= 20; // Convert chunk size from bytes to megabytes
if (vm.count("limit-rate")) if (vm.count("limit-rate"))
config.iDownloadRate <<= 10; // Convert download rate from bytes to kilobytes Globals::globalConfig.curlConf.iDownloadRate <<= 10; // Convert download rate from bytes to kilobytes
if (vm.count("check-orphans")) if (vm.count("check-orphans"))
if (config.sOrphanRegex.empty()) if (Globals::globalConfig.sOrphanRegex.empty())
config.sOrphanRegex = orphans_regex_default; Globals::globalConfig.sOrphanRegex = orphans_regex_default;
if (vm.count("report")) if (vm.count("report"))
config.bReport = true; Globals::globalConfig.bReport = true;
if (config.iWait > 0) if (Globals::globalConfig.iWait > 0)
config.iWait *= 1000; Globals::globalConfig.iWait *= 1000;
if (config.iThreads < 1) if (Globals::globalConfig.iThreads < 1)
{ {
config.iThreads = 1; Globals::globalConfig.iThreads = 1;
set_vm_value(vm, "threads", config.iThreads); set_vm_value(vm, "threads", Globals::globalConfig.iThreads);
} }
config.bVerifyPeer = !bInsecure; Globals::globalConfig.curlConf.bVerbose = Globals::globalConfig.bVerbose;
config.bColor = !bNoColor; Globals::globalConfig.curlConf.bVerifyPeer = !bInsecure;
config.bUnicode = !bNoUnicode; Globals::globalConfig.bColor = !bNoColor;
config.bDuplicateHandler = !bNoDuplicateHandler; Globals::globalConfig.bUnicode = !bNoUnicode;
config.bRemoteXML = !bNoRemoteXML; Globals::globalConfig.dlConf.bDuplicateHandler = !bNoDuplicateHandler;
config.bSubDirectories = !bNoSubDirectories; Globals::globalConfig.dlConf.bRemoteXML = !bNoRemoteXML;
config.bPlatformDetection = !bNoPlatformDetection; Globals::globalConfig.dirConf.bSubDirectories = !bNoSubDirectories;
Globals::globalConfig.bPlatformDetection = !bNoPlatformDetection;
for (auto i = unrecognized_options_cli.begin(); i != unrecognized_options_cli.end(); ++i) for (auto i = unrecognized_options_cli.begin(); i != unrecognized_options_cli.end(); ++i)
if (i->compare(0, GlobalConstants::PROTOCOL_PREFIX.length(), GlobalConstants::PROTOCOL_PREFIX) == 0) if (i->compare(0, GlobalConstants::PROTOCOL_PREFIX.length(), GlobalConstants::PROTOCOL_PREFIX) == 0)
config.sFileIdString = *i; Globals::globalConfig.sFileIdString = *i;
if (!config.sFileIdString.empty()) if (!Globals::globalConfig.sFileIdString.empty())
{ {
if (config.sFileIdString.compare(0, GlobalConstants::PROTOCOL_PREFIX.length(), GlobalConstants::PROTOCOL_PREFIX) == 0) if (Globals::globalConfig.sFileIdString.compare(0, GlobalConstants::PROTOCOL_PREFIX.length(), GlobalConstants::PROTOCOL_PREFIX) == 0)
{ {
config.sFileIdString.replace(0, GlobalConstants::PROTOCOL_PREFIX.length(), ""); Globals::globalConfig.sFileIdString.replace(0, GlobalConstants::PROTOCOL_PREFIX.length(), "");
} }
vFileIdStrings = Util::tokenize(config.sFileIdString, ","); vFileIdStrings = Util::tokenize(Globals::globalConfig.sFileIdString, ",");
} }
if (!config.sOutputFilename.empty() && vFileIdStrings.size() > 1) if (!Globals::globalConfig.sOutputFilename.empty() && vFileIdStrings.size() > 1)
{ {
std::cerr << "Cannot specify an output file name when downloading multiple files." << std::endl; std::cerr << "Cannot specify an output file name when downloading multiple files." << std::endl;
return 1; return 1;
@ -376,15 +392,15 @@ int main(int argc, char *argv[])
if (bLogin) if (bLogin)
{ {
config.bLoginAPI = true; Globals::globalConfig.bLoginAPI = true;
config.bLoginHTTP = true; Globals::globalConfig.bLoginHTTP = true;
} }
if (config.sXMLFile == "automatic") if (Globals::globalConfig.sXMLFile == "automatic")
config.bAutomaticXMLCreation = true; Globals::globalConfig.dlConf.bAutomaticXMLCreation = true;
Util::parseOptionString(sInstallerLanguage, config.vLanguagePriority, config.iInstallerLanguage, GlobalConstants::LANGUAGES); Util::parseOptionString(sInstallerLanguage, Globals::globalConfig.dlConf.vLanguagePriority, Globals::globalConfig.dlConf.iInstallerLanguage, GlobalConstants::LANGUAGES);
Util::parseOptionString(sInstallerPlatform, config.vPlatformPriority, config.iInstallerPlatform, GlobalConstants::PLATFORMS); Util::parseOptionString(sInstallerPlatform, Globals::globalConfig.dlConf.vPlatformPriority, Globals::globalConfig.dlConf.iInstallerPlatform, GlobalConstants::PLATFORMS);
unsigned int include_value = 0; unsigned int include_value = 0;
unsigned int exclude_value = 0; unsigned int exclude_value = 0;
@ -398,16 +414,16 @@ int main(int argc, char *argv[])
{ {
exclude_value |= Util::getOptionValue(*it, INCLUDE_OPTIONS); exclude_value |= Util::getOptionValue(*it, INCLUDE_OPTIONS);
} }
config.iInclude = include_value & ~exclude_value; Globals::globalConfig.dlConf.iInclude = include_value & ~exclude_value;
// Assign values // Assign values
// TODO: Use config.iInclude in Downloader class directly and get rid of this value assignment // TODO: Use config.iInclude in Downloader class directly and get rid of this value assignment
config.bCover = (config.iInclude & OPTION_COVERS); Globals::globalConfig.dlConf.bCover = (Globals::globalConfig.dlConf.iInclude & OPTION_COVERS);
config.bInstallers = (config.iInclude & OPTION_INSTALLERS); Globals::globalConfig.dlConf.bInstallers = (Globals::globalConfig.dlConf.iInclude & OPTION_INSTALLERS);
config.bExtras = (config.iInclude & OPTION_EXTRAS); Globals::globalConfig.dlConf.bExtras = (Globals::globalConfig.dlConf.iInclude & OPTION_EXTRAS);
config.bPatches = (config.iInclude & OPTION_PATCHES); Globals::globalConfig.dlConf.bPatches = (Globals::globalConfig.dlConf.iInclude & OPTION_PATCHES);
config.bLanguagePacks = (config.iInclude & OPTION_LANGPACKS); Globals::globalConfig.dlConf.bLanguagePacks = (Globals::globalConfig.dlConf.iInclude & OPTION_LANGPACKS);
config.bDLC = (config.iInclude & OPTION_DLCS); Globals::globalConfig.dlConf.bDLC = (Globals::globalConfig.dlConf.iInclude & OPTION_DLCS);
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -420,55 +436,55 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
if (config.iInstallerPlatform < GlobalConstants::PLATFORMS[0].id || config.iInstallerPlatform > platform_all) if (Globals::globalConfig.dlConf.iInstallerPlatform < GlobalConstants::PLATFORMS[0].id || Globals::globalConfig.dlConf.iInstallerPlatform > platform_all)
{ {
std::cerr << "Invalid value for --platform" << std::endl; std::cerr << "Invalid value for --platform" << std::endl;
return 1; return 1;
} }
if (config.iInstallerLanguage < GlobalConstants::LANGUAGES[0].id || config.iInstallerLanguage > language_all) if (Globals::globalConfig.dlConf.iInstallerLanguage < GlobalConstants::LANGUAGES[0].id || Globals::globalConfig.dlConf.iInstallerLanguage > language_all)
{ {
std::cerr << "Invalid value for --language" << std::endl; std::cerr << "Invalid value for --language" << std::endl;
return 1; return 1;
} }
if (!config.sXMLDirectory.empty()) if (!Globals::globalConfig.sXMLDirectory.empty())
{ {
// Make sure that xml directory doesn't have trailing slash // Make sure that xml directory doesn't have trailing slash
if (config.sXMLDirectory.at(config.sXMLDirectory.length()-1)=='/') if (Globals::globalConfig.sXMLDirectory.at(Globals::globalConfig.sXMLDirectory.length()-1)=='/')
config.sXMLDirectory.assign(config.sXMLDirectory.begin(),config.sXMLDirectory.end()-1); Globals::globalConfig.sXMLDirectory.assign(Globals::globalConfig.sXMLDirectory.begin(), Globals::globalConfig.sXMLDirectory.end()-1);
} }
// Create GOG XML for a file // Create GOG XML for a file
if (!config.sXMLFile.empty() && (config.sXMLFile != "automatic")) if (!Globals::globalConfig.sXMLFile.empty() && (Globals::globalConfig.sXMLFile != "automatic"))
{ {
Util::createXML(config.sXMLFile, config.iChunkSize, config.sXMLDirectory); Util::createXML(Globals::globalConfig.sXMLFile, Globals::globalConfig.iChunkSize, Globals::globalConfig.sXMLDirectory);
return 0; return 0;
} }
// Make sure that directory has trailing slash // Make sure that directory has trailing slash
if (!config.sDirectory.empty()) if (!Globals::globalConfig.dirConf.sDirectory.empty())
{ {
if (config.sDirectory.at(config.sDirectory.length()-1)!='/') if (Globals::globalConfig.dirConf.sDirectory.at(Globals::globalConfig.dirConf.sDirectory.length()-1)!='/')
config.sDirectory += "/"; Globals::globalConfig.dirConf.sDirectory += "/";
} }
else else
{ {
config.sDirectory = "./"; // Directory wasn't specified, use current directory Globals::globalConfig.dirConf.sDirectory = "./"; // Directory wasn't specified, use current directory
} }
// CA certificate bundle // CA certificate bundle
if (config.sCACertPath.empty()) if (Globals::globalConfig.curlConf.sCACertPath.empty())
{ {
// Use CURL_CA_BUNDLE environment variable for CA certificate path if it is set // Use CURL_CA_BUNDLE environment variable for CA certificate path if it is set
char *ca_bundle = getenv("CURL_CA_BUNDLE"); char *ca_bundle = getenv("CURL_CA_BUNDLE");
if (ca_bundle) if (ca_bundle)
config.sCACertPath = (std::string)ca_bundle; Globals::globalConfig.curlConf.sCACertPath = (std::string)ca_bundle;
} }
if (!unrecognized_options_cfg.empty() && (!config.bSaveConfig || !config.bResetConfig)) if (!unrecognized_options_cfg.empty() && (!Globals::globalConfig.bSaveConfig || !Globals::globalConfig.bResetConfig))
{ {
std::cerr << "Unrecognized options in " << config.sConfigFilePath << std::endl; std::cerr << "Unrecognized options in " << Globals::globalConfig.sConfigFilePath << std::endl;
for (unsigned int i = 0; i < unrecognized_options_cfg.size(); i+=2) for (unsigned int i = 0; i < unrecognized_options_cfg.size(); i+=2)
{ {
std::cerr << unrecognized_options_cfg[i] << " = " << unrecognized_options_cfg[i+1] << std::endl; std::cerr << unrecognized_options_cfg[i] << " = " << unrecognized_options_cfg[i+1] << std::endl;
@ -480,25 +496,25 @@ int main(int argc, char *argv[])
ssl_thread_setup(); ssl_thread_setup();
curl_global_init(CURL_GLOBAL_ALL); curl_global_init(CURL_GLOBAL_ALL);
if (config.bLoginAPI) if (Globals::globalConfig.bLoginAPI)
{ {
config.sToken = ""; Globals::globalConfig.apiConf.sToken = "";
config.sSecret = ""; Globals::globalConfig.apiConf.sSecret = "";
} }
Downloader downloader(config); Downloader downloader;
int iLoginTries = 0; int iLoginTries = 0;
bool bLoginOK = false; bool bLoginOK = false;
// Login because --login, --login-api or --login-website was used // Login because --login, --login-api or --login-website was used
if (config.bLoginAPI || config.bLoginHTTP) if (Globals::globalConfig.bLoginAPI || Globals::globalConfig.bLoginHTTP)
bLoginOK = downloader.login(); bLoginOK = downloader.login();
bool bIsLoggedin = downloader.isLoggedIn(); bool bIsLoggedin = downloader.isLoggedIn();
// Login because we are not logged in // Login because we are not logged in
while (iLoginTries++ < config.iRetries && !bIsLoggedin) while (iLoginTries++ < Globals::globalConfig.iRetries && !bIsLoggedin)
{ {
bLoginOK = downloader.login(); bLoginOK = downloader.login();
if (bLoginOK) if (bLoginOK)
@ -516,23 +532,24 @@ int main(int argc, char *argv[])
} }
// Make sure that config file and cookie file are only readable/writable by owner // Make sure that config file and cookie file are only readable/writable by owner
if (!config.bRespectUmask) if (!Globals::globalConfig.bRespectUmask)
{ {
Util::setFilePermissions(config.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write); Util::setFilePermissions(Globals::globalConfig.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write);
Util::setFilePermissions(config.sCookiePath, boost::filesystem::owner_read | boost::filesystem::owner_write); Util::setFilePermissions(Globals::globalConfig.curlConf.sCookiePath, boost::filesystem::owner_read | boost::filesystem::owner_write);
Util::setFilePermissions(Globals::galaxyConf.getFilepath(), boost::filesystem::owner_read | boost::filesystem::owner_write);
} }
if (config.bSaveConfig || bLoginOK) if (Globals::globalConfig.bSaveConfig || bLoginOK)
{ {
if (bLoginOK) if (bLoginOK)
{ {
set_vm_value(vm, "token", downloader.config.sToken); set_vm_value(vm, "token", Globals::globalConfig.apiConf.sToken);
set_vm_value(vm, "secret", downloader.config.sSecret); set_vm_value(vm, "secret", Globals::globalConfig.apiConf.sSecret);
} }
std::ofstream ofs(config.sConfigFilePath.c_str()); std::ofstream ofs(Globals::globalConfig.sConfigFilePath.c_str());
if (ofs) if (ofs)
{ {
std::cerr << "Saving config: " << config.sConfigFilePath << std::endl; std::cerr << "Saving config: " << Globals::globalConfig.sConfigFilePath << std::endl;
for (bpo::variables_map::iterator it = vm.begin(); it != vm.end(); ++it) for (bpo::variables_map::iterator it = vm.begin(); it != vm.end(); ++it)
{ {
std::string option = it->first; std::string option = it->first;
@ -577,9 +594,9 @@ int main(int argc, char *argv[])
} }
} }
ofs.close(); ofs.close();
if (!config.bRespectUmask) if (!Globals::globalConfig.bRespectUmask)
Util::setFilePermissions(config.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write); Util::setFilePermissions(Globals::globalConfig.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write);
if (config.bSaveConfig) if (Globals::globalConfig.bSaveConfig)
{ {
curl_global_cleanup(); curl_global_cleanup();
ssl_thread_cleanup(); ssl_thread_cleanup();
@ -588,25 +605,25 @@ int main(int argc, char *argv[])
} }
else else
{ {
std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; std::cerr << "Failed to create config: " << Globals::globalConfig.sConfigFilePath << std::endl;
curl_global_cleanup(); curl_global_cleanup();
ssl_thread_cleanup(); ssl_thread_cleanup();
return 1; return 1;
} }
} }
else if (config.bResetConfig) else if (Globals::globalConfig.bResetConfig)
{ {
std::ofstream ofs(config.sConfigFilePath.c_str()); std::ofstream ofs(Globals::globalConfig.sConfigFilePath.c_str());
if (ofs) if (ofs)
{ {
if (!config.sToken.empty() && !config.sSecret.empty()) if (!Globals::globalConfig.apiConf.sToken.empty() && !Globals::globalConfig.apiConf.sSecret.empty())
{ {
ofs << "token = " << config.sToken << std::endl; ofs << "token = " << Globals::globalConfig.apiConf.sToken << std::endl;
ofs << "secret = " << config.sSecret << std::endl; ofs << "secret = " << Globals::globalConfig.apiConf.sSecret << std::endl;
} }
ofs.close(); ofs.close();
if (!config.bRespectUmask) if (!Globals::globalConfig.bRespectUmask)
Util::setFilePermissions(config.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write); Util::setFilePermissions(Globals::globalConfig.sConfigFilePath, boost::filesystem::owner_read | boost::filesystem::owner_write);
curl_global_cleanup(); curl_global_cleanup();
ssl_thread_cleanup(); ssl_thread_cleanup();
@ -614,7 +631,7 @@ int main(int argc, char *argv[])
} }
else else
{ {
std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; std::cerr << "Failed to create config: " << Globals::globalConfig.sConfigFilePath << std::endl;
curl_global_cleanup(); curl_global_cleanup();
ssl_thread_cleanup(); ssl_thread_cleanup();
return 1; return 1;
@ -631,41 +648,63 @@ int main(int argc, char *argv[])
int res = 0; int res = 0;
if (config.bShowWishlist) if (Globals::globalConfig.bShowWishlist)
downloader.showWishlist(); downloader.showWishlist();
else if (config.bUpdateCache) else if (Globals::globalConfig.bUpdateCache)
downloader.updateCache(); downloader.updateCache();
else if (config.bUpdateCheck) // Update check has priority over download and list else if (Globals::globalConfig.bUpdateCheck) // Update check has priority over download and list
downloader.updateCheck(); downloader.updateCheck();
else if (!vFileIdStrings.empty()) else if (!vFileIdStrings.empty())
{ {
for (std::vector<std::string>::iterator it = vFileIdStrings.begin(); it != vFileIdStrings.end(); it++) for (std::vector<std::string>::iterator it = vFileIdStrings.begin(); it != vFileIdStrings.end(); it++)
{ {
res |= downloader.downloadFileWithId(*it, config.sOutputFilename) ? 1 : 0; res |= downloader.downloadFileWithId(*it, Globals::globalConfig.sOutputFilename) ? 1 : 0;
} }
} }
else if (config.bRepair) // Repair file else if (Globals::globalConfig.bRepair) // Repair file
downloader.repair(); downloader.repair();
else if (config.bDownload) // Download games else if (Globals::globalConfig.bDownload) // Download games
downloader.download(); downloader.download();
else if (config.bListDetails || config.bList) // Detailed list of games/extras else if (Globals::globalConfig.bListDetails || Globals::globalConfig.bList) // Detailed list of games/extras
res = downloader.listGames(); res = downloader.listGames();
else if (!config.sOrphanRegex.empty()) // Check for orphaned files if regex for orphans is set else if (!Globals::globalConfig.sOrphanRegex.empty()) // Check for orphaned files if regex for orphans is set
downloader.checkOrphans(); downloader.checkOrphans();
else if (config.bCheckStatus) else if (Globals::globalConfig.bCheckStatus)
downloader.checkStatus(); downloader.checkStatus();
else if (!galaxy_product_id_show_builds.empty())
{
int build_index = -1;
std::vector<std::string> tokens = Util::tokenize(galaxy_product_id_show_builds, "/");
std::string product_id = tokens[0];
if (tokens.size() == 2)
{
build_index = std::stoi(tokens[1]);
}
downloader.galaxyShowBuilds(product_id, build_index);
}
else if (!galaxy_product_id_install.empty())
{
int build_index = -1;
std::vector<std::string> tokens = Util::tokenize(galaxy_product_id_install, "/");
std::string product_id = tokens[0];
if (tokens.size() == 2)
{
build_index = std::stoi(tokens[1]);
}
downloader.galaxyInstallGame(product_id, build_index);
}
else else
{ {
if (!(config.bLoginAPI || config.bLoginHTTP)) if (!(Globals::globalConfig.bLoginAPI || Globals::globalConfig.bLoginHTTP))
{ {
// Show help message // Show help message
std::cerr << config.sVersionString << std::endl std::cerr << Globals::globalConfig.sVersionString << std::endl
<< options_cli_all << std::endl; << options_cli_all << std::endl;
} }
} }
// Orphan check was called at the same time as download. Perform it after download has finished // Orphan check was called at the same time as download. Perform it after download has finished
if (!config.sOrphanRegex.empty() && config.bDownload) if (!Globals::globalConfig.sOrphanRegex.empty() && Globals::globalConfig.bDownload)
downloader.checkOrphans(); downloader.checkOrphans();
curl_global_cleanup(); curl_global_cleanup();

View File

@ -11,6 +11,7 @@
#include <cstdlib> #include <cstdlib>
#include <sstream> #include <sstream>
#include <json/json.h> #include <json/json.h>
#include <unistd.h>
#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40900 #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40900
# define _regex_namespace_ std # define _regex_namespace_ std
@ -150,6 +151,8 @@ int API::login(const std::string& email, const std::string& password)
return res; return res;
} }
usleep(500); // Wait to avoid "429 Too Many Requests"
// Authorize temporary token and get verifier // Authorize temporary token and get verifier
url = this->config.oauth_authorize_temp_token + "?username=" + oauth_url_escape(email.c_str()) + "&password=" + oauth_url_escape(password.c_str()); url = this->config.oauth_authorize_temp_token + "?username=" + oauth_url_escape(email.c_str()) + "&password=" + oauth_url_escape(password.c_str());
url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY.c_str(), CONSUMER_SECRET.c_str(), token.c_str(), secret.c_str()); url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY.c_str(), CONSUMER_SECRET.c_str(), token.c_str(), secret.c_str());
@ -167,6 +170,8 @@ int API::login(const std::string& email, const std::string& password)
return res; return res;
} }
usleep(500); // Wait to avoid "429 Too Many Requests"
// Get final token and secret // Get final token and secret
url = this->config.oauth_get_token + "?oauth_verifier=" + verifier; url = this->config.oauth_get_token + "?oauth_verifier=" + verifier;
url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY.c_str(), CONSUMER_SECRET.c_str(), token.c_str(), secret.c_str()); url = oauth_sign_url2(url.c_str(), NULL, OA_HMAC, NULL, CONSUMER_KEY.c_str(), CONSUMER_SECRET.c_str(), token.c_str(), secret.c_str());

File diff suppressed because it is too large Load Diff

253
src/galaxyapi.cpp Normal file
View File

@ -0,0 +1,253 @@
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://www.wtfpl.net/ for more details. */
#include "galaxyapi.h"
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
GalaxyConfig Globals::galaxyConf;
size_t galaxyAPI::writeMemoryCallback(char *ptr, size_t size, size_t nmemb, void *userp) {
std::ostringstream *stream = (std::ostringstream*)userp;
std::streamsize count = (std::streamsize) size * nmemb;
stream->write(ptr, count);
return count;
}
galaxyAPI::galaxyAPI(CurlConfig& conf)
{
this->curlConf = conf;
curlhandle = curl_easy_init();
curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlhandle, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(curlhandle, CURLOPT_FAILONERROR, true);
curl_easy_setopt(curlhandle, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, curlConf.iTimeout);
curl_easy_setopt(curlhandle, CURLOPT_FAILONERROR, true);
curl_easy_setopt(curlhandle, CURLOPT_COOKIEFILE, curlConf.sCookiePath.c_str());
curl_easy_setopt(curlhandle, CURLOPT_COOKIEJAR, curlConf.sCookiePath.c_str());
curl_easy_setopt(curlhandle, CURLOPT_SSL_VERIFYPEER, curlConf.bVerifyPeer);
curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, curlConf.bVerbose);
curl_easy_setopt(curlhandle, CURLOPT_MAX_RECV_SPEED_LARGE, curlConf.iDownloadRate);
// Assume that we have connection error and abort transfer with CURLE_OPERATION_TIMEDOUT if download speed is less than 200 B/s for 30 seconds
curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_TIME, 30);
curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_LIMIT, 200);
if (!curlConf.sCACertPath.empty())
curl_easy_setopt(curlhandle, CURLOPT_CAINFO, curlConf.sCACertPath.c_str());
}
galaxyAPI::~galaxyAPI()
{
curl_easy_cleanup(curlhandle);
}
/* Initialize the API
returns 0 if failed
returns 1 if successful
*/
int galaxyAPI::init()
{
int res = 0;
if (!this->isTokenExpired())
{
res = 1;
}
else
res = 0;
return res;
}
bool galaxyAPI::refreshLogin()
{
bool res = false;
std::string refresh_url = "https://auth.gog.com/token?client_id=" + Globals::galaxyConf.getClientId()
+ "&client_secret=" + Globals::galaxyConf.getClientSecret()
+ "&grant_type=refresh_token"
+ "&refresh_token=" + Globals::galaxyConf.getRefreshToken();
std::string json = this->getResponse(refresh_url);
if (!json.empty())
{
Json::Value token_json;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, token_json))
{
Globals::galaxyConf.setJSON(token_json);
res = true;
}
delete jsonparser;
}
return res;
}
bool galaxyAPI::isTokenExpired()
{
int res = false;
if (Globals::galaxyConf.isExpired())
res = true;
return res;
}
std::string galaxyAPI::getResponse(const std::string& url, const bool& zlib_decompress)
{
std::ostringstream memory;
struct curl_slist *header = NULL;
std::string access_token;
if (!Globals::galaxyConf.isExpired())
access_token = Globals::galaxyConf.getAccessToken();
if (!access_token.empty())
{
std::string bearer = "Authorization: Bearer " + access_token;
header = curl_slist_append(header, bearer.c_str());
}
curl_easy_setopt(curlhandle, CURLOPT_HTTPHEADER, header);
curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, galaxyAPI::writeMemoryCallback);
curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, &memory);
curl_easy_perform(curlhandle);
std::string response = memory.str();
memory.str(std::string());
curl_easy_setopt(curlhandle, CURLOPT_HTTPHEADER, NULL);
curl_slist_free_all(header);
if (zlib_decompress)
{
std::string response_decompressed;
boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
in.push(boost::iostreams::zlib_decompressor(GlobalConstants::ZLIB_WINDOW_SIZE));
in.push(boost::make_iterator_range(response));
boost::iostreams::copy(in, boost::iostreams::back_inserter(response_decompressed));
response = response_decompressed;
}
return response;
}
Json::Value galaxyAPI::getProductBuilds(const std::string& product_id, const std::string& platform, const std::string& generation)
{
Json::Value json;
std::string url = "https://content-system.gog.com/products/" + product_id + "/os/" + platform + "/builds?generation=" + generation;
std::string response = this->getResponse(url);
Json::Reader *jsonparser = new Json::Reader;
jsonparser->parse(response, json);
delete jsonparser;
return json;
}
Json::Value galaxyAPI::getManifestV1(const std::string& product_id, const std::string& build_id, const std::string& manifest_id, const std::string& platform)
{
Json::Value json;
std::string url = "https://cdn.gog.com/content-system/v1/manifests/" + product_id + "/" + platform + "/" + build_id + "/" + manifest_id + ".json";
std::string response = this->getResponse(url);
Json::Reader *jsonparser = new Json::Reader;
jsonparser->parse(response, json);
delete jsonparser;
return json;
}
Json::Value galaxyAPI::getManifestV2(std::string manifest_hash)
{
Json::Value json;
if (!manifest_hash.empty() && manifest_hash.find("/") == std::string::npos)
manifest_hash = this->hashToGalaxyPath(manifest_hash);
std::string url = "https://cdn.gog.com/content-system/v2/meta/" + manifest_hash;
std::string response = this->getResponse(url, true);
Json::Reader *jsonparser = new Json::Reader;
jsonparser->parse(response, json);
delete jsonparser;
return json;
}
Json::Value galaxyAPI::getSecureLink(const std::string& product_id, const std::string& path)
{
Json::Value json;
std::string url = "https://content-system.gog.com/products/" + product_id + "/secure_link?generation=2&path=" + path + "&_version=2";
std::string response = this->getResponse(url);
Json::Reader *jsonparser = new Json::Reader;
jsonparser->parse(response, json);
delete jsonparser;
return json;
}
std::string galaxyAPI::hashToGalaxyPath(const std::string& hash)
{
std::string galaxy_path = hash;
if (galaxy_path.find("/") == std::string::npos)
galaxy_path.assign(hash.begin(), hash.begin()+2).append("/").append(hash.begin()+2, hash.begin()+4).append("/").append(hash);
return galaxy_path;
}
std::vector<galaxyDepotItem> galaxyAPI::getDepotItemsVector(const std::string& hash)
{
Json::Value json = this->getManifestV2(hash);
std::vector<galaxyDepotItem> items;
for (unsigned int i = 0; i < json["depot"]["items"].size(); ++i)
{
if (!json["depot"]["items"][i]["chunks"].empty())
{
galaxyDepotItem item;
item.totalSizeCompressed = 0;
item.totalSizeUncompressed = 0;
item.path = json["depot"]["items"][i]["path"].asString();
while (Util::replaceString(item.path, "\\", "/"));
for (unsigned int j = 0; j < json["depot"]["items"][i]["chunks"].size(); ++j)
{
galaxyDepotItemChunk chunk;
chunk.md5_compressed = json["depot"]["items"][i]["chunks"][j]["compressedMd5"].asString();
chunk.md5_uncompressed = json["depot"]["items"][i]["chunks"][j]["md5"].asString();
chunk.size_compressed = json["depot"]["items"][i]["chunks"][j]["compressedSize"].asLargestUInt();
chunk.size_uncompressed = json["depot"]["items"][i]["chunks"][j]["size"].asLargestUInt();
item.totalSizeCompressed += chunk.size_compressed;
item.totalSizeUncompressed += chunk.size_uncompressed;
item.chunks.push_back(chunk);
}
if (json["depot"]["items"][i].isMember("md5"))
item.md5 = json["depot"]["items"][i]["md5"].asString();
else if (json["depot"]["items"][i]["chunks"].size() == 1)
item.md5 = json["depot"]["items"][i]["chunks"][0]["md5"].asString();
items.push_back(item);
}
}
return items;
}

View File

@ -18,7 +18,7 @@ gameDetails::~gameDetails()
void gameDetails::filterWithPriorities(const gameSpecificConfig& config) void gameDetails::filterWithPriorities(const gameSpecificConfig& config)
{ {
if (config.vPlatformPriority.empty() && config.vLanguagePriority.empty()) if (config.dlConf.vPlatformPriority.empty() && config.dlConf.vLanguagePriority.empty())
return; return;
filterListWithPriorities(installers, config); filterListWithPriorities(installers, config);
@ -40,19 +40,19 @@ void gameDetails::filterListWithPriorities(std::vector<gameFile>& list, const ga
for (std::vector<gameFile>::iterator fileDetails = list.begin(); fileDetails != list.end(); fileDetails++) for (std::vector<gameFile>::iterator fileDetails = list.begin(); fileDetails != list.end(); fileDetails++)
{ {
fileDetails->score = 0; fileDetails->score = 0;
if (!config.vPlatformPriority.empty()) if (!config.dlConf.vPlatformPriority.empty())
{ {
for (size_t i = 0; i != config.vPlatformPriority.size(); i++) for (size_t i = 0; i != config.dlConf.vPlatformPriority.size(); i++)
if (fileDetails->platform & config.vPlatformPriority[i]) if (fileDetails->platform & config.dlConf.vPlatformPriority[i])
{ {
fileDetails->score += i; fileDetails->score += i;
break; break;
} }
} }
if (!config.vLanguagePriority.empty()) if (!config.dlConf.vLanguagePriority.empty())
{ {
for (size_t i = 0; i != config.vLanguagePriority.size(); i++) for (size_t i = 0; i != config.dlConf.vLanguagePriority.size(); i++)
if (fileDetails->language & config.vLanguagePriority[i]) if (fileDetails->language & config.dlConf.vLanguagePriority[i])
{ {
fileDetails->score += i; fileDetails->score += i;
break; break;
@ -71,7 +71,7 @@ void gameDetails::filterListWithPriorities(std::vector<gameFile>& list, const ga
} }
} }
void gameDetails::makeFilepaths(const gameSpecificDirectoryConfig& config) void gameDetails::makeFilepaths(const DirectoryConfig& config)
{ {
std::string filepath; std::string filepath;
std::string directory = config.sDirectory + "/" + config.sGameSubdir + "/"; std::string directory = config.sDirectory + "/" + config.sGameSubdir + "/";
@ -147,6 +147,7 @@ Json::Value gameDetails::getDetailsAsJson()
Json::Value json; Json::Value json;
json["gamename"] = this->gamename; json["gamename"] = this->gamename;
json["product_id"] = this->product_id;
json["title"] = this->title; json["title"] = this->title;
json["icon"] = this->icon; json["icon"] = this->icon;
json["serials"] = this->serials; json["serials"] = this->serials;

View File

@ -241,31 +241,31 @@ int Util::getGameSpecificConfig(std::string gamename, gameSpecificConfig* conf,
if (root.isMember("language")) if (root.isMember("language"))
{ {
if (root["language"].isInt()) if (root["language"].isInt())
conf->iInstallerLanguage = root["language"].asUInt(); conf->dlConf.iInstallerLanguage = root["language"].asUInt();
else else
{ {
Util::parseOptionString(root["language"].asString(), conf->vLanguagePriority, conf->iInstallerLanguage, GlobalConstants::LANGUAGES); Util::parseOptionString(root["language"].asString(), conf->dlConf.vLanguagePriority, conf->dlConf.iInstallerLanguage, GlobalConstants::LANGUAGES);
} }
res++; res++;
} }
if (root.isMember("platform")) if (root.isMember("platform"))
{ {
if (root["platform"].isInt()) if (root["platform"].isInt())
conf->iInstallerPlatform = root["platform"].asUInt(); conf->dlConf.iInstallerPlatform = root["platform"].asUInt();
else else
{ {
Util::parseOptionString(root["platform"].asString(), conf->vPlatformPriority, conf->iInstallerPlatform, GlobalConstants::PLATFORMS); Util::parseOptionString(root["platform"].asString(), conf->dlConf.vPlatformPriority, conf->dlConf.iInstallerPlatform, GlobalConstants::PLATFORMS);
} }
res++; res++;
} }
if (root.isMember("dlc")) if (root.isMember("dlc"))
{ {
conf->bDLC = root["dlc"].asBool(); conf->dlConf.bDLC = root["dlc"].asBool();
res++; res++;
} }
if (root.isMember("ignore-dlc-count")) if (root.isMember("ignore-dlc-count"))
{ {
conf->bIgnoreDLCCount = root["ignore-dlc-count"].asBool(); conf->dlConf.bIgnoreDLCCount = root["ignore-dlc-count"].asBool();
res++; res++;
} }
if (root.isMember("subdirectories")) if (root.isMember("subdirectories"))

View File

@ -10,30 +10,29 @@
#include <htmlcxx/html/ParserDom.h> #include <htmlcxx/html/ParserDom.h>
#include <boost/algorithm/string/case_conv.hpp> #include <boost/algorithm/string/case_conv.hpp>
Website::Website(Config &conf) Website::Website()
{ {
this->config = conf;
this->retries = 0; this->retries = 0;
curlhandle = curl_easy_init(); curlhandle = curl_easy_init();
curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curlhandle, CURLOPT_USERAGENT, config.sVersionString.c_str()); curl_easy_setopt(curlhandle, CURLOPT_USERAGENT, Globals::globalConfig.sVersionString.c_str());
curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1); curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1);
curl_easy_setopt(curlhandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curlhandle, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, config.iTimeout); curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, Globals::globalConfig.curlConf.iTimeout);
curl_easy_setopt(curlhandle, CURLOPT_FAILONERROR, true); curl_easy_setopt(curlhandle, CURLOPT_FAILONERROR, true);
curl_easy_setopt(curlhandle, CURLOPT_COOKIEFILE, config.sCookiePath.c_str()); curl_easy_setopt(curlhandle, CURLOPT_COOKIEFILE, Globals::globalConfig.curlConf.sCookiePath.c_str());
curl_easy_setopt(curlhandle, CURLOPT_COOKIEJAR, config.sCookiePath.c_str()); curl_easy_setopt(curlhandle, CURLOPT_COOKIEJAR, Globals::globalConfig.curlConf.sCookiePath.c_str());
curl_easy_setopt(curlhandle, CURLOPT_SSL_VERIFYPEER, config.bVerifyPeer); curl_easy_setopt(curlhandle, CURLOPT_SSL_VERIFYPEER, Globals::globalConfig.curlConf.bVerifyPeer);
curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, config.bVerbose); curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, Globals::globalConfig.curlConf.bVerbose);
curl_easy_setopt(curlhandle, CURLOPT_MAX_RECV_SPEED_LARGE, config.iDownloadRate); curl_easy_setopt(curlhandle, CURLOPT_MAX_RECV_SPEED_LARGE, Globals::globalConfig.curlConf.iDownloadRate);
// Assume that we have connection error and abort transfer with CURLE_OPERATION_TIMEDOUT if download speed is less than 200 B/s for 30 seconds // Assume that we have connection error and abort transfer with CURLE_OPERATION_TIMEDOUT if download speed is less than 200 B/s for 30 seconds
curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_TIME, 30); curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_TIME, 30);
curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_LIMIT, 200); curl_easy_setopt(curlhandle, CURLOPT_LOW_SPEED_LIMIT, 200);
if (!config.sCACertPath.empty()) if (!Globals::globalConfig.curlConf.sCACertPath.empty())
curl_easy_setopt(curlhandle, CURLOPT_CAINFO, config.sCACertPath.c_str()); curl_easy_setopt(curlhandle, CURLOPT_CAINFO, Globals::globalConfig.curlConf.sCACertPath.c_str());
} }
Website::~Website() Website::~Website()
@ -61,13 +60,13 @@ std::string Website::getResponse(const std::string& url)
CURLcode result; CURLcode result;
do do
{ {
if (config.iWait > 0) if (Globals::globalConfig.iWait > 0)
usleep(config.iWait); // Delay the request by specified time usleep(Globals::globalConfig.iWait); // Delay the request by specified time
result = curl_easy_perform(curlhandle); result = curl_easy_perform(curlhandle);
response = memory.str(); response = memory.str();
memory.str(std::string()); memory.str(std::string());
} }
while ((result != CURLE_OK) && response.empty() && (this->retries++ < config.iRetries)); while ((result != CURLE_OK) && response.empty() && (this->retries++ < Globals::globalConfig.iRetries));
this->retries = 0; // reset retries counter this->retries = 0; // reset retries counter
if (result != CURLE_OK) if (result != CURLE_OK)
@ -198,31 +197,31 @@ std::vector<gameItem> Website::getGames()
platform |= GlobalConstants::PLATFORM_LINUX; platform |= GlobalConstants::PLATFORM_LINUX;
// Skip if platform doesn't match // Skip if platform doesn't match
if (config.bPlatformDetection && !(platform & config.iInstallerPlatform)) if (Globals::globalConfig.bPlatformDetection && !(platform & Globals::globalConfig.dlConf.iInstallerPlatform))
continue; continue;
// Filter the game list // Filter the game list
if (!config.sGameRegex.empty()) if (!Globals::globalConfig.sGameRegex.empty())
{ {
// GameRegex filter aliases // GameRegex filter aliases
if (config.sGameRegex == "all") if (Globals::globalConfig.sGameRegex == "all")
config.sGameRegex = ".*"; Globals::globalConfig.sGameRegex = ".*";
boost::regex expression(config.sGameRegex); boost::regex expression(Globals::globalConfig.sGameRegex);
boost::match_results<std::string::const_iterator> what; boost::match_results<std::string::const_iterator> what;
if (!boost::regex_search(game.name, what, expression)) // Check if name matches the specified regex if (!boost::regex_search(game.name, what, expression)) // Check if name matches the specified regex
continue; continue;
} }
if (config.bDLC) if (Globals::globalConfig.dlConf.bDLC)
{ {
int dlcCount = product["dlcCount"].asInt(); int dlcCount = product["dlcCount"].asInt();
bool bDownloadDLCInfo = (dlcCount != 0); bool bDownloadDLCInfo = (dlcCount != 0);
if (!bDownloadDLCInfo && !config.sIgnoreDLCCountRegex.empty()) if (!bDownloadDLCInfo && !Globals::globalConfig.sIgnoreDLCCountRegex.empty())
{ {
boost::regex expression(config.sIgnoreDLCCountRegex); boost::regex expression(Globals::globalConfig.sIgnoreDLCCountRegex);
boost::match_results<std::string::const_iterator> what; boost::match_results<std::string::const_iterator> what;
if (boost::regex_search(game.name, what, expression)) // Check if name matches the specified regex if (boost::regex_search(game.name, what, expression)) // Check if name matches the specified regex
{ {
@ -230,25 +229,25 @@ std::vector<gameItem> Website::getGames()
} }
} }
if (!bDownloadDLCInfo && !config.gamehasdlc.empty()) if (!bDownloadDLCInfo && !Globals::globalConfig.gamehasdlc.empty())
{ {
if (config.gamehasdlc.isBlacklisted(game.name)) if (Globals::globalConfig.gamehasdlc.isBlacklisted(game.name))
bDownloadDLCInfo = true; bDownloadDLCInfo = true;
} }
// Check game specific config // Check game specific config
if (!config.bUpdateCache) // Disable game specific config files for cache update if (!Globals::globalConfig.bUpdateCache) // Disable game specific config files for cache update
{ {
gameSpecificConfig conf; gameSpecificConfig conf;
conf.bIgnoreDLCCount = bDownloadDLCInfo; conf.dlConf.bIgnoreDLCCount = bDownloadDLCInfo;
Util::getGameSpecificConfig(game.name, &conf); Util::getGameSpecificConfig(game.name, &conf);
bDownloadDLCInfo = conf.bIgnoreDLCCount; bDownloadDLCInfo = conf.dlConf.bIgnoreDLCCount;
} }
if (bDownloadDLCInfo && !config.sGameRegex.empty()) if (bDownloadDLCInfo && !Globals::globalConfig.sGameRegex.empty())
{ {
// don't download unnecessary info if user is only interested in a subset of his account // don't download unnecessary info if user is only interested in a subset of his account
boost::regex expression(config.sGameRegex); boost::regex expression(Globals::globalConfig.sGameRegex);
boost::match_results<std::string::const_iterator> what; boost::match_results<std::string::const_iterator> what;
if (!boost::regex_search(game.name, what, expression)) if (!boost::regex_search(game.name, what, expression))
{ {
@ -317,96 +316,40 @@ int Website::Login(const std::string& email, const std::string& password)
std::string postdata; std::string postdata;
std::ostringstream memory; std::ostringstream memory;
std::string token; std::string token;
std::string tagname_username; std::string tagname_username = "login[username]";
std::string tagname_password; std::string tagname_password = "login[password]";
std::string tagname_login; std::string tagname_login = "login[login]";
std::string tagname_token; std::string tagname_token;
std::string auth_url = "https://auth.gog.com/auth?client_id=" + Globals::galaxyConf.getClientId() + "&redirect_uri=" + (std::string)curl_easy_escape(curlhandle, Globals::galaxyConf.getRedirectUri().c_str(), Globals::galaxyConf.getRedirectUri().size()) + "&response_type=code&layout=default&brand=gog";
std::string auth_code;
// Get login token std::string login_form_html = this->getResponse(auth_url);
std::string html = this->getResponse("https://www.gog.com/"); #ifdef DEBUG
htmlcxx::HTML::ParserDom parser; std::cerr << "DEBUG INFO (Website::Login)" << std::endl;
tree<htmlcxx::HTML::Node> dom = parser.parseTree(html); std::cerr << login_form_html << std::endl;
tree<htmlcxx::HTML::Node>::iterator it = dom.begin(); #endif
tree<htmlcxx::HTML::Node>::iterator end = dom.end(); if (login_form_html.find("google.com/recaptcha") != std::string::npos)
// Find auth_url
bool bFoundAuthUrl = false;
for (; it != end; ++it)
{ {
if (it->tagName()=="script") std::cout << "Login form contains reCAPTCHA (https://www.google.com/recaptcha/)" << std::endl
{ << "Login with browser and export cookies to \"" << Globals::globalConfig.curlConf.sCookiePath << "\"" << std::endl;
std::string auth_url; return res = 0;
for (unsigned int i = 0; i < dom.number_of_children(it); ++i)
{
tree<htmlcxx::HTML::Node>::iterator script_it = dom.child(it, i);
if (!script_it->isTag() && !script_it->isComment())
{
if (script_it->text().find("GalaxyAccounts") != std::string::npos)
{
boost::match_results<std::string::const_iterator> what;
boost::regex expression(".*'(https://auth.gog.com/.*?)'.*");
boost::regex_match(script_it->text(), what, expression);
auth_url = what[1];
break;
}
}
}
if (!auth_url.empty())
{ // Found auth_url, get the necessary info for login
bFoundAuthUrl = true;
std::string login_form_html = this->getResponse(auth_url);
#ifdef DEBUG
std::cerr << "DEBUG INFO (Website::Login)" << std::endl;
std::cerr << login_form_html << std::endl;
#endif
if (login_form_html.find("google.com/recaptcha") != std::string::npos)
{
std::cout << "Login form contains reCAPTCHA (https://www.google.com/recaptcha/)" << std::endl
<< "Login with browser and export cookies to \"" << config.sCookiePath << "\"" << std::endl;
return res = 0;
}
tree<htmlcxx::HTML::Node> login_dom = parser.parseTree(login_form_html);
tree<htmlcxx::HTML::Node>::iterator login_it = login_dom.begin();
tree<htmlcxx::HTML::Node>::iterator login_it_end = login_dom.end();
for (; login_it != login_it_end; ++login_it)
{
if (login_it->tagName()=="input")
{
login_it->parseAttributes();
std::string id_login = login_it->attribute("id").second;
if (id_login == "login_username")
{
tagname_username = login_it->attribute("name").second;
}
else if (id_login == "login_password")
{
tagname_password = login_it->attribute("name").second;
}
else if (id_login == "login__token")
{
token = login_it->attribute("value").second; // login token
tagname_token = login_it->attribute("name").second;
}
}
else if (login_it->tagName()=="button")
{
login_it->parseAttributes();
std::string id_login = login_it->attribute("id").second;
if (id_login == "login_login")
{
tagname_login = login_it->attribute("name").second;
}
}
}
break;
}
}
} }
if (!bFoundAuthUrl) htmlcxx::HTML::ParserDom parser;
tree<htmlcxx::HTML::Node> login_dom = parser.parseTree(login_form_html);
tree<htmlcxx::HTML::Node>::iterator login_it = login_dom.begin();
tree<htmlcxx::HTML::Node>::iterator login_it_end = login_dom.end();
for (; login_it != login_it_end; ++login_it)
{ {
std::cout << "Failed to find url for login form" << std::endl; if (login_it->tagName()=="input")
{
login_it->parseAttributes();
if (login_it->attribute("id").second == "login__token")
{
token = login_it->attribute("value").second; // login token
tagname_token = login_it->attribute("name").second;
}
}
} }
if (token.empty()) if (token.empty())
@ -448,7 +391,14 @@ int Website::Login(const std::string& email, const std::string& password)
// Handle two step authorization // Handle two step authorization
if (std::string(redirect_url).find("two_step") != std::string::npos) if (std::string(redirect_url).find("two_step") != std::string::npos)
{ {
std::string security_code, tagname_two_step_send, tagname_two_step_auth_letter_1, tagname_two_step_auth_letter_2, tagname_two_step_auth_letter_3, tagname_two_step_auth_letter_4, tagname_two_step_token, token_two_step; std::string security_code;
std::string tagname_two_step_send = "second_step_authentication[send]";
std::string tagname_two_step_auth_letter_1 = "second_step_authentication[token][letter_1]";
std::string tagname_two_step_auth_letter_2 = "second_step_authentication[token][letter_2]";
std::string tagname_two_step_auth_letter_3 = "second_step_authentication[token][letter_3]";
std::string tagname_two_step_auth_letter_4 = "second_step_authentication[token][letter_4]";
std::string tagname_two_step_token;
std::string token_two_step;
std::string two_step_html = this->getResponse(redirect_url); std::string two_step_html = this->getResponse(redirect_url);
redirect_url = NULL; redirect_url = NULL;
@ -460,39 +410,14 @@ int Website::Login(const std::string& email, const std::string& password)
if (two_step_it->tagName()=="input") if (two_step_it->tagName()=="input")
{ {
two_step_it->parseAttributes(); two_step_it->parseAttributes();
std::string id_two_step = two_step_it->attribute("id").second; if (two_step_it->attribute("id").second == "second_step_authentication__token")
if (id_two_step == "second_step_authentication_token_letter_1")
{
tagname_two_step_auth_letter_1 = two_step_it->attribute("name").second;
}
else if (id_two_step == "second_step_authentication_token_letter_2")
{
tagname_two_step_auth_letter_2 = two_step_it->attribute("name").second;
}
else if (id_two_step == "second_step_authentication_token_letter_3")
{
tagname_two_step_auth_letter_3 = two_step_it->attribute("name").second;
}
else if (id_two_step == "second_step_authentication_token_letter_4")
{
tagname_two_step_auth_letter_4 = two_step_it->attribute("name").second;
}
else if (id_two_step == "second_step_authentication__token")
{ {
token_two_step = two_step_it->attribute("value").second; // two step token token_two_step = two_step_it->attribute("value").second; // two step token
tagname_two_step_token = two_step_it->attribute("name").second; tagname_two_step_token = two_step_it->attribute("name").second;
} }
} }
else if (two_step_it->tagName()=="button")
{
two_step_it->parseAttributes();
std::string id_two_step = two_step_it->attribute("id").second;
if (id_two_step == "second_step_authentication_send")
{
tagname_two_step_send = two_step_it->attribute("name").second;
}
}
} }
std::cerr << "Security code: "; std::cerr << "Security code: ";
std::getline(std::cin,security_code); std::getline(std::cin,security_code);
if (security_code.size() != 4) if (security_code.size() != 4)
@ -500,6 +425,7 @@ int Website::Login(const std::string& email, const std::string& password)
std::cerr << "Security code must be 4 characters long" << std::endl; std::cerr << "Security code must be 4 characters long" << std::endl;
exit(1); exit(1);
} }
postdata = (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_1.c_str(), tagname_two_step_auth_letter_1.size()) + "=" + security_code[0] postdata = (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_1.c_str(), tagname_two_step_auth_letter_1.size()) + "=" + security_code[0]
+ "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_2.c_str(), tagname_two_step_auth_letter_2.size()) + "=" + security_code[1] + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_2.c_str(), tagname_two_step_auth_letter_2.size()) + "=" + security_code[1]
+ "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_3.c_str(), tagname_two_step_auth_letter_3.size()) + "=" + security_code[2] + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_3.c_str(), tagname_two_step_auth_letter_3.size()) + "=" + security_code[2]
@ -523,6 +449,31 @@ int Website::Login(const std::string& email, const std::string& password)
curl_easy_getinfo(curlhandle, CURLINFO_REDIRECT_URL, &redirect_url); curl_easy_getinfo(curlhandle, CURLINFO_REDIRECT_URL, &redirect_url);
} }
if (!std::string(redirect_url).empty())
{
long response_code;
do
{
curl_easy_setopt(curlhandle, CURLOPT_URL, redirect_url);
result = curl_easy_perform(curlhandle);
memory.str(std::string());
result = curl_easy_getinfo(curlhandle, CURLINFO_RESPONSE_CODE, &response_code);
if ((response_code / 100) == 3)
curl_easy_getinfo(curlhandle, CURLINFO_REDIRECT_URL, &redirect_url);
std::string redir_url = std::string(redirect_url);
boost::regex re(".*code=(.*?)([\?&].*|$)", boost::regex_constants::icase);
boost::match_results<std::string::const_iterator> what;
if (boost::regex_search(redir_url, what, re))
{
auth_code = what[1];
if (!auth_code.empty())
break;
}
} while (result == CURLE_OK && (response_code / 100) == 3);
}
curl_easy_setopt(curlhandle, CURLOPT_URL, redirect_url); curl_easy_setopt(curlhandle, CURLOPT_URL, redirect_url);
curl_easy_setopt(curlhandle, CURLOPT_HTTPGET, 1); curl_easy_setopt(curlhandle, CURLOPT_HTTPGET, 1);
curl_easy_setopt(curlhandle, CURLOPT_MAXREDIRS, -1); curl_easy_setopt(curlhandle, CURLOPT_MAXREDIRS, -1);
@ -544,11 +495,41 @@ int Website::Login(const std::string& email, const std::string& password)
res = 1; // Login was successful res = 1; // Login was successful
} }
if (auth_code.empty())
res = 0;
if (res == 1) if (res == 1)
{ {
curl_easy_setopt(curlhandle, CURLOPT_COOKIELIST, "FLUSH"); // Write all known cookies to the file specified by CURLOPT_COOKIEJAR std::string token_url = "https://auth.gog.com/token?client_id=" + Globals::galaxyConf.getClientId()
+ "&client_secret=" + Globals::galaxyConf.getClientSecret()
+ "&grant_type=authorization_code&code=" + auth_code
+ "&redirect_uri=" + (std::string)curl_easy_escape(curlhandle, Globals::galaxyConf.getRedirectUri().c_str(), Globals::galaxyConf.getRedirectUri().size());
std::string json = this->getResponse(token_url);
if (json.empty())
res = 0;
else
{
Json::Value token_json;
Json::Reader *jsonparser = new Json::Reader;
if (jsonparser->parse(json, token_json))
{
Globals::galaxyConf.setJSON(token_json);
res = 1;
}
else
{
std::cerr << "Failed to parse json" << std::endl << json << std::endl;
std::cerr << jsonparser->getFormattedErrorMessages() << std::endl;
res = 0;
}
delete jsonparser;
}
} }
if (res == 1)
curl_easy_setopt(curlhandle, CURLOPT_COOKIELIST, "FLUSH"); // Write all known cookies to the file specified by CURLOPT_COOKIEJAR
return res; return res;
} }
@ -677,7 +658,7 @@ std::vector<wishlistItem> Website::getWishlistItems()
item.platform |= GlobalConstants::PLATFORM_LINUX; item.platform |= GlobalConstants::PLATFORM_LINUX;
// Skip if platform doesn't match // Skip if platform doesn't match
if (config.bPlatformDetection && !(item.platform & config.iInstallerPlatform)) if (Globals::globalConfig.bPlatformDetection && !(item.platform & Globals::globalConfig.dlConf.iInstallerPlatform))
continue; continue;
} }
@ -741,8 +722,3 @@ std::vector<wishlistItem> Website::getWishlistItems()
return wishlistItems; return wishlistItems;
} }
void Website::setConfig(Config &conf)
{
this->config = conf;
}