diff options
Diffstat (limited to 'submodules/battleNetApi/src/token.cpp')
| -rw-r--r-- | submodules/battleNetApi/src/token.cpp | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/submodules/battleNetApi/src/token.cpp b/submodules/battleNetApi/src/token.cpp new file mode 100644 index 0000000..89f0d2a --- /dev/null +++ b/submodules/battleNetApi/src/token.cpp @@ -0,0 +1,105 @@ +#include "token.hpp" +#include <charconv> +#include <chrono> +#include <curl/curl.h> +#include <format> +#include <fstream> +#include <iostream> +#include <regex> +#include <system_error> + +Token::Token(const std::string &iTokenFilePath, const std::string &iUsername, const std::string &iPassword) + : mFilePath{iTokenFilePath}, mUsername{iUsername}, mPassword{iPassword}, mToken{getValidToken()} {} + +std::string Token::toString() const { return mToken; } + +std::string Token::getValidToken() const { + refreshToken(); + std::ifstream aTokenFile{mFilePath}; + std::string aLine; + std::getline(aTokenFile, aLine); + const std::string kToken{aLine.substr(0, aLine.find(" "))}; + return kToken; +} + +void Token::refreshToken() const { + std::ifstream aTokenFile{mFilePath}; + std::string aLine; + std::getline(aTokenFile, aLine); + if (aLine.empty()) { + writeNewTokenToFile(); + } else { + const std::string kTimeLimitText{aLine.substr(aLine.find(" ") + 1)}; + long int aTimeLimit; + const auto kConversionStatus{ + std::from_chars(kTimeLimitText.data(), kTimeLimitText.data() + kTimeLimitText.size(), aTimeLimit)}; + if (kConversionStatus.ec == std::errc{}) { + const bool kTokenExpired{ + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now().time_since_epoch()) + .count() >= aTimeLimit}; + if (kTokenExpired) { + writeNewTokenToFile(); + } + } + } +} + +void Token::writeNewTokenToFile() const { + const std::string kToken{getValidTokenWithApi()}; + if (not kToken.empty()) { + writeTokenToFile(kToken); + } +} + +void Token::writeTokenToFile(const std::string &iToken) const { + std::ofstream aTokenFile{mFilePath}; + std::chrono::seconds aExpiresAt{getCurrentTime() + std::chrono::hours(24)}; + aTokenFile << iToken << " " << aExpiresAt.count() << '\n'; +} + +std::string Token::getValidTokenWithApi() const { + CURL *aCurl{curl_easy_init()}; + std::string aTokenInfo; + if (aCurl) { + curl_easy_setopt(aCurl, CURLOPT_URL, "https://oauth.battle.net/token"); + curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(aCurl, CURLOPT_WRITEFUNCTION, curlCallbackStringOutput); + curl_easy_setopt(aCurl, CURLOPT_WRITEDATA, &aTokenInfo); + curl_easy_setopt(aCurl, CURLOPT_USERPWD, std::format("{}:{}", mUsername, mPassword).data()); + curl_easy_setopt(aCurl, CURLOPT_POSTFIELDS, "grant_type=client_credentials"); + CURLcode result_code{curl_easy_perform(aCurl)}; + curl_easy_cleanup(aCurl); + if (result_code != CURLE_OK) { + std::cerr << "ERROR: token request failed" << std::endl; + std::exit(EXIT_FAILURE); + } + } + + return extractTokenWithRegex(aTokenInfo, "\"access_token\":\"(\\w+)\""); +} + +std::chrono::seconds Token::getCurrentTime() const { + return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::utc_clock::now().time_since_epoch()); +} + +std::string Token::extractTokenWithRegex(const std::string &iText, const std::string &iRegex) const { + const std::regex kRegex{iRegex}; + std::smatch aMatches; + if (std::regex_search(iText, aMatches, kRegex)) { + return aMatches[1].str(); + } + std::cerr << std::format("Unable to retrieve token with regex '{}'", iRegex); + return ""; +} + +size_t Token::curlCallbackStringOutput(void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData) { + size_t newLength{iSize * iNmemb}; + try { + iUserData->append(static_cast<char *>(iPtr), newLength); + } catch (std::bad_alloc &e) { + // handle memory problem + return 0; + } + return newLength; +} |
