diff --git a/src/commands/prod.ros.rockstargames.com/UGC/CopyJob.cpp b/src/commands/prod.ros.rockstargames.com/UGC/CopyJob.cpp index 1fa3866..bc557f3 100644 --- a/src/commands/prod.ros.rockstargames.com/UGC/CopyJob.cpp +++ b/src/commands/prod.ros.rockstargames.com/UGC/CopyJob.cpp @@ -36,12 +36,11 @@ class CopyJob : command return false; } - inline bool GetJobMetaData(string_view content_id, string& result) + inline bool GetJobMetaData(string_view content_id, string& result, string& lang) { - string data = command::get(hash()("querycontent"))->execute({ "GetLatestVersionByContentId", content_id.data() }); + string data = command::get(hash()("querycontent"))->execute({ "1", content_id.data()}); pugi::xml_document doc; int f0 = 0, f1 = 0; - string lang; if (doc.load_string(data.c_str())) { @@ -134,11 +133,11 @@ class CopyJob : command string title, description, type, image; vector tags; - string metadata; + string metadata, lang; if (GetJobDetails(content_id, title, description, type, tags, image)) { - if (!GetJobMetaData(content_id, metadata)) + if (!GetJobMetaData(content_id, metadata, lang)) { return "Couldn't download job metadata"; } @@ -161,9 +160,28 @@ class CopyJob : command datajson["mission"] = meta_json["mission"]; datajson["version"] = 2; + auto dataJsonDump = datajson.dump(); + string temp = format("ticket={}&contentType=gta5mission¶msJson=", m_ros->url_encode(TICKET)); - temp += m_ros->ex_url_encode(format(R"({{"ContentName":"{}","DataJson":"{}","Description":"{}","Publish":true,"Language":"{}","TagCsv":"{}"}})", title, replaceAll(datajson.dump(), "\"", "\\\""), description, "en", "")); + std::string tagsSep = std::accumulate( + std::begin(tags), + std::end(tags), + std::string(), + [](const std::string& accumulator, const std::string& element) { + return accumulator.empty() ? element : accumulator + "," + element; + } + ); + + nlohmann::json obj; + obj["ContentName"] = title; + obj["DataJson"] = m_ros->ex_url_encode(replaceAll(datajson.dump(), "\"", "\\\"")); + obj["Description"] = description; + obj["Publish"] = "true"; + obj["Language"] = lang.empty() ? "en" : lang; + obj["TagCsv"] = tagsSep; + + temp += obj.dump(); temp += "&data="; for (auto& i : data_len) diff --git a/src/ros_crypt.cpp b/src/ros_crypt.cpp index 1f11f14..b81f85f 100644 --- a/src/ros_crypt.cpp +++ b/src/ros_crypt.cpp @@ -205,28 +205,31 @@ string ROSCrypt::url_encode(const string& value) string ROSCrypt::ex_url_encode(const string& value) { - ostringstream escaped; + std::ostringstream escaped; escaped.fill('0'); - escaped << hex; + escaped << std::hex; - for (string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) - { - string::value_type c = (*i); - if (((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '_' || c == '.' || c == '~') || c == '+') - { - escaped << c; + for (char c : value) { + if (static_cast(c) <= 127) { + // Encode non-alphanumeric characters except '-' and '_' + if (std::isalnum(c) || c == '-' || c == '_') { + escaped << c; + } + else { + escaped << '%' << std::setw(2) << int(static_cast(c)); + } } - else if (c == ' ') - { - escaped << '+'; - } - else - { - escaped << uppercase << '%' << setw(2) << ((int)(uint8_t)c) << setw(0); + else { + // Handle non-ASCII characters properly by encoding as UTF-8 + std::stringstream utf8Encoded; + utf8Encoded << std::hex << std::setw(2) << std::setfill('0') << static_cast(static_cast(c)); + for (size_t i = 0; i < utf8Encoded.str().size(); ++i) { + escaped << '%' << utf8Encoded.str()[i]; + } } } - return string(escaped.str().c_str()); + return escaped.str(); } string ROSCrypt::BuildPostString(const map& fields)