1+ // SPDX-License-Identifier: GPL-3.0-only
2+ /*
3+ * Freesm Launcher - Minecraft Launcher
4+ * Copyright (C) 2025 so5iso4ka <so5iso4ka@icloud.com>
5+ *
6+ * This program is free software: you can redistribute it and/or modify
7+ * it under the terms of the GNU General Public License as published by
8+ * the Free Software Foundation, version 3.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License
16+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17+ */
18+
19+ #include < QJsonDocument>
20+ #include < QJsonObject>
21+ #include < QJsonParseError>
22+ #include < QUrl>
23+
24+ #include " Application.h"
25+ #include " QObjectPtr.h"
26+ #include " net/Download.h"
27+ #include " net/RawHeaderProxy.h"
28+
29+ #include " CustomGetSkinStep.h"
30+
31+ namespace {
32+ QString getSkinUrlFromProperties (const QString& encodedProps)
33+ {
34+ QJsonParseError err;
35+ QJsonDocument doc = QJsonDocument::fromJson (QByteArray::fromBase64 (encodedProps.toUtf8 ()), &err);
36+ if (err.error != QJsonParseError::NoError) {
37+ return {};
38+ }
39+
40+ auto json = doc.object ();
41+ auto url = json[" textures" ].toObject ()[" SKIN" ].toObject ()[" url" ].toString ();
42+
43+ return { url };
44+ }
45+ } // namespace
46+
47+ CustomGetSkinStep::CustomGetSkinStep (AccountData* data) : GetSkinStep(data) {}
48+
49+ CustomGetSkinStep::~CustomGetSkinStep () = default ;
50+
51+ void CustomGetSkinStep::perform ()
52+ {
53+ const QUrl url (m_data->authUrl + QString (" /sessionserver/session/minecraft/profile/%1" ).arg (m_data->minecraftProfile .id ));
54+
55+ m_response = std::make_shared<QByteArray>();
56+ m_request = Net::Download::makeByteArray (url, m_response);
57+
58+ const auto headerProxy =
59+ new Net::RawHeaderProxy (QList<Net::HeaderPair>{ { " Content-Type" , " application/json" }, { " Accept" , " application/json" } });
60+ m_request->addHeaderProxy (headerProxy);
61+ // RawHeaderProxy::addHeaderProxy takes ownership of the proxy, so no cleanup is required
62+
63+ m_task = makeShared<NetJob>(" CustomGetSkinStep" , APPLICATION->network ());
64+ m_task->setAskRetry (false );
65+ m_task->addNetAction (m_request);
66+
67+ connect (m_task.get (), &Task::finished, this , &CustomGetSkinStep::onPropertiesFetchDone);
68+
69+ m_task->start ();
70+ }
71+
72+ QString CustomGetSkinStep::describe ()
73+ {
74+ return tr (" Getting custom account skin" );
75+ }
76+
77+ void CustomGetSkinStep::onPropertiesFetchDone ()
78+ {
79+ if (m_request->error () != QNetworkReply::NoError) {
80+ qWarning () << " Reply error:" << m_request->error ();
81+ emit finished (AccountTaskState::STATE_WORKING, tr (" Failed to get skin for account: %1" ).arg (m_request->errorString ()));
82+ return ;
83+ }
84+
85+ QJsonParseError err;
86+ auto jsonResponse = QJsonDocument::fromJson (*m_response, &err);
87+ if (err.error != QJsonParseError::NoError) {
88+ emit finished (AccountTaskState::STATE_WORKING, tr (" Failed to get skin for account: %1" ).arg (err.error ));
89+ return ;
90+ }
91+
92+ auto props = jsonResponse[" properties" ].toArray ();
93+
94+ for (auto prop : props) {
95+ auto obj = prop.toObject ();
96+ if (obj[" name" ].toString () == " textures" ) {
97+ QString url = getSkinUrlFromProperties (obj[" value" ].toString ());
98+ if (url.isEmpty ()) {
99+ break ;
100+ }
101+ m_data->minecraftProfile .skin .url = url;
102+ return GetSkinStep::perform ();
103+ }
104+ }
105+
106+ emit finished (AccountTaskState::STATE_WORKING, tr (" Failed to get skin for account: %1" ).arg (err.error ));
107+ }
0 commit comments