2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2014, 2017, 2022-2023 Vladimir Golovnev <glassez@yandex.ru>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
31 #include <type_traits>
35 #include <QElapsedTimer>
37 #include <QHostAddress>
40 #include <QRegularExpression>
42 #include <QTranslator>
45 #include "base/applicationcomponent.h"
46 #include "base/global.h"
47 #include "base/http/irequesthandler.h"
48 #include "base/http/responsebuilder.h"
49 #include "base/http/types.h"
50 #include "base/path.h"
51 #include "base/utils/net.h"
52 #include "base/utils/thread.h"
53 #include "base/utils/version.h"
54 #include "api/isessionmanager.h"
56 inline const Utils::Version
<3, 2> API_VERSION
{2, 10, 1};
62 class FreeDiskSpaceChecker
;
65 class WebSession final
: public ApplicationComponent
<QObject
>, public ISession
68 explicit WebSession(const QString
&sid
, IApplication
*app
);
70 QString
id() const override
;
72 bool hasExpired(qint64 seconds
) const;
73 void updateTimestamp();
76 T
*registerAPIController(const QString
&scope
)
78 static_assert(std::is_base_of_v
<APIController
, T
>, "Class should be derived from APIController.");
79 auto *controller
= new T(app(), this);
80 m_apiControllers
[scope
] = controller
;
84 APIController
*getAPIController(const QString
&scope
) const;
88 QElapsedTimer m_timer
; // timestamp
89 QMap
<QString
, APIController
*> m_apiControllers
;
92 class WebApplication final
: public ApplicationComponent
<QObject
>
93 , public Http::IRequestHandler
, public ISessionManager
94 , private Http::ResponseBuilder
97 Q_DISABLE_COPY_MOVE(WebApplication
)
100 explicit WebApplication(IApplication
*app
, QObject
*parent
= nullptr);
101 ~WebApplication() override
;
103 Http::Response
processRequest(const Http::Request
&request
, const Http::Environment
&env
) override
;
105 const Http::Request
&request() const;
106 const Http::Environment
&env() const;
108 void setUsername(const QString
&username
);
109 void setPasswordHash(const QByteArray
&passwordHash
);
112 QString
clientId() const override
;
113 WebSession
*session() override
;
114 void sessionStart() override
;
115 void sessionEnd() override
;
117 void doProcessRequest();
120 void declarePublicAPI(const QString
&apiPath
);
122 void sendFile(const Path
&path
);
123 void sendWebUIFile();
125 void translateDocument(QString
&data
) const;
127 // Session management
128 QString
generateSid() const;
129 void sessionInitialize();
131 bool isPublicAPI(const QString
&scope
, const QString
&action
) const;
133 bool isCrossSiteRequest(const Http::Request
&request
) const;
134 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
"setPreferences"_s
}, Http::METHOD_POST
},
155 {{u
"app"_s
, u
"shutdown"_s
}, Http::METHOD_POST
},
156 {{u
"auth"_s
, u
"login"_s
}, Http::METHOD_POST
},
157 {{u
"auth"_s
, u
"logout"_s
}, Http::METHOD_POST
},
158 {{u
"rss"_s
, u
"addFeed"_s
}, Http::METHOD_POST
},
159 {{u
"rss"_s
, u
"setFeedURL"_s
}, Http::METHOD_POST
},
160 {{u
"rss"_s
, u
"addFolder"_s
}, Http::METHOD_POST
},
161 {{u
"rss"_s
, u
"markAsRead"_s
}, Http::METHOD_POST
},
162 {{u
"rss"_s
, u
"moveItem"_s
}, Http::METHOD_POST
},
163 {{u
"rss"_s
, u
"refreshItem"_s
}, Http::METHOD_POST
},
164 {{u
"rss"_s
, u
"removeItem"_s
}, Http::METHOD_POST
},
165 {{u
"rss"_s
, u
"removeRule"_s
}, Http::METHOD_POST
},
166 {{u
"rss"_s
, u
"renameRule"_s
}, Http::METHOD_POST
},
167 {{u
"rss"_s
, u
"setRule"_s
}, Http::METHOD_POST
},
168 {{u
"search"_s
, u
"delete"_s
}, Http::METHOD_POST
},
169 {{u
"search"_s
, u
"enablePlugin"_s
}, Http::METHOD_POST
},
170 {{u
"search"_s
, u
"installPlugin"_s
}, Http::METHOD_POST
},
171 {{u
"search"_s
, u
"start"_s
}, Http::METHOD_POST
},
172 {{u
"search"_s
, u
"stop"_s
}, Http::METHOD_POST
},
173 {{u
"search"_s
, u
"uninstallPlugin"_s
}, Http::METHOD_POST
},
174 {{u
"search"_s
, u
"updatePlugins"_s
}, Http::METHOD_POST
},
175 {{u
"torrents"_s
, u
"add"_s
}, Http::METHOD_POST
},
176 {{u
"torrents"_s
, u
"addPeers"_s
}, Http::METHOD_POST
},
177 {{u
"torrents"_s
, u
"addTags"_s
}, Http::METHOD_POST
},
178 {{u
"torrents"_s
, u
"addTrackers"_s
}, Http::METHOD_POST
},
179 {{u
"torrents"_s
, u
"bottomPrio"_s
}, Http::METHOD_POST
},
180 {{u
"torrents"_s
, u
"createCategory"_s
}, Http::METHOD_POST
},
181 {{u
"torrents"_s
, u
"createTags"_s
}, Http::METHOD_POST
},
182 {{u
"torrents"_s
, u
"decreasePrio"_s
}, Http::METHOD_POST
},
183 {{u
"torrents"_s
, u
"delete"_s
}, Http::METHOD_POST
},
184 {{u
"torrents"_s
, u
"deleteTags"_s
}, Http::METHOD_POST
},
185 {{u
"torrents"_s
, u
"editCategory"_s
}, Http::METHOD_POST
},
186 {{u
"torrents"_s
, u
"editTracker"_s
}, Http::METHOD_POST
},
187 {{u
"torrents"_s
, u
"filePrio"_s
}, Http::METHOD_POST
},
188 {{u
"torrents"_s
, u
"increasePrio"_s
}, Http::METHOD_POST
},
189 {{u
"torrents"_s
, u
"pause"_s
}, Http::METHOD_POST
},
190 {{u
"torrents"_s
, u
"reannounce"_s
}, Http::METHOD_POST
},
191 {{u
"torrents"_s
, u
"recheck"_s
}, Http::METHOD_POST
},
192 {{u
"torrents"_s
, u
"removeCategories"_s
}, Http::METHOD_POST
},
193 {{u
"torrents"_s
, u
"removeTags"_s
}, Http::METHOD_POST
},
194 {{u
"torrents"_s
, u
"removeTrackers"_s
}, Http::METHOD_POST
},
195 {{u
"torrents"_s
, u
"rename"_s
}, Http::METHOD_POST
},
196 {{u
"torrents"_s
, u
"renameFile"_s
}, Http::METHOD_POST
},
197 {{u
"torrents"_s
, u
"renameFolder"_s
}, Http::METHOD_POST
},
198 {{u
"torrents"_s
, u
"resume"_s
}, Http::METHOD_POST
},
199 {{u
"torrents"_s
, u
"setAutoManagement"_s
}, Http::METHOD_POST
},
200 {{u
"torrents"_s
, u
"setCategory"_s
}, Http::METHOD_POST
},
201 {{u
"torrents"_s
, u
"setDownloadLimit"_s
}, Http::METHOD_POST
},
202 {{u
"torrents"_s
, u
"setDownloadPath"_s
}, Http::METHOD_POST
},
203 {{u
"torrents"_s
, u
"setForceStart"_s
}, Http::METHOD_POST
},
204 {{u
"torrents"_s
, u
"setLocation"_s
}, Http::METHOD_POST
},
205 {{u
"torrents"_s
, u
"setSavePath"_s
}, Http::METHOD_POST
},
206 {{u
"torrents"_s
, u
"setShareLimits"_s
}, Http::METHOD_POST
},
207 {{u
"torrents"_s
, u
"setSuperSeeding"_s
}, Http::METHOD_POST
},
208 {{u
"torrents"_s
, u
"setUploadLimit"_s
}, Http::METHOD_POST
},
209 {{u
"torrents"_s
, u
"toggleFirstLastPiecePrio"_s
}, Http::METHOD_POST
},
210 {{u
"torrents"_s
, u
"toggleSequentialDownload"_s
}, Http::METHOD_POST
},
211 {{u
"torrents"_s
, u
"topPrio"_s
}, Http::METHOD_POST
},
212 {{u
"transfer"_s
, u
"banPeers"_s
}, Http::METHOD_POST
},
213 {{u
"transfer"_s
, u
"setDownloadLimit"_s
}, Http::METHOD_POST
},
214 {{u
"transfer"_s
, u
"setSpeedLimitsMode"_s
}, Http::METHOD_POST
},
215 {{u
"transfer"_s
, u
"setUploadLimit"_s
}, Http::METHOD_POST
},
216 {{u
"transfer"_s
, u
"toggleSpeedLimitsMode"_s
}, Http::METHOD_POST
},
218 bool m_isAltUIUsed
= false;
221 struct TranslatedFile
225 QDateTime lastModified
;
227 QHash
<Path
, TranslatedFile
> m_translatedFiles
;
228 QString m_currentLocale
;
229 QTranslator m_translator
;
230 bool m_translationFileLoaded
= false;
232 AuthController
*m_authController
= nullptr;
233 bool m_isLocalAuthEnabled
= false;
234 bool m_isAuthSubnetWhitelistEnabled
= false;
235 QVector
<Utils::Net::Subnet
> m_authSubnetWhitelist
;
236 int m_sessionTimeout
= 0;
237 QString m_sessionCookieName
;
240 QStringList m_domainList
;
241 bool m_isCSRFProtectionEnabled
= true;
242 bool m_isSecureCookieEnabled
= true;
243 bool m_isHostHeaderValidationEnabled
= true;
244 bool m_isHttpsEnabled
= false;
247 bool m_isReverseProxySupportEnabled
= false;
248 QVector
<Utils::Net::Subnet
> m_trustedReverseProxyList
;
249 QHostAddress m_clientAddress
;
251 QVector
<Http::Header
> m_prebuiltHeaders
;
253 Utils::Thread::UniquePtr m_workerThread
;
254 FreeDiskSpaceChecker
*m_freeDiskSpaceChecker
= nullptr;
255 QTimer
*m_freeDiskSpaceCheckingTimer
= nullptr;