/*
* Bittorrent Client using Qt and libtorrent .
* Copyright ( C ) 2014 Vladimir Golovnev < glassez @ yandex . ru >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* In addition , as a special exception , the copyright holders give permission to
* link this program with the OpenSSL project ' s " OpenSSL " library ( or with
* modified versions of it that use the same license as the " OpenSSL " library ) ,
* and distribute the linked executables . You must obey the GNU General Public
* License in all respects for all of the code used other than " OpenSSL " . If you
* modify file ( s ) , you may extend this exception to your version of the file ( s ) ,
* but you are not obligated to do so . If you do not wish to do so , delete this
* exception statement from your version .
*/
# include "webapplication.h"
# include <queue>
# include <vector>
# include <QCoreApplication>
# include <QCryptographicHash>
# include <QDebug>
# include <QRegularExpression>
# include <QTimer>
# include "base/bittorrent/session.h"
# include "base/bittorrent/torrenthandle.h"
# include "base/bittorrent/torrentinfo.h"
# include "base/bittorrent/trackerentry.h"
# include "base/iconprovider.h"
# include "base/logger.h"
# include "base/net/downloadmanager.h"
# include "base/preferences.h"
# include "base/tristatebool.h"
# include "base/utils/fs.h"
# include "base/utils/misc.h"
# include "base/utils/string.h"
# include "btjson.h"
# include "jsonutils.h"
# include "prefjson.h"
# include "websessiondata.h"
static const int API_VERSION = 16 ;
static const int API_VERSION_MIN = 15 ;
const QString WWW_FOLDER = " :/www/public/ " ;
const QString PRIVATE_FOLDER = " :/www/private/ " ;
const QString DEFAULT_SCOPE = " public " ;
const QString SCOPE_IMAGES = " images " ;
const QString SCOPE_THEME = " theme " ;
const QString DEFAULT_ACTION = " index " ;
const QString WEBUI_ACTION = " webui " ;
const QString VERSION_INFO = " version " ;
const QString MAX_AGE_MONTH = " public, max-age=2592000 " ;
# define ADD_ACTION(scope, action) actions[#scope][#action] = &WebApplication::action_##scope##_##action
QMap < QString , QMap < QString , WebApplication : : Action > > WebApplication : : initializeActions ( )
{
QMap < QString , QMap < QString , WebApplication : : Action > > actions ;
ADD_ACTION ( public , webui ) ;
ADD_ACTION ( public , index ) ;
ADD_ACTION ( public , login ) ;
ADD_ACTION ( public , logout ) ;
ADD_ACTION ( public , theme ) ;
ADD_ACTION ( public , images ) ;
ADD_ACTION ( query , torrents ) ;
ADD_ACTION ( query , preferences ) ;
ADD_ACTION ( query , transferInfo ) ;
ADD_ACTION ( query , propertiesGeneral ) ;
ADD_ACTION ( query , propertiesTrackers ) ;
ADD_ACTION ( query , propertiesWebSeeds ) ;
ADD_ACTION ( query , propertiesFiles ) ;
ADD_ACTION ( query , getLog ) ;
ADD_ACTION ( query , getPeerLog ) ;
ADD_ACTION ( query , getPieceHashes ) ;
ADD_ACTION ( query , getPieceStates ) ;
ADD_ACTION ( sync , maindata ) ;
ADD_ACTION ( sync , torrent_peers ) ;
ADD_ACTION ( command , shutdown ) ;
ADD_ACTION ( command , download ) ;
ADD_ACTION ( command , upload ) ;
ADD_ACTION ( command , addTrackers ) ;
ADD_ACTION ( command , resumeAll ) ;
ADD_ACTION ( command , pauseAll ) ;
ADD_ACTION ( command , resume ) ;
ADD_ACTION ( command , pause ) ;
ADD_ACTION ( command , setPreferences ) ;
ADD_ACTION ( command , setFilePrio ) ;
ADD_ACTION ( command , getGlobalUpLimit ) ;
ADD_ACTION ( command , getGlobalDlLimit ) ;
ADD_ACTION ( command , setGlobalUpLimit ) ;
ADD_ACTION ( command , setGlobalDlLimit ) ;
ADD_ACTION ( command , getTorrentsUpLimit ) ;
ADD_ACTION ( command , getTorrentsDlLimit ) ;
ADD_ACTION ( command , setTorrentsUpLimit ) ;
ADD_ACTION ( command , setTorrentsDlLimit ) ;
ADD_ACTION ( command , alternativeSpeedLimitsEnabled ) ;
ADD_ACTION ( command , toggleAlternativeSpeedLimits ) ;
ADD_ACTION ( command , toggleSequentialDownload ) ;
ADD_ACTION ( command , toggleFirstLastPiecePrio ) ;
ADD_ACTION ( command , setSuperSeeding ) ;
ADD_ACTION ( command , setForceStart ) ;
ADD_ACTION ( command , delete ) ;
ADD_ACTION ( command , deletePerm ) ;
ADD_ACTION ( command , increasePrio ) ;
ADD_ACTION ( command , decreasePrio ) ;
ADD_ACTION ( command , topPrio ) ;
ADD_ACTION ( command , bottomPrio ) ;
ADD_ACTION ( command , setLocation ) ;
ADD_ACTION ( command , rename ) ;
ADD_ACTION ( command , setAutoTMM ) ;
ADD_ACTION ( command , recheck ) ;
ADD_ACTION ( command , setCategory ) ;
ADD_ACTION ( command , addCategory ) ;
ADD_ACTION ( command , removeCategories ) ;
ADD_ACTION ( command , getSavePath ) ;
ADD_ACTION ( version , api ) ;
ADD_ACTION ( version , api_min ) ;
ADD_ACTION ( version , qbittorrent ) ;
return actions ;
}
namespace
{
# define CHECK_URI(ARGS_NUM) \
if ( args_ . size ( ) ! = ARGS_NUM ) { \
status ( 404 , " Not Found " ) ; \
return ; \
}
# define CHECK_PARAMETERS(PARAMETERS) \
QStringList parameters ; \
parameters < < PARAMETERS ; \
if ( parameters . size ( ) ! = request ( ) . posts . size ( ) ) { \
status ( 400 , " Bad Request " ) ; \
return ; \
} \
foreach ( QString key , request ( ) . posts . keys ( ) ) { \
if ( ! parameters . contains ( key , Qt : : CaseInsensitive ) ) { \
status ( 400 , " Bad Request " ) ; \
return ; \
} \
}
bool parseBool ( const QString & string , const bool defaultValue )
{
if ( defaultValue )
return ( string . compare ( " false " , Qt : : CaseInsensitive ) = = 0 ) ? false : true ;
return ( string . compare ( " true " , Qt : : CaseInsensitive ) = = 0 ) ? true : false ;
}
TriStateBool parseTristatebool ( const QString & string )
{
if ( string . compare ( " true " , Qt : : CaseInsensitive ) = = 0 )
return TriStateBool : : True ;
if ( string . compare ( " false " , Qt : : CaseInsensitive ) = = 0 )
return TriStateBool : : False ;
return TriStateBool : : Undefined ;
}
}
void WebApplication : : action_public_index ( )
{
QString path ;
if ( ! args_ . isEmpty ( ) ) {
if ( args_ . back ( ) = = " favicon.ico " )
path = " :/icons/skin/qbittorrent16.png " ;
else
path = WWW_FOLDER + args_ . join ( " / " ) ;
}
printFile ( path ) ;
}
void WebApplication : : action_public_webui ( )
{
if ( ! sessionActive ( ) )
printFile ( PRIVATE_FOLDER + " login.html " ) ;
else
printFile ( PRIVATE_FOLDER + " index.html " ) ;
}
void WebApplication : : action_public_login ( )
{
if ( sessionActive ( ) ) {
print ( QByteArray ( " Ok. " ) , Http : : CONTENT_TYPE_TXT ) ;
return ;
}
const Preferences * const pref = Preferences : : instance ( ) ;
QCryptographicHash md5 ( QCryptographicHash : : Md5 ) ;
md5 . addData ( request ( ) . posts [ " password " ] . toLocal8Bit ( ) ) ;
QString pass = md5 . result ( ) . toHex ( ) ;
bool equalUser = Utils : : String : : slowEquals ( request ( ) . posts [ " username " ] . toUtf8 ( ) , pref - > getWebUiUsername ( ) . toUtf8 ( ) ) ;
bool equalPass = Utils : : String : : slowEquals ( pass . toUtf8 ( ) , pref - > getWebUiPassword ( ) . toUtf8 ( ) ) ;
if ( equalUser & & equalPass ) {
sessionStart ( ) ;
print ( QByteArray ( " Ok. " ) , Http : : CONTENT_TYPE_TXT ) ;
}
else {
QString addr = env ( ) . clientAddress . toString ( ) ;
increaseFailedAttempts ( ) ;
qDebug ( " client IP: %s (%d failed attempts) " , qUtf8Printable ( addr ) , failedAttempts ( ) ) ;
print ( QByteArray ( " Fails. " ) , Http : : CONTENT_TYPE_TXT ) ;
}
}
void WebApplication : : action_public_logout ( )
{
CHECK_URI ( 0 ) ;
sessionEnd ( ) ;
}
void WebApplication : : action_public_theme ( )
{
if ( args_ . size ( ) ! = 1 ) {
status ( 404 , " Not Found " ) ;
return ;
}
QString url = IconProvider : : instance ( ) - > getIconPath ( args_ . front ( ) ) ;
qDebug ( ) < < Q_FUNC_INFO < < " There icon: " < < url ;
printFile ( url ) ;
header ( Http : : HEADER_CACHE_CONTROL , MAX_AGE_MONTH ) ;
}
void WebApplication : : action_public_images ( )
{
const QString path = " :/icons/ " + args_ . join ( " / " ) ;
printFile ( path ) ;
header ( Http : : HEADER_CACHE_CONTROL , MAX_AGE_MONTH ) ;
}
// GET params:
// - filter (string): all, downloading, seeding, completed, paused, resumed, active, inactive
// - category (string): torrent category for filtering by it (empty string means "uncategorized"; no "category" param presented means "any category")
// - sort (string): name of column for sorting by its value
// - reverse (bool): enable reverse sorting
// - limit (int): set limit number of torrents returned (if greater than 0, otherwise - unlimited)
// - offset (int): set offset (if less than 0 - offset from end)
void WebApplication : : action_query_torrents ( )
{
CHECK_URI ( 0 ) ;
const QStringMap & gets = request ( ) . gets ;
print ( btjson : : getTorrents (
gets [ " filter " ] , gets [ " category " ] , gets [ " sort " ] , parseBool ( gets [ " reverse " ] , false ) ,
gets [ " limit " ] . toInt ( ) , gets [ " offset " ] . toInt ( ) )
, Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_preferences ( )
{
CHECK_URI ( 0 ) ;
print ( prefjson : : getPreferences ( ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_transferInfo ( )
{
CHECK_URI ( 0 ) ;
print ( btjson : : getTransferInfo ( ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_propertiesGeneral ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getPropertiesForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_propertiesTrackers ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getTrackersForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_propertiesWebSeeds ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getWebSeedsForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_propertiesFiles ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getFilesForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
// GET params:
// - normal (bool): include normal messages (default true)
// - info (bool): include info messages (default true)
// - warning (bool): include warning messages (default true)
// - critical (bool): include critical messages (default true)
// - last_known_id (int): exclude messages with id <= 'last_known_id' (default -1)
void WebApplication : : action_query_getLog ( )
{
CHECK_URI ( 0 ) ;
const bool isNormal = parseBool ( request ( ) . gets [ " normal " ] , true ) ;
const bool isInfo = parseBool ( request ( ) . gets [ " info " ] , true ) ;
const bool isWarning = parseBool ( request ( ) . gets [ " warning " ] , true ) ;
const bool isCritical = parseBool ( request ( ) . gets [ " critical " ] , true ) ;
bool ok = false ;
int lastKnownId = request ( ) . gets [ " last_known_id " ] . toInt ( & ok ) ;
if ( ! ok )
lastKnownId = - 1 ;
print ( btjson : : getLog ( isNormal , isInfo , isWarning , isCritical , lastKnownId ) , Http : : CONTENT_TYPE_JSON ) ;
}
// GET params:
// - last_known_id (int): exclude messages with id <= 'last_known_id' (default -1)
void WebApplication : : action_query_getPeerLog ( )
{
CHECK_URI ( 0 ) ;
int lastKnownId ;
bool ok ;
lastKnownId = request ( ) . gets [ " last_known_id " ] . toInt ( & ok ) ;
if ( ! ok )
lastKnownId = - 1 ;
print ( btjson : : getPeerLog ( lastKnownId ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_getPieceHashes ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getPieceHashesForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_query_getPieceStates ( )
{
CHECK_URI ( 1 ) ;
print ( btjson : : getPieceStatesForTorrent ( args_ . front ( ) ) , Http : : CONTENT_TYPE_JSON ) ;
}
// GET param:
// - rid (int): last response id
void WebApplication : : action_sync_maindata ( )
{
CHECK_URI ( 0 ) ;
print ( btjson : : getSyncMainData ( request ( ) . gets [ " rid " ] . toInt ( ) ,
session ( ) - > syncMainDataLastResponse ,
session ( ) - > syncMainDataLastAcceptedResponse ) , Http : : CONTENT_TYPE_JSON ) ;
}
// GET param:
// - hash (string): torrent hash
// - rid (int): last response id
void WebApplication : : action_sync_torrent_peers ( )
{
CHECK_URI ( 0 ) ;
print ( btjson : : getSyncTorrentPeersData ( request ( ) . gets [ " rid " ] . toInt ( ) ,
request ( ) . gets [ " hash " ] ,
session ( ) - > syncTorrentPeersLastResponse ,
session ( ) - > syncTorrentPeersLastAcceptedResponse ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_version_api ( )
{
CHECK_URI ( 0 ) ;
print ( QString : : number ( API_VERSION ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_version_api_min ( )
{
CHECK_URI ( 0 ) ;
print ( QString : : number ( API_VERSION_MIN ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_version_qbittorrent ( )
{
CHECK_URI ( 0 ) ;
print ( QString ( QBT_VERSION ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_command_shutdown ( )
{
qDebug ( ) < < " Shutdown request from Web UI " ;
CHECK_URI ( 0 ) ;
// Special case handling for shutdown, we
// need to reply to the Web UI before
// actually shutting down.
QTimer : : singleShot ( 100 , qApp , SLOT ( quit ( ) ) ) ;
}
void WebApplication : : action_command_download ( )
{
CHECK_URI ( 0 ) ;
const QString urls = request ( ) . posts . value ( " urls " ) ;
const bool skipChecking = parseBool ( request ( ) . posts . value ( " skip_checking " ) , false ) ;
const bool seqDownload = parseBool ( request ( ) . posts . value ( " sequentialDownload " ) , false ) ;
const bool firstLastPiece = parseBool ( request ( ) . posts . value ( " firstLastPiecePrio " ) , false ) ;
const TriStateBool addPaused = parseTristatebool ( request ( ) . posts . value ( " paused " ) ) ;
const TriStateBool rootFolder = parseTristatebool ( request ( ) . posts . value ( " root_folder " ) ) ;
const QString savepath = request ( ) . posts . value ( " savepath " ) . trimmed ( ) ;
const QString category = request ( ) . posts . value ( " category " ) . trimmed ( ) ;
const QString cookie = request ( ) . posts . value ( " cookie " ) ;
const QString torrentName = request ( ) . posts . value ( " rename " ) . trimmed ( ) ;
const int upLimit = request ( ) . posts . value ( " upLimit " ) . toInt ( ) ;
const int dlLimit = request ( ) . posts . value ( " dlLimit " ) . toInt ( ) ;
QList < QNetworkCookie > cookies ;
if ( ! cookie . isEmpty ( ) ) {
const QStringList cookiesStr = cookie . split ( " ; " ) ;
for ( QString cookieStr : cookiesStr ) {
cookieStr = cookieStr . trimmed ( ) ;
int index = cookieStr . indexOf ( ' = ' ) ;
if ( index > 1 ) {
QByteArray name = cookieStr . left ( index ) . toLatin1 ( ) ;
QByteArray value = cookieStr . right ( cookieStr . length ( ) - index - 1 ) . toLatin1 ( ) ;
cookies + = QNetworkCookie ( name , value ) ;
}
}
}
BitTorrent : : AddTorrentParams params ;
// TODO: Check if destination actually exists
params . skipChecking = skipChecking ;
params . sequential = seqDownload ;
params . firstLastPiecePriority = firstLastPiece ;
params . addPaused = addPaused ;
params . createSubfolder = rootFolder ;
params . savePath = savepath ;
params . category = category ;
params . name = torrentName ;
params . uploadLimit = ( upLimit > 0 ) ? upLimit : - 1 ;
params . downloadLimit = ( dlLimit > 0 ) ? dlLimit : - 1 ;
bool partialSuccess = false ;
for ( QString url : urls . split ( ' \n ' ) ) {
url = url . trimmed ( ) ;
if ( ! url . isEmpty ( ) ) {
Net : : DownloadManager : : instance ( ) - > setCookiesFromUrl ( cookies , QUrl : : fromEncoded ( url . toUtf8 ( ) ) ) ;
partialSuccess | = BitTorrent : : Session : : instance ( ) - > addTorrent ( url , params ) ;
}
}
if ( partialSuccess )
print ( QByteArray ( " Ok. " ) , Http : : CONTENT_TYPE_TXT ) ;
else
print ( QByteArray ( " Fails. " ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_command_upload ( )
{
CHECK_URI ( 0 ) ;
const bool skipChecking = parseBool ( request ( ) . posts . value ( " skip_checking " ) , false ) ;
const bool seqDownload = parseBool ( request ( ) . posts . value ( " sequentialDownload " ) , false ) ;
const bool firstLastPiece = parseBool ( request ( ) . posts . value ( " firstLastPiecePrio " ) , false ) ;
const TriStateBool addPaused = parseTristatebool ( request ( ) . posts . value ( " paused " ) ) ;
const TriStateBool rootFolder = parseTristatebool ( request ( ) . posts . value ( " root_folder " ) ) ;
const QString savepath = request ( ) . posts . value ( " savepath " ) . trimmed ( ) ;
const QString category = request ( ) . posts . value ( " category " ) . trimmed ( ) ;
const QString torrentName = request ( ) . posts . value ( " rename " ) . trimmed ( ) ;
const int upLimit = request ( ) . posts . value ( " upLimit " ) . toInt ( ) ;
const int dlLimit = request ( ) . posts . value ( " dlLimit " ) . toInt ( ) ;
for ( const Http : : UploadedFile & torrent : request ( ) . files ) {
const QString filePath = saveTmpFile ( torrent . data ) ;
if ( filePath . isEmpty ( ) ) {
qWarning ( ) < < " I/O Error: Could not create temporary file " ;
status ( 500 , " Internal Server Error " ) ;
print ( QObject : : tr ( " I/O Error: Could not create temporary file. " ) , Http : : CONTENT_TYPE_TXT ) ;
continue ;
}
const BitTorrent : : TorrentInfo torrentInfo = BitTorrent : : TorrentInfo : : loadFromFile ( filePath ) ;
if ( ! torrentInfo . isValid ( ) ) {
status ( 415 , " Unsupported Media Type " ) ;
print ( QObject : : tr ( " Error: '%1' is not a valid torrent file. \n " ) . arg ( torrent . filename ) , Http : : CONTENT_TYPE_TXT ) ;
}
else {
BitTorrent : : AddTorrentParams params ;
// TODO: Check if destination actually exists
params . skipChecking = skipChecking ;
params . sequential = seqDownload ;
params . firstLastPiecePriority = firstLastPiece ;
params . addPaused = addPaused ;
params . createSubfolder = rootFolder ;
params . savePath = savepath ;
params . category = category ;
params . name = torrentName ;
params . uploadLimit = ( upLimit > 0 ) ? upLimit : - 1 ;
params . downloadLimit = ( dlLimit > 0 ) ? dlLimit : - 1 ;
if ( ! BitTorrent : : Session : : instance ( ) - > addTorrent ( torrentInfo , params ) ) {
status ( 500 , " Internal Server Error " ) ;
print ( QObject : : tr ( " Error: Could not add torrent to session. " ) , Http : : CONTENT_TYPE_TXT ) ;
}
}
// Clean up
Utils : : Fs : : forceRemove ( filePath ) ;
}
}
void WebApplication : : action_command_addTrackers ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " < < " urls " ) ;
QString hash = request ( ) . posts [ " hash " ] ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent ) {
QList < BitTorrent : : TrackerEntry > trackers ;
foreach ( QString url , request ( ) . posts [ " urls " ] . split ( ' \n ' ) ) {
url = url . trimmed ( ) ;
if ( ! url . isEmpty ( ) )
trackers < < url ;
}
torrent - > addTrackers ( trackers ) ;
}
}
void WebApplication : : action_command_resumeAll ( )
{
CHECK_URI ( 0 ) ;
foreach ( BitTorrent : : TorrentHandle * const torrent , BitTorrent : : Session : : instance ( ) - > torrents ( ) )
torrent - > resume ( ) ;
}
void WebApplication : : action_command_pauseAll ( )
{
CHECK_URI ( 0 ) ;
foreach ( BitTorrent : : TorrentHandle * const torrent , BitTorrent : : Session : : instance ( ) - > torrents ( ) )
torrent - > pause ( ) ;
}
void WebApplication : : action_command_resume ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " ) ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( request ( ) . posts [ " hash " ] ) ;
if ( torrent )
torrent - > resume ( ) ;
}
void WebApplication : : action_command_pause ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " ) ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( request ( ) . posts [ " hash " ] ) ;
if ( torrent )
torrent - > pause ( ) ;
}
void WebApplication : : action_command_setPreferences ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " json " ) ;
prefjson : : setPreferences ( request ( ) . posts [ " json " ] ) ;
}
void WebApplication : : action_command_setFilePrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " < < " id " < < " priority " ) ;
QString hash = request ( ) . posts [ " hash " ] ;
int fileID = request ( ) . posts [ " id " ] . toInt ( ) ;
int priority = request ( ) . posts [ " priority " ] . toInt ( ) ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent & & torrent - > hasMetadata ( ) )
torrent - > setFilePriority ( fileID , priority ) ;
}
void WebApplication : : action_command_getGlobalUpLimit ( )
{
CHECK_URI ( 0 ) ;
print ( QByteArray : : number ( BitTorrent : : Session : : instance ( ) - > uploadSpeedLimit ( ) ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_command_getGlobalDlLimit ( )
{
CHECK_URI ( 0 ) ;
print ( QByteArray : : number ( BitTorrent : : Session : : instance ( ) - > downloadSpeedLimit ( ) ) , Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_command_setGlobalUpLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " limit " ) ;
qlonglong limit = request ( ) . posts [ " limit " ] . toLongLong ( ) ;
if ( limit = = 0 ) limit = - 1 ;
BitTorrent : : Session : : instance ( ) - > setUploadSpeedLimit ( limit ) ;
}
void WebApplication : : action_command_setGlobalDlLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " limit " ) ;
qlonglong limit = request ( ) . posts [ " limit " ] . toLongLong ( ) ;
if ( limit = = 0 ) limit = - 1 ;
BitTorrent : : Session : : instance ( ) - > setDownloadSpeedLimit ( limit ) ;
}
void WebApplication : : action_command_getTorrentsUpLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
print ( btjson : : getTorrentsRatesLimits ( hashes , false ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_command_getTorrentsDlLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
print ( btjson : : getTorrentsRatesLimits ( hashes , true ) , Http : : CONTENT_TYPE_JSON ) ;
}
void WebApplication : : action_command_setTorrentsUpLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " limit " ) ;
qlonglong limit = request ( ) . posts [ " limit " ] . toLongLong ( ) ;
if ( limit = = 0 )
limit = - 1 ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > setUploadLimit ( limit ) ;
}
}
void WebApplication : : action_command_setTorrentsDlLimit ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " limit " ) ;
qlonglong limit = request ( ) . posts [ " limit " ] . toLongLong ( ) ;
if ( limit = = 0 )
limit = - 1 ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > setDownloadLimit ( limit ) ;
}
}
void WebApplication : : action_command_toggleAlternativeSpeedLimits ( )
{
CHECK_URI ( 0 ) ;
BitTorrent : : Session * const session = BitTorrent : : Session : : instance ( ) ;
session - > setAltGlobalSpeedLimitEnabled ( ! session - > isAltGlobalSpeedLimitEnabled ( ) ) ;
}
void WebApplication : : action_command_alternativeSpeedLimitsEnabled ( )
{
CHECK_URI ( 0 ) ;
print ( QByteArray : : number ( BitTorrent : : Session : : instance ( ) - > isAltGlobalSpeedLimitEnabled ( ) )
, Http : : CONTENT_TYPE_TXT ) ;
}
void WebApplication : : action_command_toggleSequentialDownload ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > toggleSequentialDownload ( ) ;
}
}
void WebApplication : : action_command_toggleFirstLastPiecePrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > toggleFirstLastPiecePriority ( ) ;
}
}
void WebApplication : : action_command_setSuperSeeding ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " value " ) ;
const bool value = parseBool ( request ( ) . posts [ " value " ] , false ) ;
const QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > setSuperSeeding ( value ) ;
}
}
void WebApplication : : action_command_setForceStart ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " value " ) ;
const bool value = parseBool ( request ( ) . posts [ " value " ] , false ) ;
const QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > resume ( value ) ;
}
}
void WebApplication : : action_command_delete ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes )
BitTorrent : : Session : : instance ( ) - > deleteTorrent ( hash , false ) ;
}
void WebApplication : : action_command_deletePerm ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
foreach ( const QString & hash , hashes )
BitTorrent : : Session : : instance ( ) - > deleteTorrent ( hash , true ) ;
}
void WebApplication : : action_command_increasePrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
if ( ! BitTorrent : : Session : : instance ( ) - > isQueueingSystemEnabled ( ) ) {
status ( 403 , " Torrent queueing must be enabled " ) ;
return ;
}
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
BitTorrent : : Session : : instance ( ) - > increaseTorrentsPriority ( hashes ) ;
}
void WebApplication : : action_command_decreasePrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
if ( ! BitTorrent : : Session : : instance ( ) - > isQueueingSystemEnabled ( ) ) {
status ( 403 , " Torrent queueing must be enabled " ) ;
return ;
}
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
BitTorrent : : Session : : instance ( ) - > decreaseTorrentsPriority ( hashes ) ;
}
void WebApplication : : action_command_topPrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
if ( ! BitTorrent : : Session : : instance ( ) - > isQueueingSystemEnabled ( ) ) {
status ( 403 , " Torrent queueing must be enabled " ) ;
return ;
}
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
BitTorrent : : Session : : instance ( ) - > topTorrentsPriority ( hashes ) ;
}
void WebApplication : : action_command_bottomPrio ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " ) ;
if ( ! BitTorrent : : Session : : instance ( ) - > isQueueingSystemEnabled ( ) ) {
status ( 403 , " Torrent queueing must be enabled " ) ;
return ;
}
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
BitTorrent : : Session : : instance ( ) - > bottomTorrentsPriority ( hashes ) ;
}
void WebApplication : : action_command_setLocation ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " location " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( " | " ) ;
QString newLocation = request ( ) . posts [ " location " ] . trimmed ( ) ;
// check if the location exists
if ( newLocation . isEmpty ( ) | | ! QDir ( newLocation ) . exists ( ) )
return ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent ) {
Logger : : instance ( ) - > addMessage ( tr ( " WebUI Set location: moving \" %1 \" , from \" %2 \" to \" %3 \" " ) . arg ( torrent - > name ( ) ) . arg ( torrent - > savePath ( ) ) . arg ( newLocation ) ) ;
torrent - > move ( Utils : : Fs : : expandPathAbs ( newLocation ) ) ;
}
}
}
void WebApplication : : action_command_rename ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " < < " name " ) ;
QString hash = request ( ) . posts [ " hash " ] ;
QString name = request ( ) . posts [ " name " ] . trimmed ( ) ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent & & ! name . isEmpty ( ) ) {
name . replace ( QRegularExpression ( " \r ? \n | \r " ) , " " ) ;
qDebug ( ) < < " Renaming " < < torrent - > name ( ) < < " to " < < name ;
torrent - > setName ( name ) ;
}
else {
status ( 400 , " Incorrect torrent hash or name " ) ;
}
}
void WebApplication : : action_command_setAutoTMM ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " enable " ) ;
const QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
const bool isEnabled = parseBool ( request ( ) . posts [ " enable " ] , false ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent )
torrent - > setAutoTMMEnabled ( isEnabled ) ;
}
}
void WebApplication : : action_command_recheck ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hash " ) ;
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( request ( ) . posts [ " hash " ] ) ;
if ( torrent )
torrent - > forceRecheck ( ) ;
}
void WebApplication : : action_command_setCategory ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " hashes " < < " category " ) ;
QStringList hashes = request ( ) . posts [ " hashes " ] . split ( ' | ' ) ;
QString category = request ( ) . posts [ " category " ] . trimmed ( ) ;
foreach ( const QString & hash , hashes ) {
BitTorrent : : TorrentHandle * const torrent = BitTorrent : : Session : : instance ( ) - > findTorrent ( hash ) ;
if ( torrent ) {
if ( ! torrent - > setCategory ( category ) ) {
status ( 400 , " Incorrect category name " ) ;
return ;
}
}
}
}
void WebApplication : : action_command_addCategory ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " category " ) ;
QString category = request ( ) . posts [ " category " ] . trimmed ( ) ;
if ( ! BitTorrent : : Session : : isValidCategoryName ( category ) & & ! category . isEmpty ( ) ) {
status ( 400 , tr ( " Incorrect category name " ) ) ;
return ;
}
BitTorrent : : Session : : instance ( ) - > addCategory ( category ) ;
}
void WebApplication : : action_command_removeCategories ( )
{
CHECK_URI ( 0 ) ;
CHECK_PARAMETERS ( " categories " ) ;
QStringList categories = request ( ) . posts [ " categories " ] . split ( ' \n ' ) ;
foreach ( const QString & category , categories )
BitTorrent : : Session : : instance ( ) - > removeCategory ( category ) ;
}
void WebApplication : : action_command_getSavePath ( )
{
CHECK_URI ( 0 ) ;
print ( BitTorrent : : Session : : instance ( ) - > defaultSavePath ( ) ) ;
}
bool WebApplication : : isPublicScope ( )
{
return ( scope_ = = DEFAULT_SCOPE | | scope_ = = VERSION_INFO ) ;
}
void WebApplication : : doProcessRequest ( )
{
scope_ = DEFAULT_SCOPE ;
action_ = DEFAULT_ACTION ;
parsePath ( ) ;
if ( args_ . contains ( " . " ) | | args_ . contains ( " .. " ) ) {
qDebug ( ) < < Q_FUNC_INFO < < " Invalid path: " < < request ( ) . path ;
status ( 404 , " Not Found " ) ;
return ;
}
if ( ! isPublicScope ( ) & & ! sessionActive ( ) ) {
status ( 403 , " Forbidden " ) ;
return ;
}
if ( actions_ . value ( scope_ ) . value ( action_ ) ! = 0 ) {
( this - > * ( actions_ [ scope_ ] [ action_ ] ) ) ( ) ;
}
else {
status ( 404 , " Not Found " ) ;
qDebug ( ) < < Q_FUNC_INFO < < " Resource not found: " < < request ( ) . path ;
}
}
void WebApplication : : parsePath ( )
{
if ( request ( ) . path = = " / " ) action_ = WEBUI_ACTION ;
// check action for requested path
QStringList pathItems = request ( ) . path . split ( ' / ' , QString : : SkipEmptyParts ) ;
if ( ! pathItems . empty ( ) & & actions_ . contains ( pathItems . front ( ) ) ) {
scope_ = pathItems . front ( ) ;
pathItems . pop_front ( ) ;
}
if ( ! pathItems . empty ( ) & & actions_ [ scope_ ] . contains ( pathItems . front ( ) ) ) {
action_ = pathItems . front ( ) ;
pathItems . pop_front ( ) ;
}
args_ = pathItems ;
}
WebApplication : : WebApplication ( QObject * parent )
: AbstractWebApplication ( parent )
{
}
QMap < QString , QMap < QString , WebApplication : : Action > > WebApplication : : actions_ = WebApplication : : initializeActions ( ) ;