From f1334cc7ee68ed069195fbd70ce701d5f7714b7a Mon Sep 17 00:00:00 2001 From: c0re100 Date: Fri, 21 Jul 2017 02:28:42 +0800 Subject: [PATCH] 3.3.14.1 --- .appveyor.yml | 4 +- .travis.yml | 14 +-- Changelog | 16 +++ configure | 4 +- configure.ac | 2 +- dist/mac/Info.plist | 2 +- dist/unix/CMakeLists.txt | 4 +- dist/unix/systemd/qbittorrent-nox@.service.in | 14 +++ .../windows/installer-translations/arabic.nsi | 4 +- .../windows/installer-translations/danish.nsi | 46 ++++---- dist/windows/installer-translations/greek.nsi | 46 ++++---- .../installer-translations/russian.nsi | 12 +- .../installer-translations/turkish.nsi | 2 +- dist/windows/options.nsi | 2 +- src/base/bittorrent/session.cpp | 1 + src/base/http/connection.cpp | 4 +- src/base/http/server.cpp | 4 +- src/base/http/types.h | 24 ++-- src/base/preferences.cpp | 10 ++ src/base/preferences.h | 2 + src/base/utils/misc.cpp | 19 +++- src/gui/optionsdlg.cpp | 3 + src/gui/optionsdlg.ui | 26 ++++- src/webui/abstractwebapplication.cpp | 64 +++++++++-- src/webui/abstractwebapplication.h | 5 + src/webui/prefjson.cpp | 3 + src/webui/webapplication.cpp | 9 +- src/webui/www/public/css/dynamicTable.css | 2 +- src/webui/www/public/preferences_content.html | 3 + src/webui/www/public/scripts/client.js | 49 +++++---- src/webui/www/public/scripts/contextmenu.js | 36 +++--- src/webui/www/public/scripts/download.js | 28 ++--- src/webui/www/public/scripts/dynamicTable.js | 103 ++++++++++-------- src/webui/www/public/scripts/misc.js | 16 +-- src/webui/www/public/scripts/mocha-init.js | 20 ++-- src/webui/www/public/scripts/parametrics.js | 24 ++-- src/webui/www/public/scripts/prop-files.js | 38 +++---- src/webui/www/public/scripts/prop-general.js | 18 +-- src/webui/www/public/scripts/prop-trackers.js | 14 +-- src/webui/www/public/scripts/prop-webseeds.js | 12 +- unixconf.pri | 2 +- version.pri | 2 +- winconf-msvc.pri | 7 +- winconf.pri | 13 ++- 44 files changed, 448 insertions(+), 285 deletions(-) create mode 100644 dist/unix/systemd/qbittorrent-nox@.service.in diff --git a/.appveyor.yml b/.appveyor.yml index 5c8e3da4209..a5166d618bc 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,7 +3,7 @@ version: '{branch}-{build}' # Do not build on tags (GitHub only) skip_tags: true -os: Visual Studio 2015 +image: Visual Studio 2017 environment: REPO_DIR: &REPO_DIR c:\qbittorrent @@ -36,7 +36,7 @@ install: before_build: # setup env - - CALL "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat" + - CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" - SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom; # setup project - COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%" diff --git a/.travis.yml b/.travis.yml index 702de486062..8eff7a629d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -132,15 +132,15 @@ install: cp "version" $HOME/hombebrew_cache ; cd "$HOME/hombebrew_cache" ; wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb ; - wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz ; - wget https://builds.shiki.hu/homebrew/qt5.rb ; - wget https://builds.shiki.hu/homebrew/qt5-5.7.1_1.el_capitan.bottle.tar.gz ; + wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.0.11+git20172003.8736a59adc.el_capitan.bottle.tar.gz + wget https://builds.shiki.hu/homebrew/qt.rb + wget https://builds.shiki.hu/homebrew/qt-5.9.1.el_capitan.bottle.tar.gz fi # Copy custom libtorrent bottle to homebrew's cache so it can find and install it # Also install our custom libtorrent formula by passing the local path to it # These 2 files are restored from Travis' cache. - cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.10.el_capitan.bottle.tar.gz" "$(brew --cache)" ; + cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.0.11+git20172003.8736a59adc.el_capitan.bottle.tar.gz" "$(brew --cache)" brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb" ; # Qt @@ -149,9 +149,9 @@ install: # Copy custom qt5 bottle to homebrew's cache so it can find and install it # Also install our custom qt5 formula by passing the local path to it # These 2 files are restored from Travis' cache. - cp "$HOME/hombebrew_cache/qt5-5.7.1_1.el_capitan.bottle.tar.gz" "$(brew --cache)" ; - brew install "$HOME/hombebrew_cache/qt5.rb" ; - brew link --force qt5 ; + cp "$HOME/hombebrew_cache/qt-5.9.1.el_capitan.bottle.tar.gz" "$(brew --cache)" + brew install "$HOME/hombebrew_cache/qt.rb" + brew link --force qt fi fi - | diff --git a/Changelog b/Changelog index 79edeb53cd0..4345c100b8b 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,19 @@ +* Tue Jul 18 2017 - sledgehammer999 - v3.3.14 + - BUGFIX: Set interface for outgoing traffic(libtorrent 1.1.x series). (evsh) + - WEBUI: Fix KEEP_ALIVE_DURATION value (Chocobo1) + - WEBUI: Relax CSRF defense. Closes #6882. Allow HTTP request which has neither Origin nor Referer header included. (Chocobo1) + - WEBUI: Skip username/password check for active sessions (closes #6860) (Thomas Piccirello) + - WEBUI: Fix javascript errors and follow best practices (Thomas Piccirello) + - WEBUI: Fix value comparison. Closes #7081. (Chocobo1) + - WEBUI: Avoid modifying request headers (Chocobo1) + - WEBUI: Implement HTTP host header filtering. This filtering is required to defend against DNS rebinding attack. Fixes security issues reported by @beardog108 privately. (Chocobo1) + - WEBUI: Add Status column to webui (addresses #6815) (#7032) (Tom Piccirello) + - WEBUI: Bump API_VERSION and API_VERSION_MIN to 15. + - SEARCH: Pad shorter python versions. Closes #6877. (sledgehammer999) + - WINDOWS: Updated Arabic, Turkish, Greek, Russian, Danish languages of the installer. (KingLucius, BouRock, thalieht, Andrei Stepanov, scootergrisen) + - WINDOWS: Raise total stack size on Windows to 8 MB. Closes #7021. (Chocobo1) + - LINUX: Systemd service with user switch and other fixes/optimizations. (anton.latukha) + * Thu Jun 01 2017 - sledgehammer999 - v3.3.13 - BUGFIX: Fixed UI glitch about torrent numbers in the sidepanel. Fixes #6454. (evsh) - BUGFIX: Fix downloaded/uploaded columns were not highlighted properly when selected. (Chocobo1) diff --git a/configure b/configure index 22781706e87..fa8fcd1774d 100755 --- a/configure +++ b/configure @@ -6962,7 +6962,7 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi if test "x$enable_systemd" = "xyes"; then : - ac_config_files="$ac_config_files dist/unix/systemd/qbittorrent-nox.service" + ac_config_files="$ac_config_files dist/unix/systemd/qbittorrent-nox@.service" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -7716,7 +7716,7 @@ do case $ac_config_target in "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "conf.pri") CONFIG_FILES="$CONFIG_FILES conf.pri" ;; - "dist/unix/systemd/qbittorrent-nox.service") CONFIG_FILES="$CONFIG_FILES dist/unix/systemd/qbittorrent-nox.service" ;; + "dist/unix/systemd/qbittorrent-nox@.service") CONFIG_FILES="$CONFIG_FILES dist/unix/systemd/qbittorrent-nox@.service" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac diff --git a/configure.ac b/configure.ac index ece5653c3a2..1019ffe9d6e 100644 --- a/configure.ac +++ b/configure.ac @@ -260,7 +260,7 @@ AC_SUBST(QBT_REMOVE_DEFINES) AC_OUTPUT(conf.pri) AS_IF([test "x$enable_systemd" = "xyes"], - [AC_OUTPUT(dist/unix/systemd/qbittorrent-nox.service)]) + [AC_OUTPUT(dist/unix/systemd/qbittorrent-nox@.service)]) diff --git a/dist/mac/Info.plist b/dist/mac/Info.plist index 21349dcf669..46afd486aad 100644 --- a/dist/mac/Info.plist +++ b/dist/mac/Info.plist @@ -45,7 +45,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 3.3.13 + 3.3.14 CFBundleSignature qBit CFBundleExecutable diff --git a/dist/unix/CMakeLists.txt b/dist/unix/CMakeLists.txt index f625cbc20e0..8d4be63991e 100644 --- a/dist/unix/CMakeLists.txt +++ b/dist/unix/CMakeLists.txt @@ -2,8 +2,8 @@ if (SYSTEMD) find_package(Systemd) if (SYSTEMD_FOUND) set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR}) - configure_file(systemd/qbittorrent-nox.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox.service + configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR} COMPONENT data) endif(SYSTEMD_FOUND) diff --git a/dist/unix/systemd/qbittorrent-nox@.service.in b/dist/unix/systemd/qbittorrent-nox@.service.in new file mode 100644 index 00000000000..59396a844ae --- /dev/null +++ b/dist/unix/systemd/qbittorrent-nox@.service.in @@ -0,0 +1,14 @@ +[Unit] +Description=qBittorrenti-nox service for user %I + +Documentation=man:qbittorrent-nox(1) +After=network.target + +[Service] +Type=simple +PrivateTmp=false +User=%i +ExecStart=@EXPAND_BINDIR@/qbittorrent-nox + +[Install] +WantedBy=multi-user.target diff --git a/dist/windows/installer-translations/arabic.nsi b/dist/windows/installer-translations/arabic.nsi index c7721f7bbc6..a54bd989078 100644 --- a/dist/windows/installer-translations/arabic.nsi +++ b/dist/windows/installer-translations/arabic.nsi @@ -17,13 +17,13 @@ LangString inst_firewallinfo ${LANG_ARABIC} "جاري اضافة القاعدة ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." LangString inst_warning ${LANG_ARABIC} "البرنامج يعمل. يرجى اغلاقه قبل البدء في التنصيب" ;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings." -LangString inst_uninstall_question ${LANG_ARABIC} "A previous installation was detected. It will be uninstalled without deleting user settings." +LangString inst_uninstall_question ${LANG_ARABIC} "يوجد نسخة سابقة من البرنامج. سيتم إزالتها دون حذف إعدادات المستخدم" ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." LangString inst_unist ${LANG_ARABIC} "جاري ازالة النسخة السابقة من البرنامج" ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." LangString launch_qbt ${LANG_ARABIC} "تشغيل البرنامج" ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." -LangString inst_requires_64bit ${LANG_ARABIC} "This installer works only in 64-bit Windows versions." +LangString inst_requires_64bit ${LANG_ARABIC} "هذا المثبت يعمل فقط في نسخ ويندوز 64 بت" ;------------------------------------ diff --git a/dist/windows/installer-translations/danish.nsi b/dist/windows/installer-translations/danish.nsi index 2d33c8cf3c9..c04d7b709d1 100644 --- a/dist/windows/installer-translations/danish.nsi +++ b/dist/windows/installer-translations/danish.nsi @@ -1,53 +1,53 @@ ;Installer strings ;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)" -LangString inst_qbt_req ${LANG_DANISH} "qBittorrent (required)" +LangString inst_qbt_req ${LANG_DANISH} "qBittorrent (påkrævet)" ;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut" -LangString inst_dekstop ${LANG_DANISH} "Create Desktop Shortcut" +LangString inst_dekstop ${LANG_DANISH} "Opret skrivebordsgenvej" ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" -LangString inst_startmenu ${LANG_DANISH} "Create Start Menu Shortcut" +LangString inst_startmenu ${LANG_DANISH} "Opret genvej i menuen Start" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" -LangString inst_torrent ${LANG_DANISH} "Open .torrent files with qBittorrent" +LangString inst_torrent ${LANG_DANISH} "Åbn .torrent-filer med qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" -LangString inst_magnet ${LANG_DANISH} "Open magnet links with qBittorrent" +LangString inst_magnet ${LANG_DANISH} "Åbn magnet-links med qBittorrent" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" -LangString inst_firewall ${LANG_DANISH} "Add Windows Firewall rule" +LangString inst_firewall ${LANG_DANISH} "Tilføj Windows Firewall-regel" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" -LangString inst_firewallinfo ${LANG_DANISH} "Adding Windows Firewall rule" +LangString inst_firewallinfo ${LANG_DANISH} "Tilføjer Windows Firewall-regel" ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." -LangString inst_warning ${LANG_DANISH} "qBittorrent is running. Please close the application before installing." +LangString inst_warning ${LANG_DANISH} "qBittorrent kører. Luk venligst programmet inden installation." ;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings." -LangString inst_uninstall_question ${LANG_DANISH} "A previous installation was detected. It will be uninstalled without deleting user settings." +LangString inst_uninstall_question ${LANG_DANISH} "En tidligere installation blev registreret. Den vil blive afinstalleret uden at brugerindstillingerne slettes." ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." -LangString inst_unist ${LANG_DANISH} "Uninstalling previous version." +LangString inst_unist ${LANG_DANISH} "Afinstallerer tidligere version." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." -LangString launch_qbt ${LANG_DANISH} "Launch qBittorrent." +LangString launch_qbt ${LANG_DANISH} "Start qBittorrent." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." -LangString inst_requires_64bit ${LANG_DANISH} "This installer works only in 64-bit Windows versions." +LangString inst_requires_64bit ${LANG_DANISH} "Dette installationsprogram virker kun i 64-bit Windows versioner." ;------------------------------------ ;Uninstaller strings ;LangString remove_files ${LANG_ENGLISH} "Remove files" -LangString remove_files ${LANG_DANISH} "Remove files" +LangString remove_files ${LANG_DANISH} "Fjern filer" ;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts" -LangString remove_shortcuts ${LANG_DANISH} "Remove shortcuts" +LangString remove_shortcuts ${LANG_DANISH} "Fjern genveje" ;LangString remove_associations ${LANG_ENGLISH} "Remove file associations" -LangString remove_associations ${LANG_DANISH} "Remove file associations" +LangString remove_associations ${LANG_DANISH} "Fjern filtilknytninger" ;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys" -LangString remove_registry ${LANG_DANISH} "Remove registry keys" +LangString remove_registry ${LANG_DANISH} "Fjern registreringsnøgler" ;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files" -LangString remove_conf ${LANG_DANISH} "Remove configuration files" +LangString remove_conf ${LANG_DANISH} "Fjern konfigurationsfiler" ;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule" -LangString remove_firewall ${LANG_DANISH} "Remove Windows Firewall rule" +LangString remove_firewall ${LANG_DANISH} "Fjern Windows Firewall-regel" ;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule" -LangString remove_firewallinfo ${LANG_DANISH} "Removing Windows Firewall rule" +LangString remove_firewallinfo ${LANG_DANISH} "Fjerner Windows Firewall-regel" ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" -LangString remove_cache ${LANG_DANISH} "Remove torrents and cached data" +LangString remove_cache ${LANG_DANISH} "Fjern torrents og mellemlagret data" ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." -LangString uninst_warning ${LANG_DANISH} "qBittorrent is running. Please close the application before uninstalling." +LangString uninst_warning ${LANG_DANISH} "qBittorrent kører. Luk venligst programmet inden afinstallation." ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" -LangString uninst_tor_warn ${LANG_DANISH} "Not removing .torrent association. It is associated with:" +LangString uninst_tor_warn ${LANG_DANISH} "Fjerner ikke .torrent-tilknytning. Det er tilknyttet:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" -LangString uninst_mag_warn ${LANG_DANISH} "Not removing magnet association. It is associated with:" +LangString uninst_mag_warn ${LANG_DANISH} "Fjerner ikke magnet-tilknytning. Det er tilknyttet:" diff --git a/dist/windows/installer-translations/greek.nsi b/dist/windows/installer-translations/greek.nsi index 34f171a3a00..b658b1dbf9b 100644 --- a/dist/windows/installer-translations/greek.nsi +++ b/dist/windows/installer-translations/greek.nsi @@ -1,53 +1,53 @@ ;Installer strings ;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)" -LangString inst_qbt_req ${LANG_GREEK} "qBittorrent (required)" +LangString inst_qbt_req ${LANG_GREEK} "qBittorrent (απαιτείται)" ;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut" -LangString inst_dekstop ${LANG_GREEK} "Create Desktop Shortcut" +LangString inst_dekstop ${LANG_GREEK} "Δημιουργία συντόμευσης στην Επιφάνεια Εργασίας" ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" -LangString inst_startmenu ${LANG_GREEK} "Create Start Menu Shortcut" +LangString inst_startmenu ${LANG_GREEK} "Δημιουργία συντόμευσης στο Μενού Έναρξης" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" -LangString inst_torrent ${LANG_GREEK} "Open .torrent files with qBittorrent" +LangString inst_torrent ${LANG_GREEK} "Άνοιγμα των αρχείων .torrent με το qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" -LangString inst_magnet ${LANG_GREEK} "Open magnet links with qBittorrent" +LangString inst_magnet ${LANG_GREEK} "Άνοιγμα των μαγνητικών συνδέσμων με το qBittorrent" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" -LangString inst_firewall ${LANG_GREEK} "Add Windows Firewall rule" +LangString inst_firewall ${LANG_GREEK} "Προσθήκη κανόνα εξαίρεσης στο Τείχος Προστασίας των Windows" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" -LangString inst_firewallinfo ${LANG_GREEK} "Adding Windows Firewall rule" +LangString inst_firewallinfo ${LANG_GREEK} "Προστίθεται κανόνας εξαίρεσης στο Τείχος Προστασίας των Windows" ;LangString inst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before installing." -LangString inst_warning ${LANG_GREEK} "qBittorrent is running. Please close the application before installing." +LangString inst_warning ${LANG_GREEK} "Το qBittorrent βρίσκεται σε εκτέλεση. Παρακαλούμε κλείστε την εφαρμογή πριν την εγκατάσταση." ;LangString inst_uninstall_question ${LANG_ENGLISH} "A previous installation was detected. It will be uninstalled without deleting user settings." -LangString inst_uninstall_question ${LANG_GREEK} "A previous installation was detected. It will be uninstalled without deleting user settings." +LangString inst_uninstall_question ${LANG_GREEK} "Ανιχνεύθηκε προηγούμενη εγκατάσταση. Θα απεγκατασταθεί χωρίς να διαγραφούν οι ρυθμίσεις του χρήστη." ;LangString inst_unist ${LANG_ENGLISH} "Uninstalling previous version." -LangString inst_unist ${LANG_GREEK} "Uninstalling previous version." +LangString inst_unist ${LANG_GREEK} "Γίνεται απεγκατάσταση της προηγούμενης έκδοσης." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." -LangString launch_qbt ${LANG_GREEK} "Launch qBittorrent." +LangString launch_qbt ${LANG_GREEK} "Εκκίνηση του qBittorrent." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." -LangString inst_requires_64bit ${LANG_GREEK} "This installer works only in 64-bit Windows versions." +LangString inst_requires_64bit ${LANG_GREEK} "Αυτό το αρχείο εγκατάστασης λειτουργεί μόνο σε 64-bit εκδόσεις των Windows." ;------------------------------------ ;Uninstaller strings ;LangString remove_files ${LANG_ENGLISH} "Remove files" -LangString remove_files ${LANG_GREEK} "Remove files" +LangString remove_files ${LANG_GREEK} "Να διαγραφούν τα αρχεία" ;LangString remove_shortcuts ${LANG_ENGLISH} "Remove shortcuts" -LangString remove_shortcuts ${LANG_GREEK} "Remove shortcuts" +LangString remove_shortcuts ${LANG_GREEK} "Να διαγραφούν οι συντομεύσεις" ;LangString remove_associations ${LANG_ENGLISH} "Remove file associations" -LangString remove_associations ${LANG_GREEK} "Remove file associations" +LangString remove_associations ${LANG_GREEK} "Να καταργηθούν οι συσχετίσεις αρχείων" ;LangString remove_registry ${LANG_ENGLISH} "Remove registry keys" -LangString remove_registry ${LANG_GREEK} "Remove registry keys" +LangString remove_registry ${LANG_GREEK} "Να διαγραφούν τα κλειδιά μητρώου" ;LangString remove_conf ${LANG_ENGLISH} "Remove configuration files" -LangString remove_conf ${LANG_GREEK} "Remove configuration files" +LangString remove_conf ${LANG_GREEK} "Να διαγραφούν τα αρχεία ρυθμίσεων" ;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule" -LangString remove_firewall ${LANG_GREEK} "Remove Windows Firewall rule" +LangString remove_firewall ${LANG_GREEK} "Να διαγραφεί ο κανόνας εξαίρεσης στο Τείχος Προστασίας των Windows" ;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule" -LangString remove_firewallinfo ${LANG_GREEK} "Removing Windows Firewall rule" +LangString remove_firewallinfo ${LANG_GREEK} "Γίνεται διαγραφή του κανόνα εξαίρεσης στο Τείχος Προστασίας των Windows" ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" -LangString remove_cache ${LANG_GREEK} "Remove torrents and cached data" +LangString remove_cache ${LANG_GREEK} "Να διαγραφούν τα torrents και τα δεδομένα προσωρινής αποθήκευσης" ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." -LangString uninst_warning ${LANG_GREEK} "qBittorrent is running. Please close the application before uninstalling." +LangString uninst_warning ${LANG_GREEK} "Το qBittorrent βρίσκεται σε εκτέλεση. Παρακαλούμε κλείστε την εφαρμογή πριν την απεγκατάσταση." ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" -LangString uninst_tor_warn ${LANG_GREEK} "Not removing .torrent association. It is associated with:" +LangString uninst_tor_warn ${LANG_GREEK} "Δεν θα καταργηθεί η συσχέτιση με τα αρχεία .torrent. Είναι συσχετισμένα με το:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" -LangString uninst_mag_warn ${LANG_GREEK} "Not removing magnet association. It is associated with:" +LangString uninst_mag_warn ${LANG_GREEK} "Δεν θα καταργηθεί η συσχέτιση με τους μαγνητικούς συνδέσμους. Είναι συσχετισμένοι με το:" diff --git a/dist/windows/installer-translations/russian.nsi b/dist/windows/installer-translations/russian.nsi index ca8561d6712..a265e432f2c 100644 --- a/dist/windows/installer-translations/russian.nsi +++ b/dist/windows/installer-translations/russian.nsi @@ -7,9 +7,9 @@ LangString inst_dekstop ${LANG_RUSSIAN} "Создать ярлык на рабо ;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut" LangString inst_startmenu ${LANG_RUSSIAN} "Создать ярлык в меню Пуск" ;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent" -LangString inst_torrent ${LANG_RUSSIAN} "Открывать торрент файлы с помощью qBittorrent" +LangString inst_torrent ${LANG_RUSSIAN} "Открывать торрент-файлы с помощью qBittorrent" ;LangString inst_magnet ${LANG_ENGLISH} "Open magnet links with qBittorrent" -LangString inst_magnet ${LANG_RUSSIAN} "Открывать magnet ссылки с помощью qBittorrent" +LangString inst_magnet ${LANG_RUSSIAN} "Открывать magnet-ссылки с помощью qBittorrent" ;LangString inst_firewall ${LANG_ENGLISH} "Add Windows Firewall rule" LangString inst_firewall ${LANG_RUSSIAN} "Добавить в список исключений брандмауера" ;LangString inst_firewallinfo ${LANG_ENGLISH} "Adding Windows Firewall rule" @@ -23,7 +23,7 @@ LangString inst_unist ${LANG_RUSSIAN} "Деинсталлируем старую ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." LangString launch_qbt ${LANG_RUSSIAN} "Запустить qBittorrent." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." -LangString inst_requires_64bit ${LANG_RUSSIAN} "This installer works only in 64-bit Windows versions." +LangString inst_requires_64bit ${LANG_RUSSIAN} "Этот установщик работает только на 64-битных версиях Windows." ;------------------------------------ @@ -44,10 +44,10 @@ LangString remove_firewall ${LANG_RUSSIAN} "Удалить из списка и ;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule" LangString remove_firewallinfo ${LANG_RUSSIAN} "Удаление из списка исключений брандмауера" ;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data" -LangString remove_cache ${LANG_RUSSIAN} "Удалить сохраненные торрент файлы" +LangString remove_cache ${LANG_RUSSIAN} "Удалить сохранённые торрент-файлы" ;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling." LangString uninst_warning ${LANG_RUSSIAN} "qBittorrent запущен. Пожалуйста, закройте qBittorrent и перезапустите программу удаления." ;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:" -LangString uninst_tor_warn ${LANG_RUSSIAN} "Ассоциации торрент файлов не сброшены. Уже ассоциированы с:" +LangString uninst_tor_warn ${LANG_RUSSIAN} "Ассоциации торрент-файлов не сброшены. Уже ассоциированы с:" ;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:" -LangString uninst_mag_warn ${LANG_RUSSIAN} "Ассоциации magnet ссылок не сброшены. Уже ассоциированы с:" +LangString uninst_mag_warn ${LANG_RUSSIAN} "Ассоциации magnet-ссылок не сброшены. Уже ассоциированы с:" diff --git a/dist/windows/installer-translations/turkish.nsi b/dist/windows/installer-translations/turkish.nsi index f15376c1f4f..6e87d5065d2 100644 --- a/dist/windows/installer-translations/turkish.nsi +++ b/dist/windows/installer-translations/turkish.nsi @@ -23,7 +23,7 @@ LangString inst_unist ${LANG_TURKISH} "Önceki sürüm kaldırılıyor." ;LangString launch_qbt ${LANG_ENGLISH} "Launch qBittorrent." LangString launch_qbt ${LANG_TURKISH} "qBittorrent'i çalıştır." ;LangString inst_requires_64bit ${LANG_ENGLISH} "This installer works only in 64-bit Windows versions." -LangString inst_requires_64bit ${LANG_TURKISH} "This installer works only in 64-bit Windows versions." +LangString inst_requires_64bit ${LANG_TURKISH} "Bu yükleyici sadece 64-bit Windows sürümlerinde çalışır." ;------------------------------------ diff --git a/dist/windows/options.nsi b/dist/windows/options.nsi index 8db18e9a9b6..eab1b67d258 100644 --- a/dist/windows/options.nsi +++ b/dist/windows/options.nsi @@ -27,7 +27,7 @@ XPStyle on !define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path ; Program specific -!define PROG_VERSION "3.3.13.1" +!define PROG_VERSION "3.3.14.1" !define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index b10ee6ca2c8..001f8eef893 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -934,6 +934,7 @@ void Session::configure(libtorrent::settings_pack &settingsPack) } } + settingsPack.set_str(libt::settings_pack::outgoing_interfaces, networkInterface().toStdString()); m_listenInterfaceChanged = false; } diff --git a/src/base/http/connection.cpp b/src/base/http/connection.cpp index 46dadd22b75..a6e919a1b63 100644 --- a/src/base/http/connection.cpp +++ b/src/base/http/connection.cpp @@ -77,8 +77,8 @@ void Connection::read() break; case RequestParser::NoError: - Environment env; - env.clientAddress = m_socket->peerAddress(); + const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()}; + Response response = m_requestHandler->processRequest(request, env); if (acceptsGzipEncoding(request.headers["accept-encoding"])) response.headers[HEADER_CONTENT_ENCODING] = "gzip"; diff --git a/src/base/http/server.cpp b/src/base/http/server.cpp index 7626d91f6b2..f89ffa90dbb 100644 --- a/src/base/http/server.cpp +++ b/src/base/http/server.cpp @@ -43,7 +43,7 @@ #include "connection.h" -static const int KEEP_ALIVE_DURATION = 7; // seconds +static const int KEEP_ALIVE_DURATION = 7 * 1000; // milliseconds static const int CONNECTIONS_LIMIT = 500; static const int CONNECTIONS_SCAN_INTERVAL = 2; // seconds @@ -126,7 +126,7 @@ bool Server::setupHttps(const QByteArray &certificates, const QByteArray &key) { QSslKey sslKey(key, QSsl::Rsa); if (sslKey.isNull()) -#ifdef QBT_USES_QT5 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) sslKey = QSslKey(key, QSsl::Ec); #else { diff --git a/src/base/http/types.h b/src/base/http/types.h index bf1d53ad238..488edf9e3a2 100644 --- a/src/base/http/types.h +++ b/src/base/http/types.h @@ -37,20 +37,20 @@ namespace Http { - const char HEADER_CACHE_CONTROL[] = "Cache-Control"; - const char HEADER_CONTENT_ENCODING[] = "Content-Encoding"; - const char HEADER_CONTENT_LENGTH[] = "Content-Length"; - const char HEADER_CONTENT_SECURITY_POLICY[] = "Content-Security-Policy"; - const char HEADER_CONTENT_TYPE[] = "Content-Type"; - const char HEADER_DATE[] = "Date"; + const char HEADER_CACHE_CONTROL[] = "cache-control"; + const char HEADER_CONTENT_ENCODING[] = "content-encoding"; + const char HEADER_CONTENT_LENGTH[] = "content-length"; + const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy"; + const char HEADER_CONTENT_TYPE[] = "content-type"; + const char HEADER_DATE[] = "date"; const char HEADER_HOST[] = "host"; const char HEADER_ORIGIN[] = "origin"; const char HEADER_REFERER[] = "referer"; - const char HEADER_SET_COOKIE[] = "Set-Cookie"; - const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "X-Content-Type-Options"; + const char HEADER_SET_COOKIE[] = "set-cookie"; + const char HEADER_X_CONTENT_TYPE_OPTIONS[] = "x-content-type-options"; const char HEADER_X_FORWARDED_HOST[] = "x-forwarded-host"; - const char HEADER_X_FRAME_OPTIONS[] = "X-Frame-Options"; - const char HEADER_X_XSS_PROTECTION[] = "X-XSS-Protection"; + const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options"; + const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection"; const char CONTENT_TYPE_CSS[] = "text/css; charset=UTF-8"; const char CONTENT_TYPE_GIF[] = "image/gif"; @@ -65,7 +65,11 @@ namespace Http struct Environment { + QHostAddress localAddress; + quint16 localPort; + QHostAddress clientAddress; + quint16 clientPort; }; struct UploadedFile diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 16981d10be6..a6f09386598 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -439,6 +439,16 @@ void Preferences::setWebUiLocalAuthEnabled(bool enabled) setValue("Preferences/WebUI/LocalHostAuth", enabled); } +QString Preferences::getServerDomains() const +{ + return value("Preferences/WebUI/ServerDomains", "*").toString(); +} + +void Preferences::setServerDomains(const QString &str) +{ + setValue("Preferences/WebUI/ServerDomains", str); +} + quint16 Preferences::getWebUiPort() const { return value("Preferences/WebUI/Port", 8080).toInt(); diff --git a/src/base/preferences.h b/src/base/preferences.h index 61f55440e43..83556c7d329 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -175,6 +175,8 @@ class Preferences: public QObject void setWebUiEnabled(bool enabled); bool isWebUiLocalAuthEnabled() const; void setWebUiLocalAuthEnabled(bool enabled); + QString getServerDomains() const; + void setServerDomains(const QString &str); quint16 getWebUiPort() const; void setWebUiPort(quint16 port); bool useUPnPForWebUIPort() const; diff --git a/src/base/utils/misc.cpp b/src/base/utils/misc.cpp index 4a1cfb87f3f..39522435186 100644 --- a/src/base/utils/misc.cpp +++ b/src/base/utils/misc.cpp @@ -313,11 +313,24 @@ QString Utils::Misc::pythonVersionComplete() // Software 'Anaconda' installs its own python interpreter // and `python --version` returns a string like this: // `Python 3.4.3 :: Anaconda 2.3.0 (64-bit)` - const QList verSplit = output.split(' '); - if (verSplit.size() > 1) { - version = verSplit.at(1).trimmed(); + const QList outSplit = output.split(' '); + if (outSplit.size() > 1) { + version = outSplit.at(1).trimmed(); Logger::instance()->addMessage(QCoreApplication::translate("misc", "Python version: %1").arg(version), Log::INFO); } + + // If python doesn't report a 3-piece version e.g. 3.6.1 + // then fill the missing pieces with zero + const QStringList verSplit = version.split('.', QString::SkipEmptyParts); + if (verSplit.size() < 3) { + for (int i = verSplit.size(); i < 3; ++i) { + if (version.endsWith('.')) + version.append('0'); + else + version.append(".0"); + } + Logger::instance()->addMessage(QCoreApplication::translate("misc", "Normalized Python version: %1").arg(version), Log::INFO); + } } } return version; diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index 2e5d5711f66..12e389c9e78 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -319,6 +319,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->textTrackers, SIGNAL(textChanged()), this, SLOT(enableApplyButton())); #ifndef DISABLE_WEBUI // Web UI tab + connect(m_ui->textServerDomains, SIGNAL(textChanged(QString)), this, SLOT(enableApplyButton())); connect(m_ui->checkWebUi, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(m_ui->spinWebUiPort, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton())); connect(m_ui->checkWebUIUPnP, SIGNAL(toggled(bool)), SLOT(enableApplyButton())); @@ -603,6 +604,7 @@ void OptionsDialog::saveOptions() // Web UI pref->setWebUiEnabled(isWebUiEnabled()); if (isWebUiEnabled()) { + pref->setServerDomains(m_ui->textServerDomains->text()); pref->setWebUiPort(webUiPort()); pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked()); pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked()); @@ -975,6 +977,7 @@ void OptionsDialog::loadOptions() // End Bittorrent preferences // Web UI preferences + m_ui->textServerDomains->setText(pref->getServerDomains()); m_ui->checkWebUi->setChecked(pref->isWebUiEnabled()); m_ui->spinWebUiPort->setValue(pref->getWebUiPort()); m_ui->checkWebUIUPnP->setChecked(pref->useUPnPForWebUIPort()); diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index 46250286e74..f59a94c01ea 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -2605,8 +2605,8 @@ 0 0 - 438 - 543 + 518 + 602 @@ -2622,6 +2622,28 @@ false + + + + + + Server domains: + + + + + + + Whitelist for filtering HTTP Host header values. +In order to defend against DNS rebinding attack, +you should put in domain names used by WebUI server. + +Use ';' to split multiple entries. Can use wildcard '*'. + + + + + diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index f8a8abb2a74..31198e6e311 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -28,6 +28,8 @@ #include "abstractwebapplication.h" +#include + #include #include #include @@ -96,6 +98,9 @@ AbstractWebApplication::AbstractWebApplication(QObject *parent) connect(timer, SIGNAL(timeout()), SLOT(removeInactiveSessions())); connect(m_UnbanTimer, SIGNAL(timeout()), SLOT(processUnbanRequest())); timer->start(60 * 1000); // 1 min. + + reloadDomainList(); + connect(Preferences::instance(), SIGNAL(changed()), this, SLOT(reloadDomainList())); } AbstractWebApplication::~AbstractWebApplication() @@ -120,7 +125,7 @@ Http::Response AbstractWebApplication::processRequest(const Http::Request &reque header(Http::HEADER_CONTENT_SECURITY_POLICY, "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none';"); // block cross-site requests - if (isCrossSiteRequest(request_)) { + if (isCrossSiteRequest(request_) || !validateHostHeader(request_, env, domainList)) { status(401, "Unauthorized"); return response(); } @@ -158,6 +163,12 @@ void AbstractWebApplication::removeInactiveSessions() } } +void AbstractWebApplication::reloadDomainList() +{ + domainList = Preferences::instance()->getServerDomains().split(';', QString::SkipEmptyParts); + std::for_each(domainList.begin(), domainList.end(), [](QString &entry){ entry = entry.trimmed(); }); +} + bool AbstractWebApplication::sessionInitialize() { if (session_ == 0) @@ -419,14 +430,14 @@ bool AbstractWebApplication::isCrossSiteRequest(const Http::Request &request) co && (left.host() == right.host())); }; - const QString targetOrigin = request.headers.value(Http::HEADER_X_FORWARDED_HOST, request.headers[Http::HEADER_HOST]); + const QString targetOrigin = request.headers.value(Http::HEADER_X_FORWARDED_HOST, request.headers.value(Http::HEADER_HOST)); const QString originValue = request.headers.value(Http::HEADER_ORIGIN); const QString refererValue = request.headers.value(Http::HEADER_REFERER); if (originValue.isEmpty() && refererValue.isEmpty()) { - if ((request.path == QLatin1String("/")) || (request.path == QLatin1String("/favicon.ico"))) - return false; // normal request - return true; + // owasp.org recommends to block this request, but doing so will inevitably lead Web API users to spoof headers + // so lets be permissive here + return false; } // sent with CORS requests, as well as with POST requests @@ -439,6 +450,45 @@ bool AbstractWebApplication::isCrossSiteRequest(const Http::Request &request) co return true; } +bool AbstractWebApplication::validateHostHeader(const Http::Request &request, const Http::Environment &env, const QStringList &domains) const +{ + const QUrl hostHeader = QUrl::fromUserInput( + request.headers.value(Http::HEADER_X_FORWARDED_HOST, request.headers.value(Http::HEADER_HOST))); + + // (if present) try matching host header's port with local port + const int requestPort = hostHeader.port(); + if ((requestPort != -1) && (env.localPort != requestPort)) + return false; + + // try matching host header with local address + const QString requestHost = hostHeader.host(); + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) + const bool sameAddr = env.localAddress.isEqual(QHostAddress(requestHost)); +#else + const auto equal = [](const Q_IPV6ADDR &l, const Q_IPV6ADDR &r) -> bool { + for (int i = 0; i < 16; ++i) { + if (l[i] != r[i]) + return false; + } + return true; + }; + const bool sameAddr = equal(env.localAddress.toIPv6Address(), QHostAddress(requestHost).toIPv6Address()); +#endif + + if (sameAddr) + return true; + + // try matching host header with domain list + for (const auto &domain : domains) { + QRegExp domainRegex(domain, Qt::CaseInsensitive, QRegExp::Wildcard); + if (requestHost.contains(domainRegex)) + return true; + } + + return false; +} + QStringMap AbstractWebApplication::initializeContentTypeByExtMap() { QStringMap map; @@ -460,7 +510,7 @@ QStringMap AbstractWebApplication::parseCookie(const Http::Request &request) con // [rfc6265] 4.2.1. Syntax QStringMap ret; const QString cookieStr = request.headers.value(QLatin1String("cookie")); -#ifdef QBT_USES_QT5 +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) const QVector cookies = cookieStr.splitRef(';', QString::SkipEmptyParts); #else const QStringList cookies = cookieStr.split(';', QString::SkipEmptyParts); @@ -470,7 +520,7 @@ QStringMap AbstractWebApplication::parseCookie(const Http::Request &request) con const int idx = cookie.indexOf('='); if (idx < 0) continue; -#ifdef QBT_USES_QT5 +#if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) const QString name = cookie.left(idx).trimmed().toString(); const QString value = Utils::String::unquote(cookie.mid(idx + 1).trimmed()) .toString(); diff --git a/src/webui/abstractwebapplication.h b/src/webui/abstractwebapplication.h index 9af1cad97d8..d60ec07dbb3 100644 --- a/src/webui/abstractwebapplication.h +++ b/src/webui/abstractwebapplication.h @@ -97,6 +97,8 @@ private slots: void UnbanTimerEvent(); void removeInactiveSessions(); + void reloadDomainList(); + private: // Persistent data QMap sessions_; @@ -108,11 +110,14 @@ private slots: Http::Request request_; Http::Environment env_; + QStringList domainList; + QString generateSid(); bool sessionInitialize(); QStringMap parseCookie(const Http::Request &request) const; bool isCrossSiteRequest(const Http::Request &request) const; + bool validateHostHeader(const Http::Request &request, const Http::Environment &env, const QStringList &domains) const; static void translateDocument(QString &data); diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index ee26028b3d6..86a70a51293 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -159,6 +159,7 @@ QByteArray prefjson::getPreferences() // Language data["locale"] = pref->getLocale(); // HTTP Server + data["web_ui_domain_list"] = pref->getServerDomains(); data["web_ui_port"] = pref->getWebUiPort(); data["web_ui_upnp"] = pref->useUPnPForWebUIPort(); data["use_https"] = pref->isWebUiHttpsEnabled(); @@ -387,6 +388,8 @@ void prefjson::setPreferences(const QString& json) } } // HTTP Server + if (m.contains("web_ui_domain_list")) + pref->setServerDomains(m["web_ui_domain_list"].toString()); if (m.contains("web_ui_port")) pref->setWebUiPort(m["web_ui_port"].toUInt()); if (m.contains("web_ui_upnp")) diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 1591228c888..a77ea5800ef 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -50,8 +50,8 @@ #include "websessiondata.h" #include "webapplication.h" -static const int API_VERSION = 14; -static const int API_VERSION_MIN = 14; +static const int API_VERSION = 15; +static const int API_VERSION_MIN = 15; const QString WWW_FOLDER = ":/www/public/"; const QString PRIVATE_FOLDER = ":/www/private/"; @@ -175,6 +175,11 @@ void WebApplication::action_public_webui() 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); diff --git a/src/webui/www/public/css/dynamicTable.css b/src/webui/www/public/css/dynamicTable.css index 9c3bb0cd27d..43974b1a9d4 100644 --- a/src/webui/www/public/css/dynamicTable.css +++ b/src/webui/www/public/css/dynamicTable.css @@ -33,7 +33,7 @@ cursor: pointer; } -#transferList img.statusIcon { +#transferList img.stateIcon { margin-bottom: -4px; margin-bottom: -1px; } diff --git a/src/webui/www/public/preferences_content.html b/src/webui/www/public/preferences_content.html index 50a6d234389..f3c1ce54cf2 100644 --- a/src/webui/www/public/preferences_content.html +++ b/src/webui/www/public/preferences_content.html @@ -378,6 +378,7 @@
QBT_TR(Web User Interface (Remote control))QBT_TR[CONTEXT=OptionsDialog] +


