diff --git a/src/core/libraries/np/np_web_api2/np_web_api2.cpp b/src/core/libraries/np/np_web_api2/np_web_api2.cpp index 110a5e79c..ba9d1c43d 100644 --- a/src/core/libraries/np/np_web_api2/np_web_api2.cpp +++ b/src/core/libraries/np/np_web_api2/np_web_api2.cpp @@ -17,9 +17,15 @@ s32 PS4_SYSV_ABI sceNpWebApi2AbortRequest(s64 request_id) { return ORBIS_OK; } -s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader() { - LOG_ERROR(Lib_NpWebApi2, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceNpWebApi2AddHttpRequestHeader(s64 request_id, const char* field_name, + const char* field_value) { + if (!field_name || !field_value) { + LOG_ERROR(Lib_NpWebApi2, "Invalid parameters"); + return ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT; + } + LOG_INFO(Lib_NpWebApi2, "called, request_id = {:#x}, field_name = {}, field_value = {}", + request_id, field_name, field_value); + return addHttpRequestHeader(request_id, field_name, field_value); } s32 PS4_SYSV_ABI sceNpWebApi2AddMultipartPart() { @@ -134,7 +140,8 @@ s32 PS4_SYSV_ABI sceNpWebApi2Initialize(s32 lib_http_ctx_id, u64 pool_size) { LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id, pool_size); - s32 ctx_id = createLibraryContext(lib_http_ctx_id, pool_size, nullptr); + // Uses a sceLncUtilGetAppStatus check to enable debug mode. For now, default to normal. + s32 ctx_id = createLibraryContext(lib_http_ctx_id, 1, pool_size, nullptr); if (ctx_id > 0) { LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); } @@ -145,7 +152,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2InitializeForPresence(s32 lib_http_ctx_id, u64 pool LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}", lib_http_ctx_id, pool_size); - s32 ctx_id = createLibraryContext(lib_http_ctx_id, pool_size, nullptr); + s32 ctx_id = createLibraryContext(lib_http_ctx_id, 3, pool_size, nullptr); if (ctx_id > 0) { LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); } @@ -165,7 +172,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize(const OrbisNpWebApi2IntInitializeArgs LOG_DEBUG(Lib_NpWebApi2, "called, lib_http_ctx_id = {:#x}, pool_size = {:#x}, name = {}", args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)"); - s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, args->pool_size, args->name); + s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name); if (ctx_id > 0) { LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); } @@ -184,7 +191,7 @@ s32 PS4_SYSV_ABI sceNpWebApi2IntInitialize2(const OrbisNpWebApi2IntInitialize2Ar args->lib_http_ctx_id, args->pool_size, args->name ? args->name : "(null)", args->push_config_group); - s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, args->pool_size, args->name); + s32 ctx_id = createLibraryContext(args->lib_http_ctx_id, 2, args->pool_size, args->name); if (ctx_id > 0) { LOG_INFO(Lib_NpWebApi2, "created lib_ctx_id = {:#x}", ctx_id); } diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp b/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp index a67a4d2f6..8e6b37b2a 100644 --- a/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp +++ b/src/core/libraries/np/np_web_api2/np_web_api2_context.cpp @@ -73,15 +73,16 @@ s32 UserContext::Initialize() { const char* name = this->parent_ctx->GetName(); char sw_version[8]; - memset(sw_version, 0, sizeof(sw_version)); + std::memset(sw_version, 0, sizeof(sw_version)); Libraries::Np::NpCommon::sceNpGetSdkVersion(sw_version); char user_agent_buf[0x40]; - memset(user_agent_buf, 0, sizeof(user_agent_buf)); + std::memset(user_agent_buf, 0, sizeof(user_agent_buf)); if (name) { - snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s (%s)", sw_version, name); + std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s (%s)", sw_version, + name); } else { - snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s", sw_version); + std::snprintf(user_agent_buf, sizeof(user_agent_buf), "NpWebApi2/%s", sw_version); } this->user_agent = std::string{user_agent_buf}; @@ -131,4 +132,64 @@ s32 UserContext::CreateRequest(const char* api_group, const char* path, const ch return ORBIS_OK; } +Request* UserContext::GetRequest(s64 request_id) { + this->Lock(); + if (!this->requests.contains(request_id)) { + return nullptr; + } + + Request* request = this->requests[request_id]; + request->AddUser(); + this->Unlock(); + return request; +} + +bool IsInternalHeader(const char* header_name) { + std::string lower_name{header_name}; + std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(), + [](char c) { return std::tolower(c); }); + // These headers are all skipped if the request's library context + // was not created through the IntInitialize functions. + return lower_name.compare("content-type") == 0 || lower_name.compare("user-agent") == 0 || + lower_name.compare("authorization") == 0 || + lower_name.compare("x-psn-np-title-id") == 0 || + lower_name.compare("x-psn-np-title-token") == 0 || + lower_name.compare("x-psn-request-id") == 0 || + lower_name.compare("x-psn-sdk-ver") == 0 || + lower_name.compare("x-psn-debug-settings") == 0 || lower_name.compare("npdebug") == 0 || + lower_name.compare("x-psn-trc-check-notification-enabled") == 0 || + lower_name.compare("x-psn-webtrace-enabled") == 0 || + lower_name.compare("x-psn-npwebapi2-context") == 0 || + lower_name.compare("x-psn-fake-rate-limit-enabled") == 0 || + lower_name.compare("x-psn-fake-rate-limit-targets") == 0; +} + +s32 Request::AddHttpRequestHeader(const char* field_name, const char* field_value) { + std::string name{field_name}; + std::string value{field_value}; + + if (!this->parent_ctx->IsInternal() && IsInternalHeader(field_name)) { + LOG_WARNING(Lib_NpWebApi2, "This is not an internal context, skipping field name {}", + field_name); + return ORBIS_OK; + } + + this->Lock(); + HttpRequestHeader* new_header = new HttpRequestHeader(nullptr, nullptr, name, value); + if (!this->http_headers) { + // Creates a new blank head node + this->http_headers = new HttpRequestHeader(nullptr, nullptr, "", ""); + this->http_headers->prev = this->http_headers; + this->http_headers->next = this->http_headers; + } + + new_header->prev = this->http_headers->prev; + new_header->next = this->http_headers; + this->http_headers->prev->next = new_header; + this->http_headers->prev = new_header; + http_header_count++; + this->Unlock(); + return ORBIS_OK; +} + }; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_context.h b/src/core/libraries/np/np_web_api2/np_web_api2_context.h index 2f5ced338..ec38d67ac 100644 --- a/src/core/libraries/np/np_web_api2/np_web_api2_context.h +++ b/src/core/libraries/np/np_web_api2/np_web_api2_context.h @@ -18,10 +18,12 @@ class Request; class LibraryContext { public: - LibraryContext(s32 ctx_id, s32 http_ctx_id, u64 pool_size) - : id(ctx_id), http_ctx_id(http_ctx_id), pool_size(pool_size) {} - LibraryContext(s32 ctx_id, s32 http_ctx_id, u64 pool_size, const char* name) - : id(ctx_id), http_ctx_id(http_ctx_id), pool_size(pool_size), name(name) {} + LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size) + : id(ctx_id), type(static_cast(type)), http_ctx_id(http_ctx_id), + pool_size(pool_size) {} + LibraryContext(s32 ctx_id, s32 type, s32 http_ctx_id, u64 pool_size, const char* name) + : id(ctx_id), type(static_cast(type)), http_ctx_id(http_ctx_id), + pool_size(pool_size), name(name) {} void Lock() { lock.lock(); @@ -57,6 +59,22 @@ public: return name.empty() ? nullptr : name.data(); } + bool IsDebug() { + return type == LibraryContextType::Debug; + } + + bool IsNormal() { + return type == LibraryContextType::Normal; + } + + bool IsInternal() { + return type == LibraryContextType::Internal; + } + + bool IsPresence() { + return type == LibraryContextType::Presence; + } + s32 CreateUserContext(Libraries::UserService::OrbisUserServiceUserId user_id); UserContext* GetUserContext(s32 user_ctx_id); UserContext* GetUserContextByUserId(Libraries::UserService::OrbisUserServiceUserId user_id); @@ -70,6 +88,12 @@ private: s32 id{}; s32 http_ctx_id{}; s32 user_count{}; + enum LibraryContextType : s32 { + Debug = 0, + Normal = 1, + Internal = 2, + Presence = 3, + } type; u64 pool_size{}; std::recursive_mutex lock{}; std::string name{}; @@ -93,15 +117,15 @@ public: } void AddUser() { - parent_ctx->Lock(); + Lock(); user_count++; - parent_ctx->Unlock(); + Unlock(); } void RemoveUser() { - parent_ctx->Lock(); + Lock(); user_count--; - parent_ctx->Unlock(); + Unlock(); } s32 GetId() { @@ -117,6 +141,7 @@ public: s32 CreateRequest(const char* api_group, const char* path, const char* method, const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart, Request** request); + Request* GetRequest(s64 request_id); private: s32 id{}; @@ -140,11 +165,35 @@ public: : parent_ctx(parent), id(request_id), api_group(api_group), path(path), method(method), multipart_supported(multipart), content_type(content_parameter->content_type) {} + void Lock() { + parent_ctx->Lock(); + } + + void Unlock() { + parent_ctx->Unlock(); + } + + void AddUser() { + Lock(); + user_count++; + Unlock(); + } + + void RemoveUser() { + Lock(); + user_count--; + Unlock(); + } + s64 GetId() { return id; } - s32 Initialize(); + bool HasSent() { + return sent; + } + + s32 AddHttpRequestHeader(const char* field_name, const char* field_value); private: s64 id{}; @@ -156,6 +205,15 @@ private: std::string method{}; std::string content_type{}; bool multipart_supported{}; + bool sent{}; + + struct HttpRequestHeader { + HttpRequestHeader* prev; + HttpRequestHeader* next; + std::string field_name; + std::string field_value; + }* http_headers{}; + s32 http_header_count{}; }; }; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp b/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp index cbd265194..27c1352cc 100644 --- a/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp +++ b/src/core/libraries/np/np_web_api2/np_web_api2_internal.cpp @@ -17,7 +17,7 @@ std::recursive_mutex g_mutex{}; s32 g_current_lib_context_id{}; std::map g_lib_contexts{}; -s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name) { +s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name) { std::scoped_lock lk{g_mutex}; if (g_lib_contexts.size() >= 0x8000) { @@ -34,10 +34,10 @@ s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name) { if (!name) { g_lib_contexts[g_current_lib_context_id] = - new LibraryContext(g_current_lib_context_id, http_ctx_id, pool_size); + new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size); } else { g_lib_contexts[g_current_lib_context_id] = - new LibraryContext(g_current_lib_context_id, http_ctx_id, pool_size, name); + new LibraryContext(g_current_lib_context_id, type, http_ctx_id, pool_size, name); } return g_current_lib_context_id; } @@ -146,4 +146,43 @@ s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, cons return result; } +s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value) { + s32 lib_ctx_id = static_cast(request_id >> 0x30); + s32 user_ctx_id = static_cast(request_id >> 0x20); + LibraryContext* lib_ctx = getLibraryContext(lib_ctx_id); + if (!lib_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No library context for request id {:#x}", request_id); + return ORBIS_NP_WEBAPI2_ERROR_LIB_CONTEXT_NOT_FOUND; + } + + UserContext* user_ctx = lib_ctx->GetUserContext(user_ctx_id); + if (!user_ctx) { + LOG_ERROR(Lib_NpWebApi2, "No user context for request id {:#x}", request_id); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_USER_CONTEXT_NOT_FOUND; + } + + Request* request = user_ctx->GetRequest(request_id); + if (!request) { + LOG_ERROR(Lib_NpWebApi2, "No request with id {:#x}", request_id); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_REQUEST_NOT_FOUND; + } + + if (request->HasSent()) { + LOG_ERROR(Lib_NpWebApi2, "Request is already sent"); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return ORBIS_NP_WEBAPI2_ERROR_AFTER_SEND; + } + + s32 result = request->AddHttpRequestHeader(field_name, field_value); + request->RemoveUser(); + user_ctx->RemoveUser(); + lib_ctx->RemoveUser(); + return result; +} + }; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file diff --git a/src/core/libraries/np/np_web_api2/np_web_api2_internal.h b/src/core/libraries/np/np_web_api2/np_web_api2_internal.h index 0274288d6..147816dbb 100644 --- a/src/core/libraries/np/np_web_api2/np_web_api2_internal.h +++ b/src/core/libraries/np/np_web_api2/np_web_api2_internal.h @@ -5,10 +5,11 @@ namespace Libraries::Np::NpWebApi2 { -s32 createLibraryContext(s32 http_ctx_id, u64 pool_size, const char* name); +s32 createLibraryContext(s32 http_ctx_id, s32 type, u64 pool_size, const char* name); s32 getMemoryPoolStats(s32 lib_ctx_id, OrbisNpWebApi2MemoryPoolStats* stats); s32 createUserContext(s32 lib_ctx_id, Libraries::UserService::OrbisUserServiceUserId user_id); s32 createRequest(s32 user_ctx_id, const char* api_group, const char* path, const char* method, const OrbisNpWebApi2ContentParameter* content_parameter, bool multipart, s64* request_id); +s32 addHttpRequestHeader(s64 request_id, const char* field_name, const char* field_value); }; // namespace Libraries::Np::NpWebApi2 \ No newline at end of file