diff --git a/CMakeLists.txt b/CMakeLists.txt index dd33ff1..0e490ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,9 @@ set(CROW_FEATURES compression) find_package(Crow) find_package(CURL) +find_package(OpenSSL) + +message("SSL ${OPENSSL_INCLUDE_DIR}") if (NOT CURL_FOUND) message("libcurl is required!") @@ -28,6 +31,7 @@ add_executable(crowsite ${source_files}) target_link_libraries(crowsite BLT) target_link_libraries(crowsite Crow::Crow) target_link_libraries(crowsite ${CURL_LIBRARIES}) +target_link_libraries(crowsite OpenSSL::SSL OpenSSL::Crypto) target_compile_options(crowsite PRIVATE -Wall -Wextra -Wpedantic) if (${ENABLE_ADDRSAN} MATCHES ON) diff --git a/include/crowsite/requests/curl.h b/include/crowsite/requests/curl.h index 0abb4a5..e899838 100644 --- a/include/crowsite/requests/curl.h +++ b/include/crowsite/requests/curl.h @@ -27,13 +27,20 @@ namespace cs request(); static const std::string& getResponse(const std::string& domain); + static std::string getResponseAndClear(const std::string& domain); + static void clearResponse(const std::string& domain); + void setContentHeaderJson(); + void setContentHeaderXForm(); + void setContentHeader(const std::string& header); void setAuthHeader(const std::string& header); void get(const std::string& domain, const std::string& data = ""); void post(const std::string& domain, const std::string& data = ""); + long status(); + ~request(); }; diff --git a/include/crowsite/requests/jellyfin.h b/include/crowsite/requests/jellyfin.h index 457b945..899e5f4 100644 --- a/include/crowsite/requests/jellyfin.h +++ b/include/crowsite/requests/jellyfin.h @@ -6,9 +6,31 @@ #define CROWSITE_JELLYFIN_H #include +#include namespace cs::jellyfin { + enum class auth_response { + AUTHORIZED, + USERNAME, + ERROR + }; + + struct client_data { + struct { + std::string name; + std::string serverId; + std::string Id; + std::string primaryImageTag; + bool hasPassword; + bool hasConfiguredPassword; + bool hasConfiguredEasyPassword; + bool enableAutoLogin; + std::string lastLoginDate; + std::string lastActivityDate; + } user; + std::string accessToken; + }; void setToken(std::string_view token); @@ -18,6 +40,7 @@ namespace cs::jellyfin std::string getUserData(); bool hasUser(std::string_view username); + auth_response authenticateUser(std::string_view username, std::string_view password); } diff --git a/libs/BLT b/libs/BLT index a3f6757..1e8f431 160000 --- a/libs/BLT +++ b/libs/BLT @@ -1 +1 @@ -Subproject commit a3f67571460d23fe819a3a3a572344e7030fba4a +Subproject commit 1e8f431f9eb06582083efde6489b59380f3e19ac diff --git a/src/crowsite/requests/curl.cpp b/src/crowsite/requests/curl.cpp index 81b6d37..205c8b4 100644 --- a/src/crowsite/requests/curl.cpp +++ b/src/crowsite/requests/curl.cpp @@ -58,8 +58,6 @@ namespace cs void request::setAuthHeader(const std::string& header) { - curl_slist_free_all(headers); - headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, ("Authorization: " + header).c_str()); curl_easy_setopt(handler, CURLOPT_HTTPHEADER, headers); @@ -97,4 +95,38 @@ namespace cs { return responses[domain]; } + + void request::clearResponse(const std::string& domain) + { + responses.erase(domain); + } + + std::string request::getResponseAndClear(const std::string& domain) + { + std::string res = getResponse(domain); + clearResponse(domain); + return res; + } + + void request::setContentHeaderJson() + { + setContentHeader("application/json"); + } + + void request::setContentHeaderXForm() + { + setContentHeader("application/x-www-form-urlencoded"); + } + + void request::setContentHeader(const std::string& header) + { + headers = curl_slist_append(headers, ("Content-Type: " + header).c_str()); + } + + long request::status() + { + long code = 0; + curl_easy_getinfo(handler, CURLINFO_RESPONSE_CODE, &code); + return code; + } } \ No newline at end of file diff --git a/src/crowsite/requests/jellyfin.cpp b/src/crowsite/requests/jellyfin.cpp index 661a623..0c1cb47 100644 --- a/src/crowsite/requests/jellyfin.cpp +++ b/src/crowsite/requests/jellyfin.cpp @@ -6,50 +6,106 @@ #include #include #include -#include -struct +namespace cs::jellyfin { - std::string token; - HASHMAP user_ids; -} GLOBALS; - -void cs::jellyfin::setToken(std::string_view token) -{ - GLOBALS.token = token; -} - -void cs::jellyfin::processUserData() -{ - auto data = getUserData(); - - auto json = crow::json::load(data); - for (const auto& user : json){ - auto username = user["Name"].s(); - auto userid = user["Id"].s(); - //BLT_TRACE("Processing %s = %s", username.operator std::string().c_str(), userid.operator std::string().c_str()); - GLOBALS.user_ids[username] = userid; + struct + { + std::string token; + HASHMAP user_ids; + HASHMAP logged_in_users; + } GLOBALS; + + void setToken(std::string_view token) + { + GLOBALS.token = token; } -} - -std::string cs::jellyfin::getUserData() -{ + + void processUserData() + { + auto data = getUserData(); + + auto json = crow::json::load(data); + + for (const auto& user : json) + { + auto username = user["Name"].s(); + auto userid = user["Id"].s(); + //BLT_TRACE("Processing %s = %s", username.operator std::string().c_str(), userid.operator std::string().c_str()); + GLOBALS.user_ids[username] = userid; + } + } + + std::string getUserData() + { #define url "https://media.tpgc.me/Users" + + cs::request request; + request.setContentHeaderJson(); + request.setAuthHeader(generateAuthHeader()); + request.get(url); + + return cs::request::getResponse(url); + } - cs::request request; - request.setAuthHeader(generateAuthHeader()); - request.get(url); + std::string generateAuthHeader() + { + return "MediaBrowser Client=Crowsite, Device=YourMom, Token=" + GLOBALS.token; + } - return cs::request::getResponse(url); -} - -std::string cs::jellyfin::generateAuthHeader() -{ - return "MediaBrowser Client=Crowsite, Device=YourMom, Token=" + GLOBALS.token; -} - -bool cs::jellyfin::hasUser(std::string_view username) -{ - return GLOBALS.user_ids.find(std::string(username)) != GLOBALS.user_ids.end(); -} + bool hasUser(std::string_view username) + { + return GLOBALS.user_ids.find(std::string(username)) != GLOBALS.user_ids.end(); + } + + auth_response authenticateUser(std::string_view username, std::string_view password) + { + if (!hasUser(username)) + processUserData(); + if (!hasUser(username)) + { + BLT_ERROR("User not found!"); + return auth_response::USERNAME; + } + auto l_url = "https://media.tpgc.me/Users/AuthenticateByName"; + + cs::request post; + post.setContentHeaderJson(); + post.setAuthHeader(generateAuthHeader()); + crow::json::wvalue json; + json["Username"] = std::string(username); + json["Pw"] = std::string(password); + post.post(l_url, json.dump()); + + auto response = cs::request::getResponseAndClear(l_url); + + if (post.status() == 200) + { + crow::json::rvalue read = crow::json::load(response); + + const auto& users = read["User"]; + + client_data data; + data.accessToken = read["AccessToken"].s(); + auto& user = data.user; + user.name = users["Name"].s(); + user.serverId = users["ServerId"].s(); + user.Id = users["Id"].s(); + user.primaryImageTag = users["PrimaryImageTag"].s(); + user.hasPassword = users["HasPassword"].b(); + user.hasConfiguredPassword = users["HasConfiguredPassword"].b(); + user.hasConfiguredEasyPassword = users["HasConfiguredEasyPassword"].b(); + user.enableAutoLogin = users["EnableAutoLogin"].b(); + user.lastLoginDate = users["LastLoginDate"].s(); + user.lastActivityDate = users["LastActivityDate"].s(); + + GLOBALS.logged_in_users[std::string(username)] = data; + + return auth_response::AUTHORIZED; + } + + return auth_response::ERROR; + } + +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 29e3eea..9176cbd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,10 +48,14 @@ int main(int argc, const char** argv) blt::arg_parse parser; parser.addArgument(blt::arg_builder("token").build()); + parser.addArgument(blt::arg_builder("user").build()); + parser.addArgument(blt::arg_builder("pass").build()); auto args = parser.parse_args(argc, argv); cs::jellyfin::setToken(blt::arg_parse::get(args["token"])); cs::jellyfin::processUserData(); + cs::jellyfin::authenticateUser(blt::arg_parse::get(args["user"]), blt::arg_parse::get(args["pass"])); + // blt::string::StringBuffer buffer; // std::stringstream stream; // std::string normalString;