@@ -1003,6 +1004,7 @@ $('locale_select').setProperty('value', pref.locale); // HTTP Server + $('webui_domain_textarea').setProperty('value', pref.web_ui_domain_list); $('webui_port_value').setProperty('value', pref.web_ui_port); $('webui_upnp_checkbox').setProperty('checked', pref.web_ui_upnp); $('use_https_checkbox').setProperty('checked', pref.use_https); @@ -1257,6 +1259,7 @@ settings.set('locale', $('locale_select').getProperty('value')); // HTTP Server + settings.set('web_ui_domain_list', $('webui_domain_textarea').getProperty('value')); var web_ui_port = $('webui_port_value').getProperty('value').toInt(); if(isNaN(web_ui_port) || web_ui_port < 1 || web_ui_port > 65535) { alert("QBT_TR(The port used for the Web UI must be between 1 and 65535.)QBT_TR[CONTEXT=HttpServer]"); diff --git a/src/webui/www/public/scripts/client.js b/src/webui/www/public/scripts/client.js index bccc62446f3..b4ecf42d207 100644 --- a/src/webui/www/public/scripts/client.js +++ b/src/webui/www/public/scripts/client.js @@ -70,7 +70,7 @@ window.addEvent('load', function () { var properties_height_rel = $('propertiesPanel').getSize().y / Window.getSize().y; localStorage.setItem('filters_width', filters_width); localStorage.setItem('properties_height_rel', properties_height_rel); - } + }; window.addEvent('resize', function() { // Resizing might takes some time. @@ -128,7 +128,7 @@ window.addEvent('load', function () { // Reload torrents if (typeof torrentsTable.tableBody != 'undefined') updateMainData(); - } + }; new MochaUI.Panel({ id : 'Filters', @@ -151,10 +151,9 @@ window.addEvent('load', function () { initializeWindows(); // Show Top Toolbar is enabled by default - if (localStorage.getItem('show_top_toolbar') == null) - var showTopToolbar = true; - else - var showTopToolbar = localStorage.getItem('show_top_toolbar') == "true"; + var showTopToolbar = true; + if (localStorage.getItem('show_top_toolbar') !== null) + showTopToolbar = localStorage.getItem('show_top_toolbar') == "true"; if (!showTopToolbar) { $('showTopToolbarLink').firstChild.style.opacity = '0'; $('mochaToolbar').addClass('invisible'); @@ -171,7 +170,7 @@ window.addEvent('load', function () { var serverState = {}; var removeTorrentFromCategoryList = function(hash) { - if (hash == null || hash == "") + if (hash === null || hash === "") return false; var removed = false; Object.each(category_list, function(category) { @@ -185,14 +184,14 @@ window.addEvent('load', function () { var addTorrentToCategoryList = function(torrent) { var category = torrent['category']; - if (category == null) + if (typeof category === 'undefined') return false; if (category.length === 0) { // Empty category removeTorrentFromCategoryList(torrent['hash']); return true; } var categoryHash = genHash(category); - if (category_list[categoryHash] == null) // This should not happen + if (category_list[categoryHash] === null) // This should not happen category_list[categoryHash] = {name: category, torrents: []}; if (!Object.contains(category_list[categoryHash].torrents, torrent['hash'])) { removeTorrentFromCategoryList(torrent['hash']); @@ -242,7 +241,7 @@ window.addEvent('load', function () { categoryList.appendChild(create_link(CATEGORIES_ALL, 'QBT_TR(All)QBT_TR[CONTEXT=CategoryFilterModel]', all)); categoryList.appendChild(create_link(CATEGORIES_UNCATEGORIZED, 'QBT_TR(Uncategorized)QBT_TR[CONTEXT=CategoryFilterModel]', uncategorized)); - var sortedCategories = [] + var sortedCategories = []; Object.each(category_list, function(category) { sortedCategories.push(category.name); }); @@ -268,7 +267,7 @@ window.addEvent('load', function () { else childrens[i].className = ""; } - } + }; var syncMainDataTimer; var syncMainData = function () { @@ -287,7 +286,7 @@ window.addEvent('load', function () { $('error_div').set('html', ''); if (response) { var update_categories = false; - var full_update = (response['full_update'] == true); + var full_update = (response['full_update'] === true); if (full_update) { torrentsTable.clear(); category_list = {}; @@ -313,6 +312,8 @@ window.addEvent('load', function () { for (var key in response['torrents']) { response['torrents'][key]['hash'] = key; response['torrents'][key]['rowId'] = key; + if (response['torrents'][key]['state']) + response['torrents'][key]['status'] = response['torrents'][key]['state']; torrentsTable.updateRowData(response['torrents'][key]); if (addTorrentToCategoryList(response['torrents'][key])) update_categories = true; @@ -328,8 +329,8 @@ window.addEvent('load', function () { torrentsTable.altRow(); if (response['server_state']) { var tmp = response['server_state']; - for(var key in tmp) - serverState[key] = tmp[key]; + for(var k in tmp) + serverState[k] = tmp[k]; processServerState(); } updateFiltersList(); @@ -348,7 +349,7 @@ window.addEvent('load', function () { torrentsTable.updateTable(); clearTimeout(syncMainDataTimer); syncMainDataTimer = syncMainData.delay(100); - } + }; var processServerState = function () { var transfer_info = friendlyUnit(serverState.dl_info_speed, true); @@ -368,7 +369,7 @@ window.addEvent('load', function () { document.title = "qBittorrent ${VERSION} QBT_TR(Web UI)QBT_TR[CONTEXT=OptionsDialog]"; $('DHTNodes').set('html', 'QBT_TR(DHT: %1 nodes)QBT_TR[CONTEXT=StatusBar]'.replace("%1", serverState.dht_nodes)); - + // Statistics dialog if (document.getElementById("statisticspage")) { $('AlltimeDL').set('html', 'QBT_TR(Alltime download:)QBT_TR[CONTEXT=StatsDialog]' + " " + friendlyUnit(serverState.alltime_dl, false)); $('AlltimeUL').set('html', 'QBT_TR(Alltime upload:)QBT_TR[CONTEXT=StatsDialog]' + " " + friendlyUnit(serverState.alltime_ul, false)); @@ -421,8 +422,8 @@ window.addEvent('load', function () { if (enabled) $('alternativeSpeedLimits').src = "images/slow.png"; else - $('alternativeSpeedLimits').src = "images/slow_off.png" - } + $('alternativeSpeedLimits').src = "images/slow_off.png"; + }; $('alternativeSpeedLimits').addEvent('click', function() { // Change icon immediately to give some feedback @@ -436,7 +437,7 @@ window.addEvent('load', function () { }, onFailure: function() { // Restore icon in case of failure - updateAltSpeedIcon(alternativeSpeedLimits) + updateAltSpeedIcon(alternativeSpeedLimits); } }).send(); }); @@ -493,7 +494,7 @@ window.addEvent('load', function () { if ($defined(prop_h)) prop_h = prop_h.toFloat() * Window.getSize().y; else - prop_h = Window.getSize().y / 2.; + prop_h = Window.getSize().y / 2.0; new MochaUI.Panel({ id : 'propertiesPanel', title : 'Panel', @@ -524,7 +525,7 @@ window.addEvent('load', function () { updateWebSeedsData(); else if (!$('prop_files').hasClass('invisible')) updateTorrentFilesData(); - } + }; $('PropGeneralLink').addEvent('click', function(e){ $('prop_general').removeClass("invisible"); @@ -587,7 +588,7 @@ window.addEvent('load', function () { function closeWindows() { MochaUI.closeAll(); -}; +} var keyboardEvents = new Keyboard({ defaultEventType: 'keydown', @@ -616,7 +617,7 @@ var loadTorrentPeersData = function(){ return; } var current_hash = torrentsTable.getCurrentTorrentHash(); - if (current_hash == "") { + if (current_hash === "") { syncTorrentPeersLastResponseId = 0; torrentPeersTable.clear(); clearTimeout(loadTorrentPeersTimer); @@ -638,7 +639,7 @@ var loadTorrentPeersData = function(){ onSuccess: function(response) { $('error_div').set('html', ''); if (response) { - var full_update = (response['full_update'] == true); + var full_update = (response['full_update'] === true); if (full_update) { torrentPeersTable.clear(); } diff --git a/src/webui/www/public/scripts/contextmenu.js b/src/webui/www/public/scripts/contextmenu.js index 050d09a4733..10c67bc9998 100644 --- a/src/webui/www/public/scripts/contextmenu.js +++ b/src/webui/www/public/scripts/contextmenu.js @@ -23,7 +23,7 @@ var ContextMenu = new Class({ //initialization initialize: function(options) { //set options - this.setOptions(options) + this.setOptions(options); //option diffs menu this.menu = $(this.options.menu); @@ -69,19 +69,19 @@ var ContextMenu = new Class({ }); // position the menu - var xPos = e.page.x + this.options.offsets.x; - var yPos = e.page.y + this.options.offsets.y; - if (xPos + this.menu.offsetWidth > document.documentElement.clientWidth) - xPos -= this.menu.offsetWidth; - if (yPos + this.menu.offsetHeight > document.documentElement.clientHeight) - yPos = document.documentElement.clientHeight - this.menu.offsetHeight; - if (xPos < 0) - xPos = 0; - if (yPos < 0) - yPos = 0; + var xPosMenu = e.page.x + this.options.offsets.x; + var yPosMenu = e.page.y + this.options.offsets.y; + if (xPosMenu + this.menu.offsetWidth > document.documentElement.clientWidth) + xPosMenu -= this.menu.offsetWidth; + if (yPosMenu + this.menu.offsetHeight > document.documentElement.clientHeight) + yPosMenu = document.documentElement.clientHeight - this.menu.offsetHeight; + if (xPosMenu < 0) + xPosMenu = 0; + if (yPosMenu < 0) + yPosMenu = 0; this.menu.setStyles({ - left: xPos, - top: yPos, + left: xPosMenu, + top: yPosMenu, position: 'absolute', 'z-index': '2000' }); @@ -260,19 +260,19 @@ var TorrentsTableContextMenu = new Class({ h.each(function(item, index){ var data = torrentsTable.rows.get(item).full_data; - if (data['seq_dl'] != true) + if (data['seq_dl'] !== true) all_are_seq_dl = false; else there_are_seq_dl = true; - if (data['f_l_piece_prio'] != true) + if (data['f_l_piece_prio'] !== true) all_are_f_l_piece_prio = false; else there_are_f_l_piece_prio = true; if (data['progress'] != 1.0) // not downloaded all_are_downloaded = false; - else if (data['super_seeding'] != true) + else if (data['super_seeding'] !== true) all_are_super_seeding = false; if (data['state'] != 'pausedUP' && data['state'] != 'pausedDL') @@ -280,7 +280,7 @@ var TorrentsTableContextMenu = new Class({ else there_are_paused = true; - if (data['force_start'] != true) + if (data['force_start'] !== true) all_are_force_start = false; else there_are_force_start = true; @@ -344,7 +344,7 @@ var TorrentsTableContextMenu = new Class({ categoryList.appendChild(new Element('li', {html: 'QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget] QBT_TR(New...)QBT_TR[CONTEXT=TransferListWidget]'})); categoryList.appendChild(new Element('li', {html: 'QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget] QBT_TR(Reset)QBT_TR[CONTEXT=TransferListWidget]'})); - var sortedCategories = [] + var sortedCategories = []; Object.each(category_list, function (category) { sortedCategories.push(category.name); }); diff --git a/src/webui/www/public/scripts/download.js b/src/webui/www/public/scripts/download.js index a01c8eedfca..6ca0ce1c8f4 100644 --- a/src/webui/www/public/scripts/download.js +++ b/src/webui/www/public/scripts/download.js @@ -22,20 +22,20 @@ */ getSavePath = function() { - var req = new Request({ - url: 'command/getSavePath', - method: 'get', - noCache: true, - onFailure: function() { - alert("Could not contact qBittorrent"); - }, - onSuccess: function(data) { - if (data) { - $('savepath').setProperty('value', data); - } - } - }).send(); -} + var req = new Request({ + url: 'command/getSavePath', + method: 'get', + noCache: true, + onFailure: function() { + alert("Could not contact qBittorrent"); + }, + onSuccess: function(data) { + if (data) { + $('savepath').setProperty('value', data); + } + } + }).send(); +}; $(window).addEventListener("load", function() { getSavePath(); diff --git a/src/webui/www/public/scripts/dynamicTable.js b/src/webui/www/public/scripts/dynamicTable.js index 2483ce5774f..ec7ad8c5d9e 100644 --- a/src/webui/www/public/scripts/dynamicTable.js +++ b/src/webui/www/public/scripts/dynamicTable.js @@ -45,8 +45,8 @@ var DynamicTable = new Class({ this.hiddenTableHeader = $(dynamicTableDivId).getElements('tr')[0]; this.tableBody = $(dynamicTableDivId).getElements('tbody')[0]; this.rows = new Hash(); - this.selectedRows = new Array(); - this.columns = new Array(); + this.selectedRows = []; + this.columns = []; this.contextMenu = contextMenu; this.sortedColumn = getLocalStorageItem('sorted_column_' + this.dynamicTableDivId, 0); this.reverseSort = getLocalStorageItem('reverse_sort_' + this.dynamicTableDivId, '0'); @@ -116,7 +116,7 @@ var DynamicTable = new Class({ el.setStyle('border-right-color', ''); el.setStyle('border-right-width', ''); } - } + }; var mouseMoveFn = function (e) { var brect = e.target.getBoundingClientRect(); @@ -250,7 +250,7 @@ var DynamicTable = new Class({ onDrag : onDrag, onComplete : onComplete, onCancel : onCancel - }) + }); } }, @@ -331,7 +331,7 @@ var DynamicTable = new Class({ column['width'] = getLocalStorageItem('column_' + name + '_width_' + this.dynamicTableDivId, defaultWidth); column['dataProperties'] = [name]; column['getRowValue'] = function (row, pos) { - if (pos == undefined) + if (pos === undefined) pos = 0; return row['full_data'][this.dataProperties[pos]]; }; @@ -429,8 +429,8 @@ var DynamicTable = new Class({ else { ths[pos].addClass('invisible'); fths[pos].addClass('invisible'); - for (var i = 0; i < trs.length; i++) - trs[i].getElements('td')[pos].addClass('invisible'); + for (var j = 0; j < trs.length; j++) + trs[j].getElements('td')[pos].addClass('invisible'); } if (this.columns[pos].onResize !== null) { @@ -527,7 +527,7 @@ var DynamicTable = new Class({ }, getFilteredAndSortedRows : function () { - var filteredRows = new Array(); + var filteredRows = []; var rows = this.rows.getValues(); @@ -557,7 +557,7 @@ var DynamicTable = new Class({ }, updateTable : function (fullUpdate) { - if (fullUpdate == undefined) + if (fullUpdate === undefined) fullUpdate = false; var rows = this.getFilteredAndSortedRows(); @@ -573,7 +573,7 @@ var DynamicTable = new Class({ for (var rowPos = 0; rowPos < rows.length; rowPos++) { var rowId = rows[rowPos]['rowId']; tr_found = false; - for (j = rowPos; j < trs.length; j++) + for (var j = rowPos; j < trs.length; j++) if (trs[j]['rowId'] == rowId) { tr_found = true; if (rowPos == j) @@ -635,7 +635,7 @@ var DynamicTable = new Class({ tr.addClass('selected'); } else - tr.removeClass('selected') + tr.removeClass('selected'); } } } else { @@ -648,9 +648,9 @@ var DynamicTable = new Class({ this.setupTr(tr); - for (var j = 0 ; j < this.columns.length; j++) { + for (var k = 0 ; k < this.columns.length; k++) { var td = new Element('td'); - if ((this.columns[j].visible == '0') || this.columns[j].force_hide) + if ((this.columns[k].visible == '0') || this.columns[k].force_hide) td.addClass('invisible'); td.injectInside(tr); } @@ -698,7 +698,7 @@ var DynamicTable = new Class({ removeRow : function (rowId) { this.selectedRows.erase(rowId); var tr = this.getTrByRowId(rowId); - if (tr != null) { + if (tr !== null) { tr.dispose(); this.rows.erase(rowId); return true; @@ -734,6 +734,7 @@ var TorrentsTable = new Class({ this.newColumn('name', '', 'QBT_TR(Name)QBT_TR[CONTEXT=TorrentModel]', 200, true); this.newColumn('size', '', 'QBT_TR(Size)QBT_TR[CONTEXT=TorrentModel]', 100, true); this.newColumn('progress', '', 'QBT_TR(Done)QBT_TR[CONTEXT=TorrentModel]', 85, true); + this.newColumn('status', '', 'QBT_TR(Status)QBT_TR[CONTEXT=TorrentModel]', 100, true); this.newColumn('num_seeds', '', 'QBT_TR(Seeds)QBT_TR[CONTEXT=TorrentModel]', 100, true); this.newColumn('num_leechs', '', 'QBT_TR(Peers)QBT_TR[CONTEXT=TorrentModel]', 100, true); this.newColumn('dlspeed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=TorrentModel]', 100, true); @@ -771,26 +772,25 @@ var TorrentsTable = new Class({ initColumnsFunctions : function () { // state_icon - this.columns['state_icon'].updateTd = function (td, row) { var state = this.getRowValue(row); - if (state == "forcedDL" || state == "metaDL") + if ((state === "forcedDL") || (state === "metaDL")) state = "downloading"; - else if (state == "allocating") + else if (state === "allocating") state = "stalledDL"; - else if (state == "forcedUP") + else if (state === "forcedUP") state = "uploading"; - else if (state == "pausedDL") + else if (state === "pausedDL") state = "paused"; - else if (state == "pausedUP") + else if (state === "pausedUP") state = "completed"; - else if (state == "queuedDL" || state == "queuedUP") + else if ((state === "queuedDL") || (state === "queuedUP")) state = "queued"; - else if (state == "checkingDL" || state == "checkingUP" || - state == "queuedForChecking" || state == "checkingResumeData") + else if ((state === "checkingDL") || (state === "checkingUP") || + (state === "queuedForChecking") || (state === "checkingResumeData")) state = "checking"; - else if (state == "unknown" || state == "error" || state == "missingFiles") + else if ((state === "unknown") || (state === "error") || (state === "missingFiles")) state = "error"; var img_path = 'images/skin/' + state + '.png'; @@ -803,12 +803,37 @@ var TorrentsTable = new Class({ else td.adopt(new Element('img', { 'src' : img_path, - 'class' : 'statusIcon' + 'class' : 'stateIcon' })); }; - // priority + // status + this.columns['status'].updateTd = function (td, row) { + var status = this.getRowValue(row); + if (!status) return; + + if ((status === "downloading") || (status === "forcedDL") || (status === "metaDL")) + status = "Downloading"; + else if ((status === "stalledDL") || (status === "stalledUP") || (status === "allocating")) + status = "Stalled"; + else if ((status === "uploading") || (status === "forcedUP")) + status = "Uploading"; + else if (status === "pausedDL") + status = "Paused"; + else if (status === "pausedUP") + status = "Completed"; + else if ((status === "queuedDL") || (status === "queuedUP")) + status = "Queued"; + else if ((status === "checkingDL") || (status === "checkingUP") || + (status === "queuedForChecking") || (status === "checkingResumeData")) + status = "Checking"; + else if ((status === "unknown") || (status === "error") || (status === "missingFiles")) + status = "Error"; + + td.set('html', status); + }; + // priority this.columns['priority'].updateTd = function (td, row) { var priority = this.getRowValue(row); td.set('html', priority < 1 ? '*' : priority); @@ -829,21 +854,18 @@ var TorrentsTable = new Class({ }; // name, category - this.columns['name'].updateTd = function (td, row) { td.set('html', escapeHtml(this.getRowValue(row))); }; this.columns['category'].updateTd = this.columns['name'].updateTd; // size - this.columns['size'].updateTd = function (td, row) { var size = this.getRowValue(row); td.set('html', friendlyUnit(size, false)); }; // progress - this.columns['progress'].updateTd = function (td, row) { var progress = this.getRowValue(row); var progressFormated = (progress * 100).round(1); @@ -883,7 +905,6 @@ var TorrentsTable = new Class({ }.bind(this); // num_seeds - this.columns['num_seeds'].updateTd = function (td, row) { var num_seeds = this.getRowValue(row, 0); var num_complete = this.getRowValue(row, 1); @@ -911,30 +932,25 @@ var TorrentsTable = new Class({ }; // num_leechs - this.columns['num_leechs'].updateTd = this.columns['num_seeds'].updateTd; this.columns['num_leechs'].compareRows = this.columns['num_seeds'].compareRows; // dlspeed - this.columns['dlspeed'].updateTd = function (td, row) { var speed = this.getRowValue(row); td.set('html', friendlyUnit(speed, true)); }; // upspeed - this.columns['upspeed'].updateTd = this.columns['dlspeed'].updateTd; // eta - this.columns['eta'].updateTd = function (td, row) { var eta = this.getRowValue(row); td.set('html', friendlyDuration(eta, true)); }; // ratio - this.columns['ratio'].updateTd = function (td, row) { var ratio = this.getRowValue(row); var html = null; @@ -946,14 +962,12 @@ var TorrentsTable = new Class({ }; // added on - this.columns['added_on'].updateTd = function (td, row) { var date = new Date(this.getRowValue(row) * 1000).toLocaleString(); td.set('html', date); }; // completion_on - this.columns['completion_on'].updateTd = function (td, row) { var val = this.getRowValue(row); if (val === 0xffffffff || val < 0) @@ -965,15 +979,13 @@ var TorrentsTable = new Class({ }; // seen_complete - this.columns['seen_complete'].updateTd = this.columns['completion_on'].updateTd; // dl_limit, up_limit - this.columns['dl_limit'].updateTd = function (td, row) { var speed = this.getRowValue(row); if (speed === 0) - td.set('html', '∞') + td.set('html', '∞'); else td.set('html', friendlyUnit(speed, true)); }; @@ -981,7 +993,6 @@ var TorrentsTable = new Class({ this.columns['up_limit'].updateTd = this.columns['dl_limit'].updateTd; // downloaded, uploaded, downloaded_session, uploaded_session, amount_left, completed, total_size - this.columns['downloaded'].updateTd = this.columns['size'].updateTd; this.columns['uploaded'].updateTd = this.columns['size'].updateTd; this.columns['downloaded_session'].updateTd = this.columns['size'].updateTd; @@ -992,16 +1003,13 @@ var TorrentsTable = new Class({ this.columns['total_size'].updateTd = this.columns['size'].updateTd; // save_path, tracker - this.columns['save_path'].updateTd = this.columns['name'].updateTd; this.columns['tracker'].updateTd = this.columns['name'].updateTd; // ratio_limit - this.columns['ratio_limit'].updateTd = this.columns['ratio'].updateTd; // last_activity - this.columns['last_activity'].updateTd = function (td, row) { var val = this.getRowValue(row); if (val < 1) @@ -1039,9 +1047,10 @@ var TorrentsTable = new Class({ break; case 'inactive': inactive = true; + // fallthrough case 'active': if (state == 'stalledDL') - r = (row['full_data'].upspeed > 0) + r = (row['full_data'].upspeed > 0); else r = state == 'metaDL' || state == 'downloading' || state == 'forcedDL' || state == 'uploading' || state == 'forcedUP'; if (r == inactive) @@ -1086,7 +1095,7 @@ var TorrentsTable = new Class({ }, getFilteredAndSortedRows : function () { - var filteredRows = new Array(); + var filteredRows = []; var rows = this.rows.getValues(); @@ -1218,7 +1227,7 @@ var TorrentPeersTable = new Class({ this.columns['dl_speed'].updateTd = function (td, row) { var speed = this.getRowValue(row); - if (speed == 0) + if (speed === 0) td.set('html', ''); else td.set('html', friendlyUnit(speed, true)); diff --git a/src/webui/www/public/scripts/misc.js b/src/webui/www/public/scripts/misc.js index 4b70f28682c..9444507b303 100644 --- a/src/webui/www/public/scripts/misc.js +++ b/src/webui/www/public/scripts/misc.js @@ -16,8 +16,8 @@ function friendlyUnit(value, isSpeed) { return "QBT_TR(Unknown)QBT_TR[CONTEXT=misc]"; var i = 0; - while (value >= 1024. && i < 6) { - value /= 1024.; + while (value >= 1024.0 && i < 6) { + value /= 1024.0; ++i; } @@ -28,7 +28,7 @@ function friendlyUnit(value, isSpeed) { } var ret; - if (i == 0) + if (i === 0) ret = value + " " + units[i]; else ret = (Math.floor(10 * value) / 10).toFixed(friendlyUnitPrecision(i)) //Don't round up @@ -46,7 +46,7 @@ function friendlyDuration(seconds) { var MAX_ETA = 8640000; if (seconds < 0 || seconds >= MAX_ETA) return "∞"; - if (seconds == 0) + if (seconds === 0) return "0"; if (seconds < 60) return "QBT_TR(< 1m)QBT_TR[CONTEXT=misc]"; @@ -56,11 +56,11 @@ function friendlyDuration(seconds) { var hours = minutes / 60; minutes = minutes % 60; if (hours < 24) - return "QBT_TR(%1h %2m)QBT_TR[CONTEXT=misc]".replace("%1", parseInt(hours)).replace("%2", parseInt(minutes)) + return "QBT_TR(%1h %2m)QBT_TR[CONTEXT=misc]".replace("%1", parseInt(hours)).replace("%2", parseInt(minutes)); var days = hours / 24; hours = hours % 24; if (days < 100) - return "QBT_TR(%1d %2h)QBT_TR[CONTEXT=misc]".replace("%1", parseInt(days)).replace("%2", parseInt(hours)) + return "QBT_TR(%1d %2h)QBT_TR[CONTEXT=misc]".replace("%1", parseInt(days)).replace("%2", parseInt(hours)); return "∞"; } @@ -96,11 +96,11 @@ if (!Date.prototype.toISOString) { */ function parseHtmlLinks(text) { var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; - return text.replace(exp,"$1"); + return text.replace(exp,"$1"); } function escapeHtml(str) { var div = document.createElement('div'); div.appendChild(document.createTextNode(str)); return div.innerHTML; -}; +} diff --git a/src/webui/www/public/scripts/mocha-init.js b/src/webui/www/public/scripts/mocha-init.js index 4c8b8ccfd54..4d07725460d 100644 --- a/src/webui/www/public/scripts/mocha-init.js +++ b/src/webui/www/public/scripts/mocha-init.js @@ -19,7 +19,7 @@ if (typeof localStorage == 'undefined') { duration: 365 * 10 }); } - } + }; } function getLocalStorageItem(name, defaultVal) { @@ -116,7 +116,7 @@ initializeWindows = function() { width: 424, height: 80 }); - } + }; uploadLimitFN = function() { var h = torrentsTable.selectedRowsIds(); @@ -210,7 +210,7 @@ initializeWindows = function() { width: 424, height: 80 }); - } + }; StatisticsLinkFN = function() { new MochaUI.Window({ @@ -224,8 +224,8 @@ initializeWindows = function() { width: 275, height: 370, padding: 10 - }); - } + }); + }; downloadLimitFN = function() { var h = torrentsTable.selectedRowsIds(); @@ -340,8 +340,8 @@ initializeWindows = function() { torrentSetCategoryFN = function (categoryHash) { var categoryName = ''; - if (categoryHash != 0) - var categoryName = category_list[categoryHash].name; + if (categoryHash !== 0) + categoryName = category_list[categoryHash].name; var h = torrentsTable.selectedRowsIds(); if (h.length) { new Request({ @@ -387,7 +387,7 @@ initializeWindows = function() { deleteUnusedCategoriesFN = function () { var categories = []; for (var hash in category_list) { - if (torrentsTable.getFilteredTorrentsNumber('all', hash) == 0) + if (torrentsTable.getFilteredTorrentsNumber('all', hash) === 0) categories.push(category_list[hash].name); } new Request({ @@ -499,7 +499,7 @@ initializeWindows = function() { }).send(); updateMainData(); } - } + }; addClickEvent('about', function(e) { new Event(e).stop(); @@ -544,4 +544,4 @@ initializeWindows = function() { new Event(e).stop(); }); }); -} +}; diff --git a/src/webui/www/public/scripts/parametrics.js b/src/webui/www/public/scripts/parametrics.js index cda3272f56a..f95dc7680fc 100644 --- a/src/webui/www/public/scripts/parametrics.js +++ b/src/webui/www/public/scripts/parametrics.js @@ -4,7 +4,7 @@ Script: Parametrics.js Initializes the GUI property sliders. Copyright: - Copyright (c) 2007-2008 Greg Houston, . + Copyright (c) 2007-2008 Greg Houston, . License: MIT-style license. @@ -28,7 +28,7 @@ MochaUI.extend({ if (data) { var tmp = data.toInt(); if (tmp > 0) { - maximum = tmp / 1024. + maximum = tmp / 1024.0; } else { if (hashes[0] == "global") @@ -59,7 +59,7 @@ MochaUI.extend({ }.bind(this) }); // Set default value - if (up_limit == 0) { + if (up_limit === 0) { $('uplimitUpdatevalue').value = '∞'; $('upLimitUnit').style.visibility = "hidden"; } @@ -88,7 +88,7 @@ MochaUI.extend({ var mochaSlide = new Slider($('uplimitSliderarea'), $('uplimitSliderknob'), { steps: maximum, offset: 0, - initialStep: (up_limit / 1024.).round(), + initialStep: (up_limit / 1024.0).round(), onChange: function(pos) { if (pos > 0) { $('uplimitUpdatevalue').value = pos; @@ -101,12 +101,12 @@ MochaUI.extend({ }.bind(this) }); // Set default value - if (up_limit == 0) { + if (up_limit === 0) { $('uplimitUpdatevalue').value = '∞'; $('upLimitUnit').style.visibility = "hidden"; } else { - $('uplimitUpdatevalue').value = (up_limit / 1024.).round(); + $('uplimitUpdatevalue').value = (up_limit / 1024.0).round(); $('upLimitUnit').style.visibility = "visible"; } } @@ -132,7 +132,7 @@ MochaUI.extend({ if (data) { var tmp = data.toInt(); if (tmp > 0) { - maximum = tmp / 1024. + maximum = tmp / 1024.0; } else { if (hashes[0] == "global") @@ -163,7 +163,7 @@ MochaUI.extend({ }.bind(this) }); // Set default value - if (dl_limit == 0) { + if (dl_limit === 0) { $('dllimitUpdatevalue').value = '∞'; $('dlLimitUnit').style.visibility = "hidden"; } @@ -192,7 +192,7 @@ MochaUI.extend({ var mochaSlide = new Slider($('dllimitSliderarea'), $('dllimitSliderknob'), { steps: maximum, offset: 0, - initialStep: (dl_limit / 1024.).round(), + initialStep: (dl_limit / 1024.0).round(), onChange: function(pos) { if (pos > 0) { $('dllimitUpdatevalue').value = pos; @@ -205,12 +205,12 @@ MochaUI.extend({ }.bind(this) }); // Set default value - if (dl_limit == 0) { + if (dl_limit === 0) { $('dllimitUpdatevalue').value = '∞'; $('dlLimitUnit').style.visibility = "hidden"; } else { - $('dllimitUpdatevalue').value = (dl_limit / 1024.).round(); + $('dllimitUpdatevalue').value = (dl_limit / 1024.0).round(); $('dlLimitUnit').style.visibility = "visible"; } } @@ -221,4 +221,4 @@ MochaUI.extend({ }).send(); } } -}); \ No newline at end of file +}); diff --git a/src/webui/www/public/scripts/prop-files.js b/src/webui/www/public/scripts/prop-files.js index 3098086502d..89b98a58f4e 100644 --- a/src/webui/www/public/scripts/prop-files.js +++ b/src/webui/www/public/scripts/prop-files.js @@ -40,7 +40,7 @@ var setCBState = function(state) { $("tristate_cb").checked = false; } } -} +}; var switchCBState = function() { // Uncheck @@ -69,7 +69,7 @@ var switchCBState = function() { item.set("checked", "checked"); setFilePriority(index, 1); }); -} +}; var allCBChecked = function() { var CBs = $$('input.DownloadedCB'); @@ -79,7 +79,7 @@ var allCBChecked = function() { return false; } return true; -} +}; var allCBUnchecked = function() { var CBs = $$('input.DownloadedCB'); @@ -89,10 +89,10 @@ var allCBUnchecked = function() { return false; } return true; -} +}; var setFilePriority = function(id, priority) { - if (current_hash == "") return; + if (current_hash === "") return; new Request({ url: 'command/setFilePrio', method: 'post', @@ -110,7 +110,7 @@ var setFilePriority = function(id, priority) { else { $('comboPrio' + id).addClass("invisible"); } -} +}; var createDownloadedCB = function(id, downloaded) { var CB = new Element('input'); @@ -137,7 +137,7 @@ var createDownloadedCB = function(id, downloaded) { } }); return CB; -} +}; var createPriorityCombo = function(id, selected_prio) { var select = new Element('select'); @@ -147,19 +147,19 @@ var createPriorityCombo = function(id, selected_prio) { setFilePriority(id, new_prio); }); var opt = new Element("option"); - opt.set('value', '1') + opt.set('value', '1'); opt.set('html', "QBT_TR(Normal)QBT_TR[CONTEXT=PropListDelegate]"); if (selected_prio <= 1) opt.setAttribute('selected', ''); opt.injectInside(select); opt = new Element("option"); - opt.set('value', '2') + opt.set('value', '2'); opt.set('html', "QBT_TR(High)QBT_TR[CONTEXT=PropListDelegate]"); if (selected_prio == 2) opt.setAttribute('selected', ''); opt.injectInside(select); opt = new Element("option"); - opt.set('value', '7') + opt.set('value', '7'); opt.set('html', "QBT_TR(Maximum)QBT_TR[CONTEXT=PropListDelegate]"); if (selected_prio == 7) opt.setAttribute('selected', ''); @@ -172,7 +172,7 @@ var createPriorityCombo = function(id, selected_prio) { } select.addClass("combo_priority"); return select; -} +}; var filesDynTable = new Class({ @@ -231,8 +231,8 @@ var filesDynTable = new Class({ insertRow: function(id, row) { if (this.rows.has(id)) { - var tr = this.rows.get(id); - this.updateRow(tr, row, id); + var tableRow = this.rows.get(id); + this.updateRow(tableRow, row, id); return; } //this.removeRow(id); @@ -279,7 +279,7 @@ var loadTorrentFilesData = function() { return; } var new_hash = torrentsTable.getCurrentTorrentHash(); - if (new_hash == "") { + if (new_hash === "") { fTable.removeAllRows(); clearTimeout(loadTorrentFilesDataTimer); loadTorrentFilesDataTimer = loadTorrentFilesData.delay(5000); @@ -305,17 +305,17 @@ var loadTorrentFilesData = function() { // Update Trackers data var i = 0; files.each(function(file) { - if (i == 0) { + if (i === 0) { is_seed = file.is_seed; } - var row = new Array(); + var row = []; row.length = 4; row[0] = file.priority; row[1] = escapeHtml(file.name); row[2] = friendlyUnit(file.size, false); row[3] = (file.progress * 100).round(1); if (row[3] == 100.0 && file.progress < 1.0) - row[3] = 99.9 + row[3] = 99.9; row[4] = file.priority; fTable.insertRow(i, row); i++; @@ -340,12 +340,12 @@ var loadTorrentFilesData = function() { loadTorrentFilesDataTimer = loadTorrentFilesData.delay(5000); } }).send(); -} +}; var updateTorrentFilesData = function() { clearTimeout(loadTorrentFilesDataTimer); loadTorrentFilesData(); -} +}; fTable = new filesDynTable(); fTable.setup($('filesTable')); diff --git a/src/webui/www/public/scripts/prop-general.js b/src/webui/www/public/scripts/prop-general.js index 27a0755a63e..346404c7d05 100644 --- a/src/webui/www/public/scripts/prop-general.js +++ b/src/webui/www/public/scripts/prop-general.js @@ -23,7 +23,7 @@ var clearData = function() { $('torrent_hash').set('html', ''); $('save_path').set('html', ''); $('comment').set('html', ''); -} +}; var loadTorrentDataTimer; var loadTorrentData = function() { @@ -33,7 +33,7 @@ var loadTorrentData = function() { return; } var current_hash = torrentsTable.getCurrentTorrentHash(); - if (current_hash == "") { + if (current_hash === "") { clearData(); clearTimeout(loadTorrentDataTimer); loadTorrentDataTimer = loadTorrentData.delay(5000); @@ -59,26 +59,26 @@ var loadTorrentData = function() { if (data.seeding_time > 0) temp = "QBT_TR(%1 (%2 this session))QBT_TR[CONTEXT=PropertiesWidget]" .replace("%1", friendlyDuration(data.time_elapsed)) - .replace("%2", friendlyDuration(data.seeding_time)) + .replace("%2", friendlyDuration(data.seeding_time)); else - temp = friendlyDuration(data.time_elapsed) + temp = friendlyDuration(data.time_elapsed); $('time_elapsed').set('html', temp); $('eta').set('html', friendlyDuration(data.eta)); temp = "QBT_TR(%1 (%2 max))QBT_TR[CONTEXT=PropertiesWidget]" .replace("%1", data.nb_connections) - .replace("%2", data.nb_connections_limit < 0 ? "∞" : data.nb_connections_limit) + .replace("%2", data.nb_connections_limit < 0 ? "∞" : data.nb_connections_limit); $('nb_connections').set('html', temp); temp = "QBT_TR(%1 (%2 this session))QBT_TR[CONTEXT=PropertiesWidget]" .replace("%1", friendlyUnit(data.total_downloaded)) - .replace("%2", friendlyUnit(data.total_downloaded_session)) + .replace("%2", friendlyUnit(data.total_downloaded_session)); $('total_downloaded').set('html', temp); temp = "QBT_TR(%1 (%2 this session))QBT_TR[CONTEXT=PropertiesWidget]" .replace("%1", friendlyUnit(data.total_uploaded)) - .replace("%2", friendlyUnit(data.total_uploaded_session)) + .replace("%2", friendlyUnit(data.total_uploaded_session)); $('total_uploaded').set('html', temp); temp = "QBT_TR(%1 (%2 avg.))QBT_TR[CONTEXT=PropertiesWidget]" @@ -161,9 +161,9 @@ var loadTorrentData = function() { loadTorrentDataTimer = loadTorrentData.delay(5000); } }).send(); -} +}; var updateTorrentData = function() { clearTimeout(loadTorrentDataTimer); loadTorrentData(); -} +}; diff --git a/src/webui/www/public/scripts/prop-trackers.js b/src/webui/www/public/scripts/prop-trackers.js index 7d14c8b434e..32a61b0560b 100644 --- a/src/webui/www/public/scripts/prop-trackers.js +++ b/src/webui/www/public/scripts/prop-trackers.js @@ -34,8 +34,8 @@ var trackersDynTable = new Class({ insertRow: function(row) { var url = row[0]; if (this.rows.has(url)) { - var tr = this.rows.get(url); - this.updateRow(tr, row); + var tableRow = this.rows.get(url); + this.updateRow(tableRow, row); return; } //this.removeRow(id); @@ -60,7 +60,7 @@ var loadTrackersData = function() { return; } var new_hash = torrentsTable.getCurrentTorrentHash(); - if (new_hash == "") { + if (new_hash === "") { tTable.removeAllRows(); clearTimeout(loadTrackersDataTimer); loadTrackersDataTimer = loadTrackersData.delay(10000); @@ -85,7 +85,7 @@ var loadTrackersData = function() { if (trackers) { // Update Trackers data trackers.each(function(tracker) { - var row = new Array(); + var row = []; row.length = 4; row[0] = escapeHtml(tracker.url); row[1] = tracker.status; @@ -101,19 +101,19 @@ var loadTrackersData = function() { loadTrackersDataTimer = loadTrackersData.delay(10000); } }).send(); -} +}; var updateTrackersData = function() { clearTimeout(loadTrackersDataTimer); loadTrackersData(); -} +}; tTable = new trackersDynTable(); tTable.setup($('trackersTable')); // Add trackers code $('addTrackersPlus').addEvent('click', function addTrackerDlg() { - if (current_hash.length == 0) return; + if (current_hash.length === 0) return; new MochaUI.Window({ id: 'trackersPage', title: "QBT_TR(Trackers addition dialog)QBT_TR[CONTEXT=TrackersAdditionDlg]", diff --git a/src/webui/www/public/scripts/prop-webseeds.js b/src/webui/www/public/scripts/prop-webseeds.js index fcee273205e..12f366ca915 100644 --- a/src/webui/www/public/scripts/prop-webseeds.js +++ b/src/webui/www/public/scripts/prop-webseeds.js @@ -34,8 +34,8 @@ var webseedsDynTable = new Class({ insertRow: function(row) { var url = row[0]; if (this.rows.has(url)) { - var tr = this.rows.get(url); - this.updateRow(tr, row); + var tableRow = this.rows.get(url); + this.updateRow(tableRow, row); return; } //this.removeRow(id); @@ -60,7 +60,7 @@ var loadWebSeedsData = function() { return; } var new_hash = torrentsTable.getCurrentTorrentHash(); - if (new_hash == "") { + if (new_hash === "") { wsTable.removeAllRows(); clearTimeout(loadWebSeedsDataTimer); loadWebSeedsDataTimer = loadWebSeedsData.delay(10000); @@ -85,7 +85,7 @@ var loadWebSeedsData = function() { if (webseeds) { // Update WebSeeds data webseeds.each(function(webseed) { - var row = new Array(); + var row = []; row.length = 1; row[0] = webseed.url; wsTable.insertRow(row); @@ -98,12 +98,12 @@ var loadWebSeedsData = function() { loadWebSeedsDataTimer = loadWebSeedsData.delay(10000); } }).send(); -} +}; var updateWebSeedsData = function() { clearTimeout(loadWebSeedsDataTimer); loadWebSeedsData(); -} +}; wsTable = new webseedsDynTable(); wsTable.setup($('webseedsTable')); diff --git a/unixconf.pri b/unixconf.pri index 27166909a0d..b99a1d17813 100644 --- a/unixconf.pri +++ b/unixconf.pri @@ -32,7 +32,7 @@ DIST_PATH = ../dist/unix # Systemd Service file nogui:systemd { - systemdService.files = $$DIST_PATH/systemd/qbittorrent-nox.service + systemdService.files = $$DIST_PATH/systemd/qbittorrent-nox@.service systemdService.path = $$PREFIX/lib/systemd/system/ INSTALLS += systemdService } diff --git a/version.pri b/version.pri index ad7cde4c043..654dcfc7511 100644 --- a/version.pri +++ b/version.pri @@ -4,7 +4,7 @@ PROJECT_NAME = qbittorrent # Define version numbers here VER_MAJOR = 3 VER_MINOR = 3 -VER_BUGFIX = 13 +VER_BUGFIX = 14 VER_BUILD = 1 VER_STATUS = # Should be empty for stable releases! diff --git a/winconf-msvc.pri b/winconf-msvc.pri index 7b5ff21a32b..266b55adf75 100644 --- a/winconf-msvc.pri +++ b/winconf-msvc.pri @@ -12,17 +12,18 @@ strace_win { } CONFIG -= embed_manifest_exe -QMAKE_LFLAGS += "/OPT:REF /OPT:ICF /MANIFEST:EMBED /MANIFESTINPUT:$$quote($${PWD}/src/qbittorrent.exe.manifest)" +QMAKE_LFLAGS += "/MANIFEST:EMBED /MANIFESTINPUT:$$quote($${PWD}/src/qbittorrent.exe.manifest) /STACK:0x800000" +QMAKE_LFLAGS_RELEASE += "/OPT:REF /OPT:ICF" RC_FILE = qbittorrent.rc # Adapt the lib names/versions accordingly CONFIG(debug, debug|release) { LIBS += libtorrentd.lib \ - libboost_system-vc140-mt-s-1_63.lib + libboost_system-vc140-mt-s-1_64.lib } else { LIBS += libtorrent.lib \ - libboost_system-vc140-mt-s-1_63.lib + libboost_system-vc140-mt-s-1_64.lib } LIBS += advapi32.lib shell32.lib crypt32.lib User32.lib diff --git a/winconf.pri b/winconf.pri index 4ec4c0b5a1a..d8e075d1a0e 100644 --- a/winconf.pri +++ b/winconf.pri @@ -3,24 +3,25 @@ # Point this to the boost include folder INCLUDEPATH += $$quote(C:/qBittorrent/install_msvc/base/include) # Point this to the libtorrent include folder -#INCLUDEPATH += $$quote(C:/qBittorrent/RC_0_16/include) +INCLUDEPATH += $$quote(C:/qBittorrent/RC_0_16/include) # Point this to the zlib include folder -#INCLUDEPATH += $$quote(C:/qBittorrent/Zlib/include) +INCLUDEPATH += $$quote(C:/qBittorrent/Zlib/include) # Point this to the openssl include folder -#INCLUDEPATH += $$quote(C:/qBittorrent/openssl/include) +INCLUDEPATH += $$quote(C:/qBittorrent/openssl/include) # Point this to the boost lib folder LIBS += $$quote(-LC:/qBittorrent/install_msvc/base/lib) # Point this to the libtorrent lib folder -#LIBS += $$quote(-LC:/qBittorrent/RC_0_16/bin/path-according-to-the-build-options-chosen) +LIBS += $$quote(-LC:/qBittorrent/RC_0_16/bin/path-according-to-the-build-options-chosen) # Point this to the zlib lib folder -#LIBS += $$quote(-LC:/qBittorrent/Zlib/lib) +LIBS += $$quote(-LC:/qBittorrent/Zlib/lib) # Point this to the openssl lib folder -#LIBS += $$quote(-LC:/qBittorrent/openssl/lib) +LIBS += $$quote(-LC:/qBittorrent/openssl/lib) # BOOST DEFINES DEFINES += BOOST_ALL_NO_LIB DEFINES += BOOST_ASIO_HASH_MAP_BUCKETS=1021 +# Disable this if building against libtorrent 1.1.x (RC_1_1) DEFINES += BOOST_ASIO_SEPARATE_COMPILATION # After 1.55 some Windows users reported regular UI freezes. # This makes ASIO use the pre-1.56 way of doing things. See issue #2003