2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2014-2024 Vladimir Golovnev <glassez@yandex.ru>
4 * Copyright (C) 2024 Radu Carpa <radu.carpa@cern.ch>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * In addition, as a special exception, the copyright holders give permission to
21 * link this program with the OpenSSL project's "OpenSSL" library (or with
22 * modified versions of it that use the same license as the "OpenSSL" library),
23 * and distribute the linked executables. You must obey the GNU General Public
24 * License in all respects for all of the code used other than "OpenSSL". If you
25 * modify file(s), you may extend this exception to your version of the file(s),
26 * but you are not obligated to do so. If you do not wish to do so, delete this
27 * exception statement from your version.
32 #include <type_traits>
36 #include <QElapsedTimer>
38 #include <QHostAddress>
42 #include <QRegularExpression>
44 #include <QTranslator>
46 #include "base/applicationcomponent.h"
47 #include "base/global.h"
48 #include "base/http/irequesthandler.h"
49 #include "base/http/responsebuilder.h"
50 #include "base/http/types.h"
51 #include "base/path.h"
52 #include "base/utils/net.h"
53 #include "base/utils/thread.h"
54 #include "base/utils/version.h"
55 #include "api/isessionmanager.h"
57 inline const Utils::Version
<3, 2> API_VERSION
{2, 11, 3};
63 class FreeDiskSpaceChecker
;
68 class TorrentCreationManager
;
71 class WebSession final
: public ApplicationComponent
<QObject
>, public ISession
74 explicit WebSession(const QString
&sid
, IApplication
*app
);
76 QString
id() const override
;
78 bool hasExpired(qint64 seconds
) const;
79 void updateTimestamp();
81 void registerAPIController(const QString
&scope
, APIController
*controller
);
82 APIController
*getAPIController(const QString
&scope
) const;
86 QElapsedTimer m_timer
; // timestamp
87 QMap
<QString
, APIController
*> m_apiControllers
;
90 class WebApplication final
: public ApplicationComponent
<QObject
>
91 , public Http::IRequestHandler
, public ISessionManager
92 , private Http::ResponseBuilder
95 Q_DISABLE_COPY_MOVE(WebApplication
)
98 explicit WebApplication(IApplication
*app
, QObject
*parent
= nullptr);
99 ~WebApplication() override
;
101 Http::Response
processRequest(const Http::Request
&request
, const Http::Environment
&env
) override
;
103 const Http::Request
&request() const;
104 const Http::Environment
&env() const;
106 void setUsername(const QString
&username
);
107 void setPasswordHash(const QByteArray
&passwordHash
);
110 QString
clientId() const override
;
111 WebSession
*session() override
;
112 void sessionStart() override
;
113 void sessionEnd() override
;
115 void doProcessRequest();
118 void declarePublicAPI(const QString
&apiPath
);
120 void sendFile(const Path
&path
);
121 void sendWebUIFile();
123 void translateDocument(QString
&data
) const;
125 // Session management
126 QString
generateSid() const;
127 void sessionInitialize();
129 bool isPublicAPI(const QString
&scope
, const QString
&action
) const;
131 bool isOriginTrustworthy() const;
132 bool isCrossSiteRequest(const Http::Request
&request
) const;
133 bool validateHostHeader(const QStringList
&domains
) const;
136 QHostAddress
resolveClientAddress() const;
139 QHash
<QString
, WebSession
*> m_sessions
;
142 WebSession
*m_currentSession
= nullptr;
143 Http::Request m_request
;
144 Http::Environment m_env
;
145 QHash
<QString
, QString
> m_params
;
146 const QString m_cacheID
;
148 const QRegularExpression m_apiPathPattern
{u
"^/api/v2/(?<scope>[A-Za-z_][A-Za-z_0-9]*)/(?<action>[A-Za-z_][A-Za-z_0-9]*)$"_s
};
150 QSet
<QString
> m_publicAPIs
;
151 const QHash
<std::pair
<QString
, QString
>, QString
> m_allowedMethod
=
153 // <<controller name, action name>, HTTP method>
154 {{u
"app"_s
, u
"sendTestEmail"_s
}, Http::METHOD_POST
},
155 {{u
"app"_s
, u
"setCookies"_s
}, Http::METHOD_POST
},
156 {{u
"app"_s
, u
"setPreferences"_s
}, Http::METHOD_POST
},
157 {{u
"app"_s
, u
"shutdown"_s
}, Http::METHOD_POST
},
158 {{u
"auth"_s
, u
"login"_s
}, Http::METHOD_POST
},
159 {{u
"auth"_s
, u
"logout"_s
}, Http::METHOD_POST
},
160 {{u
"rss"_s
, u
"addFeed"_s
}, Http::METHOD_POST
},
161 {{u
"rss"_s
, u
"addFolder"_s
}, Http::METHOD_POST
},
162 {{u
"rss"_s
, u
"markAsRead"_s
}, Http::METHOD_POST
},
163 {{u
"rss"_s
, u
"moveItem"_s
}, Http::METHOD_POST
},
164 {{u
"rss"_s
, u
"refreshItem"_s
}, Http::METHOD_POST
},
165 {{u
"rss"_s
, u
"removeItem"_s
}, Http::METHOD_POST
},
166 {{u
"rss"_s
, u
"removeRule"_s
}, Http::METHOD_POST
},
167 {{u
"rss"_s
, u
"renameRule"_s
}, Http::METHOD_POST
},
168 {{u
"rss"_s
, u
"setFeedURL"_s
}, Http::METHOD_POST
},
169 {{u
"rss"_s
, u
"setRule"_s
}, Http::METHOD_POST
},
170 {{u
"search"_s
, u
"delete"_s
}, Http::METHOD_POST
},
171 {{u
"search"_s
, u
"enablePlugin"_s
}, Http::METHOD_POST
},
172 {{u
"search"_s
, u
"installPlugin"_s
}, Http::METHOD_POST
},
173 {{u
"search"_s
, u
"start"_s
}, Http::METHOD_POST
},
174 {{u
"search"_s
, u
"stop"_s
}, Http::METHOD_POST
},
175 {{u
"search"_s
, u
"uninstallPlugin"_s
}, Http::METHOD_POST
},
176 {{u
"search"_s
, u
"updatePlugins"_s
}, Http::METHOD_POST
},
177 {{u
"torrentcreator"_s
, u
"addTask"_s
}, Http::METHOD_POST
},
178 {{u
"torrentcreator"_s
, u
"deleteTask"_s
}, Http::METHOD_POST
},
179 {{u
"torrents"_s
, u
"add"_s
}, Http::METHOD_POST
},
180 {{u
"torrents"_s
, u
"addPeers"_s
}, Http::METHOD_POST
},
181 {{u
"torrents"_s
, u
"addTags"_s
}, Http::METHOD_POST
},
182 {{u
"torrents"_s
, u
"addTrackers"_s
}, Http::METHOD_POST
},
183 {{u
"torrents"_s
, u
"addWebSeeds"_s
}, Http::METHOD_POST
},
184 {{u
"transfer"_s
, u
"banPeers"_s
}, Http::METHOD_POST
},
185 {{u
"torrents"_s
, u
"bottomPrio"_s
}, Http::METHOD_POST
},
186 {{u
"torrents"_s
, u
"createCategory"_s
}, Http::METHOD_POST
},
187 {{u
"torrents"_s
, u
"createTags"_s
}, Http::METHOD_POST
},
188 {{u
"torrents"_s
, u
"decreasePrio"_s
}, Http::METHOD_POST
},
189 {{u
"torrents"_s
, u
"delete"_s
}, Http::METHOD_POST
},
190 {{u
"torrents"_s
, u
"deleteTags"_s
}, Http::METHOD_POST
},
191 {{u
"torrents"_s
, u
"editCategory"_s
}, Http::METHOD_POST
},
192 {{u
"torrents"_s
, u
"editTracker"_s
}, Http::METHOD_POST
},
193 {{u
"torrents"_s
, u
"editWebSeed"_s
}, Http::METHOD_POST
},
194 {{u
"torrents"_s
, u
"filePrio"_s
}, Http::METHOD_POST
},
195 {{u
"torrents"_s
, u
"increasePrio"_s
}, Http::METHOD_POST
},
196 {{u
"torrents"_s
, u
"reannounce"_s
}, Http::METHOD_POST
},
197 {{u
"torrents"_s
, u
"recheck"_s
}, Http::METHOD_POST
},
198 {{u
"torrents"_s
, u
"removeCategories"_s
}, Http::METHOD_POST
},
199 {{u
"torrents"_s
, u
"removeTags"_s
}, Http::METHOD_POST
},
200 {{u
"torrents"_s
, u
"removeTrackers"_s
}, Http::METHOD_POST
},
201 {{u
"torrents"_s
, u
"removeWebSeeds"_s
}, Http::METHOD_POST
},
202 {{u
"torrents"_s
, u
"rename"_s
}, Http::METHOD_POST
},
203 {{u
"torrents"_s
, u
"renameFile"_s
}, Http::METHOD_POST
},
204 {{u
"torrents"_s
, u
"renameFolder"_s
}, Http::METHOD_POST
},
205 {{u
"torrents"_s
, u
"setAutoManagement"_s
}, Http::METHOD_POST
},
206 {{u
"torrents"_s
, u
"setCategory"_s
}, Http::METHOD_POST
},
207 {{u
"torrents"_s
, u
"setDownloadLimit"_s
}, Http::METHOD_POST
},
208 {{u
"torrents"_s
, u
"setDownloadPath"_s
}, Http::METHOD_POST
},
209 {{u
"torrents"_s
, u
"setForceStart"_s
}, Http::METHOD_POST
},
210 {{u
"torrents"_s
, u
"setLocation"_s
}, Http::METHOD_POST
},
211 {{u
"torrents"_s
, u
"setSavePath"_s
}, Http::METHOD_POST
},
212 {{u
"torrents"_s
, u
"setShareLimits"_s
}, Http::METHOD_POST
},
213 {{u
"torrents"_s
, u
"setSSLParameters"_s
}, Http::METHOD_POST
},
214 {{u
"torrents"_s
, u
"setSuperSeeding"_s
}, Http::METHOD_POST
},
215 {{u
"torrents"_s
, u
"setUploadLimit"_s
}, Http::METHOD_POST
},
216 {{u
"transfer"_s
, u
"setDownloadLimit"_s
}, Http::METHOD_POST
},
217 {{u
"transfer"_s
, u
"setSpeedLimitsMode"_s
}, Http::METHOD_POST
},
218 {{u
"transfer"_s
, u
"setUploadLimit"_s
}, Http::METHOD_POST
},
219 {{u
"torrents"_s
, u
"start"_s
}, Http::METHOD_POST
},
220 {{u
"torrents"_s
, u
"stop"_s
}, Http::METHOD_POST
},
221 {{u
"torrents"_s
, u
"toggleFirstLastPiecePrio"_s
}, Http::METHOD_POST
},
222 {{u
"torrents"_s
, u
"toggleSequentialDownload"_s
}, Http::METHOD_POST
},
223 {{u
"transfer"_s
, u
"toggleSpeedLimitsMode"_s
}, Http::METHOD_POST
},
224 {{u
"torrents"_s
, u
"topPrio"_s
}, Http::METHOD_POST
},
226 bool m_isAltUIUsed
= false;
229 struct TranslatedFile
233 QDateTime lastModified
;
235 QHash
<Path
, TranslatedFile
> m_translatedFiles
;
236 QString m_currentLocale
;
237 QTranslator m_translator
;
238 bool m_translationFileLoaded
= false;
240 AuthController
*m_authController
= nullptr;
241 bool m_isLocalAuthEnabled
= false;
242 bool m_isAuthSubnetWhitelistEnabled
= false;
243 QList
<Utils::Net::Subnet
> m_authSubnetWhitelist
;
244 int m_sessionTimeout
= 0;
245 QString m_sessionCookieName
;
248 QStringList m_domainList
;
249 bool m_isCSRFProtectionEnabled
= true;
250 bool m_isSecureCookieEnabled
= true;
251 bool m_isHostHeaderValidationEnabled
= true;
252 bool m_isHttpsEnabled
= false;
255 bool m_isReverseProxySupportEnabled
= false;
256 QList
<Utils::Net::Subnet
> m_trustedReverseProxyList
;
257 QHostAddress m_clientAddress
;
259 QList
<Http::Header
> m_prebuiltHeaders
;
261 Utils::Thread::UniquePtr m_workerThread
;
262 FreeDiskSpaceChecker
*m_freeDiskSpaceChecker
= nullptr;
263 QTimer
*m_freeDiskSpaceCheckingTimer
= nullptr;
264 BitTorrent::TorrentCreationManager
*m_torrentCreationManager
= nullptr;