diff --git a/.tx/config b/.tx/config index 4192cea9a..52bed83f3 100644 --- a/.tx/config +++ b/.tx/config @@ -11,7 +11,7 @@ mode = developer [qbittorrent.qbittorrentdesktop_master] -source_file = src/Icons/qBittorrent.desktop +source_file = src/icons/qBittorrent.desktop source_lang = en type = DESKTOP minimum_perc = 23 diff --git a/AUTHORS b/AUTHORS index f4db303d9..e93cb50fa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,7 +17,7 @@ Contributors: * Nick Tiskov Code from other projects: -* files src/qtsingleapp/* src/lineedit/* +* files src/qtsingleapplication/* src/lineedit/* copyright: Nokia Corporation license: LGPL @@ -34,7 +34,7 @@ Code from other projects: license: GPLv2/3 Images Authors: -* files: src/Icons/*.png +* files: src/icons/*.png copyright: Gnome Icon Theme license: GPLv2 url: http://ftp.acc.umu.se/pub/GNOME/sources/gnome-icon-theme @@ -44,21 +44,21 @@ Images Authors: license: LGPL url: http://www.oxygen-icons.org -* files: src/Icons/flags/*.png +* files: src/icons/flags/*.png copyright: Mark James license: Public Domain url: http://www.famfamfam.com -* files: src/Icons/skin/*.png +* files: src/icons/skin/*.png files: src/menuicons/YYxYY/*.png copyright: Mateusz Tobola license: GPLv2 -* file: src/Icons/skin/tabs.gif +* file: src/icons/skin/tabs.gif copyright: Greg Houston license: MIT -* file: src/Icons/skin/qbittorrent_mono* +* file: src/icons/skin/qbittorrent_mono* copyright: Daniel Eguren license: LGPL @@ -77,12 +77,12 @@ Images Authors: * file: src/search_engine/engines/torrentreactor.png copyright: Downloaded from torrentreactor.net -* file: src/Icons/oxygen/checked.png +* file: src/icons/oxygen/checked.png copyright: Victor Buinsky Translations authors: * files: src/lang/*.ts -* file: src/Icons/qBittorrent.desktop +* file: src/icons/qBittorrent.desktop copyright: - Arabic: SDERAWI (abz8868@msn.com), sn51234 (nesseyan@gmail.com) and Ibrahim Saed ibraheem_alex(Transifex) - Armenian: Hrant Ohanyan (hrantohanyan@mail.am) diff --git a/configure b/configure index f81f0a200..d42059295 100755 --- a/configure +++ b/configure @@ -717,6 +717,7 @@ with_geoip_database_embedded with_qtsingleapplication enable_debug enable_gui +enable_webui enable_qt_dbus with_boost with_boost_libdir @@ -1367,6 +1368,7 @@ Optional Features: --disable-silent-rules verbose build output (undo: "make V=0") --enable-debug Enable debug build --disable-gui Disable the GUI for headless running. Disables + --disable-webui Disable the WebUI. QtDBus and the GeoIP Database. --disable-qt-dbus Disable use of QtDBus (GUI only) @@ -4199,6 +4201,14 @@ else fi +# Check whether --enable-webui was given. +if test "${enable_webui+set}" = set; then : + enableval=$enable_webui; +else + enable_webui=yes +fi + + # Check whether --enable-qt-dbus was given. if test "${enable_qt_dbus+set}" = set; then : enableval=$enable_qt_dbus; @@ -4370,19 +4380,36 @@ case "x$enable_gui" in #( "xyes") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nox" ;; #( + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nogui" ;; #( "xno") : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } enable_qt_dbus=no enable_geoip_database=no - QBT_ADD_CONFIG="$QBT_ADD_CONFIG nox" ;; #( + QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #( *) : { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gui" >&5 $as_echo "$enable_gui" >&6; } as_fn_error $? "Unknown option \"$enable_gui\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the WebUI" >&5 +$as_echo_n "checking whether to enable the WebUI... " >&6; } +case "x$enable_webui" in #( + "xyes") : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nowebui" ;; #( + "xno") : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + QBT_ADD_CONFIG="$QBT_ADD_CONFIG nowebui" ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_webui" >&5 +$as_echo "$enable_webui" >&6; } + as_fn_error $? "Unknown option \"$enable_webui\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Qt5 should be enabled" >&5 $as_echo_n "checking whether Qt5 should be enabled... " >&6; } case "x$with_qt5" in #( diff --git a/configure.ac b/configure.ac index 0ffdf7065..a358587da 100644 --- a/configure.ac +++ b/configure.ac @@ -42,6 +42,12 @@ AC_ARG_ENABLE(gui, [], [enable_gui=yes]) +AC_ARG_ENABLE(webui, + [AS_HELP_STRING([--disable-webui], + [Disable the WebUI.])], + [], + [enable_webui=yes]) + AC_ARG_ENABLE(qt-dbus, [AS_HELP_STRING([--disable-qt-dbus], [Disable use of QtDBus (GUI only)])], @@ -78,15 +84,26 @@ AC_MSG_CHECKING([whether to enable the GUI]) AS_CASE(["x$enable_gui"], ["xyes"], [AC_MSG_RESULT([yes]) - QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nox"], + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nogui"], ["xno"], [AC_MSG_RESULT([no]) enable_qt_dbus=[no] enable_geoip_database=[no] - QBT_ADD_CONFIG="$QBT_ADD_CONFIG nox"], + QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui"], [AC_MSG_RESULT([$enable_gui]) AC_MSG_ERROR([Unknown option "$enable_gui". Use either "yes" or "no".])]) +AC_MSG_CHECKING([whether to enable the WebUI]) +AS_CASE(["x$enable_webui"], + ["xyes"], + [AC_MSG_RESULT([yes]) + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG nowebui"], + ["xno"], + [AC_MSG_RESULT([no]) + QBT_ADD_CONFIG="$QBT_ADD_CONFIG nowebui"], + [AC_MSG_RESULT([$enable_webui]) + AC_MSG_ERROR([Unknown option "$enable_webui". Use either "yes" or "no".])]) + AC_MSG_CHECKING([whether Qt5 should be enabled]) AS_CASE(["x$with_qt5"], ["xno"], diff --git a/src/mac/Info.plist b/dist/mac/Info.plist similarity index 100% rename from src/mac/Info.plist rename to dist/mac/Info.plist diff --git a/src/mac/qBitTorrentDocument.icns b/dist/mac/qBitTorrentDocument.icns similarity index 100% rename from src/mac/qBitTorrentDocument.icns rename to dist/mac/qBitTorrentDocument.icns diff --git a/src/mac/qbittorrent_mac.icns b/dist/mac/qbittorrent_mac.icns similarity index 100% rename from src/mac/qbittorrent_mac.icns rename to dist/mac/qbittorrent_mac.icns diff --git a/src/mac/qt.conf b/dist/mac/qt.conf similarity index 100% rename from src/mac/qt.conf rename to dist/mac/qt.conf diff --git a/src/qt-translations/qt_ar.qm b/dist/qt-translations/qt_ar.qm similarity index 100% rename from src/qt-translations/qt_ar.qm rename to dist/qt-translations/qt_ar.qm diff --git a/src/qt-translations/qt_bg.qm b/dist/qt-translations/qt_bg.qm similarity index 100% rename from src/qt-translations/qt_bg.qm rename to dist/qt-translations/qt_bg.qm diff --git a/src/qt-translations/qt_ca.qm b/dist/qt-translations/qt_ca.qm similarity index 100% rename from src/qt-translations/qt_ca.qm rename to dist/qt-translations/qt_ca.qm diff --git a/src/qt-translations/qt_cs.qm b/dist/qt-translations/qt_cs.qm similarity index 100% rename from src/qt-translations/qt_cs.qm rename to dist/qt-translations/qt_cs.qm diff --git a/src/qt-translations/qt_da.qm b/dist/qt-translations/qt_da.qm similarity index 100% rename from src/qt-translations/qt_da.qm rename to dist/qt-translations/qt_da.qm diff --git a/src/qt-translations/qt_de.qm b/dist/qt-translations/qt_de.qm similarity index 100% rename from src/qt-translations/qt_de.qm rename to dist/qt-translations/qt_de.qm diff --git a/src/qt-translations/qt_es.qm b/dist/qt-translations/qt_es.qm similarity index 100% rename from src/qt-translations/qt_es.qm rename to dist/qt-translations/qt_es.qm diff --git a/src/qt-translations/qt_eu.qm b/dist/qt-translations/qt_eu.qm similarity index 100% rename from src/qt-translations/qt_eu.qm rename to dist/qt-translations/qt_eu.qm diff --git a/src/qt-translations/qt_fi.qm b/dist/qt-translations/qt_fi.qm similarity index 100% rename from src/qt-translations/qt_fi.qm rename to dist/qt-translations/qt_fi.qm diff --git a/src/qt-translations/qt_fr.qm b/dist/qt-translations/qt_fr.qm similarity index 100% rename from src/qt-translations/qt_fr.qm rename to dist/qt-translations/qt_fr.qm diff --git a/src/qt-translations/qt_gl.qm b/dist/qt-translations/qt_gl.qm similarity index 100% rename from src/qt-translations/qt_gl.qm rename to dist/qt-translations/qt_gl.qm diff --git a/src/qt-translations/qt_he.qm b/dist/qt-translations/qt_he.qm similarity index 100% rename from src/qt-translations/qt_he.qm rename to dist/qt-translations/qt_he.qm diff --git a/src/qt-translations/qt_hu.qm b/dist/qt-translations/qt_hu.qm similarity index 100% rename from src/qt-translations/qt_hu.qm rename to dist/qt-translations/qt_hu.qm diff --git a/src/qt-translations/qt_it.qm b/dist/qt-translations/qt_it.qm similarity index 100% rename from src/qt-translations/qt_it.qm rename to dist/qt-translations/qt_it.qm diff --git a/src/qt-translations/qt_ja.qm b/dist/qt-translations/qt_ja.qm similarity index 100% rename from src/qt-translations/qt_ja.qm rename to dist/qt-translations/qt_ja.qm diff --git a/src/qt-translations/qt_ko.qm b/dist/qt-translations/qt_ko.qm similarity index 100% rename from src/qt-translations/qt_ko.qm rename to dist/qt-translations/qt_ko.qm diff --git a/src/qt-translations/qt_lt.qm b/dist/qt-translations/qt_lt.qm similarity index 100% rename from src/qt-translations/qt_lt.qm rename to dist/qt-translations/qt_lt.qm diff --git a/src/qt-translations/qt_nl.qm b/dist/qt-translations/qt_nl.qm similarity index 100% rename from src/qt-translations/qt_nl.qm rename to dist/qt-translations/qt_nl.qm diff --git a/src/qt-translations/qt_pl.qm b/dist/qt-translations/qt_pl.qm similarity index 100% rename from src/qt-translations/qt_pl.qm rename to dist/qt-translations/qt_pl.qm diff --git a/src/qt-translations/qt_pt.qm b/dist/qt-translations/qt_pt.qm similarity index 100% rename from src/qt-translations/qt_pt.qm rename to dist/qt-translations/qt_pt.qm diff --git a/src/qt-translations/qt_pt_BR.qm b/dist/qt-translations/qt_pt_BR.qm similarity index 100% rename from src/qt-translations/qt_pt_BR.qm rename to dist/qt-translations/qt_pt_BR.qm diff --git a/src/qt-translations/qt_ru.qm b/dist/qt-translations/qt_ru.qm similarity index 100% rename from src/qt-translations/qt_ru.qm rename to dist/qt-translations/qt_ru.qm diff --git a/src/qt-translations/qt_sk.qm b/dist/qt-translations/qt_sk.qm similarity index 100% rename from src/qt-translations/qt_sk.qm rename to dist/qt-translations/qt_sk.qm diff --git a/src/qt-translations/qt_sv.qm b/dist/qt-translations/qt_sv.qm similarity index 100% rename from src/qt-translations/qt_sv.qm rename to dist/qt-translations/qt_sv.qm diff --git a/src/qt-translations/qt_tr.qm b/dist/qt-translations/qt_tr.qm similarity index 100% rename from src/qt-translations/qt_tr.qm rename to dist/qt-translations/qt_tr.qm diff --git a/src/qt-translations/qt_uk.qm b/dist/qt-translations/qt_uk.qm similarity index 100% rename from src/qt-translations/qt_uk.qm rename to dist/qt-translations/qt_uk.qm diff --git a/src/qt-translations/qt_zh_CN.qm b/dist/qt-translations/qt_zh_CN.qm similarity index 100% rename from src/qt-translations/qt_zh_CN.qm rename to dist/qt-translations/qt_zh_CN.qm diff --git a/src/qt-translations/qt_zh_TW.qm b/dist/qt-translations/qt_zh_TW.qm similarity index 100% rename from src/qt-translations/qt_zh_TW.qm rename to dist/qt-translations/qt_zh_TW.qm diff --git a/src/menuicons/128x128/apps/qbittorrent.png b/dist/unix/menuicons/128x128/apps/qbittorrent.png similarity index 100% rename from src/menuicons/128x128/apps/qbittorrent.png rename to dist/unix/menuicons/128x128/apps/qbittorrent.png diff --git a/src/menuicons/16x16/apps/qbittorrent.png b/dist/unix/menuicons/16x16/apps/qbittorrent.png similarity index 100% rename from src/menuicons/16x16/apps/qbittorrent.png rename to dist/unix/menuicons/16x16/apps/qbittorrent.png diff --git a/src/Icons/qbittorrent.png b/dist/unix/menuicons/192x192/apps/qbittorrent.png similarity index 100% rename from src/Icons/qbittorrent.png rename to dist/unix/menuicons/192x192/apps/qbittorrent.png diff --git a/src/menuicons/22x22/apps/qbittorrent.png b/dist/unix/menuicons/22x22/apps/qbittorrent.png similarity index 100% rename from src/menuicons/22x22/apps/qbittorrent.png rename to dist/unix/menuicons/22x22/apps/qbittorrent.png diff --git a/src/menuicons/24x24/apps/qbittorrent.png b/dist/unix/menuicons/24x24/apps/qbittorrent.png similarity index 100% rename from src/menuicons/24x24/apps/qbittorrent.png rename to dist/unix/menuicons/24x24/apps/qbittorrent.png diff --git a/src/menuicons/32x32/apps/qbittorrent.png b/dist/unix/menuicons/32x32/apps/qbittorrent.png similarity index 100% rename from src/menuicons/32x32/apps/qbittorrent.png rename to dist/unix/menuicons/32x32/apps/qbittorrent.png diff --git a/src/menuicons/36x36/apps/qbittorrent.png b/dist/unix/menuicons/36x36/apps/qbittorrent.png similarity index 100% rename from src/menuicons/36x36/apps/qbittorrent.png rename to dist/unix/menuicons/36x36/apps/qbittorrent.png diff --git a/src/menuicons/48x48/apps/qbittorrent.png b/dist/unix/menuicons/48x48/apps/qbittorrent.png similarity index 100% rename from src/menuicons/48x48/apps/qbittorrent.png rename to dist/unix/menuicons/48x48/apps/qbittorrent.png diff --git a/src/menuicons/64x64/apps/qbittorrent.png b/dist/unix/menuicons/64x64/apps/qbittorrent.png similarity index 100% rename from src/menuicons/64x64/apps/qbittorrent.png rename to dist/unix/menuicons/64x64/apps/qbittorrent.png diff --git a/src/menuicons/72x72/apps/qbittorrent.png b/dist/unix/menuicons/72x72/apps/qbittorrent.png similarity index 100% rename from src/menuicons/72x72/apps/qbittorrent.png rename to dist/unix/menuicons/72x72/apps/qbittorrent.png diff --git a/src/menuicons/96x96/apps/qbittorrent.png b/dist/unix/menuicons/96x96/apps/qbittorrent.png similarity index 100% rename from src/menuicons/96x96/apps/qbittorrent.png rename to dist/unix/menuicons/96x96/apps/qbittorrent.png diff --git a/src/windows/README.txt b/dist/windows/README.txt similarity index 95% rename from src/windows/README.txt rename to dist/windows/README.txt index 7cce83eff..d1299df41 100644 --- a/src/windows/README.txt +++ b/dist/windows/README.txt @@ -41,7 +41,7 @@ installer-translations translations qt_ar.qm ... - (all the .qm files found in src/qt-translations in every source release) + (all the .qm files found in dist/qt-translations in every source release) qt_zh_TW.qm installer.nsi license.txt @@ -54,7 +54,7 @@ uninstaller.nsi 5. "license.txt" is a text file that contains the text rendered - from src\gpl.html or the text contained in COPYING + from src\gui\gpl.html or the text contained in COPYING 6. "qbittorrent.exe" is the compiled binary file. SCRIPT HACKERS: diff --git a/src/windows/UAC.nsh b/dist/windows/UAC.nsh similarity index 100% rename from src/windows/UAC.nsh rename to dist/windows/UAC.nsh diff --git a/src/windows/installer-translations/afrikaans.nsi b/dist/windows/installer-translations/afrikaans.nsi similarity index 100% rename from src/windows/installer-translations/afrikaans.nsi rename to dist/windows/installer-translations/afrikaans.nsi diff --git a/src/windows/installer-translations/albanian.nsi b/dist/windows/installer-translations/albanian.nsi similarity index 100% rename from src/windows/installer-translations/albanian.nsi rename to dist/windows/installer-translations/albanian.nsi diff --git a/src/windows/installer-translations/arabic.nsi b/dist/windows/installer-translations/arabic.nsi similarity index 100% rename from src/windows/installer-translations/arabic.nsi rename to dist/windows/installer-translations/arabic.nsi diff --git a/src/windows/installer-translations/basque.nsi b/dist/windows/installer-translations/basque.nsi similarity index 100% rename from src/windows/installer-translations/basque.nsi rename to dist/windows/installer-translations/basque.nsi diff --git a/src/windows/installer-translations/belarusian.nsi b/dist/windows/installer-translations/belarusian.nsi similarity index 100% rename from src/windows/installer-translations/belarusian.nsi rename to dist/windows/installer-translations/belarusian.nsi diff --git a/src/windows/installer-translations/bosnian.nsi b/dist/windows/installer-translations/bosnian.nsi similarity index 100% rename from src/windows/installer-translations/bosnian.nsi rename to dist/windows/installer-translations/bosnian.nsi diff --git a/src/windows/installer-translations/breton.nsi b/dist/windows/installer-translations/breton.nsi similarity index 100% rename from src/windows/installer-translations/breton.nsi rename to dist/windows/installer-translations/breton.nsi diff --git a/src/windows/installer-translations/bulgarian.nsi b/dist/windows/installer-translations/bulgarian.nsi similarity index 100% rename from src/windows/installer-translations/bulgarian.nsi rename to dist/windows/installer-translations/bulgarian.nsi diff --git a/src/windows/installer-translations/catalan.nsi b/dist/windows/installer-translations/catalan.nsi similarity index 100% rename from src/windows/installer-translations/catalan.nsi rename to dist/windows/installer-translations/catalan.nsi diff --git a/src/windows/installer-translations/croatian.nsi b/dist/windows/installer-translations/croatian.nsi similarity index 100% rename from src/windows/installer-translations/croatian.nsi rename to dist/windows/installer-translations/croatian.nsi diff --git a/src/windows/installer-translations/czech.nsi b/dist/windows/installer-translations/czech.nsi similarity index 100% rename from src/windows/installer-translations/czech.nsi rename to dist/windows/installer-translations/czech.nsi diff --git a/src/windows/installer-translations/danish.nsi b/dist/windows/installer-translations/danish.nsi similarity index 100% rename from src/windows/installer-translations/danish.nsi rename to dist/windows/installer-translations/danish.nsi diff --git a/src/windows/installer-translations/dutch.nsi b/dist/windows/installer-translations/dutch.nsi similarity index 100% rename from src/windows/installer-translations/dutch.nsi rename to dist/windows/installer-translations/dutch.nsi diff --git a/src/windows/installer-translations/english.nsi b/dist/windows/installer-translations/english.nsi similarity index 100% rename from src/windows/installer-translations/english.nsi rename to dist/windows/installer-translations/english.nsi diff --git a/src/windows/installer-translations/esperanto.nsi b/dist/windows/installer-translations/esperanto.nsi similarity index 100% rename from src/windows/installer-translations/esperanto.nsi rename to dist/windows/installer-translations/esperanto.nsi diff --git a/src/windows/installer-translations/estonian.nsi b/dist/windows/installer-translations/estonian.nsi similarity index 100% rename from src/windows/installer-translations/estonian.nsi rename to dist/windows/installer-translations/estonian.nsi diff --git a/src/windows/installer-translations/farsi.nsi b/dist/windows/installer-translations/farsi.nsi similarity index 100% rename from src/windows/installer-translations/farsi.nsi rename to dist/windows/installer-translations/farsi.nsi diff --git a/src/windows/installer-translations/finnish.nsi b/dist/windows/installer-translations/finnish.nsi similarity index 100% rename from src/windows/installer-translations/finnish.nsi rename to dist/windows/installer-translations/finnish.nsi diff --git a/src/windows/installer-translations/french.nsi b/dist/windows/installer-translations/french.nsi similarity index 100% rename from src/windows/installer-translations/french.nsi rename to dist/windows/installer-translations/french.nsi diff --git a/src/windows/installer-translations/galician.nsi b/dist/windows/installer-translations/galician.nsi similarity index 100% rename from src/windows/installer-translations/galician.nsi rename to dist/windows/installer-translations/galician.nsi diff --git a/src/windows/installer-translations/german.nsi b/dist/windows/installer-translations/german.nsi similarity index 100% rename from src/windows/installer-translations/german.nsi rename to dist/windows/installer-translations/german.nsi diff --git a/src/windows/installer-translations/greek.nsi b/dist/windows/installer-translations/greek.nsi similarity index 100% rename from src/windows/installer-translations/greek.nsi rename to dist/windows/installer-translations/greek.nsi diff --git a/src/windows/installer-translations/hebrew.nsi b/dist/windows/installer-translations/hebrew.nsi similarity index 100% rename from src/windows/installer-translations/hebrew.nsi rename to dist/windows/installer-translations/hebrew.nsi diff --git a/src/windows/installer-translations/hungarian.nsi b/dist/windows/installer-translations/hungarian.nsi similarity index 100% rename from src/windows/installer-translations/hungarian.nsi rename to dist/windows/installer-translations/hungarian.nsi diff --git a/src/windows/installer-translations/icelandic.nsi b/dist/windows/installer-translations/icelandic.nsi similarity index 100% rename from src/windows/installer-translations/icelandic.nsi rename to dist/windows/installer-translations/icelandic.nsi diff --git a/src/windows/installer-translations/indonesian.nsi b/dist/windows/installer-translations/indonesian.nsi similarity index 100% rename from src/windows/installer-translations/indonesian.nsi rename to dist/windows/installer-translations/indonesian.nsi diff --git a/src/windows/installer-translations/irish.nsi b/dist/windows/installer-translations/irish.nsi similarity index 100% rename from src/windows/installer-translations/irish.nsi rename to dist/windows/installer-translations/irish.nsi diff --git a/src/windows/installer-translations/italian.nsi b/dist/windows/installer-translations/italian.nsi similarity index 100% rename from src/windows/installer-translations/italian.nsi rename to dist/windows/installer-translations/italian.nsi diff --git a/src/windows/installer-translations/japanese.nsi b/dist/windows/installer-translations/japanese.nsi similarity index 100% rename from src/windows/installer-translations/japanese.nsi rename to dist/windows/installer-translations/japanese.nsi diff --git a/src/windows/installer-translations/korean.nsi b/dist/windows/installer-translations/korean.nsi similarity index 100% rename from src/windows/installer-translations/korean.nsi rename to dist/windows/installer-translations/korean.nsi diff --git a/src/windows/installer-translations/kurdish.nsi b/dist/windows/installer-translations/kurdish.nsi similarity index 100% rename from src/windows/installer-translations/kurdish.nsi rename to dist/windows/installer-translations/kurdish.nsi diff --git a/src/windows/installer-translations/latvian.nsi b/dist/windows/installer-translations/latvian.nsi similarity index 100% rename from src/windows/installer-translations/latvian.nsi rename to dist/windows/installer-translations/latvian.nsi diff --git a/src/windows/installer-translations/lithuanian.nsi b/dist/windows/installer-translations/lithuanian.nsi similarity index 100% rename from src/windows/installer-translations/lithuanian.nsi rename to dist/windows/installer-translations/lithuanian.nsi diff --git a/src/windows/installer-translations/luxembourgish.nsi b/dist/windows/installer-translations/luxembourgish.nsi similarity index 100% rename from src/windows/installer-translations/luxembourgish.nsi rename to dist/windows/installer-translations/luxembourgish.nsi diff --git a/src/windows/installer-translations/macedonian.nsi b/dist/windows/installer-translations/macedonian.nsi similarity index 100% rename from src/windows/installer-translations/macedonian.nsi rename to dist/windows/installer-translations/macedonian.nsi diff --git a/src/windows/installer-translations/malay.nsi b/dist/windows/installer-translations/malay.nsi similarity index 100% rename from src/windows/installer-translations/malay.nsi rename to dist/windows/installer-translations/malay.nsi diff --git a/src/windows/installer-translations/mongolian.nsi b/dist/windows/installer-translations/mongolian.nsi similarity index 100% rename from src/windows/installer-translations/mongolian.nsi rename to dist/windows/installer-translations/mongolian.nsi diff --git a/src/windows/installer-translations/norwegian.nsi b/dist/windows/installer-translations/norwegian.nsi similarity index 100% rename from src/windows/installer-translations/norwegian.nsi rename to dist/windows/installer-translations/norwegian.nsi diff --git a/src/windows/installer-translations/norwegiannynorsk.nsi b/dist/windows/installer-translations/norwegiannynorsk.nsi similarity index 100% rename from src/windows/installer-translations/norwegiannynorsk.nsi rename to dist/windows/installer-translations/norwegiannynorsk.nsi diff --git a/src/windows/installer-translations/polish.nsi b/dist/windows/installer-translations/polish.nsi similarity index 100% rename from src/windows/installer-translations/polish.nsi rename to dist/windows/installer-translations/polish.nsi diff --git a/src/windows/installer-translations/portuguese.nsi b/dist/windows/installer-translations/portuguese.nsi similarity index 100% rename from src/windows/installer-translations/portuguese.nsi rename to dist/windows/installer-translations/portuguese.nsi diff --git a/src/windows/installer-translations/portugueseBR.nsi b/dist/windows/installer-translations/portugueseBR.nsi similarity index 100% rename from src/windows/installer-translations/portugueseBR.nsi rename to dist/windows/installer-translations/portugueseBR.nsi diff --git a/src/windows/installer-translations/romanian.nsi b/dist/windows/installer-translations/romanian.nsi similarity index 100% rename from src/windows/installer-translations/romanian.nsi rename to dist/windows/installer-translations/romanian.nsi diff --git a/src/windows/installer-translations/russian.nsi b/dist/windows/installer-translations/russian.nsi similarity index 100% rename from src/windows/installer-translations/russian.nsi rename to dist/windows/installer-translations/russian.nsi diff --git a/src/windows/installer-translations/serbian.nsi b/dist/windows/installer-translations/serbian.nsi similarity index 100% rename from src/windows/installer-translations/serbian.nsi rename to dist/windows/installer-translations/serbian.nsi diff --git a/src/windows/installer-translations/serbianlatin.nsi b/dist/windows/installer-translations/serbianlatin.nsi similarity index 100% rename from src/windows/installer-translations/serbianlatin.nsi rename to dist/windows/installer-translations/serbianlatin.nsi diff --git a/src/windows/installer-translations/simpchinese.nsi b/dist/windows/installer-translations/simpchinese.nsi similarity index 100% rename from src/windows/installer-translations/simpchinese.nsi rename to dist/windows/installer-translations/simpchinese.nsi diff --git a/src/windows/installer-translations/slovak.nsi b/dist/windows/installer-translations/slovak.nsi similarity index 100% rename from src/windows/installer-translations/slovak.nsi rename to dist/windows/installer-translations/slovak.nsi diff --git a/src/windows/installer-translations/slovenian.nsi b/dist/windows/installer-translations/slovenian.nsi similarity index 100% rename from src/windows/installer-translations/slovenian.nsi rename to dist/windows/installer-translations/slovenian.nsi diff --git a/src/windows/installer-translations/spanish.nsi b/dist/windows/installer-translations/spanish.nsi similarity index 100% rename from src/windows/installer-translations/spanish.nsi rename to dist/windows/installer-translations/spanish.nsi diff --git a/src/windows/installer-translations/spanishinternational.nsi b/dist/windows/installer-translations/spanishinternational.nsi similarity index 100% rename from src/windows/installer-translations/spanishinternational.nsi rename to dist/windows/installer-translations/spanishinternational.nsi diff --git a/src/windows/installer-translations/swedish.nsi b/dist/windows/installer-translations/swedish.nsi similarity index 100% rename from src/windows/installer-translations/swedish.nsi rename to dist/windows/installer-translations/swedish.nsi diff --git a/src/windows/installer-translations/thai.nsi b/dist/windows/installer-translations/thai.nsi similarity index 100% rename from src/windows/installer-translations/thai.nsi rename to dist/windows/installer-translations/thai.nsi diff --git a/src/windows/installer-translations/tradchinese.nsi b/dist/windows/installer-translations/tradchinese.nsi similarity index 100% rename from src/windows/installer-translations/tradchinese.nsi rename to dist/windows/installer-translations/tradchinese.nsi diff --git a/src/windows/installer-translations/turkish.nsi b/dist/windows/installer-translations/turkish.nsi similarity index 100% rename from src/windows/installer-translations/turkish.nsi rename to dist/windows/installer-translations/turkish.nsi diff --git a/src/windows/installer-translations/ukrainian.nsi b/dist/windows/installer-translations/ukrainian.nsi similarity index 100% rename from src/windows/installer-translations/ukrainian.nsi rename to dist/windows/installer-translations/ukrainian.nsi diff --git a/src/windows/installer-translations/uzbek.nsi b/dist/windows/installer-translations/uzbek.nsi similarity index 100% rename from src/windows/installer-translations/uzbek.nsi rename to dist/windows/installer-translations/uzbek.nsi diff --git a/src/windows/installer-translations/welsh.nsi b/dist/windows/installer-translations/welsh.nsi similarity index 100% rename from src/windows/installer-translations/welsh.nsi rename to dist/windows/installer-translations/welsh.nsi diff --git a/src/windows/installer.nsi b/dist/windows/installer.nsi similarity index 100% rename from src/windows/installer.nsi rename to dist/windows/installer.nsi diff --git a/src/windows/nsis plugins/FindProc Unicode-source.zip b/dist/windows/nsis plugins/FindProc Unicode-source.zip similarity index 100% rename from src/windows/nsis plugins/FindProc Unicode-source.zip rename to dist/windows/nsis plugins/FindProc Unicode-source.zip diff --git a/src/windows/nsis plugins/FindProcDLL Unicode bin.zip b/dist/windows/nsis plugins/FindProcDLL Unicode bin.zip similarity index 100% rename from src/windows/nsis plugins/FindProcDLL Unicode bin.zip rename to dist/windows/nsis plugins/FindProcDLL Unicode bin.zip diff --git a/src/windows/nsis plugins/UAC Unicode.zip b/dist/windows/nsis plugins/UAC Unicode.zip similarity index 100% rename from src/windows/nsis plugins/UAC Unicode.zip rename to dist/windows/nsis plugins/UAC Unicode.zip diff --git a/src/windows/nsis plugins/UAC.zip b/dist/windows/nsis plugins/UAC.zip similarity index 100% rename from src/windows/nsis plugins/UAC.zip rename to dist/windows/nsis plugins/UAC.zip diff --git a/src/windows/nsis plugins/nsisFirewall.zip b/dist/windows/nsis plugins/nsisFirewall.zip similarity index 100% rename from src/windows/nsis plugins/nsisFirewall.zip rename to dist/windows/nsis plugins/nsisFirewall.zip diff --git a/src/windows/options.nsi b/dist/windows/options.nsi similarity index 100% rename from src/windows/options.nsi rename to dist/windows/options.nsi diff --git a/src/windows/qbittorrent.nsi b/dist/windows/qbittorrent.nsi similarity index 100% rename from src/windows/qbittorrent.nsi rename to dist/windows/qbittorrent.nsi diff --git a/src/windows/qt.conf b/dist/windows/qt.conf similarity index 100% rename from src/windows/qt.conf rename to dist/windows/qt.conf diff --git a/src/windows/translations.nsi b/dist/windows/translations.nsi similarity index 100% rename from src/windows/translations.nsi rename to dist/windows/translations.nsi diff --git a/src/windows/uninstaller.nsi b/dist/windows/uninstaller.nsi similarity index 100% rename from src/windows/uninstaller.nsi rename to dist/windows/uninstaller.nsi diff --git a/macxconf.pri b/macxconf.pri index 653a9ec43..c4261d742 100644 --- a/macxconf.pri +++ b/macxconf.pri @@ -12,46 +12,50 @@ exists($$OUT_PWD/../conf.pri) { LIBS += -framework Carbon -framework IOKit CONFIG += c++11 +QT_LANG_PATH = ../dist/qt-translations +DIST_PATH = ../dist/mac + document_icon.path = Contents/Resources -document_icon.files = mac/qBitTorrentDocument.icns +document_icon.files = $$DIST_PATH/qBitTorrentDocument.icns QMAKE_BUNDLE_DATA += document_icon qt_conf.path = Contents/Resources -qt_conf.files = mac/qt.conf +qt_conf.files = $$DIST_PATH/qt.conf QMAKE_BUNDLE_DATA += qt_conf qt_translations.path = Contents/translations -qt_translations.files = qt-translations/qt_ar.qm \ - qt-translations/qt_bg.qm \ - qt-translations/qt_ca.qm \ - qt-translations/qt_cs.qm \ - qt-translations/qt_da.qm \ - qt-translations/qt_de.qm \ - qt-translations/qt_es.qm \ - qt-translations/qt_fi.qm \ - qt-translations/qt_fr.qm \ - qt-translations/qt_gl.qm \ - qt-translations/qt_he.qm \ - qt-translations/qt_hu.qm \ - qt-translations/qt_it.qm \ - qt-translations/qt_ja.qm \ - qt-translations/qt_ko.qm \ - qt-translations/qt_lt.qm \ - qt-translations/qt_nl.qm \ - qt-translations/qt_pl.qm \ - qt-translations/qt_pt.qm \ - qt-translations/qt_pt_BR.qm \ - qt-translations/qt_ru.qm \ - qt-translations/qt_sk.qm \ - qt-translations/qt_sv.qm \ - qt-translations/qt_tr.qm \ - qt-translations/qt_uk.qm \ - qt-translations/qt_zh_CN.qm \ - qt-translations/qt_zh_TW.qm +qt_translations.files = \ + $$QT_LANG_PATH/qt_ar.qm \ + $$QT_LANG_PATH/qt_bg.qm \ + $$QT_LANG_PATH/qt_ca.qm \ + $$QT_LANG_PATH/qt_cs.qm \ + $$QT_LANG_PATH/qt_da.qm \ + $$QT_LANG_PATH/qt_de.qm \ + $$QT_LANG_PATH/qt_es.qm \ + $$QT_LANG_PATH/qt_fi.qm \ + $$QT_LANG_PATH/qt_fr.qm \ + $$QT_LANG_PATH/qt_gl.qm \ + $$QT_LANG_PATH/qt_he.qm \ + $$QT_LANG_PATH/qt_hu.qm \ + $$QT_LANG_PATH/qt_it.qm \ + $$QT_LANG_PATH/qt_ja.qm \ + $$QT_LANG_PATH/qt_ko.qm \ + $$QT_LANG_PATH/qt_lt.qm \ + $$QT_LANG_PATH/qt_nl.qm \ + $$QT_LANG_PATH/qt_pl.qm \ + $$QT_LANG_PATH/qt_pt.qm \ + $$QT_LANG_PATH/qt_pt_BR.qm \ + $$QT_LANG_PATH/qt_ru.qm \ + $$QT_LANG_PATH/qt_sk.qm \ + $$QT_LANG_PATH/qt_sv.qm \ + $$QT_LANG_PATH/qt_tr.qm \ + $$QT_LANG_PATH/qt_uk.qm \ + $$QT_LANG_PATH/qt_zh_CN.qm \ + $$QT_LANG_PATH/qt_zh_TW.qm QMAKE_BUNDLE_DATA += qt_translations -ICON = mac/qbittorrent_mac.icns -QMAKE_INFO_PLIST = mac/Info.plist +ICON = $$DIST_PATH/qbittorrent_mac.icns +QMAKE_INFO_PLIST = $$DIST_PATH/Info.plist DEFINES += WITH_GEOIP_EMBEDDED message("On Mac OS X, GeoIP database must be embedded.") diff --git a/os2conf.pri b/os2conf.pri index 2e49504e8..b0678d4ff 100644 --- a/os2conf.pri +++ b/os2conf.pri @@ -3,11 +3,12 @@ exists(conf.pri) { include(conf.pri) } -LIBS += -ltorrent-rasterbar \ - -lboost_thread \ - -lboost_system \ - -lboost_filesystem \ - -lssl -lcrypto -lidn -lpthread -lz +LIBS += \ + -ltorrent-rasterbar \ + -lboost_thread \ + -lboost_system \ + -lboost_filesystem \ + -lssl -lcrypto -lidn -lpthread -lz RC_FILE = qbittorrent_os2.rc diff --git a/qbittorrent.qc b/qbittorrent.qc deleted file mode 100644 index 6f10673f4..000000000 --- a/qbittorrent.qc +++ /dev/null @@ -1,25 +0,0 @@ - - qbittorrent - qbittorrent.pro - qcm - - - - - - - - - - - - - - - - - - - - - diff --git a/src/app/app.pri b/src/app/app.pri new file mode 100644 index 000000000..22a4d859e --- /dev/null +++ b/src/app/app.pri @@ -0,0 +1,29 @@ +INCLUDEPATH += $$PWD + +usesystemqtsingleapplication { + nogui { + CONFIG += qtsinglecoreapplication + } else { + CONFIG += qtsingleapplication + } +} else { + nogui { + include(qtsingleapplication/qtsinglecoreapplication.pri) + } else { + include(qtsingleapplication/qtsingleapplication.pri) + } +} + +HEADERS += $$PWD/application.h +SOURCES += $$PWD/application.cpp + +unix: HEADERS += $$PWD/stacktrace.h +strace_win { + HEADERS += $$PWD/stacktrace_win.h + !nogui { + HEADERS += $$PWD/stacktrace_win_dlg.h + FORMS += $$PWD/stacktrace_win_dlg.ui + } +} + +SOURCES += $$PWD/main.cpp diff --git a/src/app/application.cpp b/src/app/application.cpp new file mode 100644 index 000000000..73470159f --- /dev/null +++ b/src/app/application.cpp @@ -0,0 +1,303 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2006 Christophe Dumez + * + * 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 +#include +#include +#include +#include + +#ifndef DISABLE_GUI +#ifdef Q_OS_WIN +#include +#include +#endif // Q_OS_WIN +#ifdef Q_OS_MAC +#include +#include +#include +#endif // Q_OS_MAC +#include "mainwindow.h" +#include "addnewtorrentdialog.h" +#else // DISABLE_GUI +#include +#endif // DISABLE_GUI + +#ifndef DISABLE_WEBUI +#include "webui.h" +#endif + +#include "application.h" +#include "logger.h" +#include "preferences.h" +#include "qbtsession.h" +#include "torrentpersistentdata.h" + +static const char PARAMS_SEPARATOR[] = "|"; + +Application::Application(const QString &id, int &argc, char **argv) + : BaseApplication(id, argc, argv) + , m_running(false) +{ +#if defined(Q_OS_MACX) && !defined(DISABLE_GUI) + if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) { + // fix Mac OS X 10.9 (mavericks) font issue + // https://bugreports.qt-project.org/browse/QTBUG-32789 + QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); + } +#endif + setApplicationName("qBittorrent"); + initializeTranslation(); +#ifndef DISABLE_GUI + setStyleSheet("QStatusBar::item { border-width: 0; }"); + setQuitOnLastWindowClosed(false); +#endif + + connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &))); + connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup())); +} + +void Application::processMessage(const QString &message) +{ + QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts); + // If Application is not running (i.e., other + // components are not ready) store params + if (m_running) + processParams(params); + else + m_paramsQueue.append(params); +} + +bool Application::sendParams(const QStringList ¶ms) +{ + return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR))); +} + +// As program parameters, we can get paths or urls. +// This function parse the parameters and call +// the right addTorrent function, considering +// the parameter type. +void Application::processParams(const QStringList ¶ms) +{ +#ifndef DISABLE_GUI + if (params.isEmpty()) { + m_window->activate(); // show UI + return; + } + + const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog(); +#endif + + foreach (QString param, params) { + param = param.trimmed(); + if (misc::isUrl(param)) { + QBtSession::instance()->downloadFromUrl(param); + } + else { + if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) { + qDebug("Converting bc link to magnet link"); + param = misc::bcLinkToMagnet(param); + } + + if (param.startsWith("magnet:", Qt::CaseInsensitive)) { +#ifndef DISABLE_GUI + if (useTorrentAdditionDialog) + AddNewTorrentDialog::showMagnet(param, m_window); + else +#endif + QBtSession::instance()->addMagnetUri(param); + } + else { +#ifndef DISABLE_GUI + if (useTorrentAdditionDialog) + AddNewTorrentDialog::showTorrent(param, QString(), m_window); + else +#endif + QBtSession::instance()->addTorrent(param); + } + } + } +} + +int Application::exec(const QStringList ¶ms) +{ + // Resume unfinished torrents + QBtSession::instance()->startUpTorrents(); + +#ifndef DISABLE_WEBUI + m_webui = new WebUI; +#endif + +#ifdef DISABLE_GUI +#ifndef DISABLE_WEBUI + Preferences* const pref = Preferences::instance(); + if (pref->isWebUiEnabled()) { + // Display some information to the user + std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl; + std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(pref->getWebUiPort()))) << std::endl; + std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername())) << std::endl; + qDebug() << "Password:" << pref->getWebUiPassword(); + if (pref->getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") { + std::cout << qPrintable(tr("The Web UI administrator password is still the default one: %1").arg("adminadmin")) << std::endl; + std::cout << qPrintable(tr("This is a security risk, please consider changing your password from program preferences.")) << std::endl; + } + } +#endif // DISABLE_WEBUI +#else + m_window = new MainWindow; +#endif // DISABLE_GUI + + m_running = true; + m_paramsQueue = params + m_paramsQueue; + if (!m_paramsQueue.isEmpty()) { + processParams(m_paramsQueue); + m_paramsQueue.clear(); + } + + return BaseApplication::exec(); +} + +#ifndef DISABLE_GUI +#ifdef Q_OS_WIN +bool Application::isRunning() +{ + bool running = BaseApplication::isRunning(); + QSharedMemory *sharedMem = new QSharedMemory(id() + QLatin1String("-shared-memory-key"), this); + if (!running) { + // First instance creates shared memory and store PID + if (sharedMem->create(sizeof(DWORD)) && sharedMem->lock()) { + *(static_cast(sharedMem->data())) = ::GetCurrentProcessId(); + sharedMem->unlock(); + } + } + else { + // Later instances attach to shared memory and retrieve PID + if (sharedMem->attach() && sharedMem->lock()) { + ::AllowSetForegroundWindow(*(static_cast(sharedMem->data()))); + sharedMem->unlock(); + } + } + + if (!sharedMem->isAttached()) + qWarning() << "Failed to initialize shared memory: " << sharedMem->errorString(); + + return running; +} +#endif // Q_OS_WIN + +#ifdef Q_OS_MAC +bool Application::event(QEvent *ev) +{ + if (ev->type() == QEvent::FileOpen) { + QString path = static_cast(ev)->file(); + if (path.isEmpty()) + // Get the url instead + path = static_cast(ev)->url().toString(); + qDebug("Received a mac file open event: %s", qPrintable(path)); + if (running_) + processParams(QStringList(path)); + else + paramsQueue_.append(path); + return true; + } + else { + return BaseApplication::event(ev); + } +} +#endif // Q_OS_MAC + +bool Application::notify(QObject *receiver, QEvent *event) +{ + try { + return QApplication::notify(receiver, event); + } + catch (const std::exception &e) { + qCritical() << "Exception thrown:" << e.what() << ", receiver: " << receiver->objectName(); + receiver->dumpObjectInfo(); + } + + return false; +} +#endif // DISABLE_GUI + +void Application::initializeTranslation() +{ + Preferences* const pref = Preferences::instance(); + // Load translation + QString locale = pref->getLocale(); + if (locale.isEmpty()) { + locale = QLocale::system().name(); + pref->setLocale(locale); + } + + if (m_qtTranslator.load( +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) + QString::fromUtf8("qtbase_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)) || + m_qtTranslator.load( +#endif + QString::fromUtf8("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + qDebug("Qt %s locale recognized, using translation.", qPrintable(locale)); + } + else { + qDebug("Qt %s locale unrecognized, using default (en).", qPrintable(locale)); + } + installTranslator(&m_qtTranslator); + + if (m_translator.load(QString::fromUtf8(":/lang/qbittorrent_") + locale)) { + qDebug("%s locale recognized, using translation.", qPrintable(locale)); + } + else { + qDebug("%s locale unrecognized, using default (en).", qPrintable(locale)); + } + installTranslator(&m_translator); + +#ifndef DISABLE_GUI + if (locale.startsWith("ar") || locale.startsWith("he")) { + qDebug("Right to Left mode"); + setLayoutDirection(Qt::RightToLeft); + } + else { + setLayoutDirection(Qt::LeftToRight); + } +#endif +} + +void Application::cleanup() +{ +#ifndef DISABLE_GUI + delete m_window; +#endif +#ifndef DISABLE_WEBUI + delete m_webui; +#endif + QBtSession::drop(); + TorrentPersistentData::drop(); + Preferences::drop(); + Logger::drop(); +} diff --git a/src/app/application.h b/src/app/application.h new file mode 100644 index 000000000..6dd03ac74 --- /dev/null +++ b/src/app/application.h @@ -0,0 +1,94 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2006 Christophe Dumez + * + * 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. + */ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include +#include +#include + +#ifndef DISABLE_GUI +#include "qtsingleapplication.h" +typedef QtSingleApplication BaseApplication; +class MainWindow; +#else +#include "qtsinglecoreapplication.h" +typedef QtSingleCoreApplication BaseApplication; +#endif + +#ifndef DISABLE_WEBUI +class WebUI; +#endif + +class Application : public BaseApplication +{ + Q_OBJECT + +public: + Application(const QString &id, int &argc, char **argv); + + #if (defined(Q_OS_WIN) && !defined(DISABLE_GUI)) + bool isRunning(); +#endif + int exec(const QStringList ¶ms); + bool sendParams(const QStringList ¶ms); + +protected: +#ifndef DISABLE_GUI +#ifdef Q_OS_MAC + bool event(QEvent *); +#endif + bool notify(QObject* receiver, QEvent* event); +#endif + +private slots: + void processMessage(const QString &message); + void cleanup(); + +private: + bool m_running; + +#ifndef DISABLE_GUI + QPointer m_window; +#endif + +#ifndef DISABLE_WEBUI + QPointer m_webui; +#endif + + QTranslator m_qtTranslator; + QTranslator m_translator; + QStringList m_paramsQueue; + + void initializeTranslation(); + void processParams(const QStringList ¶ms); +}; + +#endif // APPLICATION_H diff --git a/src/main.cpp b/src/app/main.cpp similarity index 83% rename from src/main.cpp rename to src/app/main.cpp index b26a6f1e9..26605d8a7 100644 --- a/src/main.cpp +++ b/src/app/main.cpp @@ -39,8 +39,6 @@ #include #include #include -#include -#include #ifdef QBT_STATIC_QT #include #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) @@ -49,16 +47,10 @@ Q_IMPORT_PLUGIN(QICOPlugin) Q_IMPORT_PLUGIN(qico) #endif #endif // QBT_STATIC_QT -#include "mainwindow.h" -#include "ico.h" #else // DISABLE_GUI -#include -#include -#include "headlessloader.h" +#include #endif // DISABLE_GUI -#include "application.h" - #ifdef Q_OS_UNIX #include #include @@ -71,29 +63,13 @@ Q_IMPORT_PLUGIN(qico) #include "stacktrace_win_dlg.h" #endif //STACKTRACE_WIN -#include +#include +#include +#include "application.h" #include "misc.h" #include "preferences.h" #include "logger.h" -class MessagesCollector : public QObject -{ - Q_OBJECT -public slots: - void collectMessage(const QString& message) - { - messages.append(message.split("|", QString::SkipEmptyParts)); - } -public: - QStringList messages; -}; - -#include "main.moc" - -#if defined(Q_OS_WIN) && !defined(QBT_HAS_GETCURRENTPID) -#error You seem to have updated QtSingleApplication without porting our custom QtSingleApplication::getRunningPid() function. Please see previous version to understate how it works. -#endif - // Signal handlers #if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN) void sigintHandler(int); @@ -150,11 +126,8 @@ int main(int argc, char *argv[]) bool isOneArg = (argc == 2); // Create Application - QScopedPointer app(new Application("qBittorrent-" + misc::getUserIDString(), argc, argv)); - - MessagesCollector* messagesCollector = new MessagesCollector(); - QObject::connect(app.data(), SIGNAL(messageReceived(const QString &)), - messagesCollector, SLOT(collectMessage(const QString &))); + QString appId = QLatin1String("qBittorrent-") + misc::getUserIDString(); + QScopedPointer app(new Application(appId, argc, argv)); const QBtCommandLineParameters params = parseCommandLine(); @@ -214,26 +187,12 @@ int main(int argc, char *argv[]) .arg(QLatin1String("-d (or --daemon)"))); return EXIT_FAILURE; } -#else - qDebug("qBittorrent is already running for this user."); + else #endif + qDebug("qBittorrent is already running for this user."); + misc::msleep(300); -#ifdef Q_OS_WIN - DWORD pid = (DWORD)app->getRunningPid(); - if (pid > 0) { - BOOL b = AllowSetForegroundWindow(pid); - qDebug("AllowSetForegroundWindow() returns %s", b ? "TRUE" : "FALSE"); - } -#endif - if (!params.torrents.isEmpty()) { - QString message = params.torrents.join("|"); - qDebug("Passing program parameters to running instance..."); - qDebug("Message: %s", qPrintable(message)); - app->sendMessage(message); - } - else { // Raise main window - app->sendMessage("qbt://show"); - } + app->sendParams(params.torrents); return EXIT_SUCCESS; } @@ -243,7 +202,7 @@ int main(int argc, char *argv[]) if (params.shouldDaemonize) { app.reset(); // Destroy current application if ((daemon(1, 0) == 0)) { - app.reset(new Application("qBittorrent-" + misc::getUserIDString(), argc, argv)); + app.reset(new Application(appId, argc, argv)); if (app->isRunning()) { // Another instance had time to start. return EXIT_FAILURE; @@ -266,32 +225,7 @@ int main(int argc, char *argv[]) signal(SIGSEGV, sigsegvHandler); #endif -#ifndef DISABLE_GUI - MainWindow window(0, params.torrents); - QObject::connect(app.data(), SIGNAL(messageReceived(const QString &)), - &window, SLOT(processParams(const QString &))); - QObject::disconnect(app.data(), SIGNAL(messageReceived(const QString &)), - messagesCollector, SLOT(collectMessage(const QString &))); - window.processParams(messagesCollector->messages); - delete messagesCollector; - app->setActivationWindow(&window); -#ifdef Q_OS_MAC - static_cast(app.data())->setReadyToProcessEvents(); -#endif // Q_OS_MAC -#else - // Load Headless class - HeadlessLoader loader(params.torrents); - QObject::connect(app.data(), SIGNAL(messageReceived(const QString &)), - &loader, SLOT(processParams(const QString &))); - QObject::disconnect(app.data(), SIGNAL(messageReceived(const QString &)), - messagesCollector, SLOT(collectMessage(const QString &))); - loader.processParams(messagesCollector->messages); - delete messagesCollector; -#endif - - int ret = app->exec(); - qDebug("Application has exited"); - return ret; + return app->exec(params.torrents); } QBtCommandLineParameters parseCommandLine() @@ -404,7 +338,7 @@ void sigabrtHandler(int) #ifndef DISABLE_GUI void showSplashScreen() { - QPixmap splash_img(":/Icons/skin/splash.png"); + QPixmap splash_img(":/icons/skin/splash.png"); QPainter painter(&splash_img); QString version = VERSION; painter.setPen(QPen(Qt::white)); diff --git a/src/qtsingleapp/QtLockedFile b/src/app/qtsingleapplication/QtLockedFile similarity index 100% rename from src/qtsingleapp/QtLockedFile rename to src/app/qtsingleapplication/QtLockedFile diff --git a/src/qtsingleapp/QtSingleApplication b/src/app/qtsingleapplication/QtSingleApplication similarity index 100% rename from src/qtsingleapp/QtSingleApplication rename to src/app/qtsingleapplication/QtSingleApplication diff --git a/src/qtsingleapp/qtlocalpeer.cpp b/src/app/qtsingleapplication/qtlocalpeer.cpp similarity index 85% rename from src/qtsingleapp/qtlocalpeer.cpp rename to src/app/qtsingleapplication/qtlocalpeer.cpp index effa0a016..332b0643f 100644 --- a/src/qtsingleapp/qtlocalpeer.cpp +++ b/src/app/qtsingleapplication/qtlocalpeer.cpp @@ -195,56 +195,9 @@ void QtLocalPeer::receiveConnection() return; } QString message(QString::fromUtf8(uMsg)); -#ifdef Q_OS_WIN - if (message == "qbt://pid") { - qint64 pid = GetCurrentProcessId(); - socket->write((const char *)&pid, sizeof pid); - } else { - socket->write(ack, qstrlen(ack)); - } -#else socket->write(ack, qstrlen(ack)); -#endif socket->waitForBytesWritten(1000); socket->waitForDisconnected(1000); // make sure client reads ack delete socket; -#ifdef Q_OS_WIN - if (message == "qbt://pid") - return; -#endif emit messageReceived(message); //### (might take a long time to return) } - -#ifdef Q_OS_WIN -qint64 QtLocalPeer::getRunningPid() { - if (!isClient()) - return 0; - - QLocalSocket socket; - bool connOk = false; - for (int i = 0; i < 2; i++) { - // Try twice, in case the other instance is just starting up - socket.connectToServer(socketName); - connOk = socket.waitForConnected(5000/2); - if (connOk || i) - break; - Sleep(250); - } - if (!connOk) return -1; - - const char* msg = "qbt://pid"; - QDataStream ds(&socket); - ds.writeBytes(msg, qstrlen(msg)); - bool res = socket.waitForBytesWritten(5000) && socket.waitForReadyRead(5000); - if (!res) return -1; - - DWORD pid; - qint64 pid_size = sizeof pid; - while (socket.bytesAvailable() < pid_size) - socket.waitForReadyRead(); - if (socket.read((char *)&pid, pid_size) < pid_size) - return -1; - - return pid; -} -#endif diff --git a/src/qtsingleapp/qtlocalpeer.h b/src/app/qtsingleapplication/qtlocalpeer.h similarity index 98% rename from src/qtsingleapp/qtlocalpeer.h rename to src/app/qtsingleapplication/qtlocalpeer.h index ebe185445..1b533b1ab 100644 --- a/src/qtsingleapp/qtlocalpeer.h +++ b/src/app/qtsingleapplication/qtlocalpeer.h @@ -57,9 +57,6 @@ public: bool sendMessage(const QString &message, int timeout); QString applicationId() const { return id; } -#ifdef Q_OS_WIN - qint64 getRunningPid(); -#endif Q_SIGNALS: void messageReceived(const QString &message); diff --git a/src/qtsingleapp/qtlockedfile.cpp b/src/app/qtsingleapplication/qtlockedfile.cpp similarity index 100% rename from src/qtsingleapp/qtlockedfile.cpp rename to src/app/qtsingleapplication/qtlockedfile.cpp diff --git a/src/qtsingleapp/qtlockedfile.h b/src/app/qtsingleapplication/qtlockedfile.h similarity index 100% rename from src/qtsingleapp/qtlockedfile.h rename to src/app/qtsingleapplication/qtlockedfile.h diff --git a/src/qtsingleapp/qtlockedfile_unix.cpp b/src/app/qtsingleapplication/qtlockedfile_unix.cpp similarity index 100% rename from src/qtsingleapp/qtlockedfile_unix.cpp rename to src/app/qtsingleapplication/qtlockedfile_unix.cpp diff --git a/src/qtsingleapp/qtlockedfile_win.cpp b/src/app/qtsingleapplication/qtlockedfile_win.cpp similarity index 100% rename from src/qtsingleapp/qtlockedfile_win.cpp rename to src/app/qtsingleapplication/qtlockedfile_win.cpp diff --git a/src/qtsingleapp/qtsingleapplication.cpp b/src/app/qtsingleapplication/qtsingleapplication.cpp similarity index 99% rename from src/qtsingleapp/qtsingleapplication.cpp rename to src/app/qtsingleapplication/qtsingleapplication.cpp index 8a4c7f74d..d0fb15d76 100644 --- a/src/qtsingleapp/qtsingleapplication.cpp +++ b/src/app/qtsingleapplication/qtsingleapplication.cpp @@ -345,9 +345,3 @@ void QtSingleApplication::activateWindow() \obsolete */ - -#ifdef Q_OS_WIN -qint64 QtSingleApplication::getRunningPid() { - return peer->getRunningPid(); -} -#endif diff --git a/src/qtsingleapp/qtsingleapplication.h b/src/app/qtsingleapplication/qtsingleapplication.h similarity index 98% rename from src/qtsingleapp/qtsingleapplication.h rename to src/app/qtsingleapplication/qtsingleapplication.h index 80fe0d21e..049406f72 100644 --- a/src/qtsingleapp/qtsingleapplication.h +++ b/src/app/qtsingleapplication/qtsingleapplication.h @@ -86,10 +86,6 @@ public: // Obsolete: void initialize(bool dummy = true) { isRunning(); Q_UNUSED(dummy) } -#ifdef Q_OS_WIN -#define QBT_HAS_GETCURRENTPID - qint64 getRunningPid(); -#endif public Q_SLOTS: bool sendMessage(const QString &message, int timeout = 5000); diff --git a/src/qtsingleapp/qtsingleapplication.pri b/src/app/qtsingleapplication/qtsingleapplication.pri similarity index 100% rename from src/qtsingleapp/qtsingleapplication.pri rename to src/app/qtsingleapplication/qtsingleapplication.pri diff --git a/src/qtsingleapp/qtsinglecoreapplication.cpp b/src/app/qtsingleapplication/qtsinglecoreapplication.cpp similarity index 100% rename from src/qtsingleapp/qtsinglecoreapplication.cpp rename to src/app/qtsingleapplication/qtsinglecoreapplication.cpp diff --git a/src/qtsingleapp/qtsinglecoreapplication.h b/src/app/qtsingleapplication/qtsinglecoreapplication.h similarity index 100% rename from src/qtsingleapp/qtsinglecoreapplication.h rename to src/app/qtsingleapplication/qtsinglecoreapplication.h diff --git a/src/qtsingleapp/qtsinglecoreapplication.pri b/src/app/qtsingleapplication/qtsinglecoreapplication.pri similarity index 100% rename from src/qtsingleapp/qtsinglecoreapplication.pri rename to src/app/qtsingleapplication/qtsinglecoreapplication.pri diff --git a/src/stacktrace.h b/src/app/stacktrace.h similarity index 100% rename from src/stacktrace.h rename to src/app/stacktrace.h diff --git a/src/stacktrace_win.h b/src/app/stacktrace_win.h similarity index 100% rename from src/stacktrace_win.h rename to src/app/stacktrace_win.h diff --git a/src/stacktrace_win_dlg.h b/src/app/stacktrace_win_dlg.h similarity index 100% rename from src/stacktrace_win_dlg.h rename to src/app/stacktrace_win_dlg.h diff --git a/src/stacktrace_win_dlg.ui b/src/app/stacktrace_win_dlg.ui similarity index 100% rename from src/stacktrace_win_dlg.ui rename to src/app/stacktrace_win_dlg.ui diff --git a/src/application.cpp b/src/application.cpp deleted file mode 100644 index b547efeba..000000000 --- a/src/application.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2006 Christophe Dumez - * - * 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 -#include -#include -#include -#include - -#if (!defined(DISABLE_GUI) && defined(Q_OS_MAC)) -#include -#endif - -#include "application.h" -#include "preferences.h" - -Application::Application(const QString &id, int &argc, char **argv) -#ifndef DISABLE_GUI - : SessionApplication(id, argc, argv) -#else - : QtSingleCoreApplication(id, argc, argv) -#endif -{ -#if defined(Q_OS_MACX) && !defined(DISABLE_GUI) - if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) { - // fix Mac OS X 10.9 (mavericks) font issue - // https://bugreports.qt-project.org/browse/QTBUG-32789 - QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande"); - } -#endif - setApplicationName("qBittorrent"); - initializeTranslation(); -#ifndef DISABLE_GUI - setStyleSheet("QStatusBar::item { border-width: 0; }"); - setQuitOnLastWindowClosed(false); -#endif -} - -void Application::initializeTranslation() -{ - Preferences* const pref = Preferences::instance(); - // Load translation - QString locale = pref->getLocale(); - if (locale.isEmpty()) { - locale = QLocale::system().name(); - pref->setLocale(locale); - } - - if (qtTranslator_.load( -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) - QString::fromUtf8("qtbase_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)) || - qtTranslator_.load( -#endif - QString::fromUtf8("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { - qDebug("Qt %s locale recognized, using translation.", qPrintable(locale)); - } - else { - qDebug("Qt %s locale unrecognized, using default (en).", qPrintable(locale)); - } - installTranslator(&qtTranslator_); - - if (translator_.load(QString::fromUtf8(":/lang/qbittorrent_") + locale)) { - qDebug("%s locale recognized, using translation.", qPrintable(locale)); - } - else { - qDebug("%s locale unrecognized, using default (en).", qPrintable(locale)); - } - installTranslator(&translator_); - -#ifndef DISABLE_GUI - if (locale.startsWith("ar") || locale.startsWith("he")) { - qDebug("Right to Left mode"); - setLayoutDirection(Qt::RightToLeft); - } - else { - setLayoutDirection(Qt::LeftToRight); - } -#endif -} diff --git a/src/core/core.pri b/src/core/core.pri new file mode 100644 index 000000000..19707a885 --- /dev/null +++ b/src/core/core.pri @@ -0,0 +1,43 @@ +INCLUDEPATH += $$PWD + +unix:!macx:dbus: include(qtnotify/qtnotify.pri) + +include(qtlibtorrent/qtlibtorrent.pri) + +HEADERS += \ + $$PWD/misc.h \ + $$PWD/fs_utils.h \ + $$PWD/downloadthread.h \ + $$PWD/torrentpersistentdata.h \ + $$PWD/filesystemwatcher.h \ + $$PWD/scannedfoldersmodel.h \ + $$PWD/qinisettings.h \ + $$PWD/smtp.h \ + $$PWD/dnsupdater.h \ + $$PWD/logger.h \ + $$PWD/preferences.h \ + $$PWD/qtracker.h \ + $$PWD/http/irequesthandler.h \ + $$PWD/http/connection.h \ + $$PWD/http/requestparser.h \ + $$PWD/http/responsegenerator.h \ + $$PWD/http/server.h \ + $$PWD/http/types.h \ + $$PWD/http/responsebuilder.h + +SOURCES += \ + $$PWD/downloadthread.cpp \ + $$PWD/scannedfoldersmodel.cpp \ + $$PWD/torrentpersistentdata.cpp \ + $$PWD/misc.cpp \ + $$PWD/fs_utils.cpp \ + $$PWD/smtp.cpp \ + $$PWD/dnsupdater.cpp \ + $$PWD/logger.cpp \ + $$PWD/preferences.cpp \ + $$PWD/qtracker.cpp \ + $$PWD/http/connection.cpp \ + $$PWD/http/requestparser.cpp \ + $$PWD/http/responsegenerator.cpp \ + $$PWD/http/server.cpp \ + $$PWD/http/responsebuilder.cpp diff --git a/src/dnsupdater.cpp b/src/core/dnsupdater.cpp similarity index 100% rename from src/dnsupdater.cpp rename to src/core/dnsupdater.cpp diff --git a/src/dnsupdater.h b/src/core/dnsupdater.h similarity index 100% rename from src/dnsupdater.h rename to src/core/dnsupdater.h diff --git a/src/downloadthread.cpp b/src/core/downloadthread.cpp similarity index 100% rename from src/downloadthread.cpp rename to src/core/downloadthread.cpp diff --git a/src/downloadthread.h b/src/core/downloadthread.h similarity index 100% rename from src/downloadthread.h rename to src/core/downloadthread.h diff --git a/src/filesystemwatcher.h b/src/core/filesystemwatcher.h similarity index 100% rename from src/filesystemwatcher.h rename to src/core/filesystemwatcher.h diff --git a/src/fs_utils.cpp b/src/core/fs_utils.cpp similarity index 100% rename from src/fs_utils.cpp rename to src/core/fs_utils.cpp diff --git a/src/fs_utils.h b/src/core/fs_utils.h similarity index 100% rename from src/fs_utils.h rename to src/core/fs_utils.h diff --git a/src/core/http/connection.cpp b/src/core/http/connection.cpp new file mode 100644 index 000000000..84985e895 --- /dev/null +++ b/src/core/http/connection.cpp @@ -0,0 +1,110 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * Copyright (C) 2006 Ishan Arora and Christophe Dumez + * + * 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. + * + * Contact : chris@qbittorrent.org + */ + +#include +#include +#include "types.h" +#include "requestparser.h" +#include "responsegenerator.h" +#include "irequesthandler.h" +#include "connection.h" + +using namespace Http; + +Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent) + : QObject(parent) + , m_socket(socket) + , m_requestHandler(requestHandler) +{ + m_socket->setParent(this); + connect(m_socket, SIGNAL(readyRead()), SLOT(read())); + connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater())); +} + +Connection::~Connection() +{ +} + +void Connection::read() +{ + m_receivedData.append(m_socket->readAll()); + + Request request; + RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); + switch (err) { + case RequestParser::IncompleteRequest: + // Partial request waiting for the rest + break; + case RequestParser::BadRequest: + sendResponse(Response(400, "Bad Request")); + break; + case RequestParser::NoError: + Environment env; + env.clientAddress = m_socket->peerAddress(); + Response response = m_requestHandler->processRequest(request, env); + if (acceptsGzipEncoding(request.headers["accept-encoding"])) + response.headers[HEADER_CONTENT_ENCODING] = "gzip"; + sendResponse(response); + break; + } +} + +void Connection::sendResponse(const Response &response) +{ + m_socket->write(ResponseGenerator::generate(response)); + m_socket->disconnectFromHost(); +} + +bool Connection::acceptsGzipEncoding(const QString &encoding) +{ + int pos = encoding.indexOf("gzip", 0, Qt::CaseInsensitive); + if (pos == -1) + return false; + + // Let's see if there's a qvalue of 0.0 following + if (encoding[pos + 4] != ';') //there isn't, so it accepts gzip anyway + return true; + + //So let's find = and the next comma + pos = encoding.indexOf("=", pos + 4, Qt::CaseInsensitive); + int comma_pos = encoding.indexOf(",", pos, Qt::CaseInsensitive); + + QString value; + if (comma_pos == -1) + value = encoding.mid(pos + 1, comma_pos); + else + value = encoding.mid(pos + 1, comma_pos - (pos + 1)); + + if (value.toDouble() == 0.0) + return false; + + return true; +} diff --git a/src/webui/httpconnection.h b/src/core/http/connection.h similarity index 74% rename from src/webui/httpconnection.h rename to src/core/http/connection.h index 0b768276a..19c8acb6a 100644 --- a/src/webui/httpconnection.h +++ b/src/core/http/connection.h @@ -30,37 +30,42 @@ */ -#ifndef HTTPCONNECTION_H -#define HTTPCONNECTION_H +#ifndef HTTP_CONNECTION_H +#define HTTP_CONNECTION_H #include -#include "httptypes.h" - -class HttpServer; +#include "types.h" QT_BEGIN_NAMESPACE class QTcpSocket; QT_END_NAMESPACE -class HttpConnection : public QObject +namespace Http +{ + +class IRequestHandler; + +class Connection : public QObject { - Q_OBJECT - Q_DISABLE_COPY(HttpConnection) + Q_OBJECT + Q_DISABLE_COPY(Connection) public: - HttpConnection(QTcpSocket* socket, HttpServer* httpserver); - ~HttpConnection(); + Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0); + ~Connection(); private slots: - void read(); + void read(); private: - void write(const HttpResponse& response); - - static bool acceptsGzipEncoding(const QString& encoding); + static bool acceptsGzipEncoding(const QString &encoding); + void sendResponse(const Response &response); - QTcpSocket *m_socket; - QByteArray m_receivedData; + QTcpSocket *m_socket; + IRequestHandler *m_requestHandler; + QByteArray m_receivedData; }; -#endif +} + +#endif // HTTP_CONNECTION_H diff --git a/src/core/http/irequesthandler.h b/src/core/http/irequesthandler.h new file mode 100644 index 000000000..2fab8d5c6 --- /dev/null +++ b/src/core/http/irequesthandler.h @@ -0,0 +1,47 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef HTTP_IREQUESTHANDLER_H +#define HTTP_IREQUESTHANDLER_H + +#include "types.h" + +namespace Http +{ + +class IRequestHandler +{ +public: + virtual ~IRequestHandler() {} + virtual Response processRequest(const Request &request, const Environment &env) = 0; +}; + +} + +#endif // HTTP_IREQUESTHANDLER_H + diff --git a/src/core/http/requestparser.cpp b/src/core/http/requestparser.cpp new file mode 100644 index 000000000..4a766a900 --- /dev/null +++ b/src/core/http/requestparser.cpp @@ -0,0 +1,346 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * Copyright (C) 2006 Ishan Arora and Christophe Dumez + * + * 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. + * + * Contact : chris@qbittorrent.org + */ + +#include +#include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#endif +#include +#include +#include +#include "requestparser.h" + +const QByteArray EOL("\r\n"); +const QByteArray EOH("\r\n\r\n"); + +inline QString unquoted(const QString& str) +{ + if ((str[0] == '\"') && (str[str.length() - 1] == '\"')) + return str.mid(1, str.length() - 2); + + return str; +} + +using namespace Http; + +RequestParser::ErrorCode RequestParser::parse(const QByteArray& data, Request& request, uint maxContentLength) +{ + return RequestParser(maxContentLength).parseHttpRequest(data, request); +} + +RequestParser::RequestParser(uint maxContentLength) + : m_maxContentLength(maxContentLength) +{ +} + +RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray& data, Request& request) +{ + m_request = Request(); + + // Parse HTTP request header + const int header_end = data.indexOf(EOH); + if (header_end < 0) { + qDebug() << Q_FUNC_INFO << "incomplete request"; + return IncompleteRequest; + } + + if (!parseHttpHeader(data.left(header_end))) { + qWarning() << Q_FUNC_INFO << "header parsing error"; + return BadRequest; + } + + // Parse HTTP request message + int content_length = 0; + if (m_request.headers.contains("content-length")) { + content_length = m_request.headers["content-length"].toInt(); + if (content_length > static_cast(m_maxContentLength)) { + qWarning() << Q_FUNC_INFO << "bad request: message too long"; + return BadRequest; + } + + QByteArray content = data.mid(header_end + EOH.length(), content_length); + if (content.length() < content_length) { + qDebug() << Q_FUNC_INFO << "incomplete request"; + return IncompleteRequest; + } + + if (!parseContent(content)) { + qWarning() << Q_FUNC_INFO << "message parsing error"; + return BadRequest; + } + } + + // qDebug() << Q_FUNC_INFO; + // qDebug() << "HTTP Request header:"; + // qDebug() << data.left(header_end) << "\n"; + + request = m_request; + return NoError; +} + +bool RequestParser::parseStartingLine(const QString &line) +{ + const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$"); + + if (rx.indexIn(line.trimmed()) >= 0) { + m_request.method = rx.cap(1); + + QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1()); + m_request.path = url.path(); // Path + + // Parse GET parameters +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + QListIterator > i(url.queryItems()); +#else + QListIterator > i(QUrlQuery(url).queryItems()); +#endif + while (i.hasNext()) { + QPair pair = i.next(); + m_request.gets[pair.first] = pair.second; + } + + return true; + } + + qWarning() << Q_FUNC_INFO << "invalid http header:" << line; + return false; +} + +bool RequestParser::parseHeaderLine(const QString &line, QPair& out) +{ + int i = line.indexOf(QLatin1Char(':')); + if (i == -1) { + qWarning() << Q_FUNC_INFO << "invalid http header:" << line; + return false; + } + + out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed()); + return true; +} + +bool RequestParser::parseHttpHeader(const QByteArray &data) +{ + QString str = QString::fromUtf8(data); + QStringList lines = str.trimmed().split(EOL); + + QStringList headerLines; + foreach (const QString& line, lines) { + if (line[0].isSpace()) { // header line continuation + if (!headerLines.isEmpty()) { // really continuation + headerLines.last() += QLatin1Char(' '); + headerLines.last() += line.trimmed(); + } + } + else { + headerLines.append(line); + } + } + + if (headerLines.isEmpty()) + return false; // Empty header + + QStringList::Iterator it = headerLines.begin(); + if (!parseStartingLine(*it)) + return false; + + ++it; + for (; it != headerLines.end(); ++it) { + QPair header; + if (!parseHeaderLine(*it, header)) + return false; + + m_request.headers[header.first] = header.second; + } + + return true; +} + +QList RequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary) +{ + QList ret; + QByteArray sep = boundary + EOL; + const int sepLength = sep.size(); + + int start = 0, end = 0; + if ((end = data.indexOf(sep, start)) >= 0) { + start = end + sepLength; // skip first boundary + + while ((end = data.indexOf(sep, start)) >= 0) { + ret << data.mid(start, end - start); + start = end + sepLength; + } + + // last or single part + sep = boundary + "--" + EOL; + if ((end = data.indexOf(sep, start)) >= 0) + ret << data.mid(start, end - start); + } + + return ret; +} + +bool RequestParser::parseContent(const QByteArray& data) +{ + // Parse message content + qDebug() << Q_FUNC_INFO << "Content-Length: " << m_request.headers["content-length"]; + qDebug() << Q_FUNC_INFO << "data.size(): " << data.size(); + + // Parse url-encoded POST data + if (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) { + QUrl url; +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + url.setEncodedQuery(data); + QListIterator > i(url.queryItems()); +#else + url.setQuery(data); + QListIterator > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded)); +#endif + while (i.hasNext()) { + QPair pair = i.next(); + m_request.posts[pair.first.toLower()] = pair.second; + } + + return true; + } + + // Parse multipart/form data (torrent file) + /** + data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5") + +--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 +Content-Disposition: form-data; name=\"Filename\" + +PB020344.torrent +--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 +Content-Disposition: form-data; name=\"torrentfile"; filename=\"PB020344.torrent\" +Content-Type: application/x-bittorrent + +BINARY DATA IS HERE +--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 +Content-Disposition: form-data; name=\"Upload\" + +Submit Query +--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5-- +**/ + QString content_type = m_request.headers["content-type"]; + if (content_type.startsWith("multipart/form-data")) { + const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\""); + const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)"); + + QByteArray boundary; + if (boundaryRegexQuoted.indexIn(content_type) < 0) { + if (boundaryRegexNotQuoted.indexIn(content_type) < 0) { + qWarning() << "Could not find boundary in multipart/form-data header!"; + return false; + } + else { + boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1(); + } + } + else { + boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1(); + } + + qDebug() << "Boundary is " << boundary; + QList parts = splitMultipartData(data, boundary); + qDebug() << parts.size() << "parts in data"; + + foreach (const QByteArray& part, parts) { + if (!parseFormData(part)) + return false; + } + + return true; + } + + qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type); + return false; +} + +bool RequestParser::parseFormData(const QByteArray& data) +{ + // Parse form data header + const int header_end = data.indexOf(EOH); + if (header_end < 0) { + qDebug() << "Invalid form data: \n" << data; + return false; + } + + QString header_str = QString::fromUtf8(data.left(header_end)); + QStringList lines = header_str.trimmed().split(EOL); + QStringMap headers; + foreach (const QString& line, lines) { + QPair header; + if (!parseHeaderLine(line, header)) + return false; + + headers[header.first] = header.second; + } + + QStringMap disposition; + if (!headers.contains("content-disposition") + || !parseHeaderValue(headers["content-disposition"], disposition) + || !disposition.contains("name")) { + qDebug() << "Invalid form data header: \n" << header_str; + return false; + } + + if (disposition.contains("filename")) { + UploadedFile ufile; + ufile.filename = disposition["filename"]; + ufile.type = disposition["content-type"]; + ufile.data = data.mid(header_end + EOH.length()); + + m_request.files[disposition["name"]] = ufile; + } + else { + m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length())); + } + + return true; +} + +bool RequestParser::parseHeaderValue(const QString& value, QStringMap& out) +{ + QStringList items = value.split(QLatin1Char(';')); + out[""] = items[0]; + + for (QStringList::size_type i = 1; i < items.size(); ++i) { + int pos = items[i].indexOf("="); + if (pos < 0) + return false; + + out[items[i].left(pos).trimmed()] = unquoted(items[i].mid(pos + 1).trimmed()); + } + + return true; +} diff --git a/src/webui/httprequestparser.h b/src/core/http/requestparser.h similarity index 58% rename from src/webui/httprequestparser.h rename to src/core/http/requestparser.h index 9169c040f..ec8265e38 100644 --- a/src/webui/httprequestparser.h +++ b/src/core/http/requestparser.h @@ -29,36 +29,46 @@ * Contact : chris@qbittorrent.org */ -#ifndef HTTPREQUESTPARSER_H -#define HTTPREQUESTPARSER_H +#ifndef HTTP_REQUESTPARSER_H +#define HTTP_REQUESTPARSER_H -#include "httptypes.h" +#include "types.h" -class HttpRequestParser +namespace Http +{ + +class RequestParser { public: - enum ErrorCode { NoError = 0, IncompleteRequest, BadRequest }; + enum ErrorCode + { + NoError = 0, + IncompleteRequest, + BadRequest + }; - // when result != NoError parsed request is undefined - // Warning! Header names are converted to lower-case. - static ErrorCode parse(const QByteArray& data, HttpRequest& request, uint maxContentLength = 10000000 /* ~10MB */); + // when result != NoError parsed request is undefined + // Warning! Header names are converted to lower-case. + static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */); private: - HttpRequestParser(uint maxContentLength); + RequestParser(uint maxContentLength); - ErrorCode parseHttpRequest(const QByteArray& data, HttpRequest& request); + ErrorCode parseHttpRequest(const QByteArray &data, Request &request); - bool parseHttpHeader(const QByteArray& data); - bool parseStartingLine(const QString &line); - bool parseContent(const QByteArray& data); - bool parseFormData(const QByteArray& data); - QList splitMultipartData(const QByteArray& data, const QByteArray& boundary); + bool parseHttpHeader(const QByteArray &data); + bool parseStartingLine(const QString &line); + bool parseContent(const QByteArray &data); + bool parseFormData(const QByteArray &data); + QList splitMultipartData(const QByteArray &data, const QByteArray &boundary); - static bool parseHeaderLine(const QString& line, QPair& out); - static bool parseHeaderValue(const QString& value, QStringMap& out); + static bool parseHeaderLine(const QString &line, QPair &out); + static bool parseHeaderValue(const QString &value, QStringMap &out); - const uint maxContentLength_; - HttpRequest request_; + const uint m_maxContentLength; + Request m_request; }; -#endif +} + +#endif // HTTP_REQUESTPARSER_H diff --git a/src/core/http/responsebuilder.cpp b/src/core/http/responsebuilder.cpp new file mode 100644 index 000000000..954ed05b5 --- /dev/null +++ b/src/core/http/responsebuilder.cpp @@ -0,0 +1,74 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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 "responsebuilder.h" + +using namespace Http; + +ResponseBuilder::ResponseBuilder(QObject *parent) + : QObject(parent) +{ +} + +void ResponseBuilder::status(uint code, const QString &text) +{ + m_response.status = ResponseStatus(code, text); +} + +void ResponseBuilder::header(const QString &name, const QString &value) +{ + m_response.headers[name] = value; +} + +void ResponseBuilder::print(const QString &text, const QString &type) +{ + print_impl(text.toUtf8(), type); +} + +void ResponseBuilder::print(const QByteArray &data, const QString &type) +{ + print_impl(data, type); +} + +void ResponseBuilder::clear() +{ + m_response = Response(); +} + +Response ResponseBuilder::response() const +{ + return m_response; +} + +void ResponseBuilder::print_impl(const QByteArray &data, const QString &type) +{ + if (!m_response.headers.contains(HEADER_CONTENT_TYPE)) + m_response.headers[HEADER_CONTENT_TYPE] = type; + + m_response.content += data; +} diff --git a/src/application.h b/src/core/http/responsebuilder.h similarity index 65% rename from src/application.h rename to src/core/http/responsebuilder.h index 7047faa09..0a3b0d8f1 100644 --- a/src/application.h +++ b/src/core/http/responsebuilder.h @@ -1,7 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2006 Christophe Dumez + * Copyright (C) 2015 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,33 +26,35 @@ * exception statement from your version. */ -#ifndef APPLICATION_H -#define APPLICATION_H +#ifndef HTTP_RESPONSEBUILDER_H +#define HTTP_RESPONSEBUILDER_H -#include -#include +#include +#include "types.h" -#ifndef DISABLE_GUI -#include "sessionapplication.h" -#else -#include "qtsinglecoreapplication.h" -#endif +namespace Http +{ -class Application -#ifndef DISABLE_GUI - : public SessionApplication -#else - : public QtSingleCoreApplication -#endif +class ResponseBuilder : public QObject { public: - Application(const QString &id, int &argc, char **argv); + explicit ResponseBuilder(QObject *parent = 0); + +protected: + void status(uint code = 200, const QString &text = QLatin1String("OK")); + void header(const QString &name, const QString &value); + void print(const QString &text, const QString &type = CONTENT_TYPE_HTML); + void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML); + void clear(); + + Response response() const; private: - QTranslator qtTranslator_; - QTranslator translator_; + void print_impl(const QByteArray &data, const QString &type); - void initializeTranslation(); + Response m_response; }; -#endif // APPLICATION_H +} + +#endif // HTTP_RESPONSEBUILDER_H diff --git a/src/core/http/responsegenerator.cpp b/src/core/http/responsegenerator.cpp new file mode 100644 index 000000000..f6aad3afa --- /dev/null +++ b/src/core/http/responsegenerator.cpp @@ -0,0 +1,124 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * Copyright (C) 2006 Ishan Arora and Christophe Dumez + * + * 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. + * + * Contact : chris@qbittorrent.org + */ + +#include +#include "responsegenerator.h" + +bool gCompress(QByteArray data, QByteArray& dest_buffer); + +using namespace Http; + +QByteArray ResponseGenerator::generate(Response response) +{ + if (response.headers[HEADER_CONTENT_ENCODING] == "gzip") { + // A gzip seems to have 23 bytes overhead. + // Also "Content-Encoding: gzip\r\n" is 26 bytes long + // So we only benefit from gzip if the message is bigger than 23+26 = 49 + // If the message is smaller than 49 bytes we actually send MORE data if we gzip + QByteArray dest_buf; + if ((response.content.size() > 49) && (gCompress(response.content, dest_buf))) + response.content = dest_buf; + else + response.headers.remove(HEADER_CONTENT_ENCODING); + } + + if (response.content.length() > 0) + response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length()); + + QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n")); + + QString header; + foreach (const QString& key, response.headers.keys()) + header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]); + + ret = ret.arg(response.status.code).arg(response.status.text).arg(header); + + // qDebug() << Q_FUNC_INFO; + // qDebug() << "HTTP Response header:"; + // qDebug() << ret; + + return ret.toUtf8() + response.content; +} + +bool gCompress(QByteArray data, QByteArray& dest_buffer) +{ + static const int BUFSIZE = 128 * 1024; + char tmp_buf[BUFSIZE]; + int ret; + + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_in = reinterpret_cast(data.data()); + strm.avail_in = data.length(); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + + //windowBits = 15+16 to enable gzip + //From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits + //to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. + ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); + + if (ret != Z_OK) + return false; + + while (strm.avail_in != 0) { + ret = deflate(&strm, Z_NO_FLUSH); + if (ret != Z_OK) + return false; + + if (strm.avail_out == 0) { + dest_buffer.append(tmp_buf, BUFSIZE); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + } + } + + int deflate_res = Z_OK; + while (deflate_res == Z_OK) { + if (strm.avail_out == 0) { + dest_buffer.append(tmp_buf, BUFSIZE); + strm.next_out = reinterpret_cast(tmp_buf); + strm.avail_out = BUFSIZE; + } + + deflate_res = deflate(&strm, Z_FINISH); + } + + if (deflate_res != Z_STREAM_END) + return false; + + dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out); + deflateEnd(&strm); + + return true; +} diff --git a/src/webui/httpresponsegenerator.h b/src/core/http/responsegenerator.h similarity index 87% rename from src/webui/httpresponsegenerator.h rename to src/core/http/responsegenerator.h index d06e134c0..43d73d709 100644 --- a/src/webui/httpresponsegenerator.h +++ b/src/core/http/responsegenerator.h @@ -30,15 +30,20 @@ */ -#ifndef HTTPRESPONSEGENERATOR_H -#define HTTPRESPONSEGENERATOR_H +#ifndef HTTP_RESPONSEGENERATOR_H +#define HTTP_RESPONSEGENERATOR_H -#include "httptypes.h" +#include "types.h" -class HttpResponseGenerator +namespace Http +{ + +class ResponseGenerator { public: - static QByteArray generate(HttpResponse response); + static QByteArray generate(Response response); }; -#endif +} + +#endif // HTTP_RESPONSEGENERATOR_H diff --git a/src/webui/httpserver.cpp b/src/core/http/server.cpp similarity index 60% rename from src/webui/httpserver.cpp rename to src/core/http/server.cpp index c37c5301c..8bf905fec 100644 --- a/src/webui/httpserver.cpp +++ b/src/core/http/server.cpp @@ -33,65 +33,66 @@ #else #include #endif -#include "httpconnection.h" -#include "httpserver.h" +#include "connection.h" +#include "server.h" -HttpServer::HttpServer(QObject* parent) - : QTcpServer(parent) +using namespace Http; + +Server::Server(IRequestHandler *requestHandler, QObject* parent) + : QTcpServer(parent) + , m_requestHandler(requestHandler) #ifndef QT_NO_OPENSSL , m_https(false) #endif { } -HttpServer::~HttpServer() +Server::~Server() { } #ifndef QT_NO_OPENSSL -void HttpServer::enableHttps(const QSslCertificate &certificate, const QSslKey &key) +void Server::enableHttps(const QSslCertificate &certificate, const QSslKey &key) { - m_certificate = certificate; - m_key = key; - m_https = true; + m_certificate = certificate; + m_key = key; + m_https = true; } -void HttpServer::disableHttps() +void Server::disableHttps() { - m_https = false; - m_certificate.clear(); - m_key.clear(); + m_https = false; + m_certificate.clear(); + m_key.clear(); } #endif #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) -void HttpServer::incomingConnection(qintptr socketDescriptor) +void Server::incomingConnection(qintptr socketDescriptor) #else -void HttpServer::incomingConnection(int socketDescriptor) +void Server::incomingConnection(int socketDescriptor) #endif { - QTcpSocket *serverSocket; + QTcpSocket *serverSocket; #ifndef QT_NO_OPENSSL - if (m_https) - serverSocket = new QSslSocket(this); - else + if (m_https) + serverSocket = new QSslSocket(this); + else #endif - serverSocket = new QTcpSocket(this); - if (serverSocket->setSocketDescriptor(socketDescriptor)) - { + serverSocket = new QTcpSocket(this); + + if (serverSocket->setSocketDescriptor(socketDescriptor)) { #ifndef QT_NO_OPENSSL - if (m_https) - { - static_cast(serverSocket)->setProtocol(QSsl::AnyProtocol); - static_cast(serverSocket)->setPrivateKey(m_key); - static_cast(serverSocket)->setLocalCertificate(m_certificate); - static_cast(serverSocket)->startServerEncryption(); - } + if (m_https) { + static_cast(serverSocket)->setProtocol(QSsl::AnyProtocol); + static_cast(serverSocket)->setPrivateKey(m_key); + static_cast(serverSocket)->setLocalCertificate(m_certificate); + static_cast(serverSocket)->startServerEncryption(); + } #endif - new HttpConnection(serverSocket, this); - } - else - { - serverSocket->deleteLater(); - } + new Connection(serverSocket, m_requestHandler, this); + } + else { + serverSocket->deleteLater(); + } } diff --git a/src/webui/httpserver.h b/src/core/http/server.h similarity index 74% rename from src/webui/httpserver.h rename to src/core/http/server.h index d68728f75..c8cb774c3 100644 --- a/src/webui/httpserver.h +++ b/src/core/http/server.h @@ -30,8 +30,8 @@ */ -#ifndef HTTPSERVER_H -#define HTTPSERVER_H +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H #include #ifndef QT_NO_OPENSSL @@ -39,33 +39,42 @@ #include #endif -class HttpServer : public QTcpServer +namespace Http { - Q_OBJECT - Q_DISABLE_COPY(HttpServer) + +class IRequestHandler; +class Connection; + +class Server : public QTcpServer +{ + Q_OBJECT + Q_DISABLE_COPY(Server) public: - HttpServer(QObject* parent = 0); - ~HttpServer(); + Server(IRequestHandler *requestHandler, QObject *parent = 0); + ~Server(); #ifndef QT_NO_OPENSSL - void enableHttps(const QSslCertificate &certificate, const QSslKey &key); - void disableHttps(); + void enableHttps(const QSslCertificate &certificate, const QSslKey &key); + void disableHttps(); #endif private: #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) - void incomingConnection(qintptr socketDescriptor); + void incomingConnection(qintptr socketDescriptor); #else - void incomingConnection(int socketDescriptor); + void incomingConnection(int socketDescriptor); #endif private: + IRequestHandler *m_requestHandler; #ifndef QT_NO_OPENSSL - bool m_https; - QSslCertificate m_certificate; - QSslKey m_key; + bool m_https; + QSslCertificate m_certificate; + QSslKey m_key; #endif }; -#endif +} + +#endif // HTTP_SERVER_H diff --git a/src/webui/httptypes.h b/src/core/http/types.h similarity index 75% rename from src/webui/httptypes.h rename to src/core/http/types.h index 7fd95c310..5857031bb 100644 --- a/src/webui/httptypes.h +++ b/src/core/http/types.h @@ -26,8 +26,8 @@ * exception statement from your version. */ -#ifndef HTTPTYPES_H -#define HTTPTYPES_H +#ifndef HTTP_TYPES_H +#define HTTP_TYPES_H #include #include @@ -35,6 +35,9 @@ typedef QMap QStringMap; +namespace Http +{ + const QString HEADER_SET_COOKIE = "Set-Cookie"; const QString HEADER_CONTENT_TYPE = "Content-Type"; const QString HEADER_CONTENT_ENCODING = "Content-Encoding"; @@ -48,43 +51,45 @@ const QString CONTENT_TYPE_JS = "text/javascript; charset=UTF-8"; const QString CONTENT_TYPE_PNG = "image/png"; const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8"; -struct HttpEnvironment +struct Environment { - QHostAddress clientAddress; + QHostAddress clientAddress; }; struct UploadedFile { - QString filename; // original filename - QString type; // MIME type - QByteArray data; // File data + QString filename; // original filename + QString type; // MIME type + QByteArray data; // File data }; -struct HttpRequest +struct Request { - QString method; - QString path; - QStringMap headers; - QStringMap gets; - QStringMap posts; - QMap files; + QString method; + QString path; + QStringMap headers; + QStringMap gets; + QStringMap posts; + QMap files; }; -struct HttpResponseStatus +struct ResponseStatus { - uint code; - QString text; + uint code; + QString text; - HttpResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {} + ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {} }; -struct HttpResponse +struct Response { - HttpResponseStatus status; - QStringMap headers; - QByteArray content; + ResponseStatus status; + QStringMap headers; + QByteArray content; - HttpResponse(uint code = 200, const QString& text = "OK"): status(code, text) {} + Response(uint code = 200, const QString& text = "OK"): status(code, text) {} }; -#endif // HTTPTYPES_H +} + +#endif // HTTP_TYPES_H diff --git a/src/logger.cpp b/src/core/logger.cpp similarity index 100% rename from src/logger.cpp rename to src/core/logger.cpp diff --git a/src/logger.h b/src/core/logger.h similarity index 100% rename from src/logger.h rename to src/core/logger.h diff --git a/src/misc.cpp b/src/core/misc.cpp similarity index 99% rename from src/misc.cpp rename to src/core/misc.cpp index c11b68636..16995e322 100644 --- a/src/misc.cpp +++ b/src/core/misc.cpp @@ -444,10 +444,10 @@ QString misc::getUserIDString() { QString uid = "0"; #ifdef Q_OS_WIN - char buffer[UNLEN + 1] = {0}; - DWORD buffer_len = UNLEN + 1; - if (!GetUserNameA(buffer, &buffer_len)) - uid = QString(buffer); + WCHAR buffer[UNLEN + 1] = {0}; + DWORD buffer_len = sizeof(buffer)/sizeof(*buffer); + if (GetUserNameW(buffer, &buffer_len)) + uid = QString::fromWCharArray(buffer); #else uid = QString::number(getuid()); #endif diff --git a/src/misc.h b/src/core/misc.h similarity index 100% rename from src/misc.h rename to src/core/misc.h diff --git a/src/preferences/preferences.cpp b/src/core/preferences.cpp similarity index 99% rename from src/preferences/preferences.cpp rename to src/core/preferences.cpp index 22ca38016..e22f8de2f 100644 --- a/src/preferences/preferences.cpp +++ b/src/core/preferences.cpp @@ -165,6 +165,8 @@ void Preferences::save() { #else delete settings; #endif + + emit changed(); } const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const { diff --git a/src/preferences/preferences.h b/src/core/preferences.h old mode 100755 new mode 100644 similarity index 99% rename from src/preferences/preferences.h rename to src/core/preferences.h index 806755bce..0c3d2d97c --- a/src/preferences/preferences.h +++ b/src/core/preferences.h @@ -33,7 +33,6 @@ #ifndef PREFERENCES_H #define PREFERENCES_H - #include #include #include @@ -70,6 +69,9 @@ private: const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; void setValue(const QString &key, const QVariant &value); +signals: + void changed(); + public slots: void save(); diff --git a/src/qinisettings.h b/src/core/qinisettings.h similarity index 100% rename from src/qinisettings.h rename to src/core/qinisettings.h diff --git a/src/qtlibtorrent/alertdispatcher.cpp b/src/core/qtlibtorrent/alertdispatcher.cpp similarity index 100% rename from src/qtlibtorrent/alertdispatcher.cpp rename to src/core/qtlibtorrent/alertdispatcher.cpp diff --git a/src/qtlibtorrent/alertdispatcher.h b/src/core/qtlibtorrent/alertdispatcher.h similarity index 100% rename from src/qtlibtorrent/alertdispatcher.h rename to src/core/qtlibtorrent/alertdispatcher.h diff --git a/src/qtlibtorrent/bandwidthscheduler.h b/src/core/qtlibtorrent/bandwidthscheduler.h similarity index 100% rename from src/qtlibtorrent/bandwidthscheduler.h rename to src/core/qtlibtorrent/bandwidthscheduler.h diff --git a/src/qtlibtorrent/filterparserthread.cpp b/src/core/qtlibtorrent/filterparserthread.cpp similarity index 100% rename from src/qtlibtorrent/filterparserthread.cpp rename to src/core/qtlibtorrent/filterparserthread.cpp diff --git a/src/qtlibtorrent/filterparserthread.h b/src/core/qtlibtorrent/filterparserthread.h similarity index 100% rename from src/qtlibtorrent/filterparserthread.h rename to src/core/qtlibtorrent/filterparserthread.h diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/core/qtlibtorrent/qbtsession.cpp old mode 100755 new mode 100644 similarity index 98% rename from src/qtlibtorrent/qbtsession.cpp rename to src/core/qtlibtorrent/qbtsession.cpp index cd46234aa..05aff6a20 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/core/qtlibtorrent/qbtsession.cpp @@ -57,7 +57,6 @@ #include "geoipmanager.h" #endif #include "torrentpersistentdata.h" -#include "httpserver.h" #include "bandwidthscheduler.h" #include #include @@ -79,7 +78,6 @@ #include #include #include -#include "dnsupdater.h" #if LIBTORRENT_VERSION_NUM < 10000 #include @@ -117,7 +115,6 @@ QBtSession::QBtSession() #if LIBTORRENT_VERSION_NUM < 10000 , m_upnp(0), m_natpmp(0) #endif - , m_dynDNSUpdater(0) , m_alertDispatcher(0) { BigRatioTimer = new QTimer(this); @@ -159,6 +156,7 @@ QBtSession::QBtSession() connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&))); // Apply user settings to Bittorrent session configureSession(); + connect(pref, SIGNAL(changed()), SLOT(configureSession())); // Torrent speed monitor m_speedMonitor = new TorrentSpeedMonitor(this); m_torrentStatistics = new TorrentStatistics(this, this); @@ -191,9 +189,6 @@ QBtSession::~QBtSession() { delete downloader; if (bd_scheduler) delete bd_scheduler; - // HTTP Server - if (httpServer) - delete httpServer; delete m_alertDispatcher; delete m_torrentStatistics; qDebug("Deleting the session"); @@ -583,9 +578,6 @@ void QBtSession::configureSession() { }else{ disableIPFilter(); } - // Update Web UI - // Use a QTimer because the function can be called from qBtSession constructor - QTimer::singleShot(0, this, SLOT(initWebUi())); // * Proxy settings proxy_settings proxySettings; if (pref->isProxyEnabled()) { @@ -655,64 +647,6 @@ void QBtSession::configureSession() { qDebug("Session configured"); } -void QBtSession::initWebUi() { - Preferences* const pref = Preferences::instance(); - if (pref->isWebUiEnabled()) { - const quint16 port = pref->getWebUiPort(); - - if (httpServer) { - if (httpServer->serverPort() != port) { - httpServer->close(); - } - } else { - httpServer = new HttpServer(this); - } - -#ifndef QT_NO_OPENSSL - if (pref->isWebUiHttpsEnabled()) { - QSslCertificate cert(pref->getWebUiHttpsCertificate()); - QSslKey key; - const QByteArray raw_key = pref->getWebUiHttpsKey(); - key = QSslKey(raw_key, QSsl::Rsa); - if (!cert.isNull() && !key.isNull()) - httpServer->enableHttps(cert, key); - else - httpServer->disableHttps(); - } else { - httpServer->disableHttps(); - } -#endif - - if (!httpServer->isListening()) { - Logger* const logger = Logger::instance(); - bool success = httpServer->listen(QHostAddress::Any, port); - if (success) - logger->addMessage(tr("The Web UI is listening on port %1").arg(port)); - else - logger->addMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), Log::CRITICAL); - } - // DynDNS - if (pref->isDynDNSEnabled()) { - if (!m_dynDNSUpdater) - m_dynDNSUpdater = new DNSUpdater(this); - else - m_dynDNSUpdater->updateCredentials(); - } else { - if (m_dynDNSUpdater) { - delete m_dynDNSUpdater; - m_dynDNSUpdater = 0; - } - } - } else { - if (httpServer) - delete httpServer; - if (m_dynDNSUpdater) { - delete m_dynDNSUpdater; - m_dynDNSUpdater = 0; - } - } -} - void QBtSession::useAlternativeSpeedsLimit(bool alternative) { qDebug() << Q_FUNC_INFO << alternative; // Save new state to remember it on startup @@ -1495,6 +1429,7 @@ void QBtSession::enableUPnP(bool b) { s->start_upnp(); s->start_natpmp(); #endif + // TODO: Remove dependency from WebUI // Use UPnP/NAT-PMP for Web UI too if (pref->isWebUiEnabled() && pref->useUPnPForWebUIPort()) { const qint16 port = pref->getWebUiPort(); diff --git a/src/qtlibtorrent/qbtsession.h b/src/core/qtlibtorrent/qbtsession.h old mode 100755 new mode 100644 similarity index 98% rename from src/qtlibtorrent/qbtsession.h rename to src/core/qtlibtorrent/qbtsession.h index e856519d1..3edbe6a38 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/core/qtlibtorrent/qbtsession.h @@ -90,12 +90,10 @@ namespace libtorrent { class DownloadThread; class FilterParserThread; -class HttpServer; class BandwidthScheduler; class ScanFoldersModel; class TorrentSpeedMonitor; class TorrentStatistics; -class DNSUpdater; class QAlertDispatcher; enum TorrentExportFolder { @@ -209,7 +207,6 @@ public slots: #endif void addMagnetInteractive(const QString& uri); void downloadFromURLList(const QStringList& urls); - void configureSession(); void banIP(QString ip); void recursiveTorrentDownload(const QTorrentHandle &h); void unhideMagnet(const QString &hash); @@ -264,9 +261,9 @@ private slots: void mergeTorrents(QTorrentHandle& h_ex, boost::intrusive_ptr t); void mergeTorrents(QTorrentHandle& h_ex, const QString& magnet_uri); void exportTorrentFile(const QTorrentHandle &h, TorrentExportFolder folder = RegularTorrentExportFolder); - void initWebUi(); void handleIPFilterParsed(int ruleCount); void handleIPFilterError(); + void configureSession(); signals: void addedTorrent(const QTorrentHandle& h); @@ -327,8 +324,6 @@ private: // IP filtering QPointer filterParser; QString filterPath; - // Web UI - QPointer httpServer; QList url_skippingDlg; // GeoIP #ifndef DISABLE_GUI @@ -344,8 +339,6 @@ private: libtorrent::upnp *m_upnp; libtorrent::natpmp *m_natpmp; #endif - // DynDNS - DNSUpdater *m_dynDNSUpdater; QAlertDispatcher* m_alertDispatcher; TorrentStatistics* m_torrentStatistics; }; diff --git a/src/qtlibtorrent/qtlibtorrent.pri b/src/core/qtlibtorrent/qtlibtorrent.pri similarity index 100% rename from src/qtlibtorrent/qtlibtorrent.pri rename to src/core/qtlibtorrent/qtlibtorrent.pri diff --git a/src/qtlibtorrent/qtorrenthandle.cpp b/src/core/qtlibtorrent/qtorrenthandle.cpp similarity index 100% rename from src/qtlibtorrent/qtorrenthandle.cpp rename to src/core/qtlibtorrent/qtorrenthandle.cpp diff --git a/src/qtlibtorrent/qtorrenthandle.h b/src/core/qtlibtorrent/qtorrenthandle.h similarity index 100% rename from src/qtlibtorrent/qtorrenthandle.h rename to src/core/qtlibtorrent/qtorrenthandle.h diff --git a/src/qtlibtorrent/shutdownconfirm.cpp b/src/core/qtlibtorrent/shutdownconfirm.cpp similarity index 100% rename from src/qtlibtorrent/shutdownconfirm.cpp rename to src/core/qtlibtorrent/shutdownconfirm.cpp diff --git a/src/qtlibtorrent/shutdownconfirm.h b/src/core/qtlibtorrent/shutdownconfirm.h similarity index 100% rename from src/qtlibtorrent/shutdownconfirm.h rename to src/core/qtlibtorrent/shutdownconfirm.h diff --git a/src/qtlibtorrent/torrentmodel.cpp b/src/core/qtlibtorrent/torrentmodel.cpp similarity index 97% rename from src/qtlibtorrent/torrentmodel.cpp rename to src/core/qtlibtorrent/torrentmodel.cpp index 5df8298bc..25973828a 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/core/qtlibtorrent/torrentmodel.cpp @@ -41,42 +41,42 @@ using namespace libtorrent; namespace { QIcon get_paused_icon() { - static QIcon cached = QIcon(":/Icons/skin/paused.png"); + static QIcon cached = QIcon(":/icons/skin/paused.png"); return cached; } QIcon get_queued_icon() { - static QIcon cached = QIcon(":/Icons/skin/queued.png"); + static QIcon cached = QIcon(":/icons/skin/queued.png"); return cached; } QIcon get_downloading_icon() { - static QIcon cached = QIcon(":/Icons/skin/downloading.png"); + static QIcon cached = QIcon(":/icons/skin/downloading.png"); return cached; } QIcon get_stalled_downloading_icon() { - static QIcon cached = QIcon(":/Icons/skin/stalledDL.png"); + static QIcon cached = QIcon(":/icons/skin/stalledDL.png"); return cached; } QIcon get_uploading_icon() { - static QIcon cached = QIcon(":/Icons/skin/uploading.png"); + static QIcon cached = QIcon(":/icons/skin/uploading.png"); return cached; } QIcon get_stalled_uploading_icon() { - static QIcon cached = QIcon(":/Icons/skin/stalledUP.png"); + static QIcon cached = QIcon(":/icons/skin/stalledUP.png"); return cached; } QIcon get_checking_icon() { - static QIcon cached = QIcon(":/Icons/skin/checking.png"); + static QIcon cached = QIcon(":/icons/skin/checking.png"); return cached; } QIcon get_error_icon() { - static QIcon cached = QIcon(":/Icons/skin/error.png"); + static QIcon cached = QIcon(":/icons/skin/error.png"); return cached; } } diff --git a/src/qtlibtorrent/torrentmodel.h b/src/core/qtlibtorrent/torrentmodel.h similarity index 100% rename from src/qtlibtorrent/torrentmodel.h rename to src/core/qtlibtorrent/torrentmodel.h diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/core/qtlibtorrent/torrentspeedmonitor.cpp similarity index 100% rename from src/qtlibtorrent/torrentspeedmonitor.cpp rename to src/core/qtlibtorrent/torrentspeedmonitor.cpp diff --git a/src/qtlibtorrent/torrentspeedmonitor.h b/src/core/qtlibtorrent/torrentspeedmonitor.h similarity index 100% rename from src/qtlibtorrent/torrentspeedmonitor.h rename to src/core/qtlibtorrent/torrentspeedmonitor.h diff --git a/src/qtlibtorrent/torrentstatistics.cpp b/src/core/qtlibtorrent/torrentstatistics.cpp similarity index 100% rename from src/qtlibtorrent/torrentstatistics.cpp rename to src/core/qtlibtorrent/torrentstatistics.cpp diff --git a/src/qtlibtorrent/torrentstatistics.h b/src/core/qtlibtorrent/torrentstatistics.h similarity index 100% rename from src/qtlibtorrent/torrentstatistics.h rename to src/core/qtlibtorrent/torrentstatistics.h diff --git a/src/qtlibtorrent/trackerinfos.h b/src/core/qtlibtorrent/trackerinfos.h similarity index 100% rename from src/qtlibtorrent/trackerinfos.h rename to src/core/qtlibtorrent/trackerinfos.h diff --git a/src/qtnotify/notifications.cpp b/src/core/qtnotify/notifications.cpp similarity index 100% rename from src/qtnotify/notifications.cpp rename to src/core/qtnotify/notifications.cpp diff --git a/src/qtnotify/notifications.h b/src/core/qtnotify/notifications.h similarity index 100% rename from src/qtnotify/notifications.h rename to src/core/qtnotify/notifications.h diff --git a/src/qtnotify/notifications.xml b/src/core/qtnotify/notifications.xml similarity index 100% rename from src/qtnotify/notifications.xml rename to src/core/qtnotify/notifications.xml diff --git a/src/qtnotify/qtnotify.pri b/src/core/qtnotify/qtnotify.pri similarity index 100% rename from src/qtnotify/qtnotify.pri rename to src/core/qtnotify/qtnotify.pri diff --git a/src/core/qtracker.cpp b/src/core/qtracker.cpp new file mode 100644 index 000000000..cc7146861 --- /dev/null +++ b/src/core/qtracker.cpp @@ -0,0 +1,248 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * Copyright (C) 2006 Christophe Dumez + * + * 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. + * + * Contact : chris@qbittorrent.org + */ + +#include +#include + +#include "preferences.h" +#include "http/server.h" +#include "qtracker.h" + +// QPeer +bool QPeer::operator!=(const QPeer &other) const +{ + return qhash() != other.qhash(); +} + +bool QPeer::operator==(const QPeer &other) const +{ + return qhash() == other.qhash(); +} + +QString QPeer::qhash() const +{ + return ip + ":" + QString::number(port); +} + +libtorrent::entry QPeer::toEntry(bool no_peer_id) const +{ + libtorrent::entry::dictionary_type peer_map; + if (!no_peer_id) + peer_map["id"] = libtorrent::entry(peer_id.toStdString()); + peer_map["ip"] = libtorrent::entry(ip.toStdString()); + peer_map["port"] = libtorrent::entry(port); + + return libtorrent::entry(peer_map); +} + +// QTracker + +QTracker::QTracker(QObject *parent) + : Http::ResponseBuilder(parent) + , m_server(new Http::Server(this, this)) +{ +} + +QTracker::~QTracker() +{ + if (m_server->isListening()) + qDebug("Shutting down the embedded tracker..."); + // TODO: Store the torrent list +} + +bool QTracker::start() +{ + const int listen_port = Preferences::instance()->getTrackerPort(); + + if (m_server->isListening()) { + if (m_server->serverPort() == listen_port) { + // Already listening on the right port, just return + return true; + } + // Wrong port, closing the server + m_server->close(); + } + + qDebug("Starting the embedded tracker..."); + // Listen on the predefined port + return m_server->listen(QHostAddress::Any, listen_port); +} + +Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env) +{ + clear(); // clear response + + //qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString())); + // Is request a GET request? + if (request.method != "GET") { + qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method)); + status(100, "Invalid request type"); + } + else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) { + qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path)); + status(100, "Invalid request type"); + } + else { + // OK, this is a GET request + m_request = request; + m_env = env; + respondToAnnounceRequest(); + } + + return response(); +} + +void QTracker::respondToAnnounceRequest() +{ + const QStringMap &gets = m_request.gets; + TrackerAnnounceRequest annonce_req; + + // IP + annonce_req.peer.ip = m_env.clientAddress.toString(); + + // 1. Get info_hash + if (!gets.contains("info_hash")) { + qDebug("QTracker: Missing info_hash"); + status(101, "Missing info_hash"); + return; + } + annonce_req.info_hash = gets.value("info_hash"); + // info_hash cannot be longer than 20 bytes + /*if (annonce_req.info_hash.toLatin1().length() > 20) { + qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); + status(150, "Invalid infohash"); + return; + }*/ + + // 2. Get peer ID + if (!gets.contains("peer_id")) { + qDebug("QTracker: Missing peer_id"); + status(102, "Missing peer_id"); + return; + } + annonce_req.peer.peer_id = gets.value("peer_id"); + // peer_id cannot be longer than 20 bytes + /*if (annonce_req.peer.peer_id.length() > 20) { + qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id)); + status(151, "Invalid peerid"); + return; + }*/ + + // 3. Get port + if (!gets.contains("port")) { + qDebug("QTracker: Missing port"); + status(103, "Missing port"); + return; + } + bool ok = false; + annonce_req.peer.port = gets.value("port").toInt(&ok); + if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) { + qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port); + status(103, "Missing port"); + return; + } + + // 4. Get event + annonce_req.event = ""; + if (gets.contains("event")) { + annonce_req.event = gets.value("event"); + qDebug("QTracker: event is %s", qPrintable(annonce_req.event)); + } + + // 5. Get numwant + annonce_req.numwant = 50; + if (gets.contains("numwant")) { + int tmp = gets.value("numwant").toInt(); + if (tmp > 0) { + qDebug("QTracker: numwant = %d", tmp); + annonce_req.numwant = tmp; + } + } + + // 6. no_peer_id (extension) + annonce_req.no_peer_id = false; + if (gets.contains("no_peer_id")) + annonce_req.no_peer_id = true; + + // 7. TODO: support "compact" extension + + // Done parsing, now let's reply + if (m_torrents.contains(annonce_req.info_hash)) { + if (annonce_req.event == "stopped") { + qDebug("QTracker: Peer stopped downloading, deleting it from the list"); + m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash()); + return; + } + } + else { + // Unknown torrent + if (m_torrents.size() == MAX_TORRENTS) { + // Reached max size, remove a random torrent + m_torrents.erase(m_torrents.begin()); + } + } + // Register the user + PeerList peers = m_torrents.value(annonce_req.info_hash); + if (peers.size() == MAX_PEERS_PER_TORRENT) { + // Too many peers, remove a random one + peers.erase(peers.begin()); + } + peers[annonce_req.peer.qhash()] = annonce_req.peer; + m_torrents[annonce_req.info_hash] = peers; + + // Reply + replyWithPeerList(annonce_req); +} + +void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req) +{ + // Prepare the entry for bencoding + libtorrent::entry::dictionary_type reply_dict; + reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL); + QList peers = m_torrents.value(annonce_req.info_hash).values(); + libtorrent::entry::list_type peer_list; + foreach (const QPeer &p, peers) { + //if (p != annonce_req.peer) + peer_list.push_back(p.toEntry(annonce_req.no_peer_id)); + } + reply_dict["peers"] = libtorrent::entry(peer_list); + libtorrent::entry reply_entry(reply_dict); + // bencode + std::vector buf; + libtorrent::bencode(std::back_inserter(buf), reply_entry); + QByteArray reply(&buf[0], static_cast(buf.size())); + qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); + + // HTTP reply + print(reply, Http::CONTENT_TYPE_TXT); +} + + diff --git a/src/tracker/qtracker.h b/src/core/qtracker.h similarity index 59% rename from src/tracker/qtracker.h rename to src/core/qtracker.h index bf313ba18..3c1484f7a 100644 --- a/src/tracker/qtracker.h +++ b/src/core/qtracker.h @@ -1,5 +1,6 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -31,11 +32,33 @@ #ifndef QTRACKER_H #define QTRACKER_H -#include +#include #include +#include "http/types.h" +#include "http/responsebuilder.h" +#include "http/irequesthandler.h" -#include "trackerannouncerequest.h" -#include "qpeer.h" +struct QPeer +{ + QString ip; + QString peer_id; + int port; + + bool operator!=(const QPeer &other) const; + bool operator==(const QPeer &other) const; + QString qhash() const; + libtorrent::entry toEntry(bool no_peer_id) const; +}; + +struct TrackerAnnounceRequest +{ + QString info_hash; + QString event; + int numwant; + QPeer peer; + // Extensions + bool no_peer_id; +}; // static limits const int MAX_TORRENTS = 100; @@ -45,28 +68,31 @@ const int ANNOUNCE_INTERVAL = 1800; // 30min typedef QHash PeerList; typedef QHash TorrentList; -/* Basic Bittorrent tracker implementation in Qt4 */ +namespace Http { class Server; } + +/* Basic Bittorrent tracker implementation in Qt */ /* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */ -class QTracker : public QTcpServer +class QTracker : public Http::ResponseBuilder, public Http::IRequestHandler { - Q_OBJECT - Q_DISABLE_COPY(QTracker) + Q_OBJECT + Q_DISABLE_COPY(QTracker) public: - explicit QTracker(QObject *parent = 0); - ~QTracker(); - bool start(); + explicit QTracker(QObject *parent = 0); + ~QTracker(); -protected slots: - void readRequest(); - void handlePeerConnection(); - void respondInvalidRequest(QTcpSocket *socket, int code, QString msg); - void respondToAnnounceRequest(QTcpSocket *socket, const QMap& get_parameters); - void ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req); + bool start(); + Http::Response processRequest(const Http::Request &request, const Http::Environment &env); private: - TorrentList m_torrents; + void respondToAnnounceRequest(); + void replyWithPeerList(const TrackerAnnounceRequest &annonce_req); + + Http::Server *m_server; + TorrentList m_torrents; + Http::Request m_request; + Http::Environment m_env; }; #endif // QTRACKER_H diff --git a/src/scannedfoldersmodel.cpp b/src/core/scannedfoldersmodel.cpp similarity index 100% rename from src/scannedfoldersmodel.cpp rename to src/core/scannedfoldersmodel.cpp diff --git a/src/scannedfoldersmodel.h b/src/core/scannedfoldersmodel.h similarity index 100% rename from src/scannedfoldersmodel.h rename to src/core/scannedfoldersmodel.h diff --git a/src/smtp.cpp b/src/core/smtp.cpp similarity index 100% rename from src/smtp.cpp rename to src/core/smtp.cpp diff --git a/src/smtp.h b/src/core/smtp.h similarity index 100% rename from src/smtp.h rename to src/core/smtp.h diff --git a/src/torrentpersistentdata.cpp b/src/core/torrentpersistentdata.cpp similarity index 100% rename from src/torrentpersistentdata.cpp rename to src/core/torrentpersistentdata.cpp diff --git a/src/torrentpersistentdata.h b/src/core/torrentpersistentdata.h similarity index 100% rename from src/torrentpersistentdata.h rename to src/core/torrentpersistentdata.h diff --git a/src/about.qrc b/src/gui/about.qrc similarity index 100% rename from src/about.qrc rename to src/gui/about.qrc diff --git a/src/about.ui b/src/gui/about.ui similarity index 99% rename from src/about.ui rename to src/gui/about.ui index b08cf2a1b..8dd7d07d3 100644 --- a/src/about.ui +++ b/src/gui/about.ui @@ -89,7 +89,7 @@ - :/Icons/skin/mascot.png + :/icons/skin/mascot.png diff --git a/src/about_imp.h b/src/gui/about_imp.h similarity index 99% rename from src/about_imp.h rename to src/gui/about_imp.h index 4990f528a..3972e5c09 100644 --- a/src/about_imp.h +++ b/src/gui/about_imp.h @@ -67,7 +67,7 @@ class about : public QDialog, private Ui::AboutDlg{ "

"); lb_about->setText(aboutText); // Set icons - logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png"))); + logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/skin/qbittorrent22.png"))); //Title lb_name->setText(QString::fromUtf8("

qBittorrent")+QString::fromUtf8(" " VERSION"

")); // Thanks diff --git a/src/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp similarity index 100% rename from src/addnewtorrentdialog.cpp rename to src/gui/addnewtorrentdialog.cpp diff --git a/src/addnewtorrentdialog.h b/src/gui/addnewtorrentdialog.h similarity index 100% rename from src/addnewtorrentdialog.h rename to src/gui/addnewtorrentdialog.h diff --git a/src/addnewtorrentdialog.ui b/src/gui/addnewtorrentdialog.ui similarity index 100% rename from src/addnewtorrentdialog.ui rename to src/gui/addnewtorrentdialog.ui diff --git a/src/preferences/advancedsettings.h b/src/gui/advancedsettings.h similarity index 100% rename from src/preferences/advancedsettings.h rename to src/gui/advancedsettings.h diff --git a/src/autoexpandabledialog.cpp b/src/gui/autoexpandabledialog.cpp similarity index 100% rename from src/autoexpandabledialog.cpp rename to src/gui/autoexpandabledialog.cpp diff --git a/src/autoexpandabledialog.h b/src/gui/autoexpandabledialog.h similarity index 100% rename from src/autoexpandabledialog.h rename to src/gui/autoexpandabledialog.h diff --git a/src/autoexpandabledialog.ui b/src/gui/autoexpandabledialog.ui similarity index 100% rename from src/autoexpandabledialog.ui rename to src/gui/autoexpandabledialog.ui diff --git a/src/bandwidth_limit.ui b/src/gui/bandwidth_limit.ui similarity index 100% rename from src/bandwidth_limit.ui rename to src/gui/bandwidth_limit.ui diff --git a/src/confirmdeletiondlg.ui b/src/gui/confirmdeletiondlg.ui similarity index 100% rename from src/confirmdeletiondlg.ui rename to src/gui/confirmdeletiondlg.ui diff --git a/src/deletionconfirmationdlg.h b/src/gui/deletionconfirmationdlg.h similarity index 100% rename from src/deletionconfirmationdlg.h rename to src/gui/deletionconfirmationdlg.h diff --git a/src/downloadfromurldlg.h b/src/gui/downloadfromurldlg.h similarity index 100% rename from src/downloadfromurldlg.h rename to src/gui/downloadfromurldlg.h diff --git a/src/downloadfromurldlg.ui b/src/gui/downloadfromurldlg.ui similarity index 100% rename from src/downloadfromurldlg.ui rename to src/gui/downloadfromurldlg.ui diff --git a/src/executionlog.cpp b/src/gui/executionlog.cpp similarity index 100% rename from src/executionlog.cpp rename to src/gui/executionlog.cpp diff --git a/src/executionlog.h b/src/gui/executionlog.h similarity index 100% rename from src/executionlog.h rename to src/gui/executionlog.h diff --git a/src/executionlog.ui b/src/gui/executionlog.ui similarity index 100% rename from src/executionlog.ui rename to src/gui/executionlog.ui diff --git a/src/gui/geoip/GeoIP.dat b/src/gui/geoip/GeoIP.dat new file mode 100644 index 000000000..44eb02eb7 Binary files /dev/null and b/src/gui/geoip/GeoIP.dat differ diff --git a/src/geoip/README b/src/gui/geoip/README similarity index 100% rename from src/geoip/README rename to src/gui/geoip/README diff --git a/src/geoip/geoip.pri b/src/gui/geoip/geoip.pri similarity index 100% rename from src/geoip/geoip.pri rename to src/gui/geoip/geoip.pri diff --git a/src/geoip/geoip.qrc b/src/gui/geoip/geoip.qrc similarity index 100% rename from src/geoip/geoip.qrc rename to src/gui/geoip/geoip.qrc diff --git a/src/geoip/geoipmanager.cpp b/src/gui/geoip/geoipmanager.cpp similarity index 99% rename from src/geoip/geoipmanager.cpp rename to src/gui/geoip/geoipmanager.cpp index 6b970d52c..1e0c58c55 100644 --- a/src/geoip/geoipmanager.cpp +++ b/src/gui/geoip/geoipmanager.cpp @@ -198,6 +198,6 @@ QString GeoIPManager::CountryISOCodeToName(const char* iso) { QIcon GeoIPManager::CountryISOCodeToIcon(const char* iso) { if (iso[0] == 0 || iso[0] == '!') return QIcon(); const QString isoStr = QString(QByteArray(iso, 2)).toLower(); - return QIcon(":/Icons/flags/"+isoStr+".png"); + return QIcon(":/icons/flags/"+isoStr+".png"); } diff --git a/src/geoip/geoipmanager.h b/src/gui/geoip/geoipmanager.h similarity index 100% rename from src/geoip/geoipmanager.h rename to src/gui/geoip/geoipmanager.h diff --git a/src/gpl.html b/src/gui/gpl.html similarity index 100% rename from src/gpl.html rename to src/gui/gpl.html diff --git a/src/gui/gui.pri b/src/gui/gui.pri new file mode 100644 index 000000000..b297435d2 --- /dev/null +++ b/src/gui/gui.pri @@ -0,0 +1,96 @@ +INCLUDEPATH += $$PWD + +include(lineedit/lineedit.pri) +include(properties/properties.pri) +include(searchengine/searchengine.pri) +include(rss/rss.pri) +include(torrentcreator/torrentcreator.pri) +include(geoip/geoip.pri) +include(powermanagement/powermanagement.pri) + +HEADERS += \ + $$PWD/mainwindow.h \ + $$PWD/transferlistwidget.h \ + $$PWD/transferlistdelegate.h \ + $$PWD/transferlistfilterswidget.h \ + $$PWD/transferlistsortmodel.h \ + $$PWD/torrentcontentmodel.h \ + $$PWD/torrentcontentmodelitem.h \ + $$PWD/torrentcontentmodelfolder.h \ + $$PWD/torrentcontentmodelfile.h \ + $$PWD/torrentcontentfiltermodel.h \ + $$PWD/torrentcontenttreeview.h \ + $$PWD/deletionconfirmationdlg.h \ + $$PWD/statusbar.h \ + $$PWD/reverseresolution.h \ + $$PWD/ico.h \ + $$PWD/speedlimitdlg.h \ + $$PWD/about_imp.h \ + $$PWD/previewselect.h \ + $$PWD/previewlistdelegate.h \ + $$PWD/downloadfromurldlg.h \ + $$PWD/trackerlogin.h \ + $$PWD/hidabletabwidget.h \ + $$PWD/torrentimportdlg.h \ + $$PWD/executionlog.h \ + $$PWD/iconprovider.h \ + $$PWD/updownratiodlg.h \ + $$PWD/loglistwidget.h \ + $$PWD/addnewtorrentdialog.h \ + $$PWD/autoexpandabledialog.h \ + $$PWD/statsdialog.h \ + $$PWD/messageboxraised.h \ + $$PWD/torrentfilterenum.h \ + $$PWD/options_imp.h \ + $$PWD/advancedsettings.h + +SOURCES += \ + $$PWD/mainwindow.cpp \ + $$PWD/ico.cpp \ + $$PWD/transferlistwidget.cpp \ + $$PWD/transferlistsortmodel.cpp \ + $$PWD/transferlistdelegate.cpp \ + $$PWD/transferlistfilterswidget.cpp \ + $$PWD/torrentcontentmodel.cpp \ + $$PWD/torrentcontentmodelitem.cpp \ + $$PWD/torrentcontentmodelfolder.cpp \ + $$PWD/torrentcontentmodelfile.cpp \ + $$PWD/torrentcontentfiltermodel.cpp \ + $$PWD/torrentcontenttreeview.cpp \ + $$PWD/torrentimportdlg.cpp \ + $$PWD/executionlog.cpp \ + $$PWD/speedlimitdlg.cpp \ + $$PWD/previewselect.cpp \ + $$PWD/iconprovider.cpp \ + $$PWD/updownratiodlg.cpp \ + $$PWD/loglistwidget.cpp \ + $$PWD/addnewtorrentdialog.cpp \ + $$PWD/autoexpandabledialog.cpp \ + $$PWD/statsdialog.cpp \ + $$PWD/messageboxraised.cpp \ + $$PWD/statusbar.cpp \ + $$PWD/trackerlogin.cpp \ + $$PWD/options_imp.cpp + +win32|macx { + HEADERS += $$PWD/programupdater.h + SOURCES += $$PWD/programupdater.cpp +} + +FORMS += \ + $$PWD/mainwindow.ui \ + $$PWD/about.ui \ + $$PWD/preview.ui \ + $$PWD/login.ui \ + $$PWD/downloadfromurldlg.ui \ + $$PWD/bandwidth_limit.ui \ + $$PWD/updownratiodlg.ui \ + $$PWD/confirmdeletiondlg.ui \ + $$PWD/torrentimportdlg.ui \ + $$PWD/executionlog.ui \ + $$PWD/addnewtorrentdialog.ui \ + $$PWD/autoexpandabledialog.ui \ + $$PWD/statsdialog.ui \ + $$PWD/options.ui + +RESOURCES += $$PWD/about.qrc diff --git a/src/hidabletabwidget.h b/src/gui/hidabletabwidget.h similarity index 76% rename from src/hidabletabwidget.h rename to src/gui/hidabletabwidget.h index 9d87e850e..654556f9a 100644 --- a/src/hidabletabwidget.h +++ b/src/gui/hidabletabwidget.h @@ -1,5 +1,5 @@ /* - * Bittorrent Client using Qt4 and libtorrent. + * Bittorrent Client using Qt and libtorrent. * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -34,31 +34,27 @@ #include #include -class HidableTabWidget : public QTabWidget { +class HidableTabWidget : public QTabWidget +{ public: - - void showTabBar(bool show) { - tabBar()->setVisible(show); - } + explicit HidableTabWidget(QWidget *parent = 0) + : QTabWidget(parent) + { + } protected: - void tabInserted(int index) { - QTabWidget::tabInserted(index); - if (count() == 1) { - showTabBar(false); - } else { - showTabBar(true); + void tabInserted(int index) + { + QTabWidget::tabInserted(index); + tabBar()->setVisible(count() != 1); } - } - void tabRemoved(int index) { - QTabWidget::tabInserted(index); - if (count() == 1) { - showTabBar(false); - } else { - showTabBar(true); + void tabRemoved(int index) + { + //QTabWidget::tabInserted(index); + QTabWidget::tabRemoved(index); + tabBar()->setVisible(count() != 1); } - } }; #endif // HIDABLETABWIDGET_H diff --git a/src/ico.cpp b/src/gui/ico.cpp similarity index 100% rename from src/ico.cpp rename to src/gui/ico.cpp diff --git a/src/ico.h b/src/gui/ico.h similarity index 100% rename from src/ico.h rename to src/gui/ico.h diff --git a/src/iconprovider.cpp b/src/gui/iconprovider.cpp similarity index 94% rename from src/iconprovider.cpp rename to src/gui/iconprovider.cpp index a26cb44f5..2213993a4 100644 --- a/src/iconprovider.cpp +++ b/src/gui/iconprovider.cpp @@ -64,12 +64,12 @@ QIcon IconProvider::getIcon(const QString &iconId) { #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) if (m_useSystemTheme) { - QIcon icon = QIcon::fromTheme(iconId, QIcon(":/Icons/oxygen/"+iconId+".png")); + QIcon icon = QIcon::fromTheme(iconId, QIcon(":/icons/oxygen/"+iconId+".png")); icon = generateDifferentSizes(icon); return icon; } #endif - return QIcon(":/Icons/oxygen/"+iconId+".png"); + return QIcon(":/icons/oxygen/"+iconId+".png"); } #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) @@ -112,12 +112,12 @@ QString IconProvider::getIconPath(const QString& iconId) QString path = QDir::temp().absoluteFilePath(iconId+".png"); if (!QFile::exists(path)) { const QIcon icon = QIcon::fromTheme(iconId); - if (icon.isNull()) return ":/Icons/oxygen/"+iconId+".png"; + if (icon.isNull()) return ":/icons/oxygen/"+iconId+".png"; QPixmap px = icon.pixmap(32); px.save(path); } return path; } #endif - return ":/Icons/oxygen/"+iconId+".png"; + return ":/icons/oxygen/"+iconId+".png"; } diff --git a/src/iconprovider.h b/src/gui/iconprovider.h similarity index 100% rename from src/iconprovider.h rename to src/gui/iconprovider.h diff --git a/src/lineedit/lineedit.pri b/src/gui/lineedit/lineedit.pri similarity index 100% rename from src/lineedit/lineedit.pri rename to src/gui/lineedit/lineedit.pri diff --git a/src/lineedit/resources/lineeditimages.qrc b/src/gui/lineedit/resources/lineeditimages.qrc similarity index 100% rename from src/lineedit/resources/lineeditimages.qrc rename to src/gui/lineedit/resources/lineeditimages.qrc diff --git a/src/lineedit/resources/lineeditimages/clear_left.png b/src/gui/lineedit/resources/lineeditimages/clear_left.png similarity index 100% rename from src/lineedit/resources/lineeditimages/clear_left.png rename to src/gui/lineedit/resources/lineeditimages/clear_left.png diff --git a/src/lineedit/resources/lineeditimages/search.png b/src/gui/lineedit/resources/lineeditimages/search.png similarity index 100% rename from src/lineedit/resources/lineeditimages/search.png rename to src/gui/lineedit/resources/lineeditimages/search.png diff --git a/src/lineedit/src/lineedit.cpp b/src/gui/lineedit/src/lineedit.cpp similarity index 100% rename from src/lineedit/src/lineedit.cpp rename to src/gui/lineedit/src/lineedit.cpp diff --git a/src/lineedit/src/lineedit.h b/src/gui/lineedit/src/lineedit.h similarity index 100% rename from src/lineedit/src/lineedit.h rename to src/gui/lineedit/src/lineedit.h diff --git a/src/login.ui b/src/gui/login.ui similarity index 100% rename from src/login.ui rename to src/gui/login.ui diff --git a/src/loglistwidget.cpp b/src/gui/loglistwidget.cpp similarity index 100% rename from src/loglistwidget.cpp rename to src/gui/loglistwidget.cpp diff --git a/src/loglistwidget.h b/src/gui/loglistwidget.h similarity index 100% rename from src/loglistwidget.h rename to src/gui/loglistwidget.h diff --git a/src/mainwindow.cpp b/src/gui/mainwindow.cpp old mode 100755 new mode 100644 similarity index 92% rename from src/mainwindow.cpp rename to src/gui/mainwindow.cpp index d0300fe3f..1adc167bd --- a/src/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -77,11 +77,10 @@ #include "autoexpandabledialog.h" #endif #ifdef Q_OS_MAC -#include "qmacapplication.h" void qt_mac_set_dock_menu(QMenu *menu); #endif #include "lineedit.h" -#include "sessionapplication.h" +#include "application.h" #if defined(Q_OS_WIN) || defined(Q_OS_MAC) #include "programupdater.h" #endif @@ -104,7 +103,11 @@ using namespace libtorrent; *****************************************************/ // Constructor -MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMainWindow(parent), m_posInitialized(false), force_exit(false), unlockDlgShowing(false) +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , m_posInitialized(false) + , force_exit(false) + , unlockDlgShowing(false) #ifdef Q_OS_WIN , has_python(false) #endif @@ -120,19 +123,19 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai // Setting icons #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) if (Preferences::instance()->useSystemIconTheme()) - setWindowIcon(QIcon::fromTheme("qbittorrent", QIcon(QString::fromUtf8(":/Icons/skin/qbittorrent32.png")))); + setWindowIcon(QIcon::fromTheme("qbittorrent", QIcon(QString::fromUtf8(":/icons/skin/qbittorrent32.png")))); else #endif - setWindowIcon(QIcon(QString::fromUtf8(":/Icons/skin/qbittorrent32.png"))); + setWindowIcon(QIcon(QString::fromUtf8(":/icons/skin/qbittorrent32.png"))); addToolbarContextMenu(); actionOpen->setIcon(IconProvider::instance()->getIcon("list-add")); actionDownload_from_URL->setIcon(IconProvider::instance()->getIcon("insert-link")); - actionSet_upload_limit->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))); - actionSet_download_limit->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/download.png"))); - actionSet_global_upload_limit->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))); - actionSet_global_download_limit->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/download.png"))); + actionSet_upload_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/seeding.png"))); + actionSet_download_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/download.png"))); + actionSet_global_upload_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/seeding.png"))); + actionSet_global_download_limit->setIcon(QIcon(QString::fromUtf8(":/icons/skin/download.png"))); actionCreate_torrent->setIcon(IconProvider::instance()->getIcon("document-edit")); actionAbout->setIcon(IconProvider::instance()->getIcon("help-about")); actionStatistics->setIcon(IconProvider::instance()->getIcon("view-statistics")); @@ -166,6 +169,7 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai QAction *clearUiLockPasswdAct = lockMenu->addAction(tr("Clear the password")); connect(clearUiLockPasswdAct, SIGNAL(triggered()), this, SLOT(clearUILockPassword())); actionLock_qBittorrent->setMenu(lockMenu); + // Creating Bittorrent session connect(QBtSession::instance(), SIGNAL(fullDiskError(QTorrentHandle, QString)), this, SLOT(fullDiskError(QTorrentHandle, QString))); connect(QBtSession::instance(), SIGNAL(finishedTorrent(QTorrentHandle)), this, SLOT(finishedTorrent(QTorrentHandle))); @@ -175,24 +179,24 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai connect(QBtSession::instance(), SIGNAL(downloadFromUrlFailure(QString, QString)), this, SLOT(handleDownloadFromUrlFailure(QString, QString))); connect(QBtSession::instance(), SIGNAL(alternativeSpeedsModeChanged(bool)), this, SLOT(updateAltSpeedsBtn(bool))); connect(QBtSession::instance(), SIGNAL(recursiveTorrentDownloadPossible(QTorrentHandle)), this, SLOT(askRecursiveTorrentDownloadConfirmation(QTorrentHandle))); -#ifdef Q_OS_MAC - connect(static_cast(qApp), SIGNAL(newFileOpenMacEvent(QString)), this, SLOT(processParams(QString))); -#endif qDebug("create tabWidget"); - tabs = new HidableTabWidget(); + tabs = new HidableTabWidget(this); connect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tab_changed(int))); - vSplitter = new QSplitter(Qt::Horizontal); + + vSplitter = new QSplitter(Qt::Horizontal, this); //vSplitter->setChildrenCollapsible(false); - hSplitter = new QSplitter(Qt::Vertical); + + hSplitter = new QSplitter(Qt::Vertical, this); hSplitter->setChildrenCollapsible(false); hSplitter->setContentsMargins(0, 4, 0, 0); // Name filter - search_filter = new LineEdit(); + search_filter = new LineEdit(this); searchFilterAct = toolBar->insertWidget(actionLock_qBittorrent, search_filter); search_filter->setPlaceholderText(tr("Filter torrent list...")); search_filter->setFixedWidth(200); + QWidget *spacer = new QWidget(this); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); toolBar->insertWidget(searchFilterAct, spacer); @@ -275,7 +279,7 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai QTimer::singleShot(0, this, SLOT(on_actionSearch_engine_triggered())); // Auto shutdown actions - QActionGroup * autoShutdownGroup = new QActionGroup(this); + QActionGroup *autoShutdownGroup = new QActionGroup(this); autoShutdownGroup->setExclusive(true); autoShutdownGroup->addAction(actionAutoShutdown_Disabled); autoShutdownGroup->addAction(actionAutoExit_qBittorrent); @@ -310,15 +314,10 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai properties->readSettings(); // Start watching the executable for updates - executable_watcher = new QFileSystemWatcher(); + executable_watcher = new QFileSystemWatcher(this); connect(executable_watcher, SIGNAL(fileChanged(QString)), this, SLOT(notifyOfUpdate(QString))); executable_watcher->addPath(qApp->applicationFilePath()); - // Resume unfinished torrents - QBtSession::instance()->startUpTorrents(); - // Add torrent given on command line - processParams(torrentCmdLine); - // Populate the transfer list transferList->getSourceModel()->populate(); transferList->setFocus(); @@ -359,6 +358,16 @@ MainWindow::MainWindow(QWidget *parent, const QStringList& torrentCmdLine): QMai } } +MainWindow::~MainWindow() +{ + // Save window size, columns size + writeSettings(); +#ifdef Q_OS_MAC + // Workaround to avoid bug http://bugreports.qt.nokia.com/browse/QTBUG-7305 + setUnifiedTitleAndToolBarOnMac(false); +#endif +} + void MainWindow::addToolbarContextMenu() { const Preferences* const pref = Preferences::instance(); @@ -450,68 +459,6 @@ void MainWindow::toolbarFollowSystem() Preferences::instance()->setToolbarTextPosition(Qt::ToolButtonFollowStyle); } -void MainWindow::shutdownCleanUp() -{ - qDebug("GUI destruction"); - hide(); - guiUpdater->stop(); - status_bar->stopTimer(); - m_pwr->setActivityState(false); - QBtSession::drop(); - // Save window size, columns size - writeSettings(); -#ifdef Q_OS_MAC - // Workaround to avoid bug http://bugreports.qt.nokia.com/browse/QTBUG-7305 - setUnifiedTitleAndToolBarOnMac(false); -#endif - disconnect(tabs, SIGNAL(currentChanged(int)), this, SLOT(tab_changed(int))); - // Delete other GUI objects - if (executable_watcher) - delete executable_watcher; - delete status_bar; - delete search_filter; - delete transferList; - delete guiUpdater; - if (createTorrentDlg) - delete createTorrentDlg; - if (m_executionLog) - delete m_executionLog; - if (aboutDlg) - delete aboutDlg; - if (statsDlg) - delete statsDlg; - if (options) - delete options; - if (downloadFromURLDialog) - delete downloadFromURLDialog; - if (rssWidget) - delete rssWidget; - if (searchEngine) - delete searchEngine; - delete transferListFilters; - delete properties; - delete hSplitter; - delete vSplitter; - if (systrayCreator) - delete systrayCreator; - if (systrayIcon) - delete systrayIcon; - if (myTrayIconMenu) - delete myTrayIconMenu; - delete tabs; - // Keyboard shortcuts - delete switchSearchShortcut; - delete switchSearchShortcut2; - delete switchTransferShortcut; - delete switchRSSShortcut; - delete toolbarMenu; - IconProvider::drop(); - TorrentPersistentData::drop(); - Preferences::drop(); - Logger::drop(); - qDebug("Finished GUI destruction"); -} - void MainWindow::defineUILockPassword() { QString old_pass_md5 = Preferences::instance()->getUILockPasswordMD5(); @@ -565,8 +512,9 @@ void MainWindow::displayRSSTab(bool enable) tabs->setTabIcon(index_tab, IconProvider::instance()->getIcon("application-rss+xml")); } } - else if (rssWidget) + else if (rssWidget) { delete rssWidget; + } } @@ -580,8 +528,9 @@ void MainWindow::displaySearchTab(bool enable) tabs->insertTab(1, searchEngine, IconProvider::instance()->getIcon("edit-find"), tr("Search")); } } - else if (searchEngine) + else if (searchEngine) { delete searchEngine; + } } @@ -686,14 +635,16 @@ void MainWindow::createKeyboardShortcuts() actionOpen->setShortcut(QKeySequence(QString::fromUtf8("Ctrl+O"))); actionDownload_from_URL->setShortcut(QKeySequence(QString::fromUtf8("Ctrl+Shift+O"))); actionExit->setShortcut(QKeySequence(QString::fromUtf8("Ctrl+Q"))); - switchTransferShortcut = new QShortcut(QKeySequence("Alt+1"), this); + + QShortcut *switchTransferShortcut = new QShortcut(QKeySequence("Alt+1"), this); connect(switchTransferShortcut, SIGNAL(activated()), this, SLOT(displayTransferTab())); - switchSearchShortcut = new QShortcut(QKeySequence("Alt+2"), this); + QShortcut *switchSearchShortcut = new QShortcut(QKeySequence("Alt+2"), this); connect(switchSearchShortcut, SIGNAL(activated()), this, SLOT(displaySearchTab())); - switchSearchShortcut2 = new QShortcut(QKeySequence("Ctrl+F"), this); + QShortcut *switchSearchShortcut2 = new QShortcut(QKeySequence("Ctrl+F"), this); connect(switchSearchShortcut2, SIGNAL(activated()), this, SLOT(displaySearchTab())); - switchRSSShortcut = new QShortcut(QKeySequence("Alt+3"), this); + QShortcut *switchRSSShortcut = new QShortcut(QKeySequence("Alt+3"), this); connect(switchRSSShortcut, SIGNAL(activated()), this, SLOT(displayRSSTab())); + actionDocumentation->setShortcut(QKeySequence("F1")); actionOptions->setShortcut(QKeySequence(QString::fromUtf8("Alt+O"))); actionStart->setShortcut(QKeySequence(QString::fromUtf8("Ctrl+S"))); @@ -1105,51 +1056,12 @@ void MainWindow::on_actionOpen_triggered() } } -// As program parameters, we can get paths or urls. -// This function parse the parameters and call -// the right addTorrent function, considering -// the parameter type. -void MainWindow::processParams(const QString& params_str) +void MainWindow::activate() { - processParams(params_str.split("|", QString::SkipEmptyParts)); -} - -void MainWindow::processParams(const QStringList& params) -{ - Preferences* const pref = Preferences::instance(); - const bool useTorrentAdditionDialog = pref->useAdditionDialog(); - foreach (QString param, params) { - param = param.trimmed(); - if (misc::isUrl(param)) { - QBtSession::instance()->downloadFromUrl(param); - } - else { - if(param.startsWith("qbt://show")) { - if(ui_locked) - if(!unlockUI()) - return; - show(); - activateWindow(); - raise(); - return; // Do not process more params - } - if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - param = misc::bcLinkToMagnet(param); - } - if (param.startsWith("magnet:", Qt::CaseInsensitive)) { - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showMagnet(param, this); - else - QBtSession::instance()->addMagnetUri(param); - } - else { - if (useTorrentAdditionDialog) - AddNewTorrentDialog::showTorrent(param, QString(), this); - else - QBtSession::instance()->addTorrent(param); - } - } + if (!ui_locked || unlockUI()) { + show(); + activateWindow(); + raise(); } } @@ -1188,7 +1100,7 @@ void MainWindow::loadPreferences(bool configure_session) const Preferences* const pref = Preferences::instance(); const bool newSystrayIntegration = pref->systrayIntegration(); actionLock_qBittorrent->setVisible(newSystrayIntegration); - if (newSystrayIntegration != (systrayIcon!=0)) { + if (newSystrayIntegration != (systrayIcon != 0)) { if (newSystrayIntegration) { // create the trayicon if (!QSystemTrayIcon::isSystemTrayAvailable()) { @@ -1273,9 +1185,6 @@ void MainWindow::loadPreferences(bool configure_session) IconProvider::instance()->useSystemIconTheme(pref->useSystemIconTheme()); #endif - if (configure_session) - QBtSession::instance()->configureSession(); - #if defined(Q_OS_WIN) || defined(Q_OS_MAC) if (pref->isUpdateCheckEnabled()) checkProgramUpdate(); @@ -1311,10 +1220,10 @@ void MainWindow::updateGUI() html += "qBittorrent"; html += ""; html += "
"; - html += " " + tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(misc::accurateDoubleToString(QBtSession::instance()->getPayloadDownloadRate() / 1024., 1)); + html += " " + tr("DL speed: %1 KiB/s", "e.g: Download speed: 10 KiB/s").arg(misc::accurateDoubleToString(QBtSession::instance()->getPayloadDownloadRate() / 1024., 1)); html += "
"; html += "
"; - html += " " + tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(misc::accurateDoubleToString(QBtSession::instance()->getPayloadUploadRate() / 1024., 1)); + html += " " + tr("UP speed: %1 KiB/s", "e.g: Upload speed: 10 KiB/s").arg(misc::accurateDoubleToString(QBtSession::instance()->getPayloadUploadRate() / 1024., 1)); html += "
"; #else // OSes such as Windows do not support html here @@ -1613,8 +1522,9 @@ void MainWindow::on_actionExecution_Logs_triggered(bool checked) int index_tab = tabs->addTab(m_executionLog, tr("Execution Log")); tabs->setTabIcon(index_tab, IconProvider::instance()->getIcon("view-calendar-journal")); } - else if (m_executionLog) + else if (m_executionLog) { delete m_executionLog; + } Preferences::instance()->setExecutionLogEnabled(checked); } @@ -1653,9 +1563,9 @@ QIcon MainWindow::getSystrayIcon() const TrayIcon::Style style = Preferences::instance()->trayIconStyle(); switch(style) { case TrayIcon::MONO_DARK: - return QIcon(":/Icons/skin/qbittorrent_mono_dark.png"); + return QIcon(":/icons/skin/qbittorrent_mono_dark.png"); case TrayIcon::MONO_LIGHT: - return QIcon(":/Icons/skin/qbittorrent_mono_light.png"); + return QIcon(":/icons/skin/qbittorrent_mono_light.png"); default: break; } @@ -1667,9 +1577,9 @@ QIcon MainWindow::getSystrayIcon() const #endif if (icon.isNull()) { - icon.addFile(":/Icons/skin/qbittorrent22.png", QSize(22, 22)); - icon.addFile(":/Icons/skin/qbittorrent16.png", QSize(16, 16)); - icon.addFile(":/Icons/skin/qbittorrent32.png", QSize(32, 32)); + icon.addFile(":/icons/skin/qbittorrent22.png", QSize(22, 22)); + icon.addFile(":/icons/skin/qbittorrent16.png", QSize(16, 16)); + icon.addFile(":/icons/skin/qbittorrent32.png", QSize(32, 32)); } return icon; } diff --git a/src/mainwindow.h b/src/gui/mainwindow.h similarity index 94% rename from src/mainwindow.h rename to src/gui/mainwindow.h index b18559ba1..f7a9b9d69 100644 --- a/src/mainwindow.h +++ b/src/gui/mainwindow.h @@ -48,11 +48,9 @@ class TransferListWidget; class TransferListFiltersWidget; class PropertiesWidget; class StatusBar; -class consoleDlg; class about; class TorrentCreatorDlg; class downloadFromURL; -class HidableTabWidget; class LineEdit; class ExecutionLog; class PowerManagement; @@ -72,7 +70,8 @@ class MainWindow: public QMainWindow, private Ui::MainWindow public: // Construct / Destruct - MainWindow(QWidget *parent = 0, const QStringList& torrentCmdLine = QStringList()); + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); // Methods QWidget* getCurrentTabWidget() const; TransferListWidget* getTransferList() const { return transferList; } @@ -86,13 +85,10 @@ public slots: void downloadFromURLList(const QStringList& urls); void updateAltSpeedsBtn(bool alternative); void updateNbTorrents(); - void shutdownCleanUp(); - void processParams(const QStringList& params); + void activate(); protected slots: // GUI related slots - void dropEvent(QDropEvent *event); - void dragEnterEvent(QDragEnterEvent *event); void toggleVisibility(QSystemTrayIcon::ActivationReason e = QSystemTrayIcon::Trigger); void on_actionAbout_triggered(); void on_actionStatistics_triggered(); @@ -128,7 +124,6 @@ protected slots: void on_actionOpen_triggered(); void updateGUI(); void loadPreferences(bool configure_session = true); - void processParams(const QString& params); void addTorrent(QString path); void addUnauthenticatedTracker(const QPair &tracker); void processDownloadedFiles(QString path, QString url); @@ -145,6 +140,8 @@ protected slots: #endif protected: + void dropEvent(QDropEvent *event); + void dragEnterEvent(QDragEnterEvent *event); void closeEvent(QCloseEvent *); void showEvent(QShowEvent *); bool event(QEvent * event); @@ -170,10 +167,9 @@ private: // GUI related bool m_posInitialized; QTimer *guiUpdater; - HidableTabWidget *tabs; + QTabWidget *tabs; StatusBar *status_bar; QPointer options; - QPointer console; QPointer aboutDlg; QPointer statsDlg; QPointer createTorrentDlg; @@ -190,11 +186,6 @@ private: bool unlockDlgShowing; LineEdit *search_filter; QAction *searchFilterAct; - // Keyboard shortcuts - QShortcut *switchSearchShortcut; - QShortcut *switchSearchShortcut2; - QShortcut *switchTransferShortcut; - QShortcut *switchRSSShortcut; // Widgets QAction *prioSeparator; QAction *prioSeparatorMenu; diff --git a/src/mainwindow.ui b/src/gui/mainwindow.ui similarity index 99% rename from src/mainwindow.ui rename to src/gui/mainwindow.ui index 358976a8a..76e98190b 100644 --- a/src/mainwindow.ui +++ b/src/gui/mainwindow.ui @@ -197,7 +197,7 @@ - :/Icons/skin/qbittorrent32.png:/Icons/skin/qbittorrent32.png + :/icons/skin/qbittorrent32.png:/icons/skin/qbittorrent32.png Visit &Website diff --git a/src/messageboxraised.cpp b/src/gui/messageboxraised.cpp similarity index 100% rename from src/messageboxraised.cpp rename to src/gui/messageboxraised.cpp diff --git a/src/messageboxraised.h b/src/gui/messageboxraised.h similarity index 100% rename from src/messageboxraised.h rename to src/gui/messageboxraised.h diff --git a/src/preferences/options.ui b/src/gui/options.ui old mode 100755 new mode 100644 similarity index 99% rename from src/preferences/options.ui rename to src/gui/options.ui index 7247112c4..1b50886af --- a/src/preferences/options.ui +++ b/src/gui/options.ui @@ -1555,7 +1555,7 @@ - :/Icons/slow_off.png + :/icons/slow_off.png @@ -1707,7 +1707,7 @@ - :/Icons/slow.png + :/icons/slow.png false diff --git a/src/preferences/options_imp.cpp b/src/gui/options_imp.cpp old mode 100755 new mode 100644 similarity index 99% rename from src/preferences/options_imp.cpp rename to src/gui/options_imp.cpp index d0db28536..cbfa62c8a --- a/src/preferences/options_imp.cpp +++ b/src/gui/options_imp.cpp @@ -70,7 +70,11 @@ options_imp::options_imp(QWidget *parent): tabSelection->item(TAB_CONNECTION)->setIcon(IconProvider::instance()->getIcon("network-wired")); tabSelection->item(TAB_DOWNLOADS)->setIcon(IconProvider::instance()->getIcon("download")); tabSelection->item(TAB_SPEED)->setIcon(IconProvider::instance()->getIcon("chronometer")); +#ifndef DISABLE_WEBUI tabSelection->item(TAB_WEBUI)->setIcon(IconProvider::instance()->getIcon("network-server")); +#else + tabSelection->item(TAB_WEBUI)->setHidden(true); +#endif tabSelection->item(TAB_ADVANCED)->setIcon(IconProvider::instance()->getIcon("preferences-other")); IpFilterRefreshBtn->setIcon(IconProvider::instance()->getIcon("view-refresh")); @@ -229,6 +233,7 @@ options_imp::options_imp(QWidget *parent): connect(spinMaxActiveUploads, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); connect(spinMaxActiveTorrents, SIGNAL(valueChanged(QString)), this, SLOT(enableApplyButton())); connect(checkIgnoreSlowTorrentsForQueueing, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); +#ifndef DISABLE_WEBUI // Web UI tab connect(checkWebUi, SIGNAL(toggled(bool)), this, SLOT(enableApplyButton())); connect(spinWebUiPort, SIGNAL(valueChanged(int)), this, SLOT(enableApplyButton())); @@ -244,6 +249,7 @@ options_imp::options_imp(QWidget *parent): connect(domainNameTxt, SIGNAL(textChanged(QString)), SLOT(enableApplyButton())); connect(DNSUsernameTxt, SIGNAL(textChanged(QString)), SLOT(enableApplyButton())); connect(DNSPasswordTxt, SIGNAL(textChanged(QString)), SLOT(enableApplyButton())); +#endif // Disable apply Button applyButton->setEnabled(false); // Tab selection mecanism @@ -275,7 +281,7 @@ void options_imp::initializeLanguageCombo() localeStr.chop(3); // Remove ".qm" QLocale locale(localeStr); QString language_name = languageToLocalizedString(locale); - comboI18n->addItem(/*QIcon(":/Icons/flags/"+country+".png"), */language_name, localeStr); + comboI18n->addItem(/*QIcon(":/icons/flags/"+country+".png"), */language_name, localeStr); qDebug() << "Supported locale:" << localeStr; } } @@ -1327,10 +1333,10 @@ void options_imp::setSslKey(const QByteArray &key, bool interactive) { #ifndef QT_NO_OPENSSL if (!key.isEmpty() && !QSslKey(key, QSsl::Rsa).isNull()) { - lblSslKeyStatus->setPixmap(QPixmap(":/Icons/oxygen/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); + lblSslKeyStatus->setPixmap(QPixmap(":/icons/oxygen/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); m_sslKey = key; } else { - lblSslKeyStatus->setPixmap(QPixmap(":/Icons/oxygen/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); + lblSslKeyStatus->setPixmap(QPixmap(":/icons/oxygen/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); m_sslKey.clear(); if (interactive) QMessageBox::warning(this, tr("Invalid key"), tr("This is not a valid SSL key.")); @@ -1342,10 +1348,10 @@ void options_imp::setSslCertificate(const QByteArray &cert, bool interactive) { #ifndef QT_NO_OPENSSL if (!cert.isEmpty() && !QSslCertificate(cert).isNull()) { - lblSslCertStatus->setPixmap(QPixmap(":/Icons/oxygen/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); + lblSslCertStatus->setPixmap(QPixmap(":/icons/oxygen/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); m_sslCert = cert; } else { - lblSslCertStatus->setPixmap(QPixmap(":/Icons/oxygen/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); + lblSslCertStatus->setPixmap(QPixmap(":/icons/oxygen/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); m_sslCert.clear(); if (interactive) QMessageBox::warning(this, tr("Invalid certificate"), tr("This is not a valid SSL certificate.")); diff --git a/src/preferences/options_imp.h b/src/gui/options_imp.h old mode 100755 new mode 100644 similarity index 100% rename from src/preferences/options_imp.h rename to src/gui/options_imp.h diff --git a/src/powermanagement/powermanagement.cpp b/src/gui/powermanagement/powermanagement.cpp similarity index 100% rename from src/powermanagement/powermanagement.cpp rename to src/gui/powermanagement/powermanagement.cpp diff --git a/src/powermanagement/powermanagement.h b/src/gui/powermanagement/powermanagement.h similarity index 100% rename from src/powermanagement/powermanagement.h rename to src/gui/powermanagement/powermanagement.h diff --git a/src/powermanagement/powermanagement.pri b/src/gui/powermanagement/powermanagement.pri similarity index 100% rename from src/powermanagement/powermanagement.pri rename to src/gui/powermanagement/powermanagement.pri diff --git a/src/powermanagement/powermanagement_x11.cpp b/src/gui/powermanagement/powermanagement_x11.cpp similarity index 100% rename from src/powermanagement/powermanagement_x11.cpp rename to src/gui/powermanagement/powermanagement_x11.cpp diff --git a/src/powermanagement/powermanagement_x11.h b/src/gui/powermanagement/powermanagement_x11.h similarity index 100% rename from src/powermanagement/powermanagement_x11.h rename to src/gui/powermanagement/powermanagement_x11.h diff --git a/src/preview.ui b/src/gui/preview.ui similarity index 100% rename from src/preview.ui rename to src/gui/preview.ui diff --git a/src/previewlistdelegate.h b/src/gui/previewlistdelegate.h similarity index 100% rename from src/previewlistdelegate.h rename to src/gui/previewlistdelegate.h diff --git a/src/previewselect.cpp b/src/gui/previewselect.cpp similarity index 100% rename from src/previewselect.cpp rename to src/gui/previewselect.cpp diff --git a/src/previewselect.h b/src/gui/previewselect.h similarity index 100% rename from src/previewselect.h rename to src/gui/previewselect.h diff --git a/src/programupdater.cpp b/src/gui/programupdater.cpp similarity index 100% rename from src/programupdater.cpp rename to src/gui/programupdater.cpp diff --git a/src/programupdater.h b/src/gui/programupdater.h similarity index 100% rename from src/programupdater.h rename to src/gui/programupdater.h diff --git a/src/properties/downloadedpiecesbar.cpp b/src/gui/properties/downloadedpiecesbar.cpp similarity index 100% rename from src/properties/downloadedpiecesbar.cpp rename to src/gui/properties/downloadedpiecesbar.cpp diff --git a/src/properties/downloadedpiecesbar.h b/src/gui/properties/downloadedpiecesbar.h similarity index 100% rename from src/properties/downloadedpiecesbar.h rename to src/gui/properties/downloadedpiecesbar.h diff --git a/src/properties/peer.ui b/src/gui/properties/peer.ui similarity index 100% rename from src/properties/peer.ui rename to src/gui/properties/peer.ui diff --git a/src/properties/peeraddition.h b/src/gui/properties/peeraddition.h similarity index 100% rename from src/properties/peeraddition.h rename to src/gui/properties/peeraddition.h diff --git a/src/properties/peerlistdelegate.h b/src/gui/properties/peerlistdelegate.h similarity index 100% rename from src/properties/peerlistdelegate.h rename to src/gui/properties/peerlistdelegate.h diff --git a/src/properties/peerlistsortmodel.h b/src/gui/properties/peerlistsortmodel.h similarity index 100% rename from src/properties/peerlistsortmodel.h rename to src/gui/properties/peerlistsortmodel.h diff --git a/src/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp similarity index 99% rename from src/properties/peerlistwidget.cpp rename to src/gui/properties/peerlistwidget.cpp index a1ad8d5c2..69ebfd355 100644 --- a/src/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -178,8 +178,8 @@ void PeerListWidget::showPeerListMenu(const QPoint&) copyPeerAct = menu.addAction(IconProvider::instance()->getIcon("edit-copy"), tr("Copy selected")); menu.addSeparator(); #if LIBTORRENT_VERSION_NUM < 10000 - dlLimitAct = menu.addAction(QIcon(":/Icons/skin/download.png"), tr("Limit download rate...")); - upLimitAct = menu.addAction(QIcon(":/Icons/skin/seeding.png"), tr("Limit upload rate...")); + dlLimitAct = menu.addAction(QIcon(":/icons/skin/download.png"), tr("Limit download rate...")); + upLimitAct = menu.addAction(QIcon(":/icons/skin/seeding.png"), tr("Limit upload rate...")); menu.addSeparator(); #endif banAct = menu.addAction(IconProvider::instance()->getIcon("user-group-delete"), tr("Ban peer permanently")); diff --git a/src/properties/peerlistwidget.h b/src/gui/properties/peerlistwidget.h similarity index 100% rename from src/properties/peerlistwidget.h rename to src/gui/properties/peerlistwidget.h diff --git a/src/properties/pieceavailabilitybar.cpp b/src/gui/properties/pieceavailabilitybar.cpp similarity index 100% rename from src/properties/pieceavailabilitybar.cpp rename to src/gui/properties/pieceavailabilitybar.cpp diff --git a/src/properties/pieceavailabilitybar.h b/src/gui/properties/pieceavailabilitybar.h similarity index 100% rename from src/properties/pieceavailabilitybar.h rename to src/gui/properties/pieceavailabilitybar.h diff --git a/src/properties/properties.pri b/src/gui/properties/properties.pri similarity index 100% rename from src/properties/properties.pri rename to src/gui/properties/properties.pri diff --git a/src/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp similarity index 100% rename from src/properties/propertieswidget.cpp rename to src/gui/properties/propertieswidget.cpp diff --git a/src/properties/propertieswidget.h b/src/gui/properties/propertieswidget.h similarity index 100% rename from src/properties/propertieswidget.h rename to src/gui/properties/propertieswidget.h diff --git a/src/properties/propertieswidget.ui b/src/gui/properties/propertieswidget.ui similarity index 100% rename from src/properties/propertieswidget.ui rename to src/gui/properties/propertieswidget.ui diff --git a/src/properties/proplistdelegate.h b/src/gui/properties/proplistdelegate.h similarity index 100% rename from src/properties/proplistdelegate.h rename to src/gui/properties/proplistdelegate.h diff --git a/src/properties/proptabbar.cpp b/src/gui/properties/proptabbar.cpp similarity index 100% rename from src/properties/proptabbar.cpp rename to src/gui/properties/proptabbar.cpp diff --git a/src/properties/proptabbar.h b/src/gui/properties/proptabbar.h similarity index 100% rename from src/properties/proptabbar.h rename to src/gui/properties/proptabbar.h diff --git a/src/properties/trackerlist.cpp b/src/gui/properties/trackerlist.cpp similarity index 100% rename from src/properties/trackerlist.cpp rename to src/gui/properties/trackerlist.cpp diff --git a/src/properties/trackerlist.h b/src/gui/properties/trackerlist.h similarity index 100% rename from src/properties/trackerlist.h rename to src/gui/properties/trackerlist.h diff --git a/src/properties/trackersadditiondlg.h b/src/gui/properties/trackersadditiondlg.h similarity index 100% rename from src/properties/trackersadditiondlg.h rename to src/gui/properties/trackersadditiondlg.h diff --git a/src/properties/trackersadditiondlg.ui b/src/gui/properties/trackersadditiondlg.ui similarity index 100% rename from src/properties/trackersadditiondlg.ui rename to src/gui/properties/trackersadditiondlg.ui diff --git a/src/reverseresolution.h b/src/gui/reverseresolution.h similarity index 100% rename from src/reverseresolution.h rename to src/gui/reverseresolution.h diff --git a/src/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp similarity index 100% rename from src/rss/automatedrssdownloader.cpp rename to src/gui/rss/automatedrssdownloader.cpp diff --git a/src/rss/automatedrssdownloader.h b/src/gui/rss/automatedrssdownloader.h similarity index 100% rename from src/rss/automatedrssdownloader.h rename to src/gui/rss/automatedrssdownloader.h diff --git a/src/rss/automatedrssdownloader.ui b/src/gui/rss/automatedrssdownloader.ui similarity index 99% rename from src/rss/automatedrssdownloader.ui rename to src/gui/rss/automatedrssdownloader.ui index 4a10460de..41ce6742c 100644 --- a/src/rss/automatedrssdownloader.ui +++ b/src/gui/rss/automatedrssdownloader.ui @@ -331,7 +331,7 @@ - + @@ -395,7 +395,7 @@ - + diff --git a/src/rss/cookiesdlg.cpp b/src/gui/rss/cookiesdlg.cpp similarity index 100% rename from src/rss/cookiesdlg.cpp rename to src/gui/rss/cookiesdlg.cpp diff --git a/src/rss/cookiesdlg.h b/src/gui/rss/cookiesdlg.h similarity index 100% rename from src/rss/cookiesdlg.h rename to src/gui/rss/cookiesdlg.h diff --git a/src/rss/cookiesdlg.ui b/src/gui/rss/cookiesdlg.ui similarity index 100% rename from src/rss/cookiesdlg.ui rename to src/gui/rss/cookiesdlg.ui diff --git a/src/rss/feedlistwidget.cpp b/src/gui/rss/feedlistwidget.cpp similarity index 100% rename from src/rss/feedlistwidget.cpp rename to src/gui/rss/feedlistwidget.cpp diff --git a/src/rss/feedlistwidget.h b/src/gui/rss/feedlistwidget.h similarity index 100% rename from src/rss/feedlistwidget.h rename to src/gui/rss/feedlistwidget.h diff --git a/src/rss/htmlbrowser.cpp b/src/gui/rss/htmlbrowser.cpp similarity index 100% rename from src/rss/htmlbrowser.cpp rename to src/gui/rss/htmlbrowser.cpp diff --git a/src/rss/htmlbrowser.h b/src/gui/rss/htmlbrowser.h similarity index 100% rename from src/rss/htmlbrowser.h rename to src/gui/rss/htmlbrowser.h diff --git a/src/rss/rss.pri b/src/gui/rss/rss.pri similarity index 100% rename from src/rss/rss.pri rename to src/gui/rss/rss.pri diff --git a/src/rss/rss.ui b/src/gui/rss/rss.ui similarity index 100% rename from src/rss/rss.ui rename to src/gui/rss/rss.ui diff --git a/src/rss/rss_imp.cpp b/src/gui/rss/rss_imp.cpp similarity index 98% rename from src/rss/rss_imp.cpp rename to src/gui/rss/rss_imp.cpp index 38d3d5e24..f99a6e52b 100644 --- a/src/rss/rss_imp.cpp +++ b/src/gui/rss/rss_imp.cpp @@ -327,7 +327,7 @@ void RSSImp::saveFoldersOpenState() void RSSImp::refreshAllFeeds() { foreach (QTreeWidgetItem* item, m_feedList->getAllFeedItems()) - item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/loading.png"))); + item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png"))); m_rssManager->refresh(); } @@ -417,12 +417,12 @@ void RSSImp::refreshSelectedItems() continue; // Update UI if (qSharedPointerDynamicCast(file)) { - item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/loading.png"))); + item->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png"))); } else if (qSharedPointerDynamicCast(file)) { // Update feeds in the folder foreach (QTreeWidgetItem *feed, m_feedList->getAllFeedItems(item)) - feed->setData(0, Qt::DecorationRole, QVariant(QIcon(":/Icons/loading.png"))); + feed->setData(0, Qt::DecorationRole, QVariant(QIcon(":/icons/loading.png"))); } } } @@ -497,11 +497,11 @@ QListWidgetItem* RSSImp::createArticleListItem(const RssArticlePtr& article) item->setData(Article::IdRole, article->guid()); if (article->isRead()) { item->setData(Article::ColorRole, QVariant(QColor("grey"))); - item->setData(Article::IconRole, QVariant(QIcon(":/Icons/sphere.png"))); + item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png"))); } else { item->setData(Article::ColorRole, QVariant(QColor("blue"))); - item->setData(Article::IconRole, QVariant(QIcon(":/Icons/sphere2.png"))); + item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere2.png"))); } return item; @@ -604,7 +604,7 @@ void RSSImp::refreshTextBrowser() textBrowser->setHtml(html); article->markAsRead(); item->setData(Article::ColorRole, QVariant(QColor("grey"))); - item->setData(Article::IconRole, QVariant(QIcon(":/Icons/sphere.png"))); + item->setData(Article::IconRole, QVariant(QIcon(":/icons/sphere.png"))); // Decrement feed nb unread news updateItemInfos(m_feedList->stickyUnreadItem()); updateItemInfos(m_feedList->getTreeItemFromUrl(item->data(Article::FeedUrlRole).toString())); diff --git a/src/rss/rss_imp.h b/src/gui/rss/rss_imp.h similarity index 100% rename from src/rss/rss_imp.h rename to src/gui/rss/rss_imp.h diff --git a/src/rss/rssarticle.cpp b/src/gui/rss/rssarticle.cpp similarity index 100% rename from src/rss/rssarticle.cpp rename to src/gui/rss/rssarticle.cpp diff --git a/src/rss/rssarticle.h b/src/gui/rss/rssarticle.h similarity index 100% rename from src/rss/rssarticle.h rename to src/gui/rss/rssarticle.h diff --git a/src/rss/rssdownloadrule.cpp b/src/gui/rss/rssdownloadrule.cpp similarity index 100% rename from src/rss/rssdownloadrule.cpp rename to src/gui/rss/rssdownloadrule.cpp diff --git a/src/rss/rssdownloadrule.h b/src/gui/rss/rssdownloadrule.h similarity index 100% rename from src/rss/rssdownloadrule.h rename to src/gui/rss/rssdownloadrule.h diff --git a/src/rss/rssdownloadrulelist.cpp b/src/gui/rss/rssdownloadrulelist.cpp similarity index 100% rename from src/rss/rssdownloadrulelist.cpp rename to src/gui/rss/rssdownloadrulelist.cpp diff --git a/src/rss/rssdownloadrulelist.h b/src/gui/rss/rssdownloadrulelist.h similarity index 100% rename from src/rss/rssdownloadrulelist.h rename to src/gui/rss/rssdownloadrulelist.h diff --git a/src/rss/rssfeed.cpp b/src/gui/rss/rssfeed.cpp similarity index 99% rename from src/rss/rssfeed.cpp rename to src/gui/rss/rssfeed.cpp index a33215fe9..63b23e92f 100644 --- a/src/rss/rssfeed.cpp +++ b/src/gui/rss/rssfeed.cpp @@ -52,7 +52,7 @@ RssFeed::RssFeed(RssManager* manager, RssFolder* parent, const QString& url): m_manager(manager), m_parent(parent), m_url (QUrl::fromEncoded(url.toUtf8()).toString()), - m_icon(":/Icons/oxygen/application-rss+xml.png"), + m_icon(":/icons/oxygen/application-rss+xml.png"), m_unreadCount(0), m_dirty(false), m_inErrorState(false), @@ -232,7 +232,7 @@ QString RssFeed::url() const QIcon RssFeed::icon() const { if (m_inErrorState) - return QIcon(":/Icons/oxygen/unavailable.png"); + return QIcon(":/icons/oxygen/unavailable.png"); return QIcon(m_icon); } diff --git a/src/rss/rssfeed.h b/src/gui/rss/rssfeed.h similarity index 100% rename from src/rss/rssfeed.h rename to src/gui/rss/rssfeed.h diff --git a/src/rss/rssfile.cpp b/src/gui/rss/rssfile.cpp similarity index 100% rename from src/rss/rssfile.cpp rename to src/gui/rss/rssfile.cpp diff --git a/src/rss/rssfile.h b/src/gui/rss/rssfile.h similarity index 100% rename from src/rss/rssfile.h rename to src/gui/rss/rssfile.h diff --git a/src/rss/rssfolder.cpp b/src/gui/rss/rssfolder.cpp similarity index 100% rename from src/rss/rssfolder.cpp rename to src/gui/rss/rssfolder.cpp diff --git a/src/rss/rssfolder.h b/src/gui/rss/rssfolder.h similarity index 100% rename from src/rss/rssfolder.h rename to src/gui/rss/rssfolder.h diff --git a/src/rss/rssmanager.cpp b/src/gui/rss/rssmanager.cpp similarity index 100% rename from src/rss/rssmanager.cpp rename to src/gui/rss/rssmanager.cpp diff --git a/src/rss/rssmanager.h b/src/gui/rss/rssmanager.h similarity index 100% rename from src/rss/rssmanager.h rename to src/gui/rss/rssmanager.h diff --git a/src/rss/rssparser.cpp b/src/gui/rss/rssparser.cpp similarity index 100% rename from src/rss/rssparser.cpp rename to src/gui/rss/rssparser.cpp diff --git a/src/rss/rssparser.h b/src/gui/rss/rssparser.h similarity index 100% rename from src/rss/rssparser.h rename to src/gui/rss/rssparser.h diff --git a/src/rss/rsssettingsdlg.cpp b/src/gui/rss/rsssettingsdlg.cpp similarity index 100% rename from src/rss/rsssettingsdlg.cpp rename to src/gui/rss/rsssettingsdlg.cpp diff --git a/src/rss/rsssettingsdlg.h b/src/gui/rss/rsssettingsdlg.h similarity index 100% rename from src/rss/rsssettingsdlg.h rename to src/gui/rss/rsssettingsdlg.h diff --git a/src/rss/rsssettingsdlg.ui b/src/gui/rss/rsssettingsdlg.ui similarity index 98% rename from src/rss/rsssettingsdlg.ui rename to src/gui/rss/rsssettingsdlg.ui index 956685e39..a6e39f45e 100644 --- a/src/rss/rsssettingsdlg.ui +++ b/src/gui/rss/rsssettingsdlg.ui @@ -34,7 +34,7 @@ - :/Icons/oxygen/application-rss+xml.png + :/icons/oxygen/application-rss+xml.png true diff --git a/src/searchengine/engineselect.ui b/src/gui/searchengine/engineselect.ui similarity index 100% rename from src/searchengine/engineselect.ui rename to src/gui/searchengine/engineselect.ui diff --git a/src/searchengine/engineselectdlg.cpp b/src/gui/searchengine/engineselectdlg.cpp similarity index 100% rename from src/searchengine/engineselectdlg.cpp rename to src/gui/searchengine/engineselectdlg.cpp diff --git a/src/searchengine/engineselectdlg.h b/src/gui/searchengine/engineselectdlg.h similarity index 100% rename from src/searchengine/engineselectdlg.h rename to src/gui/searchengine/engineselectdlg.h diff --git a/src/searchengine/nova/__init__.py b/src/gui/searchengine/nova/__init__.py similarity index 100% rename from src/searchengine/nova/__init__.py rename to src/gui/searchengine/nova/__init__.py diff --git a/src/searchengine/nova/engines/__init__.py b/src/gui/searchengine/nova/engines/__init__.py similarity index 100% rename from src/searchengine/nova/engines/__init__.py rename to src/gui/searchengine/nova/engines/__init__.py diff --git a/src/searchengine/nova/engines/btdigg.png b/src/gui/searchengine/nova/engines/btdigg.png similarity index 100% rename from src/searchengine/nova/engines/btdigg.png rename to src/gui/searchengine/nova/engines/btdigg.png diff --git a/src/searchengine/nova/engines/btdigg.py b/src/gui/searchengine/nova/engines/btdigg.py similarity index 100% rename from src/searchengine/nova/engines/btdigg.py rename to src/gui/searchengine/nova/engines/btdigg.py diff --git a/src/searchengine/nova/engines/extratorrent.png b/src/gui/searchengine/nova/engines/extratorrent.png similarity index 100% rename from src/searchengine/nova/engines/extratorrent.png rename to src/gui/searchengine/nova/engines/extratorrent.png diff --git a/src/searchengine/nova/engines/extratorrent.py b/src/gui/searchengine/nova/engines/extratorrent.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova/engines/extratorrent.py rename to src/gui/searchengine/nova/engines/extratorrent.py diff --git a/src/searchengine/nova/engines/kickasstorrents.png b/src/gui/searchengine/nova/engines/kickasstorrents.png similarity index 100% rename from src/searchengine/nova/engines/kickasstorrents.png rename to src/gui/searchengine/nova/engines/kickasstorrents.png diff --git a/src/searchengine/nova/engines/kickasstorrents.py b/src/gui/searchengine/nova/engines/kickasstorrents.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova/engines/kickasstorrents.py rename to src/gui/searchengine/nova/engines/kickasstorrents.py diff --git a/src/searchengine/nova/engines/legittorrents.png b/src/gui/searchengine/nova/engines/legittorrents.png similarity index 100% rename from src/searchengine/nova/engines/legittorrents.png rename to src/gui/searchengine/nova/engines/legittorrents.png diff --git a/src/searchengine/nova/engines/legittorrents.py b/src/gui/searchengine/nova/engines/legittorrents.py similarity index 100% rename from src/searchengine/nova/engines/legittorrents.py rename to src/gui/searchengine/nova/engines/legittorrents.py diff --git a/src/searchengine/nova/engines/mininova.png b/src/gui/searchengine/nova/engines/mininova.png similarity index 100% rename from src/searchengine/nova/engines/mininova.png rename to src/gui/searchengine/nova/engines/mininova.png diff --git a/src/searchengine/nova/engines/mininova.py b/src/gui/searchengine/nova/engines/mininova.py similarity index 100% rename from src/searchengine/nova/engines/mininova.py rename to src/gui/searchengine/nova/engines/mininova.py diff --git a/src/searchengine/nova/engines/piratebay.png b/src/gui/searchengine/nova/engines/piratebay.png similarity index 100% rename from src/searchengine/nova/engines/piratebay.png rename to src/gui/searchengine/nova/engines/piratebay.png diff --git a/src/searchengine/nova/engines/piratebay.py b/src/gui/searchengine/nova/engines/piratebay.py similarity index 100% rename from src/searchengine/nova/engines/piratebay.py rename to src/gui/searchengine/nova/engines/piratebay.py diff --git a/src/searchengine/nova/engines/torrentreactor.png b/src/gui/searchengine/nova/engines/torrentreactor.png similarity index 100% rename from src/searchengine/nova/engines/torrentreactor.png rename to src/gui/searchengine/nova/engines/torrentreactor.png diff --git a/src/searchengine/nova/engines/torrentreactor.py b/src/gui/searchengine/nova/engines/torrentreactor.py similarity index 100% rename from src/searchengine/nova/engines/torrentreactor.py rename to src/gui/searchengine/nova/engines/torrentreactor.py diff --git a/src/searchengine/nova/engines/versions.txt b/src/gui/searchengine/nova/engines/versions.txt similarity index 100% rename from src/searchengine/nova/engines/versions.txt rename to src/gui/searchengine/nova/engines/versions.txt diff --git a/src/searchengine/nova/fix_encoding.py b/src/gui/searchengine/nova/fix_encoding.py similarity index 100% rename from src/searchengine/nova/fix_encoding.py rename to src/gui/searchengine/nova/fix_encoding.py diff --git a/src/searchengine/nova/helpers.py b/src/gui/searchengine/nova/helpers.py similarity index 100% rename from src/searchengine/nova/helpers.py rename to src/gui/searchengine/nova/helpers.py diff --git a/src/searchengine/nova/nova2.py b/src/gui/searchengine/nova/nova2.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova/nova2.py rename to src/gui/searchengine/nova/nova2.py diff --git a/src/searchengine/nova/nova2dl.py b/src/gui/searchengine/nova/nova2dl.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova/nova2dl.py rename to src/gui/searchengine/nova/nova2dl.py diff --git a/src/searchengine/nova/novaprinter.py b/src/gui/searchengine/nova/novaprinter.py similarity index 100% rename from src/searchengine/nova/novaprinter.py rename to src/gui/searchengine/nova/novaprinter.py diff --git a/src/searchengine/nova/socks.py b/src/gui/searchengine/nova/socks.py similarity index 100% rename from src/searchengine/nova/socks.py rename to src/gui/searchengine/nova/socks.py diff --git a/src/searchengine/nova3/__init__.py b/src/gui/searchengine/nova3/__init__.py similarity index 100% rename from src/searchengine/nova3/__init__.py rename to src/gui/searchengine/nova3/__init__.py diff --git a/src/searchengine/nova3/engines/__init__.py b/src/gui/searchengine/nova3/engines/__init__.py similarity index 100% rename from src/searchengine/nova3/engines/__init__.py rename to src/gui/searchengine/nova3/engines/__init__.py diff --git a/src/searchengine/nova3/engines/btdigg.png b/src/gui/searchengine/nova3/engines/btdigg.png similarity index 100% rename from src/searchengine/nova3/engines/btdigg.png rename to src/gui/searchengine/nova3/engines/btdigg.png diff --git a/src/searchengine/nova3/engines/btdigg.py b/src/gui/searchengine/nova3/engines/btdigg.py similarity index 100% rename from src/searchengine/nova3/engines/btdigg.py rename to src/gui/searchengine/nova3/engines/btdigg.py diff --git a/src/searchengine/nova3/engines/extratorrent.png b/src/gui/searchengine/nova3/engines/extratorrent.png similarity index 100% rename from src/searchengine/nova3/engines/extratorrent.png rename to src/gui/searchengine/nova3/engines/extratorrent.png diff --git a/src/searchengine/nova3/engines/extratorrent.py b/src/gui/searchengine/nova3/engines/extratorrent.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova3/engines/extratorrent.py rename to src/gui/searchengine/nova3/engines/extratorrent.py diff --git a/src/searchengine/nova3/engines/kickasstorrents.png b/src/gui/searchengine/nova3/engines/kickasstorrents.png similarity index 100% rename from src/searchengine/nova3/engines/kickasstorrents.png rename to src/gui/searchengine/nova3/engines/kickasstorrents.png diff --git a/src/searchengine/nova3/engines/kickasstorrents.py b/src/gui/searchengine/nova3/engines/kickasstorrents.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova3/engines/kickasstorrents.py rename to src/gui/searchengine/nova3/engines/kickasstorrents.py diff --git a/src/searchengine/nova3/engines/legittorrents.png b/src/gui/searchengine/nova3/engines/legittorrents.png similarity index 100% rename from src/searchengine/nova3/engines/legittorrents.png rename to src/gui/searchengine/nova3/engines/legittorrents.png diff --git a/src/searchengine/nova3/engines/legittorrents.py b/src/gui/searchengine/nova3/engines/legittorrents.py similarity index 100% rename from src/searchengine/nova3/engines/legittorrents.py rename to src/gui/searchengine/nova3/engines/legittorrents.py diff --git a/src/searchengine/nova3/engines/mininova.png b/src/gui/searchengine/nova3/engines/mininova.png similarity index 100% rename from src/searchengine/nova3/engines/mininova.png rename to src/gui/searchengine/nova3/engines/mininova.png diff --git a/src/searchengine/nova3/engines/mininova.py b/src/gui/searchengine/nova3/engines/mininova.py similarity index 100% rename from src/searchengine/nova3/engines/mininova.py rename to src/gui/searchengine/nova3/engines/mininova.py diff --git a/src/searchengine/nova3/engines/piratebay.png b/src/gui/searchengine/nova3/engines/piratebay.png similarity index 100% rename from src/searchengine/nova3/engines/piratebay.png rename to src/gui/searchengine/nova3/engines/piratebay.png diff --git a/src/searchengine/nova3/engines/piratebay.py b/src/gui/searchengine/nova3/engines/piratebay.py similarity index 100% rename from src/searchengine/nova3/engines/piratebay.py rename to src/gui/searchengine/nova3/engines/piratebay.py diff --git a/src/searchengine/nova3/engines/torrentreactor.png b/src/gui/searchengine/nova3/engines/torrentreactor.png similarity index 100% rename from src/searchengine/nova3/engines/torrentreactor.png rename to src/gui/searchengine/nova3/engines/torrentreactor.png diff --git a/src/searchengine/nova3/engines/torrentreactor.py b/src/gui/searchengine/nova3/engines/torrentreactor.py similarity index 100% rename from src/searchengine/nova3/engines/torrentreactor.py rename to src/gui/searchengine/nova3/engines/torrentreactor.py diff --git a/src/searchengine/nova3/engines/versions.txt b/src/gui/searchengine/nova3/engines/versions.txt similarity index 100% rename from src/searchengine/nova3/engines/versions.txt rename to src/gui/searchengine/nova3/engines/versions.txt diff --git a/src/searchengine/nova3/helpers.py b/src/gui/searchengine/nova3/helpers.py similarity index 100% rename from src/searchengine/nova3/helpers.py rename to src/gui/searchengine/nova3/helpers.py diff --git a/src/searchengine/nova3/nova2.py b/src/gui/searchengine/nova3/nova2.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova3/nova2.py rename to src/gui/searchengine/nova3/nova2.py diff --git a/src/searchengine/nova3/nova2dl.py b/src/gui/searchengine/nova3/nova2dl.py old mode 100755 new mode 100644 similarity index 100% rename from src/searchengine/nova3/nova2dl.py rename to src/gui/searchengine/nova3/nova2dl.py diff --git a/src/searchengine/nova3/novaprinter.py b/src/gui/searchengine/nova3/novaprinter.py similarity index 100% rename from src/searchengine/nova3/novaprinter.py rename to src/gui/searchengine/nova3/novaprinter.py diff --git a/src/searchengine/nova3/sgmllib3.py b/src/gui/searchengine/nova3/sgmllib3.py similarity index 100% rename from src/searchengine/nova3/sgmllib3.py rename to src/gui/searchengine/nova3/sgmllib3.py diff --git a/src/searchengine/nova3/socks.py b/src/gui/searchengine/nova3/socks.py similarity index 100% rename from src/searchengine/nova3/socks.py rename to src/gui/searchengine/nova3/socks.py diff --git a/src/searchengine/pluginsource.h b/src/gui/searchengine/pluginsource.h similarity index 100% rename from src/searchengine/pluginsource.h rename to src/gui/searchengine/pluginsource.h diff --git a/src/searchengine/pluginsource.ui b/src/gui/searchengine/pluginsource.ui similarity index 100% rename from src/searchengine/pluginsource.ui rename to src/gui/searchengine/pluginsource.ui diff --git a/src/searchengine/search.qrc b/src/gui/searchengine/search.qrc similarity index 100% rename from src/searchengine/search.qrc rename to src/gui/searchengine/search.qrc diff --git a/src/searchengine/search.ui b/src/gui/searchengine/search.ui similarity index 100% rename from src/searchengine/search.ui rename to src/gui/searchengine/search.ui diff --git a/src/searchengine/searchengine.cpp b/src/gui/searchengine/searchengine.cpp similarity index 100% rename from src/searchengine/searchengine.cpp rename to src/gui/searchengine/searchengine.cpp diff --git a/src/searchengine/searchengine.h b/src/gui/searchengine/searchengine.h similarity index 100% rename from src/searchengine/searchengine.h rename to src/gui/searchengine/searchengine.h diff --git a/src/searchengine/searchengine.pri b/src/gui/searchengine/searchengine.pri similarity index 100% rename from src/searchengine/searchengine.pri rename to src/gui/searchengine/searchengine.pri diff --git a/src/searchengine/searchlistdelegate.h b/src/gui/searchengine/searchlistdelegate.h similarity index 100% rename from src/searchengine/searchlistdelegate.h rename to src/gui/searchengine/searchlistdelegate.h diff --git a/src/searchengine/searchsortmodel.h b/src/gui/searchengine/searchsortmodel.h similarity index 100% rename from src/searchengine/searchsortmodel.h rename to src/gui/searchengine/searchsortmodel.h diff --git a/src/searchengine/searchtab.cpp b/src/gui/searchengine/searchtab.cpp similarity index 100% rename from src/searchengine/searchtab.cpp rename to src/gui/searchengine/searchtab.cpp diff --git a/src/searchengine/searchtab.h b/src/gui/searchengine/searchtab.h similarity index 100% rename from src/searchengine/searchtab.h rename to src/gui/searchengine/searchtab.h diff --git a/src/searchengine/supportedengines.h b/src/gui/searchengine/supportedengines.h similarity index 100% rename from src/searchengine/supportedengines.h rename to src/gui/searchengine/supportedengines.h diff --git a/src/speedlimitdlg.cpp b/src/gui/speedlimitdlg.cpp similarity index 100% rename from src/speedlimitdlg.cpp rename to src/gui/speedlimitdlg.cpp diff --git a/src/speedlimitdlg.h b/src/gui/speedlimitdlg.h similarity index 100% rename from src/speedlimitdlg.h rename to src/gui/speedlimitdlg.h diff --git a/src/statsdialog.cpp b/src/gui/statsdialog.cpp similarity index 100% rename from src/statsdialog.cpp rename to src/gui/statsdialog.cpp diff --git a/src/statsdialog.h b/src/gui/statsdialog.h similarity index 100% rename from src/statsdialog.h rename to src/gui/statsdialog.h diff --git a/src/statsdialog.ui b/src/gui/statsdialog.ui similarity index 100% rename from src/statsdialog.ui rename to src/gui/statsdialog.ui diff --git a/src/statusbar.cpp b/src/gui/statusbar.cpp similarity index 95% rename from src/statusbar.cpp rename to src/gui/statusbar.cpp index 29d2264ad..d775d068a 100644 --- a/src/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -57,17 +57,17 @@ StatusBar::StatusBar(QStatusBar *bar) connecStatusLblIcon->setFlat(true); connecStatusLblIcon->setFocusPolicy(Qt::NoFocus); connecStatusLblIcon->setCursor(Qt::PointingHandCursor); - connecStatusLblIcon->setIcon(QIcon(":/Icons/skin/firewalled.png")); + connecStatusLblIcon->setIcon(QIcon(":/icons/skin/firewalled.png")); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection status:")+QString::fromUtf8("
")+QString::fromUtf8("")+tr("No direct connections. This may indicate network configuration problems.")+QString::fromUtf8("")); dlSpeedLbl = new QPushButton(bar); - dlSpeedLbl->setIcon(QIcon(":/Icons/skin/download.png")); + dlSpeedLbl->setIcon(QIcon(":/icons/skin/download.png")); connect(dlSpeedLbl, SIGNAL(clicked()), this, SLOT(capDownloadSpeed())); dlSpeedLbl->setFlat(true); dlSpeedLbl->setFocusPolicy(Qt::NoFocus); dlSpeedLbl->setCursor(Qt::PointingHandCursor); upSpeedLbl = new QPushButton(bar); - upSpeedLbl->setIcon(QIcon(":/Icons/skin/seeding.png")); + upSpeedLbl->setIcon(QIcon(":/icons/skin/seeding.png")); connect(upSpeedLbl, SIGNAL(clicked()), this, SLOT(capUploadSpeed())); upSpeedLbl->setFlat(true); upSpeedLbl->setFocusPolicy(Qt::NoFocus); @@ -142,7 +142,7 @@ void StatusBar::showRestartRequired() { // Restart required notification const QString restart_text = tr("qBittorrent needs to be restarted"); QLabel *restartIconLbl = new QLabel(m_bar); - restartIconLbl->setPixmap(QPixmap(":/Icons/oxygen/dialog-warning.png").scaled(QSize(24,24))); + restartIconLbl->setPixmap(QPixmap(":/icons/oxygen/dialog-warning.png").scaled(QSize(24,24))); restartIconLbl->setToolTip(restart_text); m_bar->insertWidget(0,restartIconLbl); QLabel *restartLbl = new QLabel(m_bar); @@ -161,15 +161,15 @@ void StatusBar::refreshStatusBar() { // Update connection status const libtorrent::session_status sessionStatus = QBtSession::instance()->getSessionStatus(); if (!QBtSession::instance()->getSession()->is_listening()) { - connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/disconnected.png"))); + connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/icons/skin/disconnected.png"))); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection Status:")+QString::fromUtf8("
")+tr("Offline. This usually means that qBittorrent failed to listen on the selected port for incoming connections.")); } else { if (sessionStatus.has_incoming_connections) { // Connection OK - connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/connected.png"))); + connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/icons/skin/connected.png"))); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection Status:")+QString::fromUtf8("
")+tr("Online")); }else{ - connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/firewalled.png"))); + connecStatusLblIcon->setIcon(QIcon(QString::fromUtf8(":/icons/skin/firewalled.png"))); connecStatusLblIcon->setToolTip(QString::fromUtf8("")+tr("Connection status:")+QString::fromUtf8("
")+QString::fromUtf8("")+tr("No direct connections. This may indicate network configuration problems.")+QString::fromUtf8("")); } } @@ -197,11 +197,11 @@ void StatusBar::refreshStatusBar() { void StatusBar::updateAltSpeedsBtn(bool alternative) { if (alternative) { - altSpeedsBtn->setIcon(QIcon(":/Icons/slow.png")); + altSpeedsBtn->setIcon(QIcon(":/icons/slow.png")); altSpeedsBtn->setToolTip(tr("Click to switch to regular speed limits")); altSpeedsBtn->setDown(true); } else { - altSpeedsBtn->setIcon(QIcon(":/Icons/slow_off.png")); + altSpeedsBtn->setIcon(QIcon(":/icons/slow_off.png")); altSpeedsBtn->setToolTip(tr("Click to switch to alternative speed limits")); altSpeedsBtn->setDown(false); } diff --git a/src/statusbar.h b/src/gui/statusbar.h similarity index 100% rename from src/statusbar.h rename to src/gui/statusbar.h diff --git a/src/torrentcontentfiltermodel.cpp b/src/gui/torrentcontentfiltermodel.cpp similarity index 100% rename from src/torrentcontentfiltermodel.cpp rename to src/gui/torrentcontentfiltermodel.cpp diff --git a/src/torrentcontentfiltermodel.h b/src/gui/torrentcontentfiltermodel.h similarity index 100% rename from src/torrentcontentfiltermodel.h rename to src/gui/torrentcontentfiltermodel.h diff --git a/src/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp similarity index 100% rename from src/torrentcontentmodel.cpp rename to src/gui/torrentcontentmodel.cpp diff --git a/src/torrentcontentmodel.h b/src/gui/torrentcontentmodel.h similarity index 100% rename from src/torrentcontentmodel.h rename to src/gui/torrentcontentmodel.h diff --git a/src/torrentcontentmodelfile.cpp b/src/gui/torrentcontentmodelfile.cpp similarity index 100% rename from src/torrentcontentmodelfile.cpp rename to src/gui/torrentcontentmodelfile.cpp diff --git a/src/torrentcontentmodelfile.h b/src/gui/torrentcontentmodelfile.h similarity index 100% rename from src/torrentcontentmodelfile.h rename to src/gui/torrentcontentmodelfile.h diff --git a/src/torrentcontentmodelfolder.cpp b/src/gui/torrentcontentmodelfolder.cpp similarity index 100% rename from src/torrentcontentmodelfolder.cpp rename to src/gui/torrentcontentmodelfolder.cpp diff --git a/src/torrentcontentmodelfolder.h b/src/gui/torrentcontentmodelfolder.h similarity index 100% rename from src/torrentcontentmodelfolder.h rename to src/gui/torrentcontentmodelfolder.h diff --git a/src/torrentcontentmodelitem.cpp b/src/gui/torrentcontentmodelitem.cpp similarity index 100% rename from src/torrentcontentmodelitem.cpp rename to src/gui/torrentcontentmodelitem.cpp diff --git a/src/torrentcontentmodelitem.h b/src/gui/torrentcontentmodelitem.h similarity index 100% rename from src/torrentcontentmodelitem.h rename to src/gui/torrentcontentmodelitem.h diff --git a/src/torrentcontenttreeview.cpp b/src/gui/torrentcontenttreeview.cpp similarity index 100% rename from src/torrentcontenttreeview.cpp rename to src/gui/torrentcontenttreeview.cpp diff --git a/src/torrentcontenttreeview.h b/src/gui/torrentcontenttreeview.h similarity index 100% rename from src/torrentcontenttreeview.h rename to src/gui/torrentcontenttreeview.h diff --git a/src/torrentcreator/createtorrent.ui b/src/gui/torrentcreator/createtorrent.ui similarity index 100% rename from src/torrentcreator/createtorrent.ui rename to src/gui/torrentcreator/createtorrent.ui diff --git a/src/torrentcreator/torrentcreator.pri b/src/gui/torrentcreator/torrentcreator.pri similarity index 100% rename from src/torrentcreator/torrentcreator.pri rename to src/gui/torrentcreator/torrentcreator.pri diff --git a/src/torrentcreator/torrentcreatordlg.cpp b/src/gui/torrentcreator/torrentcreatordlg.cpp similarity index 100% rename from src/torrentcreator/torrentcreatordlg.cpp rename to src/gui/torrentcreator/torrentcreatordlg.cpp diff --git a/src/torrentcreator/torrentcreatordlg.h b/src/gui/torrentcreator/torrentcreatordlg.h similarity index 100% rename from src/torrentcreator/torrentcreatordlg.h rename to src/gui/torrentcreator/torrentcreatordlg.h diff --git a/src/torrentcreator/torrentcreatorthread.cpp b/src/gui/torrentcreator/torrentcreatorthread.cpp similarity index 100% rename from src/torrentcreator/torrentcreatorthread.cpp rename to src/gui/torrentcreator/torrentcreatorthread.cpp diff --git a/src/torrentcreator/torrentcreatorthread.h b/src/gui/torrentcreator/torrentcreatorthread.h similarity index 100% rename from src/torrentcreator/torrentcreatorthread.h rename to src/gui/torrentcreator/torrentcreatorthread.h diff --git a/src/torrentfilterenum.h b/src/gui/torrentfilterenum.h similarity index 100% rename from src/torrentfilterenum.h rename to src/gui/torrentfilterenum.h diff --git a/src/torrentimportdlg.cpp b/src/gui/torrentimportdlg.cpp similarity index 100% rename from src/torrentimportdlg.cpp rename to src/gui/torrentimportdlg.cpp diff --git a/src/torrentimportdlg.h b/src/gui/torrentimportdlg.h similarity index 100% rename from src/torrentimportdlg.h rename to src/gui/torrentimportdlg.h diff --git a/src/torrentimportdlg.ui b/src/gui/torrentimportdlg.ui similarity index 100% rename from src/torrentimportdlg.ui rename to src/gui/torrentimportdlg.ui diff --git a/src/trackerlogin.cpp b/src/gui/trackerlogin.cpp similarity index 97% rename from src/trackerlogin.cpp rename to src/gui/trackerlogin.cpp index d0ab212b7..fabcc9572 100644 --- a/src/trackerlogin.cpp +++ b/src/gui/trackerlogin.cpp @@ -36,7 +36,7 @@ trackerLogin::trackerLogin(QWidget *parent, QTorrentHandle h) { setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - login_logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/oxygen/encrypted.png"))); + login_logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/oxygen/encrypted.png"))); tracker_url->setText(h.current_tracker()); connect(this, SIGNAL(trackerLoginCancelled(QPair)), parent, SLOT(addUnauthenticatedTracker(QPair))); show(); diff --git a/src/trackerlogin.h b/src/gui/trackerlogin.h similarity index 100% rename from src/trackerlogin.h rename to src/gui/trackerlogin.h diff --git a/src/transferlistdelegate.cpp b/src/gui/transferlistdelegate.cpp similarity index 99% rename from src/transferlistdelegate.cpp rename to src/gui/transferlistdelegate.cpp index a4d306aec..2098fd054 100644 --- a/src/transferlistdelegate.cpp +++ b/src/gui/transferlistdelegate.cpp @@ -236,7 +236,7 @@ QSize TransferListDelegate::sizeHint(const QStyleOptionViewItem & option, const static int icon_height = -1; if (icon_height == -1) { - QIcon icon(":/Icons/skin/downloading.png"); + QIcon icon(":/icons/skin/downloading.png"); QList ic_sizes(icon.availableSizes()); icon_height = ic_sizes[0].height(); } diff --git a/src/transferlistdelegate.h b/src/gui/transferlistdelegate.h similarity index 100% rename from src/transferlistdelegate.h rename to src/gui/transferlistdelegate.h diff --git a/src/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp similarity index 97% rename from src/transferlistfilterswidget.cpp rename to src/gui/transferlistfilterswidget.cpp index b6b84cb01..fa0ba662f 100644 --- a/src/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -184,25 +184,25 @@ TransferListFiltersWidget::TransferListFiltersWidget(QWidget *parent, TransferLi // Add status filters QListWidgetItem *all = new QListWidgetItem(statusFilters); all->setData(Qt::DisplayRole, QVariant(tr("All") + " (0)")); - all->setData(Qt::DecorationRole, QIcon(":/Icons/skin/filterall.png")); + all->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterall.png")); QListWidgetItem *downloading = new QListWidgetItem(statusFilters); downloading->setData(Qt::DisplayRole, QVariant(tr("Downloading") + " (0)")); - downloading->setData(Qt::DecorationRole, QIcon(":/Icons/skin/downloading.png")); + downloading->setData(Qt::DecorationRole, QIcon(":/icons/skin/downloading.png")); QListWidgetItem *completed = new QListWidgetItem(statusFilters); completed->setData(Qt::DisplayRole, QVariant(tr("Completed") + " (0)")); - completed->setData(Qt::DecorationRole, QIcon(":/Icons/skin/uploading.png")); + completed->setData(Qt::DecorationRole, QIcon(":/icons/skin/uploading.png")); QListWidgetItem *paused = new QListWidgetItem(statusFilters); paused->setData(Qt::DisplayRole, QVariant(tr("Paused") + " (0)")); - paused->setData(Qt::DecorationRole, QIcon(":/Icons/skin/paused.png")); + paused->setData(Qt::DecorationRole, QIcon(":/icons/skin/paused.png")); QListWidgetItem *resumed = new QListWidgetItem(statusFilters); resumed->setData(Qt::DisplayRole, QVariant(tr("Resumed") + " (0)")); - resumed->setData(Qt::DecorationRole, QIcon(":/Icons/skin/resumed.png")); + resumed->setData(Qt::DecorationRole, QIcon(":/icons/skin/resumed.png")); QListWidgetItem *active = new QListWidgetItem(statusFilters); active->setData(Qt::DisplayRole, QVariant(tr("Active") + " (0)")); - active->setData(Qt::DecorationRole, QIcon(":/Icons/skin/filteractive.png")); + active->setData(Qt::DecorationRole, QIcon(":/icons/skin/filteractive.png")); QListWidgetItem *inactive = new QListWidgetItem(statusFilters); inactive->setData(Qt::DisplayRole, QVariant(tr("Inactive") + " (0)")); - inactive->setData(Qt::DecorationRole, QIcon(":/Icons/skin/filterinactive.png")); + inactive->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterinactive.png")); // SIGNAL/SLOT connect(statusFilters, SIGNAL(currentRowChanged(int)), transferList, SLOT(applyStatusFilter(int))); diff --git a/src/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h similarity index 100% rename from src/transferlistfilterswidget.h rename to src/gui/transferlistfilterswidget.h diff --git a/src/transferlistsortmodel.cpp b/src/gui/transferlistsortmodel.cpp similarity index 100% rename from src/transferlistsortmodel.cpp rename to src/gui/transferlistsortmodel.cpp diff --git a/src/transferlistsortmodel.h b/src/gui/transferlistsortmodel.h similarity index 100% rename from src/transferlistsortmodel.h rename to src/gui/transferlistsortmodel.h diff --git a/src/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp similarity index 99% rename from src/transferlistwidget.cpp rename to src/gui/transferlistwidget.cpp index f1df423fc..4fd03ee6a 100644 --- a/src/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -719,11 +719,11 @@ void TransferListWidget::displayListMenu(const QPoint&) connect(&actionDelete, SIGNAL(triggered()), this, SLOT(deleteSelectedTorrents())); QAction actionPreview_file(IconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); - QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/Icons/skin/ratio.png")), tr("Limit share ratio..."), 0); + QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/icons/skin/ratio.png")), tr("Limit share ratio..."), 0); connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); - QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png")), tr("Limit upload rate..."), 0); + QAction actionSet_upload_limit(QIcon(QString::fromUtf8(":/icons/skin/seeding.png")), tr("Limit upload rate..."), 0); connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); - QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/Icons/skin/download.png")), tr("Limit download rate..."), 0); + QAction actionSet_download_limit(QIcon(QString::fromUtf8(":/icons/skin/download.png")), tr("Limit download rate..."), 0); connect(&actionSet_download_limit, SIGNAL(triggered()), this, SLOT(setDlLimitSelectedTorrents())); QAction actionOpen_destination_folder(IconProvider::instance()->getIcon("inode-directory"), tr("Open destination folder"), 0); connect(&actionOpen_destination_folder, SIGNAL(triggered()), this, SLOT(openSelectedTorrentsFolder())); @@ -739,7 +739,7 @@ void TransferListWidget::displayListMenu(const QPoint&) connect(&actionSetTorrentPath, SIGNAL(triggered()), this, SLOT(setSelectedTorrentsLocation())); QAction actionForce_recheck(IconProvider::instance()->getIcon("document-edit-verify"), tr("Force recheck"), 0); connect(&actionForce_recheck, SIGNAL(triggered()), this, SLOT(recheckSelectedTorrents())); - QAction actionCopy_magnet_link(QIcon(":/Icons/magnet.png"), tr("Copy magnet link"), 0); + QAction actionCopy_magnet_link(QIcon(":/icons/magnet.png"), tr("Copy magnet link"), 0); connect(&actionCopy_magnet_link, SIGNAL(triggered()), this, SLOT(copySelectedMagnetURIs())); QAction actionSuper_seeding_mode(tr("Super seeding mode"), 0); actionSuper_seeding_mode.setCheckable(true); diff --git a/src/transferlistwidget.h b/src/gui/transferlistwidget.h similarity index 100% rename from src/transferlistwidget.h rename to src/gui/transferlistwidget.h diff --git a/src/updownratiodlg.cpp b/src/gui/updownratiodlg.cpp similarity index 100% rename from src/updownratiodlg.cpp rename to src/gui/updownratiodlg.cpp diff --git a/src/updownratiodlg.h b/src/gui/updownratiodlg.h similarity index 100% rename from src/updownratiodlg.h rename to src/gui/updownratiodlg.h diff --git a/src/updownratiodlg.ui b/src/gui/updownratiodlg.ui similarity index 100% rename from src/updownratiodlg.ui rename to src/gui/updownratiodlg.ui diff --git a/src/headlessloader.h b/src/headlessloader.h deleted file mode 100644 index 5bbc2c3c2..000000000 --- a/src/headlessloader.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2006 Christophe Dumez, Frédéric Lassabe - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#ifndef HEADLESSLOADER_H -#define HEADLESSLOADER_H - -#include -#include -#include -#include "preferences.h" -#include "qbtsession.h" -#include "fs_utils.h" -#include "misc.h" -#include "logger.h" -#include "torrentpersistentdata.h" - -class HeadlessLoader: public QObject { - Q_OBJECT - -public: - HeadlessLoader(const QStringList &torrentCmdLine) { - connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(shutdownCleanUp()), Qt::DirectConnection); - Preferences* const pref = Preferences::instance(); - // Enable Web UI - pref->setWebUiEnabled(true); - // Instanciate Bittorrent Object - connect(QBtSession::instance(), SIGNAL(newConsoleMessage(QString)), this, SLOT(displayConsoleMessage(QString))); - // Resume unfinished torrents - QBtSession::instance()->startUpTorrents(); - // Process command line parameters - processParams(torrentCmdLine); - // Display some information to the user - std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl; - std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(pref->getWebUiPort()))) << std::endl; - std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername())) << std::endl; - qDebug() << "Password:" << pref->getWebUiPassword(); - if (pref->getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") { - std::cout << qPrintable(tr("The Web UI administrator password is still the default one: %1").arg("adminadmin")) << std::endl; - std::cout << qPrintable(tr("This is a security risk, please consider changing your password from program preferences.")) << std::endl; - } - } - -public slots: - void shutdownCleanUp() { - QBtSession::drop(); - TorrentPersistentData::drop(); - Preferences::drop(); - Logger::drop(); - } - - // Call this function to exit qBittorrent headless loader - // and return to prompt (object will be deleted by main) - void exit() { - qApp->quit(); - } - - void displayConsoleMessage(const QString &msg) { - std::cout << qPrintable(msg) << std::endl; - } - - void processParams(const QString& params_str) { - processParams(params_str.split(" ", QString::SkipEmptyParts)); - } - - // As program parameters, we can get paths or urls. - // This function parse the parameters and call - // the right addTorrent function, considering - // the parameter type. - void processParams(const QStringList& params) { - foreach (QString param, params) { - param = fsutils::fromNativePath(param).trimmed(); - if (param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) { - QBtSession::instance()->downloadFromUrl(param); - }else{ - if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - param = misc::bcLinkToMagnet(param); - } - if (param.startsWith("magnet:", Qt::CaseInsensitive)) { - QBtSession::instance()->addMagnetUri(param); - } else { - QBtSession::instance()->addTorrent(param); - } - } - } - } - -}; - -#endif diff --git a/src/icons.qrc b/src/icons.qrc index 8628ef1f4..5548f8a0f 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -1,371 +1,371 @@ - Icons/qbittorrent.png - Icons/3-state-checkbox.gif - Icons/L.gif - Icons/loading.png - Icons/magnet.png - Icons/slow.png - Icons/slow_off.png - Icons/sphere.png - Icons/sphere2.png - Icons/url.png - Icons/flags/ad.png - Icons/flags/ae.png - Icons/flags/af.png - Icons/flags/ag.png - Icons/flags/ai.png - Icons/flags/al.png - Icons/flags/am.png - Icons/flags/an.png - Icons/flags/ao.png - Icons/flags/ar.png - Icons/flags/as.png - Icons/flags/at.png - Icons/flags/au.png - Icons/flags/aw.png - Icons/flags/ax.png - Icons/flags/az.png - Icons/flags/ba.png - Icons/flags/bb.png - Icons/flags/bd.png - Icons/flags/be.png - Icons/flags/bf.png - Icons/flags/bg.png - Icons/flags/bh.png - Icons/flags/bi.png - Icons/flags/bj.png - Icons/flags/bm.png - Icons/flags/bn.png - Icons/flags/bo.png - Icons/flags/br.png - Icons/flags/bs.png - Icons/flags/bt.png - Icons/flags/bv.png - Icons/flags/bw.png - Icons/flags/by.png - Icons/flags/bz.png - Icons/flags/ca.png - Icons/flags/cc.png - Icons/flags/cd.png - Icons/flags/cf.png - Icons/flags/cg.png - Icons/flags/ch.png - Icons/flags/ci.png - Icons/flags/ck.png - Icons/flags/cl.png - Icons/flags/cm.png - Icons/flags/cn.png - Icons/flags/co.png - Icons/flags/cr.png - Icons/flags/cs.png - Icons/flags/cu.png - Icons/flags/cv.png - Icons/flags/cx.png - Icons/flags/cy.png - Icons/flags/cz.png - Icons/flags/de.png - Icons/flags/dj.png - Icons/flags/dk.png - Icons/flags/dm.png - Icons/flags/do.png - Icons/flags/dz.png - Icons/flags/ec.png - Icons/flags/ee.png - Icons/flags/eg.png - Icons/flags/eh.png - Icons/flags/er.png - Icons/flags/es.png - Icons/flags/et.png - Icons/flags/fi.png - Icons/flags/fj.png - Icons/flags/fk.png - Icons/flags/fm.png - Icons/flags/fo.png - Icons/flags/fr.png - Icons/flags/ga.png - Icons/flags/gb.png - Icons/flags/gd.png - Icons/flags/ge.png - Icons/flags/gf.png - Icons/flags/gh.png - Icons/flags/gi.png - Icons/flags/gl.png - Icons/flags/gm.png - Icons/flags/gn.png - Icons/flags/gp.png - Icons/flags/gq.png - Icons/flags/gr.png - Icons/flags/gs.png - Icons/flags/gt.png - Icons/flags/gu.png - Icons/flags/gw.png - Icons/flags/gy.png - Icons/flags/hk.png - Icons/flags/hm.png - Icons/flags/hn.png - Icons/flags/hr.png - Icons/flags/ht.png - Icons/flags/hu.png - Icons/flags/id.png - Icons/flags/ie.png - Icons/flags/il.png - Icons/flags/in.png - Icons/flags/io.png - Icons/flags/iq.png - Icons/flags/ir.png - Icons/flags/is.png - Icons/flags/it.png - Icons/flags/jm.png - Icons/flags/jo.png - Icons/flags/jp.png - Icons/flags/ke.png - Icons/flags/kg.png - Icons/flags/kh.png - Icons/flags/ki.png - Icons/flags/km.png - Icons/flags/kn.png - Icons/flags/kp.png - Icons/flags/kr.png - Icons/flags/kw.png - Icons/flags/ky.png - Icons/flags/kz.png - Icons/flags/la.png - Icons/flags/lb.png - Icons/flags/lc.png - Icons/flags/li.png - Icons/flags/lk.png - Icons/flags/lr.png - Icons/flags/ls.png - Icons/flags/lt.png - Icons/flags/lu.png - Icons/flags/lv.png - Icons/flags/ly.png - Icons/flags/ma.png - Icons/flags/mc.png - Icons/flags/md.png - Icons/flags/me.png - Icons/flags/mg.png - Icons/flags/mh.png - Icons/flags/mk.png - Icons/flags/ml.png - Icons/flags/mm.png - Icons/flags/mn.png - Icons/flags/mo.png - Icons/flags/mp.png - Icons/flags/mq.png - Icons/flags/mr.png - Icons/flags/ms.png - Icons/flags/mt.png - Icons/flags/mu.png - Icons/flags/mv.png - Icons/flags/mw.png - Icons/flags/mx.png - Icons/flags/my.png - Icons/flags/mz.png - Icons/flags/na.png - Icons/flags/nc.png - Icons/flags/ne.png - Icons/flags/nf.png - Icons/flags/ng.png - Icons/flags/ni.png - Icons/flags/nl.png - Icons/flags/no.png - Icons/flags/np.png - Icons/flags/nr.png - Icons/flags/nu.png - Icons/flags/nz.png - Icons/flags/om.png - Icons/flags/pa.png - Icons/flags/pe.png - Icons/flags/pf.png - Icons/flags/pg.png - Icons/flags/ph.png - Icons/flags/pk.png - Icons/flags/pl.png - Icons/flags/pm.png - Icons/flags/pn.png - Icons/flags/pr.png - Icons/flags/ps.png - Icons/flags/pt.png - Icons/flags/pw.png - Icons/flags/py.png - Icons/flags/qa.png - Icons/flags/re.png - Icons/flags/ro.png - Icons/flags/rs.png - Icons/flags/ru.png - Icons/flags/rw.png - Icons/flags/sa.png - Icons/flags/sb.png - Icons/flags/sc.png - Icons/flags/sd.png - Icons/flags/se.png - Icons/flags/sg.png - Icons/flags/sh.png - Icons/flags/si.png - Icons/flags/sj.png - Icons/flags/sk.png - Icons/flags/sl.png - Icons/flags/sm.png - Icons/flags/sn.png - Icons/flags/so.png - Icons/flags/sr.png - Icons/flags/st.png - Icons/flags/sv.png - Icons/flags/sy.png - Icons/flags/sz.png - Icons/flags/tc.png - Icons/flags/td.png - Icons/flags/tf.png - Icons/flags/tg.png - Icons/flags/th.png - Icons/flags/tj.png - Icons/flags/tk.png - Icons/flags/tl.png - Icons/flags/tm.png - Icons/flags/tn.png - Icons/flags/to.png - Icons/flags/tr.png - Icons/flags/tt.png - Icons/flags/tv.png - Icons/flags/tw.png - Icons/flags/tz.png - Icons/flags/ua.png - Icons/flags/ug.png - Icons/flags/um.png - Icons/flags/us.png - Icons/flags/uy.png - Icons/flags/uz.png - Icons/flags/va.png - Icons/flags/vc.png - Icons/flags/ve.png - Icons/flags/vg.png - Icons/flags/vi.png - Icons/flags/vn.png - Icons/flags/vu.png - Icons/flags/wf.png - Icons/flags/ws.png - Icons/flags/ye.png - Icons/flags/yt.png - Icons/flags/za.png - Icons/flags/zm.png - Icons/flags/zw.png - Icons/oxygen/application-exit.png - Icons/oxygen/application-rss+xml.png - Icons/oxygen/application-x-mswinurl.png - Icons/oxygen/chronometer.png - Icons/oxygen/dialog-cancel.png - Icons/oxygen/dialog-information.png - Icons/oxygen/dialog-warning.png - Icons/oxygen/document-edit-verify.png - Icons/oxygen/document-edit.png - Icons/oxygen/document-encrypt.png - Icons/oxygen/document-import.png - Icons/oxygen/document-new.png - Icons/oxygen/document-properties.png - Icons/oxygen/document-save.png - Icons/oxygen/download.png - Icons/oxygen/edit-clear-history.png - Icons/oxygen/edit-clear.png - Icons/oxygen/edit-copy.png - Icons/oxygen/edit-cut.png - Icons/oxygen/edit-delete.png - Icons/oxygen/edit-find-user.png - Icons/oxygen/edit-find.png - Icons/oxygen/edit-paste.png - Icons/oxygen/edit-rename.png - Icons/oxygen/folder-documents.png - Icons/oxygen/folder-new.png - Icons/oxygen/folder-remote.png - Icons/oxygen/gear.png - Icons/oxygen/gear32.png - Icons/oxygen/go-down.png - Icons/oxygen/go-up.png - Icons/oxygen/help-about.png - Icons/oxygen/help-contents.png - Icons/oxygen/inode-directory.png - Icons/oxygen/insert-link.png - Icons/oxygen/list-add.png - Icons/oxygen/list-remove.png - Icons/oxygen/mail-folder-inbox.png - Icons/oxygen/mail-mark-read.png - Icons/oxygen/media-playback-pause.png - Icons/oxygen/media-playback-start.png - Icons/oxygen/network-server.png - Icons/oxygen/network-wired.png - Icons/oxygen/object-locked.png - Icons/oxygen/preferences-desktop.png - Icons/oxygen/preferences-other.png - Icons/oxygen/preferences-system-network.png - Icons/oxygen/preferences-system.png - Icons/oxygen/preferences-web-browser-cookies.png - Icons/oxygen/security-high.png - Icons/oxygen/security-low.png - Icons/oxygen/services.png - Icons/oxygen/tab-close.png - Icons/oxygen/task-attention.png - Icons/oxygen/text-plain.png - Icons/oxygen/tools-report-bug.png - Icons/oxygen/unavailable.png - Icons/oxygen/user-group-delete.png - Icons/oxygen/user-group-new.png - Icons/oxygen/view-calendar-journal.png - Icons/oxygen/view-categories.png - Icons/oxygen/view-filter.png - Icons/oxygen/view-preview.png - Icons/oxygen/view-refresh.png - Icons/oxygen/view-statistics.png - Icons/oxygen/wallet-open.png - Icons/oxygen/webui.png - Icons/skin/arrow-right.gif - Icons/skin/bg-dropdown.gif - Icons/skin/bg-handle-horizontal.gif - Icons/skin/bg-header.gif - Icons/skin/bg-panel-header.gif - Icons/skin/checking.png - Icons/skin/collapse-expand.gif - Icons/skin/connected.png - Icons/skin/disconnected.png - Icons/skin/dock-tabs.gif - Icons/skin/download.png - Icons/skin/downloading.png - Icons/skin/error.png - Icons/skin/filteractive.png - Icons/skin/filterall.png - Icons/skin/filterinactive.png - Icons/skin/firewalled.png - Icons/skin/handle-icon-horizontal.gif - Icons/skin/handle-icon.gif - Icons/skin/knob.gif - Icons/skin/logo-blank.gif - Icons/skin/logo.gif - Icons/skin/logo2.gif - Icons/skin/mascot.png - Icons/skin/paused.png - Icons/skin/qbittorrent16.png - Icons/skin/qbittorrent22.png - Icons/skin/qbittorrent32.png - Icons/skin/qbittorrent_mono_dark.png - Icons/skin/qbittorrent_mono_light.png - Icons/skin/queued.png - Icons/skin/ratio.png - Icons/skin/seeding.png - Icons/skin/slider-area.gif - Icons/skin/spacer.gif - Icons/skin/spinner-placeholder.gif - Icons/skin/spinner.gif - Icons/skin/splash.png - Icons/skin/stalledDL.png - Icons/skin/stalledUP.png - Icons/skin/tabs.gif - Icons/skin/toolbox-divider.gif - Icons/skin/toolbox-divider2.gif - Icons/skin/resumed.png - Icons/skin/uploading.png - Icons/oxygen/system-log-out.png - Icons/oxygen/go-bottom.png - Icons/oxygen/go-top.png - Icons/oxygen/checked.png + icons/qbittorrent.png + icons/3-state-checkbox.gif + icons/L.gif + icons/loading.png + icons/magnet.png + icons/slow.png + icons/slow_off.png + icons/sphere.png + icons/sphere2.png + icons/url.png + icons/flags/ad.png + icons/flags/ae.png + icons/flags/af.png + icons/flags/ag.png + icons/flags/ai.png + icons/flags/al.png + icons/flags/am.png + icons/flags/an.png + icons/flags/ao.png + icons/flags/ar.png + icons/flags/as.png + icons/flags/at.png + icons/flags/au.png + icons/flags/aw.png + icons/flags/ax.png + icons/flags/az.png + icons/flags/ba.png + icons/flags/bb.png + icons/flags/bd.png + icons/flags/be.png + icons/flags/bf.png + icons/flags/bg.png + icons/flags/bh.png + icons/flags/bi.png + icons/flags/bj.png + icons/flags/bm.png + icons/flags/bn.png + icons/flags/bo.png + icons/flags/br.png + icons/flags/bs.png + icons/flags/bt.png + icons/flags/bv.png + icons/flags/bw.png + icons/flags/by.png + icons/flags/bz.png + icons/flags/ca.png + icons/flags/cc.png + icons/flags/cd.png + icons/flags/cf.png + icons/flags/cg.png + icons/flags/ch.png + icons/flags/ci.png + icons/flags/ck.png + icons/flags/cl.png + icons/flags/cm.png + icons/flags/cn.png + icons/flags/co.png + icons/flags/cr.png + icons/flags/cs.png + icons/flags/cu.png + icons/flags/cv.png + icons/flags/cx.png + icons/flags/cy.png + icons/flags/cz.png + icons/flags/de.png + icons/flags/dj.png + icons/flags/dk.png + icons/flags/dm.png + icons/flags/do.png + icons/flags/dz.png + icons/flags/ec.png + icons/flags/ee.png + icons/flags/eg.png + icons/flags/eh.png + icons/flags/er.png + icons/flags/es.png + icons/flags/et.png + icons/flags/fi.png + icons/flags/fj.png + icons/flags/fk.png + icons/flags/fm.png + icons/flags/fo.png + icons/flags/fr.png + icons/flags/ga.png + icons/flags/gb.png + icons/flags/gd.png + icons/flags/ge.png + icons/flags/gf.png + icons/flags/gh.png + icons/flags/gi.png + icons/flags/gl.png + icons/flags/gm.png + icons/flags/gn.png + icons/flags/gp.png + icons/flags/gq.png + icons/flags/gr.png + icons/flags/gs.png + icons/flags/gt.png + icons/flags/gu.png + icons/flags/gw.png + icons/flags/gy.png + icons/flags/hk.png + icons/flags/hm.png + icons/flags/hn.png + icons/flags/hr.png + icons/flags/ht.png + icons/flags/hu.png + icons/flags/id.png + icons/flags/ie.png + icons/flags/il.png + icons/flags/in.png + icons/flags/io.png + icons/flags/iq.png + icons/flags/ir.png + icons/flags/is.png + icons/flags/it.png + icons/flags/jm.png + icons/flags/jo.png + icons/flags/jp.png + icons/flags/ke.png + icons/flags/kg.png + icons/flags/kh.png + icons/flags/ki.png + icons/flags/km.png + icons/flags/kn.png + icons/flags/kp.png + icons/flags/kr.png + icons/flags/kw.png + icons/flags/ky.png + icons/flags/kz.png + icons/flags/la.png + icons/flags/lb.png + icons/flags/lc.png + icons/flags/li.png + icons/flags/lk.png + icons/flags/lr.png + icons/flags/ls.png + icons/flags/lt.png + icons/flags/lu.png + icons/flags/lv.png + icons/flags/ly.png + icons/flags/ma.png + icons/flags/mc.png + icons/flags/md.png + icons/flags/me.png + icons/flags/mg.png + icons/flags/mh.png + icons/flags/mk.png + icons/flags/ml.png + icons/flags/mm.png + icons/flags/mn.png + icons/flags/mo.png + icons/flags/mp.png + icons/flags/mq.png + icons/flags/mr.png + icons/flags/ms.png + icons/flags/mt.png + icons/flags/mu.png + icons/flags/mv.png + icons/flags/mw.png + icons/flags/mx.png + icons/flags/my.png + icons/flags/mz.png + icons/flags/na.png + icons/flags/nc.png + icons/flags/ne.png + icons/flags/nf.png + icons/flags/ng.png + icons/flags/ni.png + icons/flags/nl.png + icons/flags/no.png + icons/flags/np.png + icons/flags/nr.png + icons/flags/nu.png + icons/flags/nz.png + icons/flags/om.png + icons/flags/pa.png + icons/flags/pe.png + icons/flags/pf.png + icons/flags/pg.png + icons/flags/ph.png + icons/flags/pk.png + icons/flags/pl.png + icons/flags/pm.png + icons/flags/pn.png + icons/flags/pr.png + icons/flags/ps.png + icons/flags/pt.png + icons/flags/pw.png + icons/flags/py.png + icons/flags/qa.png + icons/flags/re.png + icons/flags/ro.png + icons/flags/rs.png + icons/flags/ru.png + icons/flags/rw.png + icons/flags/sa.png + icons/flags/sb.png + icons/flags/sc.png + icons/flags/sd.png + icons/flags/se.png + icons/flags/sg.png + icons/flags/sh.png + icons/flags/si.png + icons/flags/sj.png + icons/flags/sk.png + icons/flags/sl.png + icons/flags/sm.png + icons/flags/sn.png + icons/flags/so.png + icons/flags/sr.png + icons/flags/st.png + icons/flags/sv.png + icons/flags/sy.png + icons/flags/sz.png + icons/flags/tc.png + icons/flags/td.png + icons/flags/tf.png + icons/flags/tg.png + icons/flags/th.png + icons/flags/tj.png + icons/flags/tk.png + icons/flags/tl.png + icons/flags/tm.png + icons/flags/tn.png + icons/flags/to.png + icons/flags/tr.png + icons/flags/tt.png + icons/flags/tv.png + icons/flags/tw.png + icons/flags/tz.png + icons/flags/ua.png + icons/flags/ug.png + icons/flags/um.png + icons/flags/us.png + icons/flags/uy.png + icons/flags/uz.png + icons/flags/va.png + icons/flags/vc.png + icons/flags/ve.png + icons/flags/vg.png + icons/flags/vi.png + icons/flags/vn.png + icons/flags/vu.png + icons/flags/wf.png + icons/flags/ws.png + icons/flags/ye.png + icons/flags/yt.png + icons/flags/za.png + icons/flags/zm.png + icons/flags/zw.png + icons/oxygen/application-exit.png + icons/oxygen/application-rss+xml.png + icons/oxygen/application-x-mswinurl.png + icons/oxygen/chronometer.png + icons/oxygen/dialog-cancel.png + icons/oxygen/dialog-information.png + icons/oxygen/dialog-warning.png + icons/oxygen/document-edit-verify.png + icons/oxygen/document-edit.png + icons/oxygen/document-encrypt.png + icons/oxygen/document-import.png + icons/oxygen/document-new.png + icons/oxygen/document-properties.png + icons/oxygen/document-save.png + icons/oxygen/download.png + icons/oxygen/edit-clear-history.png + icons/oxygen/edit-clear.png + icons/oxygen/edit-copy.png + icons/oxygen/edit-cut.png + icons/oxygen/edit-delete.png + icons/oxygen/edit-find-user.png + icons/oxygen/edit-find.png + icons/oxygen/edit-paste.png + icons/oxygen/edit-rename.png + icons/oxygen/folder-documents.png + icons/oxygen/folder-new.png + icons/oxygen/folder-remote.png + icons/oxygen/gear.png + icons/oxygen/gear32.png + icons/oxygen/go-down.png + icons/oxygen/go-up.png + icons/oxygen/help-about.png + icons/oxygen/help-contents.png + icons/oxygen/inode-directory.png + icons/oxygen/insert-link.png + icons/oxygen/list-add.png + icons/oxygen/list-remove.png + icons/oxygen/mail-folder-inbox.png + icons/oxygen/mail-mark-read.png + icons/oxygen/media-playback-pause.png + icons/oxygen/media-playback-start.png + icons/oxygen/network-server.png + icons/oxygen/network-wired.png + icons/oxygen/object-locked.png + icons/oxygen/preferences-desktop.png + icons/oxygen/preferences-other.png + icons/oxygen/preferences-system-network.png + icons/oxygen/preferences-system.png + icons/oxygen/preferences-web-browser-cookies.png + icons/oxygen/security-high.png + icons/oxygen/security-low.png + icons/oxygen/services.png + icons/oxygen/tab-close.png + icons/oxygen/task-attention.png + icons/oxygen/text-plain.png + icons/oxygen/tools-report-bug.png + icons/oxygen/unavailable.png + icons/oxygen/user-group-delete.png + icons/oxygen/user-group-new.png + icons/oxygen/view-calendar-journal.png + icons/oxygen/view-categories.png + icons/oxygen/view-filter.png + icons/oxygen/view-preview.png + icons/oxygen/view-refresh.png + icons/oxygen/view-statistics.png + icons/oxygen/wallet-open.png + icons/oxygen/webui.png + icons/skin/arrow-right.gif + icons/skin/bg-dropdown.gif + icons/skin/bg-handle-horizontal.gif + icons/skin/bg-header.gif + icons/skin/bg-panel-header.gif + icons/skin/checking.png + icons/skin/collapse-expand.gif + icons/skin/connected.png + icons/skin/disconnected.png + icons/skin/dock-tabs.gif + icons/skin/download.png + icons/skin/downloading.png + icons/skin/error.png + icons/skin/filteractive.png + icons/skin/filterall.png + icons/skin/filterinactive.png + icons/skin/firewalled.png + icons/skin/handle-icon-horizontal.gif + icons/skin/handle-icon.gif + icons/skin/knob.gif + icons/skin/logo-blank.gif + icons/skin/logo.gif + icons/skin/logo2.gif + icons/skin/mascot.png + icons/skin/paused.png + icons/skin/qbittorrent16.png + icons/skin/qbittorrent22.png + icons/skin/qbittorrent32.png + icons/skin/qbittorrent_mono_dark.png + icons/skin/qbittorrent_mono_light.png + icons/skin/queued.png + icons/skin/ratio.png + icons/skin/seeding.png + icons/skin/slider-area.gif + icons/skin/spacer.gif + icons/skin/spinner-placeholder.gif + icons/skin/spinner.gif + icons/skin/splash.png + icons/skin/stalledDL.png + icons/skin/stalledUP.png + icons/skin/tabs.gif + icons/skin/toolbox-divider.gif + icons/skin/toolbox-divider2.gif + icons/skin/resumed.png + icons/skin/uploading.png + icons/oxygen/system-log-out.png + icons/oxygen/go-bottom.png + icons/oxygen/go-top.png + icons/oxygen/checked.png diff --git a/src/Icons/3-state-checkbox.gif b/src/icons/3-state-checkbox.gif similarity index 100% rename from src/Icons/3-state-checkbox.gif rename to src/icons/3-state-checkbox.gif diff --git a/src/Icons/L.gif b/src/icons/L.gif similarity index 100% rename from src/Icons/L.gif rename to src/icons/L.gif diff --git a/src/Icons/flags/ad.png b/src/icons/flags/ad.png similarity index 100% rename from src/Icons/flags/ad.png rename to src/icons/flags/ad.png diff --git a/src/Icons/flags/ae.png b/src/icons/flags/ae.png similarity index 100% rename from src/Icons/flags/ae.png rename to src/icons/flags/ae.png diff --git a/src/Icons/flags/af.png b/src/icons/flags/af.png similarity index 100% rename from src/Icons/flags/af.png rename to src/icons/flags/af.png diff --git a/src/Icons/flags/ag.png b/src/icons/flags/ag.png similarity index 100% rename from src/Icons/flags/ag.png rename to src/icons/flags/ag.png diff --git a/src/Icons/flags/ai.png b/src/icons/flags/ai.png similarity index 100% rename from src/Icons/flags/ai.png rename to src/icons/flags/ai.png diff --git a/src/Icons/flags/al.png b/src/icons/flags/al.png similarity index 100% rename from src/Icons/flags/al.png rename to src/icons/flags/al.png diff --git a/src/Icons/flags/am.png b/src/icons/flags/am.png similarity index 100% rename from src/Icons/flags/am.png rename to src/icons/flags/am.png diff --git a/src/Icons/flags/an.png b/src/icons/flags/an.png similarity index 100% rename from src/Icons/flags/an.png rename to src/icons/flags/an.png diff --git a/src/Icons/flags/ao.png b/src/icons/flags/ao.png similarity index 100% rename from src/Icons/flags/ao.png rename to src/icons/flags/ao.png diff --git a/src/Icons/flags/ar.png b/src/icons/flags/ar.png similarity index 100% rename from src/Icons/flags/ar.png rename to src/icons/flags/ar.png diff --git a/src/Icons/flags/as.png b/src/icons/flags/as.png similarity index 100% rename from src/Icons/flags/as.png rename to src/icons/flags/as.png diff --git a/src/Icons/flags/at.png b/src/icons/flags/at.png similarity index 100% rename from src/Icons/flags/at.png rename to src/icons/flags/at.png diff --git a/src/Icons/flags/au.png b/src/icons/flags/au.png similarity index 100% rename from src/Icons/flags/au.png rename to src/icons/flags/au.png diff --git a/src/Icons/flags/aw.png b/src/icons/flags/aw.png similarity index 100% rename from src/Icons/flags/aw.png rename to src/icons/flags/aw.png diff --git a/src/Icons/flags/ax.png b/src/icons/flags/ax.png similarity index 100% rename from src/Icons/flags/ax.png rename to src/icons/flags/ax.png diff --git a/src/Icons/flags/az.png b/src/icons/flags/az.png similarity index 100% rename from src/Icons/flags/az.png rename to src/icons/flags/az.png diff --git a/src/Icons/flags/ba.png b/src/icons/flags/ba.png similarity index 100% rename from src/Icons/flags/ba.png rename to src/icons/flags/ba.png diff --git a/src/Icons/flags/bb.png b/src/icons/flags/bb.png similarity index 100% rename from src/Icons/flags/bb.png rename to src/icons/flags/bb.png diff --git a/src/Icons/flags/bd.png b/src/icons/flags/bd.png similarity index 100% rename from src/Icons/flags/bd.png rename to src/icons/flags/bd.png diff --git a/src/Icons/flags/be.png b/src/icons/flags/be.png similarity index 100% rename from src/Icons/flags/be.png rename to src/icons/flags/be.png diff --git a/src/Icons/flags/bf.png b/src/icons/flags/bf.png similarity index 100% rename from src/Icons/flags/bf.png rename to src/icons/flags/bf.png diff --git a/src/Icons/flags/bg.png b/src/icons/flags/bg.png similarity index 100% rename from src/Icons/flags/bg.png rename to src/icons/flags/bg.png diff --git a/src/Icons/flags/bh.png b/src/icons/flags/bh.png similarity index 100% rename from src/Icons/flags/bh.png rename to src/icons/flags/bh.png diff --git a/src/Icons/flags/bi.png b/src/icons/flags/bi.png similarity index 100% rename from src/Icons/flags/bi.png rename to src/icons/flags/bi.png diff --git a/src/Icons/flags/bj.png b/src/icons/flags/bj.png similarity index 100% rename from src/Icons/flags/bj.png rename to src/icons/flags/bj.png diff --git a/src/Icons/flags/bm.png b/src/icons/flags/bm.png similarity index 100% rename from src/Icons/flags/bm.png rename to src/icons/flags/bm.png diff --git a/src/Icons/flags/bn.png b/src/icons/flags/bn.png similarity index 100% rename from src/Icons/flags/bn.png rename to src/icons/flags/bn.png diff --git a/src/Icons/flags/bo.png b/src/icons/flags/bo.png similarity index 100% rename from src/Icons/flags/bo.png rename to src/icons/flags/bo.png diff --git a/src/Icons/flags/br.png b/src/icons/flags/br.png similarity index 100% rename from src/Icons/flags/br.png rename to src/icons/flags/br.png diff --git a/src/Icons/flags/bs.png b/src/icons/flags/bs.png similarity index 100% rename from src/Icons/flags/bs.png rename to src/icons/flags/bs.png diff --git a/src/Icons/flags/bt.png b/src/icons/flags/bt.png similarity index 100% rename from src/Icons/flags/bt.png rename to src/icons/flags/bt.png diff --git a/src/Icons/flags/bv.png b/src/icons/flags/bv.png similarity index 100% rename from src/Icons/flags/bv.png rename to src/icons/flags/bv.png diff --git a/src/Icons/flags/bw.png b/src/icons/flags/bw.png similarity index 100% rename from src/Icons/flags/bw.png rename to src/icons/flags/bw.png diff --git a/src/Icons/flags/by.png b/src/icons/flags/by.png similarity index 100% rename from src/Icons/flags/by.png rename to src/icons/flags/by.png diff --git a/src/Icons/flags/bz.png b/src/icons/flags/bz.png similarity index 100% rename from src/Icons/flags/bz.png rename to src/icons/flags/bz.png diff --git a/src/Icons/flags/ca.png b/src/icons/flags/ca.png similarity index 100% rename from src/Icons/flags/ca.png rename to src/icons/flags/ca.png diff --git a/src/Icons/flags/cc.png b/src/icons/flags/cc.png similarity index 100% rename from src/Icons/flags/cc.png rename to src/icons/flags/cc.png diff --git a/src/Icons/flags/cd.png b/src/icons/flags/cd.png similarity index 100% rename from src/Icons/flags/cd.png rename to src/icons/flags/cd.png diff --git a/src/Icons/flags/cf.png b/src/icons/flags/cf.png similarity index 100% rename from src/Icons/flags/cf.png rename to src/icons/flags/cf.png diff --git a/src/Icons/flags/cg.png b/src/icons/flags/cg.png similarity index 100% rename from src/Icons/flags/cg.png rename to src/icons/flags/cg.png diff --git a/src/Icons/flags/ch.png b/src/icons/flags/ch.png similarity index 100% rename from src/Icons/flags/ch.png rename to src/icons/flags/ch.png diff --git a/src/Icons/flags/ci.png b/src/icons/flags/ci.png similarity index 100% rename from src/Icons/flags/ci.png rename to src/icons/flags/ci.png diff --git a/src/Icons/flags/ck.png b/src/icons/flags/ck.png similarity index 100% rename from src/Icons/flags/ck.png rename to src/icons/flags/ck.png diff --git a/src/Icons/flags/cl.png b/src/icons/flags/cl.png similarity index 100% rename from src/Icons/flags/cl.png rename to src/icons/flags/cl.png diff --git a/src/Icons/flags/cm.png b/src/icons/flags/cm.png similarity index 100% rename from src/Icons/flags/cm.png rename to src/icons/flags/cm.png diff --git a/src/Icons/flags/cn.png b/src/icons/flags/cn.png similarity index 100% rename from src/Icons/flags/cn.png rename to src/icons/flags/cn.png diff --git a/src/Icons/flags/co.png b/src/icons/flags/co.png similarity index 100% rename from src/Icons/flags/co.png rename to src/icons/flags/co.png diff --git a/src/Icons/flags/cr.png b/src/icons/flags/cr.png similarity index 100% rename from src/Icons/flags/cr.png rename to src/icons/flags/cr.png diff --git a/src/Icons/flags/cs.png b/src/icons/flags/cs.png similarity index 100% rename from src/Icons/flags/cs.png rename to src/icons/flags/cs.png diff --git a/src/Icons/flags/cu.png b/src/icons/flags/cu.png similarity index 100% rename from src/Icons/flags/cu.png rename to src/icons/flags/cu.png diff --git a/src/Icons/flags/cv.png b/src/icons/flags/cv.png similarity index 100% rename from src/Icons/flags/cv.png rename to src/icons/flags/cv.png diff --git a/src/Icons/flags/cx.png b/src/icons/flags/cx.png similarity index 100% rename from src/Icons/flags/cx.png rename to src/icons/flags/cx.png diff --git a/src/Icons/flags/cy.png b/src/icons/flags/cy.png similarity index 100% rename from src/Icons/flags/cy.png rename to src/icons/flags/cy.png diff --git a/src/Icons/flags/cz.png b/src/icons/flags/cz.png similarity index 100% rename from src/Icons/flags/cz.png rename to src/icons/flags/cz.png diff --git a/src/Icons/flags/de.png b/src/icons/flags/de.png similarity index 100% rename from src/Icons/flags/de.png rename to src/icons/flags/de.png diff --git a/src/Icons/flags/dj.png b/src/icons/flags/dj.png similarity index 100% rename from src/Icons/flags/dj.png rename to src/icons/flags/dj.png diff --git a/src/Icons/flags/dk.png b/src/icons/flags/dk.png similarity index 100% rename from src/Icons/flags/dk.png rename to src/icons/flags/dk.png diff --git a/src/Icons/flags/dm.png b/src/icons/flags/dm.png similarity index 100% rename from src/Icons/flags/dm.png rename to src/icons/flags/dm.png diff --git a/src/Icons/flags/do.png b/src/icons/flags/do.png similarity index 100% rename from src/Icons/flags/do.png rename to src/icons/flags/do.png diff --git a/src/Icons/flags/dz.png b/src/icons/flags/dz.png similarity index 100% rename from src/Icons/flags/dz.png rename to src/icons/flags/dz.png diff --git a/src/Icons/flags/ec.png b/src/icons/flags/ec.png similarity index 100% rename from src/Icons/flags/ec.png rename to src/icons/flags/ec.png diff --git a/src/Icons/flags/ee.png b/src/icons/flags/ee.png similarity index 100% rename from src/Icons/flags/ee.png rename to src/icons/flags/ee.png diff --git a/src/Icons/flags/eg.png b/src/icons/flags/eg.png similarity index 100% rename from src/Icons/flags/eg.png rename to src/icons/flags/eg.png diff --git a/src/Icons/flags/eh.png b/src/icons/flags/eh.png similarity index 100% rename from src/Icons/flags/eh.png rename to src/icons/flags/eh.png diff --git a/src/Icons/flags/er.png b/src/icons/flags/er.png similarity index 100% rename from src/Icons/flags/er.png rename to src/icons/flags/er.png diff --git a/src/Icons/flags/es.png b/src/icons/flags/es.png similarity index 100% rename from src/Icons/flags/es.png rename to src/icons/flags/es.png diff --git a/src/Icons/flags/et.png b/src/icons/flags/et.png similarity index 100% rename from src/Icons/flags/et.png rename to src/icons/flags/et.png diff --git a/src/Icons/flags/fi.png b/src/icons/flags/fi.png similarity index 100% rename from src/Icons/flags/fi.png rename to src/icons/flags/fi.png diff --git a/src/Icons/flags/fj.png b/src/icons/flags/fj.png similarity index 100% rename from src/Icons/flags/fj.png rename to src/icons/flags/fj.png diff --git a/src/Icons/flags/fk.png b/src/icons/flags/fk.png similarity index 100% rename from src/Icons/flags/fk.png rename to src/icons/flags/fk.png diff --git a/src/Icons/flags/fm.png b/src/icons/flags/fm.png similarity index 100% rename from src/Icons/flags/fm.png rename to src/icons/flags/fm.png diff --git a/src/Icons/flags/fo.png b/src/icons/flags/fo.png similarity index 100% rename from src/Icons/flags/fo.png rename to src/icons/flags/fo.png diff --git a/src/Icons/flags/fr.png b/src/icons/flags/fr.png similarity index 100% rename from src/Icons/flags/fr.png rename to src/icons/flags/fr.png diff --git a/src/Icons/flags/ga.png b/src/icons/flags/ga.png similarity index 100% rename from src/Icons/flags/ga.png rename to src/icons/flags/ga.png diff --git a/src/Icons/flags/gb.png b/src/icons/flags/gb.png similarity index 100% rename from src/Icons/flags/gb.png rename to src/icons/flags/gb.png diff --git a/src/Icons/flags/gd.png b/src/icons/flags/gd.png similarity index 100% rename from src/Icons/flags/gd.png rename to src/icons/flags/gd.png diff --git a/src/Icons/flags/ge.png b/src/icons/flags/ge.png similarity index 100% rename from src/Icons/flags/ge.png rename to src/icons/flags/ge.png diff --git a/src/Icons/flags/gf.png b/src/icons/flags/gf.png similarity index 100% rename from src/Icons/flags/gf.png rename to src/icons/flags/gf.png diff --git a/src/Icons/flags/gh.png b/src/icons/flags/gh.png similarity index 100% rename from src/Icons/flags/gh.png rename to src/icons/flags/gh.png diff --git a/src/Icons/flags/gi.png b/src/icons/flags/gi.png similarity index 100% rename from src/Icons/flags/gi.png rename to src/icons/flags/gi.png diff --git a/src/Icons/flags/gl.png b/src/icons/flags/gl.png similarity index 100% rename from src/Icons/flags/gl.png rename to src/icons/flags/gl.png diff --git a/src/Icons/flags/gm.png b/src/icons/flags/gm.png similarity index 100% rename from src/Icons/flags/gm.png rename to src/icons/flags/gm.png diff --git a/src/Icons/flags/gn.png b/src/icons/flags/gn.png similarity index 100% rename from src/Icons/flags/gn.png rename to src/icons/flags/gn.png diff --git a/src/Icons/flags/gp.png b/src/icons/flags/gp.png similarity index 100% rename from src/Icons/flags/gp.png rename to src/icons/flags/gp.png diff --git a/src/Icons/flags/gq.png b/src/icons/flags/gq.png similarity index 100% rename from src/Icons/flags/gq.png rename to src/icons/flags/gq.png diff --git a/src/Icons/flags/gr.png b/src/icons/flags/gr.png similarity index 100% rename from src/Icons/flags/gr.png rename to src/icons/flags/gr.png diff --git a/src/Icons/flags/gs.png b/src/icons/flags/gs.png similarity index 100% rename from src/Icons/flags/gs.png rename to src/icons/flags/gs.png diff --git a/src/Icons/flags/gt.png b/src/icons/flags/gt.png similarity index 100% rename from src/Icons/flags/gt.png rename to src/icons/flags/gt.png diff --git a/src/Icons/flags/gu.png b/src/icons/flags/gu.png similarity index 100% rename from src/Icons/flags/gu.png rename to src/icons/flags/gu.png diff --git a/src/Icons/flags/gw.png b/src/icons/flags/gw.png similarity index 100% rename from src/Icons/flags/gw.png rename to src/icons/flags/gw.png diff --git a/src/Icons/flags/gy.png b/src/icons/flags/gy.png similarity index 100% rename from src/Icons/flags/gy.png rename to src/icons/flags/gy.png diff --git a/src/Icons/flags/hk.png b/src/icons/flags/hk.png similarity index 100% rename from src/Icons/flags/hk.png rename to src/icons/flags/hk.png diff --git a/src/Icons/flags/hm.png b/src/icons/flags/hm.png similarity index 100% rename from src/Icons/flags/hm.png rename to src/icons/flags/hm.png diff --git a/src/Icons/flags/hn.png b/src/icons/flags/hn.png similarity index 100% rename from src/Icons/flags/hn.png rename to src/icons/flags/hn.png diff --git a/src/Icons/flags/hr.png b/src/icons/flags/hr.png similarity index 100% rename from src/Icons/flags/hr.png rename to src/icons/flags/hr.png diff --git a/src/Icons/flags/ht.png b/src/icons/flags/ht.png similarity index 100% rename from src/Icons/flags/ht.png rename to src/icons/flags/ht.png diff --git a/src/Icons/flags/hu.png b/src/icons/flags/hu.png similarity index 100% rename from src/Icons/flags/hu.png rename to src/icons/flags/hu.png diff --git a/src/Icons/flags/icons-set-readme.txt b/src/icons/flags/icons-set-readme.txt similarity index 100% rename from src/Icons/flags/icons-set-readme.txt rename to src/icons/flags/icons-set-readme.txt diff --git a/src/Icons/flags/id.png b/src/icons/flags/id.png similarity index 100% rename from src/Icons/flags/id.png rename to src/icons/flags/id.png diff --git a/src/Icons/flags/ie.png b/src/icons/flags/ie.png similarity index 100% rename from src/Icons/flags/ie.png rename to src/icons/flags/ie.png diff --git a/src/Icons/flags/il.png b/src/icons/flags/il.png similarity index 100% rename from src/Icons/flags/il.png rename to src/icons/flags/il.png diff --git a/src/Icons/flags/in.png b/src/icons/flags/in.png similarity index 100% rename from src/Icons/flags/in.png rename to src/icons/flags/in.png diff --git a/src/Icons/flags/io.png b/src/icons/flags/io.png similarity index 100% rename from src/Icons/flags/io.png rename to src/icons/flags/io.png diff --git a/src/Icons/flags/iq.png b/src/icons/flags/iq.png similarity index 100% rename from src/Icons/flags/iq.png rename to src/icons/flags/iq.png diff --git a/src/Icons/flags/ir.png b/src/icons/flags/ir.png similarity index 100% rename from src/Icons/flags/ir.png rename to src/icons/flags/ir.png diff --git a/src/Icons/flags/is.png b/src/icons/flags/is.png similarity index 100% rename from src/Icons/flags/is.png rename to src/icons/flags/is.png diff --git a/src/Icons/flags/it.png b/src/icons/flags/it.png similarity index 100% rename from src/Icons/flags/it.png rename to src/icons/flags/it.png diff --git a/src/Icons/flags/jm.png b/src/icons/flags/jm.png similarity index 100% rename from src/Icons/flags/jm.png rename to src/icons/flags/jm.png diff --git a/src/Icons/flags/jo.png b/src/icons/flags/jo.png similarity index 100% rename from src/Icons/flags/jo.png rename to src/icons/flags/jo.png diff --git a/src/Icons/flags/jp.png b/src/icons/flags/jp.png similarity index 100% rename from src/Icons/flags/jp.png rename to src/icons/flags/jp.png diff --git a/src/Icons/flags/ke.png b/src/icons/flags/ke.png similarity index 100% rename from src/Icons/flags/ke.png rename to src/icons/flags/ke.png diff --git a/src/Icons/flags/kg.png b/src/icons/flags/kg.png similarity index 100% rename from src/Icons/flags/kg.png rename to src/icons/flags/kg.png diff --git a/src/Icons/flags/kh.png b/src/icons/flags/kh.png similarity index 100% rename from src/Icons/flags/kh.png rename to src/icons/flags/kh.png diff --git a/src/Icons/flags/ki.png b/src/icons/flags/ki.png similarity index 100% rename from src/Icons/flags/ki.png rename to src/icons/flags/ki.png diff --git a/src/Icons/flags/km.png b/src/icons/flags/km.png similarity index 100% rename from src/Icons/flags/km.png rename to src/icons/flags/km.png diff --git a/src/Icons/flags/kn.png b/src/icons/flags/kn.png similarity index 100% rename from src/Icons/flags/kn.png rename to src/icons/flags/kn.png diff --git a/src/Icons/flags/kp.png b/src/icons/flags/kp.png similarity index 100% rename from src/Icons/flags/kp.png rename to src/icons/flags/kp.png diff --git a/src/Icons/flags/kr.png b/src/icons/flags/kr.png similarity index 100% rename from src/Icons/flags/kr.png rename to src/icons/flags/kr.png diff --git a/src/Icons/flags/kw.png b/src/icons/flags/kw.png similarity index 100% rename from src/Icons/flags/kw.png rename to src/icons/flags/kw.png diff --git a/src/Icons/flags/ky.png b/src/icons/flags/ky.png similarity index 100% rename from src/Icons/flags/ky.png rename to src/icons/flags/ky.png diff --git a/src/Icons/flags/kz.png b/src/icons/flags/kz.png similarity index 100% rename from src/Icons/flags/kz.png rename to src/icons/flags/kz.png diff --git a/src/Icons/flags/la.png b/src/icons/flags/la.png similarity index 100% rename from src/Icons/flags/la.png rename to src/icons/flags/la.png diff --git a/src/Icons/flags/lb.png b/src/icons/flags/lb.png similarity index 100% rename from src/Icons/flags/lb.png rename to src/icons/flags/lb.png diff --git a/src/Icons/flags/lc.png b/src/icons/flags/lc.png similarity index 100% rename from src/Icons/flags/lc.png rename to src/icons/flags/lc.png diff --git a/src/Icons/flags/li.png b/src/icons/flags/li.png similarity index 100% rename from src/Icons/flags/li.png rename to src/icons/flags/li.png diff --git a/src/Icons/flags/lk.png b/src/icons/flags/lk.png similarity index 100% rename from src/Icons/flags/lk.png rename to src/icons/flags/lk.png diff --git a/src/Icons/flags/lr.png b/src/icons/flags/lr.png similarity index 100% rename from src/Icons/flags/lr.png rename to src/icons/flags/lr.png diff --git a/src/Icons/flags/ls.png b/src/icons/flags/ls.png similarity index 100% rename from src/Icons/flags/ls.png rename to src/icons/flags/ls.png diff --git a/src/Icons/flags/lt.png b/src/icons/flags/lt.png similarity index 100% rename from src/Icons/flags/lt.png rename to src/icons/flags/lt.png diff --git a/src/Icons/flags/lu.png b/src/icons/flags/lu.png similarity index 100% rename from src/Icons/flags/lu.png rename to src/icons/flags/lu.png diff --git a/src/Icons/flags/lv.png b/src/icons/flags/lv.png similarity index 100% rename from src/Icons/flags/lv.png rename to src/icons/flags/lv.png diff --git a/src/Icons/flags/ly.png b/src/icons/flags/ly.png similarity index 100% rename from src/Icons/flags/ly.png rename to src/icons/flags/ly.png diff --git a/src/Icons/flags/ma.png b/src/icons/flags/ma.png similarity index 100% rename from src/Icons/flags/ma.png rename to src/icons/flags/ma.png diff --git a/src/Icons/flags/mc.png b/src/icons/flags/mc.png similarity index 100% rename from src/Icons/flags/mc.png rename to src/icons/flags/mc.png diff --git a/src/Icons/flags/md.png b/src/icons/flags/md.png similarity index 100% rename from src/Icons/flags/md.png rename to src/icons/flags/md.png diff --git a/src/Icons/flags/me.png b/src/icons/flags/me.png similarity index 100% rename from src/Icons/flags/me.png rename to src/icons/flags/me.png diff --git a/src/Icons/flags/mg.png b/src/icons/flags/mg.png similarity index 100% rename from src/Icons/flags/mg.png rename to src/icons/flags/mg.png diff --git a/src/Icons/flags/mh.png b/src/icons/flags/mh.png similarity index 100% rename from src/Icons/flags/mh.png rename to src/icons/flags/mh.png diff --git a/src/Icons/flags/mk.png b/src/icons/flags/mk.png similarity index 100% rename from src/Icons/flags/mk.png rename to src/icons/flags/mk.png diff --git a/src/Icons/flags/ml.png b/src/icons/flags/ml.png similarity index 100% rename from src/Icons/flags/ml.png rename to src/icons/flags/ml.png diff --git a/src/Icons/flags/mm.png b/src/icons/flags/mm.png similarity index 100% rename from src/Icons/flags/mm.png rename to src/icons/flags/mm.png diff --git a/src/Icons/flags/mn.png b/src/icons/flags/mn.png similarity index 100% rename from src/Icons/flags/mn.png rename to src/icons/flags/mn.png diff --git a/src/Icons/flags/mo.png b/src/icons/flags/mo.png similarity index 100% rename from src/Icons/flags/mo.png rename to src/icons/flags/mo.png diff --git a/src/Icons/flags/mp.png b/src/icons/flags/mp.png similarity index 100% rename from src/Icons/flags/mp.png rename to src/icons/flags/mp.png diff --git a/src/Icons/flags/mq.png b/src/icons/flags/mq.png similarity index 100% rename from src/Icons/flags/mq.png rename to src/icons/flags/mq.png diff --git a/src/Icons/flags/mr.png b/src/icons/flags/mr.png similarity index 100% rename from src/Icons/flags/mr.png rename to src/icons/flags/mr.png diff --git a/src/Icons/flags/ms.png b/src/icons/flags/ms.png similarity index 100% rename from src/Icons/flags/ms.png rename to src/icons/flags/ms.png diff --git a/src/Icons/flags/mt.png b/src/icons/flags/mt.png similarity index 100% rename from src/Icons/flags/mt.png rename to src/icons/flags/mt.png diff --git a/src/Icons/flags/mu.png b/src/icons/flags/mu.png similarity index 100% rename from src/Icons/flags/mu.png rename to src/icons/flags/mu.png diff --git a/src/Icons/flags/mv.png b/src/icons/flags/mv.png similarity index 100% rename from src/Icons/flags/mv.png rename to src/icons/flags/mv.png diff --git a/src/Icons/flags/mw.png b/src/icons/flags/mw.png similarity index 100% rename from src/Icons/flags/mw.png rename to src/icons/flags/mw.png diff --git a/src/Icons/flags/mx.png b/src/icons/flags/mx.png similarity index 100% rename from src/Icons/flags/mx.png rename to src/icons/flags/mx.png diff --git a/src/Icons/flags/my.png b/src/icons/flags/my.png similarity index 100% rename from src/Icons/flags/my.png rename to src/icons/flags/my.png diff --git a/src/Icons/flags/mz.png b/src/icons/flags/mz.png similarity index 100% rename from src/Icons/flags/mz.png rename to src/icons/flags/mz.png diff --git a/src/Icons/flags/na.png b/src/icons/flags/na.png similarity index 100% rename from src/Icons/flags/na.png rename to src/icons/flags/na.png diff --git a/src/Icons/flags/nc.png b/src/icons/flags/nc.png similarity index 100% rename from src/Icons/flags/nc.png rename to src/icons/flags/nc.png diff --git a/src/Icons/flags/ne.png b/src/icons/flags/ne.png similarity index 100% rename from src/Icons/flags/ne.png rename to src/icons/flags/ne.png diff --git a/src/Icons/flags/nf.png b/src/icons/flags/nf.png similarity index 100% rename from src/Icons/flags/nf.png rename to src/icons/flags/nf.png diff --git a/src/Icons/flags/ng.png b/src/icons/flags/ng.png similarity index 100% rename from src/Icons/flags/ng.png rename to src/icons/flags/ng.png diff --git a/src/Icons/flags/ni.png b/src/icons/flags/ni.png similarity index 100% rename from src/Icons/flags/ni.png rename to src/icons/flags/ni.png diff --git a/src/Icons/flags/nl.png b/src/icons/flags/nl.png similarity index 100% rename from src/Icons/flags/nl.png rename to src/icons/flags/nl.png diff --git a/src/Icons/flags/no.png b/src/icons/flags/no.png similarity index 100% rename from src/Icons/flags/no.png rename to src/icons/flags/no.png diff --git a/src/Icons/flags/np.png b/src/icons/flags/np.png similarity index 100% rename from src/Icons/flags/np.png rename to src/icons/flags/np.png diff --git a/src/Icons/flags/nr.png b/src/icons/flags/nr.png similarity index 100% rename from src/Icons/flags/nr.png rename to src/icons/flags/nr.png diff --git a/src/Icons/flags/nu.png b/src/icons/flags/nu.png similarity index 100% rename from src/Icons/flags/nu.png rename to src/icons/flags/nu.png diff --git a/src/Icons/flags/nz.png b/src/icons/flags/nz.png similarity index 100% rename from src/Icons/flags/nz.png rename to src/icons/flags/nz.png diff --git a/src/Icons/flags/om.png b/src/icons/flags/om.png similarity index 100% rename from src/Icons/flags/om.png rename to src/icons/flags/om.png diff --git a/src/Icons/flags/pa.png b/src/icons/flags/pa.png similarity index 100% rename from src/Icons/flags/pa.png rename to src/icons/flags/pa.png diff --git a/src/Icons/flags/pe.png b/src/icons/flags/pe.png similarity index 100% rename from src/Icons/flags/pe.png rename to src/icons/flags/pe.png diff --git a/src/Icons/flags/pf.png b/src/icons/flags/pf.png similarity index 100% rename from src/Icons/flags/pf.png rename to src/icons/flags/pf.png diff --git a/src/Icons/flags/pg.png b/src/icons/flags/pg.png similarity index 100% rename from src/Icons/flags/pg.png rename to src/icons/flags/pg.png diff --git a/src/Icons/flags/ph.png b/src/icons/flags/ph.png similarity index 100% rename from src/Icons/flags/ph.png rename to src/icons/flags/ph.png diff --git a/src/Icons/flags/pk.png b/src/icons/flags/pk.png similarity index 100% rename from src/Icons/flags/pk.png rename to src/icons/flags/pk.png diff --git a/src/Icons/flags/pl.png b/src/icons/flags/pl.png similarity index 100% rename from src/Icons/flags/pl.png rename to src/icons/flags/pl.png diff --git a/src/Icons/flags/pm.png b/src/icons/flags/pm.png similarity index 100% rename from src/Icons/flags/pm.png rename to src/icons/flags/pm.png diff --git a/src/Icons/flags/pn.png b/src/icons/flags/pn.png similarity index 100% rename from src/Icons/flags/pn.png rename to src/icons/flags/pn.png diff --git a/src/Icons/flags/pr.png b/src/icons/flags/pr.png similarity index 100% rename from src/Icons/flags/pr.png rename to src/icons/flags/pr.png diff --git a/src/Icons/flags/ps.png b/src/icons/flags/ps.png similarity index 100% rename from src/Icons/flags/ps.png rename to src/icons/flags/ps.png diff --git a/src/Icons/flags/pt.png b/src/icons/flags/pt.png similarity index 100% rename from src/Icons/flags/pt.png rename to src/icons/flags/pt.png diff --git a/src/Icons/flags/pw.png b/src/icons/flags/pw.png similarity index 100% rename from src/Icons/flags/pw.png rename to src/icons/flags/pw.png diff --git a/src/Icons/flags/py.png b/src/icons/flags/py.png similarity index 100% rename from src/Icons/flags/py.png rename to src/icons/flags/py.png diff --git a/src/Icons/flags/qa.png b/src/icons/flags/qa.png similarity index 100% rename from src/Icons/flags/qa.png rename to src/icons/flags/qa.png diff --git a/src/Icons/flags/re.png b/src/icons/flags/re.png similarity index 100% rename from src/Icons/flags/re.png rename to src/icons/flags/re.png diff --git a/src/Icons/flags/ro.png b/src/icons/flags/ro.png similarity index 100% rename from src/Icons/flags/ro.png rename to src/icons/flags/ro.png diff --git a/src/Icons/flags/rs.png b/src/icons/flags/rs.png similarity index 100% rename from src/Icons/flags/rs.png rename to src/icons/flags/rs.png diff --git a/src/Icons/flags/ru.png b/src/icons/flags/ru.png similarity index 100% rename from src/Icons/flags/ru.png rename to src/icons/flags/ru.png diff --git a/src/Icons/flags/rw.png b/src/icons/flags/rw.png similarity index 100% rename from src/Icons/flags/rw.png rename to src/icons/flags/rw.png diff --git a/src/Icons/flags/sa.png b/src/icons/flags/sa.png similarity index 100% rename from src/Icons/flags/sa.png rename to src/icons/flags/sa.png diff --git a/src/Icons/flags/sb.png b/src/icons/flags/sb.png similarity index 100% rename from src/Icons/flags/sb.png rename to src/icons/flags/sb.png diff --git a/src/Icons/flags/sc.png b/src/icons/flags/sc.png similarity index 100% rename from src/Icons/flags/sc.png rename to src/icons/flags/sc.png diff --git a/src/Icons/flags/sd.png b/src/icons/flags/sd.png similarity index 100% rename from src/Icons/flags/sd.png rename to src/icons/flags/sd.png diff --git a/src/Icons/flags/se.png b/src/icons/flags/se.png similarity index 100% rename from src/Icons/flags/se.png rename to src/icons/flags/se.png diff --git a/src/Icons/flags/sg.png b/src/icons/flags/sg.png similarity index 100% rename from src/Icons/flags/sg.png rename to src/icons/flags/sg.png diff --git a/src/Icons/flags/sh.png b/src/icons/flags/sh.png similarity index 100% rename from src/Icons/flags/sh.png rename to src/icons/flags/sh.png diff --git a/src/Icons/flags/si.png b/src/icons/flags/si.png similarity index 100% rename from src/Icons/flags/si.png rename to src/icons/flags/si.png diff --git a/src/Icons/flags/sj.png b/src/icons/flags/sj.png similarity index 100% rename from src/Icons/flags/sj.png rename to src/icons/flags/sj.png diff --git a/src/Icons/flags/sk.png b/src/icons/flags/sk.png similarity index 100% rename from src/Icons/flags/sk.png rename to src/icons/flags/sk.png diff --git a/src/Icons/flags/sl.png b/src/icons/flags/sl.png similarity index 100% rename from src/Icons/flags/sl.png rename to src/icons/flags/sl.png diff --git a/src/Icons/flags/sm.png b/src/icons/flags/sm.png similarity index 100% rename from src/Icons/flags/sm.png rename to src/icons/flags/sm.png diff --git a/src/Icons/flags/sn.png b/src/icons/flags/sn.png similarity index 100% rename from src/Icons/flags/sn.png rename to src/icons/flags/sn.png diff --git a/src/Icons/flags/so.png b/src/icons/flags/so.png similarity index 100% rename from src/Icons/flags/so.png rename to src/icons/flags/so.png diff --git a/src/Icons/flags/sr.png b/src/icons/flags/sr.png similarity index 100% rename from src/Icons/flags/sr.png rename to src/icons/flags/sr.png diff --git a/src/Icons/flags/st.png b/src/icons/flags/st.png similarity index 100% rename from src/Icons/flags/st.png rename to src/icons/flags/st.png diff --git a/src/Icons/flags/sv.png b/src/icons/flags/sv.png similarity index 100% rename from src/Icons/flags/sv.png rename to src/icons/flags/sv.png diff --git a/src/Icons/flags/sy.png b/src/icons/flags/sy.png similarity index 100% rename from src/Icons/flags/sy.png rename to src/icons/flags/sy.png diff --git a/src/Icons/flags/sz.png b/src/icons/flags/sz.png similarity index 100% rename from src/Icons/flags/sz.png rename to src/icons/flags/sz.png diff --git a/src/Icons/flags/tc.png b/src/icons/flags/tc.png similarity index 100% rename from src/Icons/flags/tc.png rename to src/icons/flags/tc.png diff --git a/src/Icons/flags/td.png b/src/icons/flags/td.png similarity index 100% rename from src/Icons/flags/td.png rename to src/icons/flags/td.png diff --git a/src/Icons/flags/tf.png b/src/icons/flags/tf.png similarity index 100% rename from src/Icons/flags/tf.png rename to src/icons/flags/tf.png diff --git a/src/Icons/flags/tg.png b/src/icons/flags/tg.png similarity index 100% rename from src/Icons/flags/tg.png rename to src/icons/flags/tg.png diff --git a/src/Icons/flags/th.png b/src/icons/flags/th.png similarity index 100% rename from src/Icons/flags/th.png rename to src/icons/flags/th.png diff --git a/src/Icons/flags/tj.png b/src/icons/flags/tj.png similarity index 100% rename from src/Icons/flags/tj.png rename to src/icons/flags/tj.png diff --git a/src/Icons/flags/tk.png b/src/icons/flags/tk.png similarity index 100% rename from src/Icons/flags/tk.png rename to src/icons/flags/tk.png diff --git a/src/Icons/flags/tl.png b/src/icons/flags/tl.png similarity index 100% rename from src/Icons/flags/tl.png rename to src/icons/flags/tl.png diff --git a/src/Icons/flags/tm.png b/src/icons/flags/tm.png similarity index 100% rename from src/Icons/flags/tm.png rename to src/icons/flags/tm.png diff --git a/src/Icons/flags/tn.png b/src/icons/flags/tn.png similarity index 100% rename from src/Icons/flags/tn.png rename to src/icons/flags/tn.png diff --git a/src/Icons/flags/to.png b/src/icons/flags/to.png similarity index 100% rename from src/Icons/flags/to.png rename to src/icons/flags/to.png diff --git a/src/Icons/flags/tr.png b/src/icons/flags/tr.png similarity index 100% rename from src/Icons/flags/tr.png rename to src/icons/flags/tr.png diff --git a/src/Icons/flags/tt.png b/src/icons/flags/tt.png similarity index 100% rename from src/Icons/flags/tt.png rename to src/icons/flags/tt.png diff --git a/src/Icons/flags/tv.png b/src/icons/flags/tv.png similarity index 100% rename from src/Icons/flags/tv.png rename to src/icons/flags/tv.png diff --git a/src/Icons/flags/tw.png b/src/icons/flags/tw.png similarity index 100% rename from src/Icons/flags/tw.png rename to src/icons/flags/tw.png diff --git a/src/Icons/flags/tz.png b/src/icons/flags/tz.png similarity index 100% rename from src/Icons/flags/tz.png rename to src/icons/flags/tz.png diff --git a/src/Icons/flags/ua.png b/src/icons/flags/ua.png similarity index 100% rename from src/Icons/flags/ua.png rename to src/icons/flags/ua.png diff --git a/src/Icons/flags/ug.png b/src/icons/flags/ug.png similarity index 100% rename from src/Icons/flags/ug.png rename to src/icons/flags/ug.png diff --git a/src/Icons/flags/um.png b/src/icons/flags/um.png similarity index 100% rename from src/Icons/flags/um.png rename to src/icons/flags/um.png diff --git a/src/Icons/flags/us.png b/src/icons/flags/us.png similarity index 100% rename from src/Icons/flags/us.png rename to src/icons/flags/us.png diff --git a/src/Icons/flags/uy.png b/src/icons/flags/uy.png similarity index 100% rename from src/Icons/flags/uy.png rename to src/icons/flags/uy.png diff --git a/src/Icons/flags/uz.png b/src/icons/flags/uz.png similarity index 100% rename from src/Icons/flags/uz.png rename to src/icons/flags/uz.png diff --git a/src/Icons/flags/va.png b/src/icons/flags/va.png similarity index 100% rename from src/Icons/flags/va.png rename to src/icons/flags/va.png diff --git a/src/Icons/flags/vc.png b/src/icons/flags/vc.png similarity index 100% rename from src/Icons/flags/vc.png rename to src/icons/flags/vc.png diff --git a/src/Icons/flags/ve.png b/src/icons/flags/ve.png similarity index 100% rename from src/Icons/flags/ve.png rename to src/icons/flags/ve.png diff --git a/src/Icons/flags/vg.png b/src/icons/flags/vg.png similarity index 100% rename from src/Icons/flags/vg.png rename to src/icons/flags/vg.png diff --git a/src/Icons/flags/vi.png b/src/icons/flags/vi.png similarity index 100% rename from src/Icons/flags/vi.png rename to src/icons/flags/vi.png diff --git a/src/Icons/flags/vn.png b/src/icons/flags/vn.png similarity index 100% rename from src/Icons/flags/vn.png rename to src/icons/flags/vn.png diff --git a/src/Icons/flags/vu.png b/src/icons/flags/vu.png similarity index 100% rename from src/Icons/flags/vu.png rename to src/icons/flags/vu.png diff --git a/src/Icons/flags/wf.png b/src/icons/flags/wf.png similarity index 100% rename from src/Icons/flags/wf.png rename to src/icons/flags/wf.png diff --git a/src/Icons/flags/ws.png b/src/icons/flags/ws.png similarity index 100% rename from src/Icons/flags/ws.png rename to src/icons/flags/ws.png diff --git a/src/Icons/flags/ye.png b/src/icons/flags/ye.png similarity index 100% rename from src/Icons/flags/ye.png rename to src/icons/flags/ye.png diff --git a/src/Icons/flags/yt.png b/src/icons/flags/yt.png similarity index 100% rename from src/Icons/flags/yt.png rename to src/icons/flags/yt.png diff --git a/src/Icons/flags/za.png b/src/icons/flags/za.png similarity index 100% rename from src/Icons/flags/za.png rename to src/icons/flags/za.png diff --git a/src/Icons/flags/zm.png b/src/icons/flags/zm.png similarity index 100% rename from src/Icons/flags/zm.png rename to src/icons/flags/zm.png diff --git a/src/Icons/flags/zw.png b/src/icons/flags/zw.png similarity index 100% rename from src/Icons/flags/zw.png rename to src/icons/flags/zw.png diff --git a/src/Icons/loading.png b/src/icons/loading.png similarity index 100% rename from src/Icons/loading.png rename to src/icons/loading.png diff --git a/src/Icons/magnet.png b/src/icons/magnet.png similarity index 100% rename from src/Icons/magnet.png rename to src/icons/magnet.png diff --git a/src/Icons/oxygen/application-exit.png b/src/icons/oxygen/application-exit.png similarity index 100% rename from src/Icons/oxygen/application-exit.png rename to src/icons/oxygen/application-exit.png diff --git a/src/Icons/oxygen/application-rss+xml.png b/src/icons/oxygen/application-rss+xml.png similarity index 100% rename from src/Icons/oxygen/application-rss+xml.png rename to src/icons/oxygen/application-rss+xml.png diff --git a/src/Icons/oxygen/application-x-mswinurl.png b/src/icons/oxygen/application-x-mswinurl.png similarity index 100% rename from src/Icons/oxygen/application-x-mswinurl.png rename to src/icons/oxygen/application-x-mswinurl.png diff --git a/src/Icons/oxygen/checked.png b/src/icons/oxygen/checked.png similarity index 100% rename from src/Icons/oxygen/checked.png rename to src/icons/oxygen/checked.png diff --git a/src/Icons/oxygen/chronometer.png b/src/icons/oxygen/chronometer.png similarity index 100% rename from src/Icons/oxygen/chronometer.png rename to src/icons/oxygen/chronometer.png diff --git a/src/Icons/oxygen/dialog-cancel.png b/src/icons/oxygen/dialog-cancel.png similarity index 100% rename from src/Icons/oxygen/dialog-cancel.png rename to src/icons/oxygen/dialog-cancel.png diff --git a/src/Icons/oxygen/dialog-information.png b/src/icons/oxygen/dialog-information.png similarity index 100% rename from src/Icons/oxygen/dialog-information.png rename to src/icons/oxygen/dialog-information.png diff --git a/src/Icons/oxygen/dialog-warning.png b/src/icons/oxygen/dialog-warning.png similarity index 100% rename from src/Icons/oxygen/dialog-warning.png rename to src/icons/oxygen/dialog-warning.png diff --git a/src/Icons/oxygen/document-edit-verify.png b/src/icons/oxygen/document-edit-verify.png similarity index 100% rename from src/Icons/oxygen/document-edit-verify.png rename to src/icons/oxygen/document-edit-verify.png diff --git a/src/Icons/oxygen/document-edit.png b/src/icons/oxygen/document-edit.png similarity index 100% rename from src/Icons/oxygen/document-edit.png rename to src/icons/oxygen/document-edit.png diff --git a/src/Icons/oxygen/document-encrypt.png b/src/icons/oxygen/document-encrypt.png similarity index 100% rename from src/Icons/oxygen/document-encrypt.png rename to src/icons/oxygen/document-encrypt.png diff --git a/src/Icons/oxygen/document-import.png b/src/icons/oxygen/document-import.png similarity index 100% rename from src/Icons/oxygen/document-import.png rename to src/icons/oxygen/document-import.png diff --git a/src/Icons/oxygen/document-new.png b/src/icons/oxygen/document-new.png similarity index 100% rename from src/Icons/oxygen/document-new.png rename to src/icons/oxygen/document-new.png diff --git a/src/Icons/oxygen/document-properties.png b/src/icons/oxygen/document-properties.png similarity index 100% rename from src/Icons/oxygen/document-properties.png rename to src/icons/oxygen/document-properties.png diff --git a/src/Icons/oxygen/document-save.png b/src/icons/oxygen/document-save.png similarity index 100% rename from src/Icons/oxygen/document-save.png rename to src/icons/oxygen/document-save.png diff --git a/src/Icons/oxygen/download.png b/src/icons/oxygen/download.png similarity index 100% rename from src/Icons/oxygen/download.png rename to src/icons/oxygen/download.png diff --git a/src/Icons/oxygen/edit-clear-history.png b/src/icons/oxygen/edit-clear-history.png similarity index 100% rename from src/Icons/oxygen/edit-clear-history.png rename to src/icons/oxygen/edit-clear-history.png diff --git a/src/Icons/oxygen/edit-clear.png b/src/icons/oxygen/edit-clear.png similarity index 100% rename from src/Icons/oxygen/edit-clear.png rename to src/icons/oxygen/edit-clear.png diff --git a/src/Icons/oxygen/edit-copy.png b/src/icons/oxygen/edit-copy.png similarity index 100% rename from src/Icons/oxygen/edit-copy.png rename to src/icons/oxygen/edit-copy.png diff --git a/src/Icons/oxygen/edit-cut.png b/src/icons/oxygen/edit-cut.png similarity index 100% rename from src/Icons/oxygen/edit-cut.png rename to src/icons/oxygen/edit-cut.png diff --git a/src/Icons/oxygen/edit-delete.png b/src/icons/oxygen/edit-delete.png similarity index 100% rename from src/Icons/oxygen/edit-delete.png rename to src/icons/oxygen/edit-delete.png diff --git a/src/Icons/oxygen/edit-find-user.png b/src/icons/oxygen/edit-find-user.png similarity index 100% rename from src/Icons/oxygen/edit-find-user.png rename to src/icons/oxygen/edit-find-user.png diff --git a/src/Icons/oxygen/edit-find.png b/src/icons/oxygen/edit-find.png similarity index 100% rename from src/Icons/oxygen/edit-find.png rename to src/icons/oxygen/edit-find.png diff --git a/src/Icons/oxygen/edit-paste.png b/src/icons/oxygen/edit-paste.png similarity index 100% rename from src/Icons/oxygen/edit-paste.png rename to src/icons/oxygen/edit-paste.png diff --git a/src/Icons/oxygen/edit-rename.png b/src/icons/oxygen/edit-rename.png similarity index 100% rename from src/Icons/oxygen/edit-rename.png rename to src/icons/oxygen/edit-rename.png diff --git a/src/Icons/oxygen/folder-documents.png b/src/icons/oxygen/folder-documents.png similarity index 100% rename from src/Icons/oxygen/folder-documents.png rename to src/icons/oxygen/folder-documents.png diff --git a/src/Icons/oxygen/folder-new.png b/src/icons/oxygen/folder-new.png similarity index 100% rename from src/Icons/oxygen/folder-new.png rename to src/icons/oxygen/folder-new.png diff --git a/src/Icons/oxygen/folder-remote.png b/src/icons/oxygen/folder-remote.png similarity index 100% rename from src/Icons/oxygen/folder-remote.png rename to src/icons/oxygen/folder-remote.png diff --git a/src/Icons/oxygen/gear.png b/src/icons/oxygen/gear.png similarity index 100% rename from src/Icons/oxygen/gear.png rename to src/icons/oxygen/gear.png diff --git a/src/Icons/oxygen/gear32.png b/src/icons/oxygen/gear32.png similarity index 100% rename from src/Icons/oxygen/gear32.png rename to src/icons/oxygen/gear32.png diff --git a/src/Icons/oxygen/go-bottom.png b/src/icons/oxygen/go-bottom.png similarity index 100% rename from src/Icons/oxygen/go-bottom.png rename to src/icons/oxygen/go-bottom.png diff --git a/src/Icons/oxygen/go-down.png b/src/icons/oxygen/go-down.png similarity index 100% rename from src/Icons/oxygen/go-down.png rename to src/icons/oxygen/go-down.png diff --git a/src/Icons/oxygen/go-top.png b/src/icons/oxygen/go-top.png similarity index 100% rename from src/Icons/oxygen/go-top.png rename to src/icons/oxygen/go-top.png diff --git a/src/Icons/oxygen/go-up.png b/src/icons/oxygen/go-up.png similarity index 100% rename from src/Icons/oxygen/go-up.png rename to src/icons/oxygen/go-up.png diff --git a/src/Icons/oxygen/help-about.png b/src/icons/oxygen/help-about.png similarity index 100% rename from src/Icons/oxygen/help-about.png rename to src/icons/oxygen/help-about.png diff --git a/src/Icons/oxygen/help-contents.png b/src/icons/oxygen/help-contents.png similarity index 100% rename from src/Icons/oxygen/help-contents.png rename to src/icons/oxygen/help-contents.png diff --git a/src/Icons/oxygen/inode-directory.png b/src/icons/oxygen/inode-directory.png similarity index 100% rename from src/Icons/oxygen/inode-directory.png rename to src/icons/oxygen/inode-directory.png diff --git a/src/Icons/oxygen/insert-link.png b/src/icons/oxygen/insert-link.png similarity index 100% rename from src/Icons/oxygen/insert-link.png rename to src/icons/oxygen/insert-link.png diff --git a/src/Icons/oxygen/list-add.png b/src/icons/oxygen/list-add.png similarity index 100% rename from src/Icons/oxygen/list-add.png rename to src/icons/oxygen/list-add.png diff --git a/src/Icons/oxygen/list-remove.png b/src/icons/oxygen/list-remove.png similarity index 100% rename from src/Icons/oxygen/list-remove.png rename to src/icons/oxygen/list-remove.png diff --git a/src/Icons/oxygen/mail-folder-inbox.png b/src/icons/oxygen/mail-folder-inbox.png similarity index 100% rename from src/Icons/oxygen/mail-folder-inbox.png rename to src/icons/oxygen/mail-folder-inbox.png diff --git a/src/Icons/oxygen/mail-mark-read.png b/src/icons/oxygen/mail-mark-read.png similarity index 100% rename from src/Icons/oxygen/mail-mark-read.png rename to src/icons/oxygen/mail-mark-read.png diff --git a/src/Icons/oxygen/media-playback-pause.png b/src/icons/oxygen/media-playback-pause.png similarity index 100% rename from src/Icons/oxygen/media-playback-pause.png rename to src/icons/oxygen/media-playback-pause.png diff --git a/src/Icons/oxygen/media-playback-start.png b/src/icons/oxygen/media-playback-start.png similarity index 100% rename from src/Icons/oxygen/media-playback-start.png rename to src/icons/oxygen/media-playback-start.png diff --git a/src/Icons/oxygen/network-server.png b/src/icons/oxygen/network-server.png similarity index 100% rename from src/Icons/oxygen/network-server.png rename to src/icons/oxygen/network-server.png diff --git a/src/Icons/oxygen/network-wired.png b/src/icons/oxygen/network-wired.png similarity index 100% rename from src/Icons/oxygen/network-wired.png rename to src/icons/oxygen/network-wired.png diff --git a/src/Icons/oxygen/object-locked.png b/src/icons/oxygen/object-locked.png similarity index 100% rename from src/Icons/oxygen/object-locked.png rename to src/icons/oxygen/object-locked.png diff --git a/src/Icons/oxygen/preferences-desktop.png b/src/icons/oxygen/preferences-desktop.png similarity index 100% rename from src/Icons/oxygen/preferences-desktop.png rename to src/icons/oxygen/preferences-desktop.png diff --git a/src/Icons/oxygen/preferences-other.png b/src/icons/oxygen/preferences-other.png similarity index 100% rename from src/Icons/oxygen/preferences-other.png rename to src/icons/oxygen/preferences-other.png diff --git a/src/Icons/oxygen/preferences-system-network.png b/src/icons/oxygen/preferences-system-network.png similarity index 100% rename from src/Icons/oxygen/preferences-system-network.png rename to src/icons/oxygen/preferences-system-network.png diff --git a/src/Icons/oxygen/preferences-system.png b/src/icons/oxygen/preferences-system.png similarity index 100% rename from src/Icons/oxygen/preferences-system.png rename to src/icons/oxygen/preferences-system.png diff --git a/src/Icons/oxygen/preferences-web-browser-cookies.png b/src/icons/oxygen/preferences-web-browser-cookies.png similarity index 100% rename from src/Icons/oxygen/preferences-web-browser-cookies.png rename to src/icons/oxygen/preferences-web-browser-cookies.png diff --git a/src/Icons/oxygen/security-high.png b/src/icons/oxygen/security-high.png similarity index 100% rename from src/Icons/oxygen/security-high.png rename to src/icons/oxygen/security-high.png diff --git a/src/Icons/oxygen/security-low.png b/src/icons/oxygen/security-low.png similarity index 100% rename from src/Icons/oxygen/security-low.png rename to src/icons/oxygen/security-low.png diff --git a/src/Icons/oxygen/services.png b/src/icons/oxygen/services.png similarity index 100% rename from src/Icons/oxygen/services.png rename to src/icons/oxygen/services.png diff --git a/src/Icons/oxygen/system-log-out.png b/src/icons/oxygen/system-log-out.png similarity index 100% rename from src/Icons/oxygen/system-log-out.png rename to src/icons/oxygen/system-log-out.png diff --git a/src/Icons/oxygen/tab-close.png b/src/icons/oxygen/tab-close.png similarity index 100% rename from src/Icons/oxygen/tab-close.png rename to src/icons/oxygen/tab-close.png diff --git a/src/Icons/oxygen/task-attention.png b/src/icons/oxygen/task-attention.png similarity index 100% rename from src/Icons/oxygen/task-attention.png rename to src/icons/oxygen/task-attention.png diff --git a/src/Icons/oxygen/text-plain.png b/src/icons/oxygen/text-plain.png similarity index 100% rename from src/Icons/oxygen/text-plain.png rename to src/icons/oxygen/text-plain.png diff --git a/src/Icons/oxygen/tools-report-bug.png b/src/icons/oxygen/tools-report-bug.png similarity index 100% rename from src/Icons/oxygen/tools-report-bug.png rename to src/icons/oxygen/tools-report-bug.png diff --git a/src/Icons/oxygen/unavailable.png b/src/icons/oxygen/unavailable.png similarity index 100% rename from src/Icons/oxygen/unavailable.png rename to src/icons/oxygen/unavailable.png diff --git a/src/Icons/oxygen/user-group-delete.png b/src/icons/oxygen/user-group-delete.png similarity index 100% rename from src/Icons/oxygen/user-group-delete.png rename to src/icons/oxygen/user-group-delete.png diff --git a/src/Icons/oxygen/user-group-new.png b/src/icons/oxygen/user-group-new.png similarity index 100% rename from src/Icons/oxygen/user-group-new.png rename to src/icons/oxygen/user-group-new.png diff --git a/src/Icons/oxygen/view-calendar-journal.png b/src/icons/oxygen/view-calendar-journal.png similarity index 100% rename from src/Icons/oxygen/view-calendar-journal.png rename to src/icons/oxygen/view-calendar-journal.png diff --git a/src/Icons/oxygen/view-categories.png b/src/icons/oxygen/view-categories.png similarity index 100% rename from src/Icons/oxygen/view-categories.png rename to src/icons/oxygen/view-categories.png diff --git a/src/Icons/oxygen/view-filter.png b/src/icons/oxygen/view-filter.png similarity index 100% rename from src/Icons/oxygen/view-filter.png rename to src/icons/oxygen/view-filter.png diff --git a/src/Icons/oxygen/view-preview.png b/src/icons/oxygen/view-preview.png similarity index 100% rename from src/Icons/oxygen/view-preview.png rename to src/icons/oxygen/view-preview.png diff --git a/src/Icons/oxygen/view-refresh.png b/src/icons/oxygen/view-refresh.png similarity index 100% rename from src/Icons/oxygen/view-refresh.png rename to src/icons/oxygen/view-refresh.png diff --git a/src/Icons/oxygen/view-statistics.png b/src/icons/oxygen/view-statistics.png similarity index 100% rename from src/Icons/oxygen/view-statistics.png rename to src/icons/oxygen/view-statistics.png diff --git a/src/Icons/oxygen/wallet-open.png b/src/icons/oxygen/wallet-open.png similarity index 100% rename from src/Icons/oxygen/wallet-open.png rename to src/icons/oxygen/wallet-open.png diff --git a/src/Icons/oxygen/webui.png b/src/icons/oxygen/webui.png similarity index 100% rename from src/Icons/oxygen/webui.png rename to src/icons/oxygen/webui.png diff --git a/src/Icons/qBittorrent.desktop b/src/icons/qBittorrent.desktop similarity index 100% rename from src/Icons/qBittorrent.desktop rename to src/icons/qBittorrent.desktop diff --git a/src/menuicons/192x192/apps/qbittorrent.png b/src/icons/qbittorrent.png similarity index 100% rename from src/menuicons/192x192/apps/qbittorrent.png rename to src/icons/qbittorrent.png diff --git a/src/Icons/skin/arrow-right.gif b/src/icons/skin/arrow-right.gif similarity index 100% rename from src/Icons/skin/arrow-right.gif rename to src/icons/skin/arrow-right.gif diff --git a/src/Icons/skin/bg-dropdown.gif b/src/icons/skin/bg-dropdown.gif similarity index 100% rename from src/Icons/skin/bg-dropdown.gif rename to src/icons/skin/bg-dropdown.gif diff --git a/src/Icons/skin/bg-handle-horizontal.gif b/src/icons/skin/bg-handle-horizontal.gif similarity index 100% rename from src/Icons/skin/bg-handle-horizontal.gif rename to src/icons/skin/bg-handle-horizontal.gif diff --git a/src/Icons/skin/bg-header.gif b/src/icons/skin/bg-header.gif similarity index 100% rename from src/Icons/skin/bg-header.gif rename to src/icons/skin/bg-header.gif diff --git a/src/Icons/skin/bg-panel-header.gif b/src/icons/skin/bg-panel-header.gif similarity index 100% rename from src/Icons/skin/bg-panel-header.gif rename to src/icons/skin/bg-panel-header.gif diff --git a/src/Icons/skin/checking.png b/src/icons/skin/checking.png similarity index 100% rename from src/Icons/skin/checking.png rename to src/icons/skin/checking.png diff --git a/src/Icons/skin/collapse-expand.gif b/src/icons/skin/collapse-expand.gif similarity index 100% rename from src/Icons/skin/collapse-expand.gif rename to src/icons/skin/collapse-expand.gif diff --git a/src/Icons/skin/connected.png b/src/icons/skin/connected.png similarity index 100% rename from src/Icons/skin/connected.png rename to src/icons/skin/connected.png diff --git a/src/Icons/skin/disconnected.png b/src/icons/skin/disconnected.png similarity index 100% rename from src/Icons/skin/disconnected.png rename to src/icons/skin/disconnected.png diff --git a/src/Icons/skin/dock-tabs.gif b/src/icons/skin/dock-tabs.gif similarity index 100% rename from src/Icons/skin/dock-tabs.gif rename to src/icons/skin/dock-tabs.gif diff --git a/src/Icons/skin/download.png b/src/icons/skin/download.png similarity index 100% rename from src/Icons/skin/download.png rename to src/icons/skin/download.png diff --git a/src/Icons/skin/downloading.png b/src/icons/skin/downloading.png similarity index 100% rename from src/Icons/skin/downloading.png rename to src/icons/skin/downloading.png diff --git a/src/Icons/skin/error.png b/src/icons/skin/error.png similarity index 100% rename from src/Icons/skin/error.png rename to src/icons/skin/error.png diff --git a/src/Icons/skin/filteractive.png b/src/icons/skin/filteractive.png similarity index 100% rename from src/Icons/skin/filteractive.png rename to src/icons/skin/filteractive.png diff --git a/src/Icons/skin/filterall.png b/src/icons/skin/filterall.png similarity index 100% rename from src/Icons/skin/filterall.png rename to src/icons/skin/filterall.png diff --git a/src/Icons/skin/filterinactive.png b/src/icons/skin/filterinactive.png similarity index 100% rename from src/Icons/skin/filterinactive.png rename to src/icons/skin/filterinactive.png diff --git a/src/Icons/skin/firewalled.png b/src/icons/skin/firewalled.png similarity index 100% rename from src/Icons/skin/firewalled.png rename to src/icons/skin/firewalled.png diff --git a/src/Icons/skin/handle-icon-horizontal.gif b/src/icons/skin/handle-icon-horizontal.gif similarity index 100% rename from src/Icons/skin/handle-icon-horizontal.gif rename to src/icons/skin/handle-icon-horizontal.gif diff --git a/src/Icons/skin/handle-icon.gif b/src/icons/skin/handle-icon.gif similarity index 100% rename from src/Icons/skin/handle-icon.gif rename to src/icons/skin/handle-icon.gif diff --git a/src/Icons/skin/knob.gif b/src/icons/skin/knob.gif similarity index 100% rename from src/Icons/skin/knob.gif rename to src/icons/skin/knob.gif diff --git a/src/Icons/skin/logo-blank.gif b/src/icons/skin/logo-blank.gif similarity index 100% rename from src/Icons/skin/logo-blank.gif rename to src/icons/skin/logo-blank.gif diff --git a/src/Icons/skin/logo.gif b/src/icons/skin/logo.gif similarity index 100% rename from src/Icons/skin/logo.gif rename to src/icons/skin/logo.gif diff --git a/src/Icons/skin/logo2.gif b/src/icons/skin/logo2.gif similarity index 100% rename from src/Icons/skin/logo2.gif rename to src/icons/skin/logo2.gif diff --git a/src/Icons/skin/mascot.png b/src/icons/skin/mascot.png similarity index 100% rename from src/Icons/skin/mascot.png rename to src/icons/skin/mascot.png diff --git a/src/Icons/skin/paused.png b/src/icons/skin/paused.png similarity index 100% rename from src/Icons/skin/paused.png rename to src/icons/skin/paused.png diff --git a/src/Icons/skin/qbittorrent16.png b/src/icons/skin/qbittorrent16.png similarity index 100% rename from src/Icons/skin/qbittorrent16.png rename to src/icons/skin/qbittorrent16.png diff --git a/src/Icons/skin/qbittorrent22.png b/src/icons/skin/qbittorrent22.png similarity index 100% rename from src/Icons/skin/qbittorrent22.png rename to src/icons/skin/qbittorrent22.png diff --git a/src/Icons/skin/qbittorrent32.png b/src/icons/skin/qbittorrent32.png similarity index 100% rename from src/Icons/skin/qbittorrent32.png rename to src/icons/skin/qbittorrent32.png diff --git a/src/Icons/skin/qbittorrent_mono.svg b/src/icons/skin/qbittorrent_mono.svg similarity index 100% rename from src/Icons/skin/qbittorrent_mono.svg rename to src/icons/skin/qbittorrent_mono.svg diff --git a/src/Icons/skin/qbittorrent_mono_dark.png b/src/icons/skin/qbittorrent_mono_dark.png similarity index 100% rename from src/Icons/skin/qbittorrent_mono_dark.png rename to src/icons/skin/qbittorrent_mono_dark.png diff --git a/src/Icons/skin/qbittorrent_mono_light.png b/src/icons/skin/qbittorrent_mono_light.png similarity index 100% rename from src/Icons/skin/qbittorrent_mono_light.png rename to src/icons/skin/qbittorrent_mono_light.png diff --git a/src/Icons/skin/queued.png b/src/icons/skin/queued.png similarity index 100% rename from src/Icons/skin/queued.png rename to src/icons/skin/queued.png diff --git a/src/Icons/skin/ratio.png b/src/icons/skin/ratio.png similarity index 100% rename from src/Icons/skin/ratio.png rename to src/icons/skin/ratio.png diff --git a/src/Icons/skin/resumed.png b/src/icons/skin/resumed.png similarity index 100% rename from src/Icons/skin/resumed.png rename to src/icons/skin/resumed.png diff --git a/src/Icons/skin/seeding.png b/src/icons/skin/seeding.png similarity index 100% rename from src/Icons/skin/seeding.png rename to src/icons/skin/seeding.png diff --git a/src/Icons/skin/slider-area.gif b/src/icons/skin/slider-area.gif similarity index 100% rename from src/Icons/skin/slider-area.gif rename to src/icons/skin/slider-area.gif diff --git a/src/Icons/skin/spacer.gif b/src/icons/skin/spacer.gif similarity index 100% rename from src/Icons/skin/spacer.gif rename to src/icons/skin/spacer.gif diff --git a/src/Icons/skin/spinner-placeholder.gif b/src/icons/skin/spinner-placeholder.gif similarity index 100% rename from src/Icons/skin/spinner-placeholder.gif rename to src/icons/skin/spinner-placeholder.gif diff --git a/src/Icons/skin/spinner.gif b/src/icons/skin/spinner.gif similarity index 100% rename from src/Icons/skin/spinner.gif rename to src/icons/skin/spinner.gif diff --git a/src/Icons/skin/splash.png b/src/icons/skin/splash.png similarity index 100% rename from src/Icons/skin/splash.png rename to src/icons/skin/splash.png diff --git a/src/Icons/skin/stalledDL.png b/src/icons/skin/stalledDL.png similarity index 100% rename from src/Icons/skin/stalledDL.png rename to src/icons/skin/stalledDL.png diff --git a/src/Icons/skin/stalledUP.png b/src/icons/skin/stalledUP.png similarity index 100% rename from src/Icons/skin/stalledUP.png rename to src/icons/skin/stalledUP.png diff --git a/src/Icons/skin/tabs.gif b/src/icons/skin/tabs.gif similarity index 100% rename from src/Icons/skin/tabs.gif rename to src/icons/skin/tabs.gif diff --git a/src/Icons/skin/toolbox-divider.gif b/src/icons/skin/toolbox-divider.gif similarity index 100% rename from src/Icons/skin/toolbox-divider.gif rename to src/icons/skin/toolbox-divider.gif diff --git a/src/Icons/skin/toolbox-divider2.gif b/src/icons/skin/toolbox-divider2.gif similarity index 100% rename from src/Icons/skin/toolbox-divider2.gif rename to src/icons/skin/toolbox-divider2.gif diff --git a/src/Icons/skin/uploading.png b/src/icons/skin/uploading.png similarity index 100% rename from src/Icons/skin/uploading.png rename to src/icons/skin/uploading.png diff --git a/src/Icons/slow.png b/src/icons/slow.png similarity index 100% rename from src/Icons/slow.png rename to src/icons/slow.png diff --git a/src/Icons/slow_off.png b/src/icons/slow_off.png similarity index 100% rename from src/Icons/slow_off.png rename to src/icons/slow_off.png diff --git a/src/Icons/sphere.png b/src/icons/sphere.png similarity index 100% rename from src/Icons/sphere.png rename to src/icons/sphere.png diff --git a/src/Icons/sphere2.png b/src/icons/sphere2.png similarity index 100% rename from src/Icons/sphere2.png rename to src/icons/sphere2.png diff --git a/src/Icons/url.png b/src/icons/url.png similarity index 100% rename from src/Icons/url.png rename to src/icons/url.png diff --git a/src/preferences/preferences.pri b/src/preferences/preferences.pri deleted file mode 100644 index 7b9c3e777..000000000 --- a/src/preferences/preferences.pri +++ /dev/null @@ -1,15 +0,0 @@ -INCLUDEPATH += $$PWD - -!contains(DEFINES, DISABLE_GUI) { - - HEADERS += $$PWD/options_imp.h \ - $$PWD/advancedsettings.h - - SOURCES += $$PWD/options_imp.cpp - - FORMS += $$PWD/options.ui -} - -HEADERS += $$PWD/preferences.h - -SOURCES += $$PWD/preferences.cpp diff --git a/src/qmacapplication.cpp b/src/qmacapplication.cpp deleted file mode 100644 index 1361db949..000000000 --- a/src/qmacapplication.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include -#include -#include "qmacapplication.h" - -QMacApplication::QMacApplication(QString appid, int &argc, char** argv) : - QtSingleApplication(appid, argc, argv), - m_readyToProcessEvents(false) -{ - qDebug("Constructing a QMacApplication to receive file open events"); -} - -void QMacApplication::setReadyToProcessEvents() -{ - m_readyToProcessEvents = true; - if (!m_torrentsQueue.isEmpty()) { - emit newFileOpenMacEvent(m_torrentsQueue.join("|")); - m_torrentsQueue.clear(); - } -} - -bool QMacApplication::event(QEvent * ev) { - switch (ev->type()) { - case QEvent::FileOpen: - { - QString path = static_cast(ev)->file(); - if (path.isEmpty()) { - // Get the url instead - path = static_cast(ev)->url().toString(); - } - qDebug("Received a mac file open event: %s", qPrintable(path)); - if (m_readyToProcessEvents) - emit newFileOpenMacEvent(path); - else - m_torrentsQueue.append(path); - return true; - } - default: - return QtSingleApplication::event(ev); - } -} - diff --git a/src/qmacapplication.h b/src/qmacapplication.h deleted file mode 100644 index 8148a4e13..000000000 --- a/src/qmacapplication.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ -#ifndef QMACAPPLICATION_H -#define QMACAPPLICATION_H - -#include "qtsingleapplication.h" -#include - -class QMacApplication : public QtSingleApplication -{ - Q_OBJECT -public: - explicit QMacApplication(QString appid, int &argc, char** argv); - void setReadyToProcessEvents(); - -signals: - void newFileOpenMacEvent(const QString &path); - -protected: - bool event(QEvent *); - -private: - bool m_readyToProcessEvents; - QStringList m_torrentsQueue; -}; - -#endif // QMACAPPLICATION_H diff --git a/src/sessionapplication.cpp b/src/sessionapplication.cpp deleted file mode 100644 index fb834cece..000000000 --- a/src/sessionapplication.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include "sessionapplication.h" - -SessionApplication::SessionApplication(const QString &id, int &argc, char **argv) : -#ifdef Q_OS_MAC -QMacApplication(id, argc, argv) -#else -QtSingleApplication(id, argc, argv) -#endif -{} - -bool SessionApplication::notify(QObject* receiver, QEvent* event) { - try { - return QApplication::notify(receiver, event); - } catch(const std::exception& e) { - qCritical() << "Exception thrown:" << e.what() << ", receiver: " << receiver->objectName(); - receiver->dumpObjectInfo(); - } - return false; -} diff --git a/src/sessionapplication.h b/src/sessionapplication.h deleted file mode 100644 index 3ad8d4e93..000000000 --- a/src/sessionapplication.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2010 Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#ifndef SESSIONAPPLICATION_H -#define SESSIONAPPLICATION_H - -#include - -#ifdef Q_OS_MAC -#include "qmacapplication.h" -#else -#include "qtsingleapplication.h" -#endif - -#include - -class SessionApplication : -#ifdef Q_OS_MAC - public QMacApplication -#else - public QtSingleApplication -#endif -{ - Q_OBJECT - -public: - SessionApplication(const QString &id, int &argc, char **argv); - -protected: - virtual bool notify(QObject* receiver, QEvent* event); -}; - -#endif // SESSIONAPPLICATION_H diff --git a/src/src.pro b/src/src.pro index 17fc838a5..902b0231a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -3,52 +3,44 @@ TEMPLATE = app CONFIG += qt thread # Windows specific configuration -win32 { - include(../winconf.pri) -} +win32: include(../winconf.pri) # Mac specific configuration -macx { - include(../macxconf.pri) -} +macx: include(../macxconf.pri) # Unix specific configuration -unix:!macx { - include(../unixconf.pri) -} +unix:!macx: include(../unixconf.pri) # eCS(OS/2) specific configuration -os2 { - include(../os2conf.pri) -} +os2: include(../os2conf.pri) -nox { - QT -= gui - TARGET = qbittorrent-nox - DEFINES += DISABLE_GUI +nogui { + QT -= gui + DEFINES += DISABLE_GUI + TARGET = qbittorrent-nox } else { - QT += xml - CONFIG(static) { - DEFINES += QBT_STATIC_QT - QTPLUGIN += qico - } - TARGET = qbittorrent + QT += xml + CONFIG(static) { + DEFINES += QBT_STATIC_QT + QTPLUGIN += qico + } + TARGET = qbittorrent } +nowebui: DEFINES += DISABLE_WEBUI +strace_win: DEFINES += STACKTRACE_WIN QT += network - greaterThan(QT_MAJOR_VERSION, 4): QT += widgets # Vars LANG_PATH = lang -ICONS_PATH = Icons -CONFIG(debug, debug|release):message(Project is built in DEBUG mode.) -CONFIG(release, debug|release):message(Project is built in RELEASE mode.) +CONFIG(debug, debug|release): message(Project is built in DEBUG mode.) +CONFIG(release, debug|release): message(Project is built in RELEASE mode.) # Disable debug output in release mode CONFIG(release, debug|release) { - message(Disabling debug output.) - DEFINES += QT_NO_DEBUG_OUTPUT + message(Disabling debug output.) + DEFINES += QT_NO_DEBUG_OUTPUT } # VERSION DEFINES @@ -62,208 +54,59 @@ DEFINES += QT_USE_FAST_CONCATENATION QT_USE_FAST_OPERATOR_PLUS # filesystem v3 is the default. DEFINES += BOOST_FILESYSTEM_VERSION=2 -INCLUDEPATH += $$PWD +win32: DEFINES += NOMINMAX +include(app/app.pri) +include(core/core.pri) +!nowebui: include(webui/webui.pri) +!nogui: include(gui/gui.pri) # Resource files -RESOURCES += icons.qrc \ - lang.qrc \ - about.qrc - -# Source code -usesystemqtsingleapplication { - nox { - CONFIG += qtsinglecoreapplication - } else { - CONFIG += qtsingleapplication - } -} else { - nox { - include(qtsingleapp/qtsinglecoreapplication.pri) - } else { - include(qtsingleapp/qtsingleapplication.pri) - } -} - -include(qtlibtorrent/qtlibtorrent.pri) -include(webui/webui.pri) -include(tracker/tracker.pri) -include(preferences/preferences.pri) - -!nox { - include(lineedit/lineedit.pri) - include(properties/properties.pri) - include(searchengine/searchengine.pri) - include(rss/rss.pri) - include(torrentcreator/torrentcreator.pri) - include(geoip/geoip.pri) - include(powermanagement/powermanagement.pri) -} - -HEADERS += misc.h \ - fs_utils.h \ - downloadthread.h \ - stacktrace.h \ - torrentpersistentdata.h \ - filesystemwatcher.h \ - scannedfoldersmodel.h \ - qinisettings.h \ - smtp.h \ - dnsupdater.h \ - application.h \ - logger.h +RESOURCES += \ + icons.qrc \ + lang.qrc -SOURCES += main.cpp \ - downloadthread.cpp \ - scannedfoldersmodel.cpp \ - torrentpersistentdata.cpp \ - misc.cpp \ - fs_utils.cpp \ - smtp.cpp \ - dnsupdater.cpp \ - application.cpp \ - logger.cpp - -nox { - HEADERS += headlessloader.h -} else { - HEADERS += mainwindow.h\ - transferlistwidget.h \ - transferlistdelegate.h \ - transferlistfilterswidget.h \ - transferlistsortmodel.h \ - torrentcontentmodel.h \ - torrentcontentmodelitem.h \ - torrentcontentmodelfolder.h \ - torrentcontentmodelfile.h \ - torrentcontentfiltermodel.h \ - torrentcontenttreeview.h \ - deletionconfirmationdlg.h \ - statusbar.h \ - reverseresolution.h \ - ico.h \ - speedlimitdlg.h \ - about_imp.h \ - previewselect.h \ - previewlistdelegate.h \ - downloadfromurldlg.h \ - trackerlogin.h \ - hidabletabwidget.h \ - sessionapplication.h \ - torrentimportdlg.h \ - executionlog.h \ - iconprovider.h \ - updownratiodlg.h \ - loglistwidget.h \ - addnewtorrentdialog.h \ - autoexpandabledialog.h \ - statsdialog.h \ - messageboxraised.h \ - torrentfilterenum.h - - SOURCES += mainwindow.cpp \ - ico.cpp \ - transferlistwidget.cpp \ - transferlistsortmodel.cpp \ - transferlistdelegate.cpp \ - transferlistfilterswidget.cpp \ - torrentcontentmodel.cpp \ - torrentcontentmodelitem.cpp \ - torrentcontentmodelfolder.cpp \ - torrentcontentmodelfile.cpp \ - torrentcontentfiltermodel.cpp \ - torrentcontenttreeview.cpp \ - sessionapplication.cpp \ - torrentimportdlg.cpp \ - executionlog.cpp \ - previewselect.cpp \ - iconprovider.cpp \ - updownratiodlg.cpp \ - loglistwidget.cpp \ - addnewtorrentdialog.cpp \ - autoexpandabledialog.cpp \ - statsdialog.cpp \ - messageboxraised.cpp \ - statusbar.cpp \ - trackerlogin.cpp \ - speedlimitdlg.cpp - - win32 { - HEADERS += programupdater.h - SOURCES += programupdater.cpp - DEFINES += NOMINMAX - } - - macx { - HEADERS += qmacapplication.h \ - programupdater.h - - SOURCES += qmacapplication.cpp \ - programupdater.cpp - } - - FORMS += mainwindow.ui \ - about.ui \ - preview.ui \ - login.ui \ - downloadfromurldlg.ui \ - bandwidth_limit.ui \ - updownratiodlg.ui \ - confirmdeletiondlg.ui \ - torrentimportdlg.ui \ - executionlog.ui \ - addnewtorrentdialog.ui \ - autoexpandabledialog.ui \ - statsdialog.ui -} +# Translations +TRANSLATIONS = \ + $$LANG_PATH/qbittorrent_fr.ts \ + $$LANG_PATH/qbittorrent_zh.ts \ + $$LANG_PATH/qbittorrent_zh_TW.ts \ + $$LANG_PATH/qbittorrent_en.ts \ + $$LANG_PATH/qbittorrent_en_AU.ts \ + $$LANG_PATH/qbittorrent_en_GB.ts \ + $$LANG_PATH/qbittorrent_ca.ts \ + $$LANG_PATH/qbittorrent_es.ts \ + $$LANG_PATH/qbittorrent_pl.ts \ + $$LANG_PATH/qbittorrent_ko.ts \ + $$LANG_PATH/qbittorrent_de.ts \ + $$LANG_PATH/qbittorrent_nl.ts \ + $$LANG_PATH/qbittorrent_tr.ts \ + $$LANG_PATH/qbittorrent_sv.ts \ + $$LANG_PATH/qbittorrent_el.ts \ + $$LANG_PATH/qbittorrent_ru.ts \ + $$LANG_PATH/qbittorrent_uk.ts \ + $$LANG_PATH/qbittorrent_bg.ts \ + $$LANG_PATH/qbittorrent_it.ts \ + $$LANG_PATH/qbittorrent_sk.ts \ + $$LANG_PATH/qbittorrent_ro.ts \ + $$LANG_PATH/qbittorrent_pt.ts \ + $$LANG_PATH/qbittorrent_nb.ts \ + $$LANG_PATH/qbittorrent_fi.ts \ + $$LANG_PATH/qbittorrent_da.ts \ + $$LANG_PATH/qbittorrent_ja.ts \ + $$LANG_PATH/qbittorrent_hu.ts \ + $$LANG_PATH/qbittorrent_pt_BR.ts \ + $$LANG_PATH/qbittorrent_cs.ts \ + $$LANG_PATH/qbittorrent_sr.ts \ + $$LANG_PATH/qbittorrent_ar.ts \ + $$LANG_PATH/qbittorrent_hr.ts \ + $$LANG_PATH/qbittorrent_gl.ts \ + $$LANG_PATH/qbittorrent_hy.ts \ + $$LANG_PATH/qbittorrent_lt.ts \ + $$LANG_PATH/qbittorrent_ka.ts \ + $$LANG_PATH/qbittorrent_be.ts \ + $$LANG_PATH/qbittorrent_eu.ts \ + $$LANG_PATH/qbittorrent_he.ts \ + $$LANG_PATH/qbittorrent_vi.ts DESTDIR = . - -# OS specific config -OTHER_FILES += ../winconf.pri ../macxconf.pri ../unixconf.pri ../os2conf.pri -# compiler specific config -OTHER_FILES += ../winconf-mingw.pri ../winconf-msvc.pri -# version file -OTHER_FILES += ../version.pri - -# Translations -TRANSLATIONS = $$LANG_PATH/qbittorrent_fr.ts \ - $$LANG_PATH/qbittorrent_zh.ts \ - $$LANG_PATH/qbittorrent_zh_TW.ts \ - $$LANG_PATH/qbittorrent_en.ts \ - $$LANG_PATH/qbittorrent_en_AU.ts \ - $$LANG_PATH/qbittorrent_en_GB.ts \ - $$LANG_PATH/qbittorrent_ca.ts \ - $$LANG_PATH/qbittorrent_es.ts \ - $$LANG_PATH/qbittorrent_pl.ts \ - $$LANG_PATH/qbittorrent_ko.ts \ - $$LANG_PATH/qbittorrent_de.ts \ - $$LANG_PATH/qbittorrent_nl.ts \ - $$LANG_PATH/qbittorrent_tr.ts \ - $$LANG_PATH/qbittorrent_sv.ts \ - $$LANG_PATH/qbittorrent_el.ts \ - $$LANG_PATH/qbittorrent_ru.ts \ - $$LANG_PATH/qbittorrent_uk.ts \ - $$LANG_PATH/qbittorrent_bg.ts \ - $$LANG_PATH/qbittorrent_it.ts \ - $$LANG_PATH/qbittorrent_sk.ts \ - $$LANG_PATH/qbittorrent_ro.ts \ - $$LANG_PATH/qbittorrent_pt.ts \ - $$LANG_PATH/qbittorrent_nb.ts \ - $$LANG_PATH/qbittorrent_fi.ts \ - $$LANG_PATH/qbittorrent_da.ts \ - $$LANG_PATH/qbittorrent_ja.ts \ - $$LANG_PATH/qbittorrent_hu.ts \ - $$LANG_PATH/qbittorrent_pt_BR.ts \ - $$LANG_PATH/qbittorrent_cs.ts \ - $$LANG_PATH/qbittorrent_sr.ts \ - $$LANG_PATH/qbittorrent_ar.ts \ - $$LANG_PATH/qbittorrent_hr.ts \ - $$LANG_PATH/qbittorrent_gl.ts \ - $$LANG_PATH/qbittorrent_hy.ts \ - $$LANG_PATH/qbittorrent_lt.ts \ - $$LANG_PATH/qbittorrent_ka.ts \ - $$LANG_PATH/qbittorrent_be.ts \ - $$LANG_PATH/qbittorrent_eu.ts \ - $$LANG_PATH/qbittorrent_he.ts \ - $$LANG_PATH/qbittorrent_vi.ts diff --git a/src/tracker/qpeer.h b/src/tracker/qpeer.h deleted file mode 100644 index 05073ee0f..000000000 --- a/src/tracker/qpeer.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef QPEER_H -#define QPEER_H - -#include -#include - -struct QPeer { - - bool operator!=(const QPeer &other) const { - return qhash() != other.qhash(); - } - - bool operator==(const QPeer &other) const { - return qhash() == other.qhash(); - } - - QString qhash() const { - return ip+":"+QString::number(port); - } - - libtorrent::entry toEntry(bool no_peer_id) const { - libtorrent::entry::dictionary_type peer_map; - if (!no_peer_id) - peer_map["id"] = libtorrent::entry(peer_id.toStdString()); - peer_map["ip"] = libtorrent::entry(ip.toStdString()); - peer_map["port"] = libtorrent::entry(port); - return libtorrent::entry(peer_map); - } - - QString ip; - QString peer_id; - int port; -}; - -#endif // QPEER_H diff --git a/src/tracker/qtracker.cpp b/src/tracker/qtracker.cpp deleted file mode 100644 index 0e334ccda..000000000 --- a/src/tracker/qtracker.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Bittorrent Client using Qt4 and libtorrent. - * Copyright (C) 2006 Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include - -#include -#include - -#include "qtracker.h" -#include "preferences.h" -#include "httpresponsegenerator.h" -#include "httprequestparser.h" - -#include - -using namespace libtorrent; - -QTracker::QTracker(QObject *parent) : - QTcpServer(parent) -{ - Q_ASSERT(Preferences::instance()->isTrackerEnabled()); - connect(this, SIGNAL(newConnection()), this, SLOT(handlePeerConnection())); -} - -QTracker::~QTracker() { - if (isListening()) { - qDebug("Shutting down the embedded tracker..."); - close(); - } - // TODO: Store the torrent list -} - -void QTracker::handlePeerConnection() -{ - QTcpSocket *socket; - while((socket = nextPendingConnection())) - { - qDebug("QTracker: New peer connection"); - connect(socket, SIGNAL(readyRead()), SLOT(readRequest())); - } -} - -bool QTracker::start() -{ - const int listen_port = Preferences::instance()->getTrackerPort(); - // - if (isListening()) { - if (serverPort() == listen_port) { - // Already listening on the right port, just return - return true; - } - // Wrong port, closing the server - close(); - } - qDebug("Starting the embedded tracker..."); - // Listen on the predefined port - return listen(QHostAddress::Any, listen_port); -} - -void QTracker::readRequest() -{ - QTcpSocket *socket = static_cast(sender()); - QByteArray input = socket->readAll(); - //qDebug("QTracker: Raw request:\n%s", input.data()); - HttpRequest request; - if (HttpRequestParser::parse(input, request) != HttpRequestParser::NoError) { - qDebug("QTracker: Invalid HTTP Request:\n %s", qPrintable(input)); - respondInvalidRequest(socket, 100, "Invalid request type"); - return; - } - //qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString())); - // Request is correct, is it a GET request? - if (request.method != "GET") { - qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method)); - respondInvalidRequest(socket, 100, "Invalid request type"); - return; - } - if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) { - qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path)); - respondInvalidRequest(socket, 100, "Invalid request type"); - return; - } - - // OK, this is a GET request - respondToAnnounceRequest(socket, request.gets); -} - -void QTracker::respondInvalidRequest(QTcpSocket *socket, int code, QString msg) -{ - HttpResponse response(code, msg); - socket->write(HttpResponseGenerator::generate(response)); - socket->disconnectFromHost(); -} - -void QTracker::respondToAnnounceRequest(QTcpSocket *socket, - const QMap& get_parameters) -{ - TrackerAnnounceRequest annonce_req; - // IP - annonce_req.peer.ip = socket->peerAddress().toString(); - // 1. Get info_hash - if (!get_parameters.contains("info_hash")) { - qDebug("QTracker: Missing info_hash"); - respondInvalidRequest(socket, 101, "Missing info_hash"); - return; - } - annonce_req.info_hash = get_parameters.value("info_hash"); - // info_hash cannot be longer than 20 bytes - /*if (annonce_req.info_hash.toLatin1().length() > 20) { - qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length()); - respondInvalidRequest(socket, 150, "Invalid infohash"); - return; - }*/ - // 2. Get peer ID - if (!get_parameters.contains("peer_id")) { - qDebug("QTracker: Missing peer_id"); - respondInvalidRequest(socket, 102, "Missing peer_id"); - return; - } - annonce_req.peer.peer_id = get_parameters.value("peer_id"); - // peer_id cannot be longer than 20 bytes - /*if (annonce_req.peer.peer_id.length() > 20) { - qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id)); - respondInvalidRequest(socket, 151, "Invalid peerid"); - return; - }*/ - // 3. Get port - if (!get_parameters.contains("port")) { - qDebug("QTracker: Missing port"); - respondInvalidRequest(socket, 103, "Missing port"); - return; - } - bool ok = false; - annonce_req.peer.port = get_parameters.value("port").toInt(&ok); - if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) { - qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port); - respondInvalidRequest(socket, 103, "Missing port"); - return; - } - // 4. Get event - annonce_req.event = ""; - if (get_parameters.contains("event")) { - annonce_req.event = get_parameters.value("event"); - qDebug("QTracker: event is %s", qPrintable(annonce_req.event)); - } - // 5. Get numwant - annonce_req.numwant = 50; - if (get_parameters.contains("numwant")) { - int tmp = get_parameters.value("numwant").toInt(); - if (tmp > 0) { - qDebug("QTracker: numwant=%d", tmp); - annonce_req.numwant = tmp; - } - } - // 6. no_peer_id (extension) - annonce_req.no_peer_id = false; - if (get_parameters.contains("no_peer_id")) { - annonce_req.no_peer_id = true; - } - // 7. TODO: support "compact" extension - // Done parsing, now let's reply - if (m_torrents.contains(annonce_req.info_hash)) { - if (annonce_req.event == "stopped") { - qDebug("QTracker: Peer stopped downloading, deleting it from the list"); - m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash()); - return; - } - } else { - // Unknown torrent - if (m_torrents.size() == MAX_TORRENTS) { - // Reached max size, remove a random torrent - m_torrents.erase(m_torrents.begin()); - } - } - // Register the user - PeerList peers = m_torrents.value(annonce_req.info_hash); - if (peers.size() == MAX_PEERS_PER_TORRENT) { - // Too many peers, remove a random one - peers.erase(peers.begin()); - } - peers[annonce_req.peer.qhash()] = annonce_req.peer; - m_torrents[annonce_req.info_hash] = peers; - // Reply - ReplyWithPeerList(socket, annonce_req); -} - -void QTracker::ReplyWithPeerList(QTcpSocket *socket, const TrackerAnnounceRequest &annonce_req) -{ - // Prepare the entry for bencoding - entry::dictionary_type reply_dict; - reply_dict["interval"] = entry(ANNOUNCE_INTERVAL); - QList peers = m_torrents.value(annonce_req.info_hash).values(); - entry::list_type peer_list; - foreach (const QPeer & p, peers) { - //if (p != annonce_req.peer) - peer_list.push_back(p.toEntry(annonce_req.no_peer_id)); - } - reply_dict["peers"] = entry(peer_list); - entry reply_entry(reply_dict); - // bencode - std::vector buf; - bencode(std::back_inserter(buf), reply_entry); - QByteArray reply(&buf[0], static_cast(buf.size())); - qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData()); - // HTTP reply - HttpResponse response(200, "OK"); - response.content = reply; - socket->write(HttpResponseGenerator::generate(response)); - socket->disconnectFromHost(); -} - - diff --git a/src/tracker/tracker.pri b/src/tracker/tracker.pri deleted file mode 100644 index 5b5809a86..000000000 --- a/src/tracker/tracker.pri +++ /dev/null @@ -1,9 +0,0 @@ -INCLUDEPATH += $$PWD - -HEADERS += \ - $$PWD/qtracker.h \ - $$PWD/trackerannouncerequest.h \ - $$PWD/qpeer.h - -SOURCES += \ - $$PWD/qtracker.cpp diff --git a/src/tracker/trackerannouncerequest.h b/src/tracker/trackerannouncerequest.h deleted file mode 100644 index 273ced385..000000000 --- a/src/tracker/trackerannouncerequest.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TRACKERANNOUNCEREQUEST_H -#define TRACKERANNOUNCEREQUEST_H - -#include - -struct TrackerAnnounceRequest { - QString info_hash; - QString event; - int numwant; - QPeer peer; - // Extensions - bool no_peer_id; -}; - -#endif // TRACKERANNOUNCEREQUEST_H diff --git a/src/update_qrc_files.py b/src/update_qrc_files.py index 813ff1a05..8fda40e4e 100755 --- a/src/update_qrc_files.py +++ b/src/update_qrc_files.py @@ -45,7 +45,7 @@ lang_file.write(output) lang_file.close() # update search_engine directory -os.chdir('searchengine') +os.chdir('gui/searchengine') search_list = [] for nova_folder in ['nova/', 'nova3']: for root, dirs, files in os.walk(nova_folder): @@ -67,11 +67,11 @@ search_file = open('search.qrc', 'w') search_file.write(output) search_file.close() -os.chdir('..'); +os.chdir('../..'); # update icons files directory icons_list = [] -for root, dirs, files in os.walk('Icons'): +for root, dirs, files in os.walk('icons'): if 'skin_unused' in dirs: dirs.remove('skin_unused') for file in files: diff --git a/src/webui/abstractrequesthandler.cpp b/src/webui/abstractrequesthandler.cpp deleted file mode 100644 index 8753e9409..000000000 --- a/src/webui/abstractrequesthandler.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2012, Christophe Dumez - * - * 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 -#include -#include -#include -#include "preferences.h" -#include "webapplication.h" -#include "abstractrequesthandler.h" - -AbstractRequestHandler::AbstractRequestHandler(const HttpRequest &request, const HttpEnvironment &env, WebApplication *app) - : app_(app), session_(0), request_(request), env_(env) -{ - sessionInitialize(); - if (!sessionActive() && !isAuthNeeded()) - sessionStart(); -} - -HttpResponse AbstractRequestHandler::run() -{ - if (isBanned()) { - status(403, "Forbidden"); - print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."), CONTENT_TYPE_TXT); - } - else { - processRequest(); - } - - return response_; -} - -bool AbstractRequestHandler::isAuthNeeded() -{ - return - ( - env_.clientAddress != QHostAddress::LocalHost && - env_.clientAddress != QHostAddress::LocalHostIPv6 - ) || Preferences::instance()->isWebUiLocalAuthEnabled(); -} - -void AbstractRequestHandler::status(uint code, const QString& text) -{ - response_.status = HttpResponseStatus(code, text); -} - -void AbstractRequestHandler::header(const QString& name, const QString& value) -{ - response_.headers[name] = value; -} - -void AbstractRequestHandler::print(const QString& text, const QString& type) -{ - print_impl(text.toUtf8(), type); -} - -void AbstractRequestHandler::print(const QByteArray& data, const QString& type) -{ - print_impl(data, type); -} - -void AbstractRequestHandler::print_impl(const QByteArray& data, const QString& type) -{ - if (!response_.headers.contains(HEADER_CONTENT_TYPE)) - response_.headers[HEADER_CONTENT_TYPE] = type; - - response_.content += data; -} - -void AbstractRequestHandler::printFile(const QString& path) -{ - QByteArray data; - QString type; - - if (!app_->readFile(path, data, type)) { - status(404, "Not Found"); - return; - } - - print(data, type); -} - -void AbstractRequestHandler::sessionInitialize() -{ - app_->sessionInitialize(this); -} - -void AbstractRequestHandler::sessionStart() -{ - if (app_->sessionStart(this)) { - QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); - cookie.setPath("/"); - header(HEADER_SET_COOKIE, cookie.toRawForm()); - } -} - -void AbstractRequestHandler::sessionEnd() -{ - if (sessionActive()) { - QNetworkCookie cookie(C_SID.toUtf8(), session_->id.toUtf8()); - cookie.setPath("/"); - cookie.setExpirationDate(QDateTime::currentDateTime()); - - if (app_->sessionEnd(this)) - header(HEADER_SET_COOKIE, cookie.toRawForm()); - } -} - -bool AbstractRequestHandler::isBanned() const -{ - return app_->isBanned(this); -} - -int AbstractRequestHandler::failedAttempts() const -{ - return app_->failedAttempts(this); -} - -void AbstractRequestHandler::resetFailedAttempts() -{ - app_->resetFailedAttempts(this); -} - -void AbstractRequestHandler::increaseFailedAttempts() -{ - app_->increaseFailedAttempts(this); -} - -QString AbstractRequestHandler::saveTmpFile(const QByteArray &data) -{ - QTemporaryFile tmpfile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent")); - tmpfile.setAutoRemove(false); - if (tmpfile.open()) { - tmpfile.write(data); - tmpfile.close(); - return tmpfile.fileName(); - } - - qWarning() << "I/O Error: Could not create temporary file"; - return QString(); -} diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp new file mode 100644 index 000000000..1e13e1318 --- /dev/null +++ b/src/webui/abstractwebapplication.cpp @@ -0,0 +1,394 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2014 Vladimir Golovnev + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "preferences.h" +#include "websessiondata.h" +#include "abstractwebapplication.h" + +// UnbanTimer + +class UnbanTimer: public QTimer +{ +public: + UnbanTimer(const QHostAddress& peer_ip, QObject *parent) + : QTimer(parent), m_peerIp(peer_ip) + { + setSingleShot(true); + setInterval(BAN_TIME); + } + + inline const QHostAddress& peerIp() const { return m_peerIp; } + +private: + QHostAddress m_peerIp; +}; + +// WebSession + +struct WebSession +{ + const QString id; + uint timestamp; + WebSessionData data; + + WebSession(const QString& id) + : id(id) + { + } + + void updateTimestamp() + { + timestamp = QDateTime::currentDateTime().toTime_t(); + } +}; + +// AbstractWebApplication + +AbstractWebApplication::AbstractWebApplication(QObject *parent) + : Http::ResponseBuilder(parent) + , session_(0) +{ + QTimer *timer = new QTimer(this); + timer->setInterval(60000); // 1 min. + connect(timer, SIGNAL(timeout()), SLOT(removeInactiveSessions())); +} + +AbstractWebApplication::~AbstractWebApplication() +{ + // cleanup sessions data + qDeleteAll(sessions_); +} + +Http::Response AbstractWebApplication::processRequest(const Http::Request &request, const Http::Environment &env) +{ + request_ = request; + env_ = env; + + clear(); // clear response + + sessionInitialize(); + if (!sessionActive() && !isAuthNeeded()) + sessionStart(); + + if (isBanned()) { + status(403, "Forbidden"); + print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."), Http::CONTENT_TYPE_TXT); + } + else { + processRequest(); + } + + return response(); +} + +void AbstractWebApplication::UnbanTimerEvent() +{ + UnbanTimer* ubantimer = static_cast(sender()); + qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp().toString())); + clientFailedAttempts_.remove(ubantimer->peerIp()); + ubantimer->deleteLater(); +} + +void AbstractWebApplication::removeInactiveSessions() +{ + const uint now = QDateTime::currentDateTime().toTime_t(); + + foreach (const QString &id, sessions_.keys()) { + if ((now - sessions_[id]->timestamp) > INACTIVE_TIME) + delete sessions_.take(id); + } +} + +bool AbstractWebApplication::sessionInitialize() +{ + static const QString SID_START = QLatin1String(C_SID) + QLatin1String("="); + + if (session_ == 0) + { + QString cookie = request_.headers.value("cookie"); + //qDebug() << Q_FUNC_INFO << "cookie: " << cookie; + + QString sessionId; + int pos = cookie.indexOf(SID_START); + if (pos >= 0) { + pos += SID_START.length(); + int end = cookie.indexOf(QRegExp("[,;]"), pos); + sessionId = cookie.mid(pos, end >= 0 ? end - pos : end); + } + + // TODO: Additional session check + + if (!sessionId.isNull()) { + if (sessions_.contains(sessionId)) { + session_ = sessions_[sessionId]; + session_->updateTimestamp(); + return true; + } + else { + qDebug() << Q_FUNC_INFO << "session does not exist!"; + } + } + } + + return false; +} + +bool AbstractWebApplication::readFile(const QString& path, QByteArray &data, QString &type) +{ + QString ext = ""; + int index = path.lastIndexOf('.') + 1; + if (index > 0) + ext = path.mid(index); + + // find translated file in cache + if (translatedFiles_.contains(path)) { + data = translatedFiles_[path]; + } + else { + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + qDebug("File %s was not found!", qPrintable(path)); + return false; + } + + data = file.readAll(); + file.close(); + + // Translate the file + if ((ext == "html") || ((ext == "js") && !path.endsWith("excanvas-compressed.js"))) { + QString dataStr = QString::fromUtf8(data.constData()); + translateDocument(dataStr); + + if (path.endsWith("about.html")) + dataStr.replace("${VERSION}", VERSION); + + data = dataStr.toUtf8(); + translatedFiles_[path] = data; // cashing translated file + } + } + + type = CONTENT_TYPE_BY_EXT[ext]; + return true; +} + +WebSessionData *AbstractWebApplication::session() +{ + Q_ASSERT(session_ != 0); + return &session_->data; +} + + +QString AbstractWebApplication::generateSid() +{ + QString sid; + + qsrand(QDateTime::currentDateTime().toTime_t()); + do { + const size_t size = 6; + quint32 tmp[size]; + + for (size_t i = 0; i < size; ++i) + tmp[i] = qrand(); + + sid = QByteArray::fromRawData(reinterpret_cast(tmp), sizeof(quint32) * size).toBase64(); + } + while (sessions_.contains(sid)); + + return sid; +} + +void AbstractWebApplication::translateDocument(QString& data) +{ + const QRegExp regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR"); + const QRegExp mnemonic("\\(?&([a-zA-Z]?\\))?"); + const std::string contexts[] = { + "TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", + "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", + "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel", + "PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc", + "StatusBar" + }; + const size_t context_count = sizeof(contexts) / sizeof(contexts[0]); + int i = 0; + bool found = true; + + const QString locale = Preferences::instance()->getLocale(); + bool isTranslationNeeded = !locale.startsWith("en") || locale.startsWith("en_AU") || locale.startsWith("en_GB"); + + while(i < data.size() && found) { + i = regex.indexIn(data, i); + if (i >= 0) { + //qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data()); + QByteArray word = regex.cap(1).toUtf8(); + + QString translation = word; + if (isTranslationNeeded) { + size_t context_index = 0; + while ((context_index < context_count) && (translation == word)) { +#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) + translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, QCoreApplication::UnicodeUTF8, 1); +#else + translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, 1); +#endif + ++context_index; + } + } + // Remove keyboard shortcuts + translation.replace(mnemonic, ""); + + data.replace(i, regex.matchedLength(), translation); + i += translation.length(); + } + else { + found = false; // no more translatable strings + } + } +} + +bool AbstractWebApplication::isBanned() const +{ + return clientFailedAttempts_.value(env_.clientAddress, 0) >= MAX_AUTH_FAILED_ATTEMPTS; +} + +int AbstractWebApplication::failedAttempts() const +{ + return clientFailedAttempts_.value(env_.clientAddress, 0); +} + +void AbstractWebApplication::resetFailedAttempts() +{ + clientFailedAttempts_.remove(env_.clientAddress); +} + +void AbstractWebApplication::increaseFailedAttempts() +{ + const int nb_fail = clientFailedAttempts_.value(env_.clientAddress, 0) + 1; + + clientFailedAttempts_[env_.clientAddress] = nb_fail; + if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS) { + // Max number of failed attempts reached + // Start ban period + UnbanTimer* ubantimer = new UnbanTimer(env_.clientAddress, this); + connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent())); + ubantimer->start(); + } +} + +bool AbstractWebApplication::isAuthNeeded() +{ + return (env_.clientAddress != QHostAddress::LocalHost + && env_.clientAddress != QHostAddress::LocalHostIPv6) + || Preferences::instance()->isWebUiLocalAuthEnabled(); +} + +void AbstractWebApplication::printFile(const QString& path) +{ + QByteArray data; + QString type; + + if (!readFile(path, data, type)) { + status(404, "Not Found"); + return; + } + + print(data, type); +} + +bool AbstractWebApplication::sessionStart() +{ + if (session_ == 0) { + session_ = new WebSession(generateSid()); + session_->updateTimestamp(); + sessions_[session_->id] = session_; + + QNetworkCookie cookie(C_SID, session_->id.toUtf8()); + cookie.setPath(QLatin1String("/")); + header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); + + return true; + } + + return false; +} + +bool AbstractWebApplication::sessionEnd() +{ + if ((session_ != 0) && (sessions_.contains(session_->id))) { + QNetworkCookie cookie(C_SID, session_->id.toUtf8()); + cookie.setPath(QLatin1String("/")); + cookie.setExpirationDate(QDateTime::currentDateTime()); + + sessions_.remove(session_->id); + delete session_; + session_ = 0; + + header(Http::HEADER_SET_COOKIE, cookie.toRawForm()); + return true; + } + + return false; +} + +QString AbstractWebApplication::saveTmpFile(const QByteArray &data) +{ + QTemporaryFile tmpfile(QDir::temp().absoluteFilePath("qBT-XXXXXX.torrent")); + tmpfile.setAutoRemove(false); + if (tmpfile.open()) { + tmpfile.write(data); + tmpfile.close(); + return tmpfile.fileName(); + } + + qWarning() << "I/O Error: Could not create temporary file"; + return QString(); +} + +QStringMap AbstractWebApplication::initializeContentTypeByExtMap() +{ + QStringMap map; + + map["htm"] = Http::CONTENT_TYPE_HTML; + map["html"] = Http::CONTENT_TYPE_HTML; + map["css"] = Http::CONTENT_TYPE_CSS; + map["gif"] = Http::CONTENT_TYPE_GIF; + map["png"] = Http::CONTENT_TYPE_PNG; + map["js"] = Http::CONTENT_TYPE_JS; + + return map; +} + +const QStringMap AbstractWebApplication::CONTENT_TYPE_BY_EXT = AbstractWebApplication::initializeContentTypeByExtMap(); diff --git a/src/webui/abstractrequesthandler.h b/src/webui/abstractwebapplication.h similarity index 51% rename from src/webui/abstractrequesthandler.h rename to src/webui/abstractwebapplication.h index b279d1272..bac977fca 100644 --- a/src/webui/abstractrequesthandler.h +++ b/src/webui/abstractwebapplication.h @@ -26,64 +26,83 @@ * exception statement from your version. */ -#ifndef ABSTRACTREQUESTHANDLER_H -#define ABSTRACTREQUESTHANDLER_H +#ifndef ABSTRACTWEBAPPLICATION_H +#define ABSTRACTWEBAPPLICATION_H -#include -#include "httptypes.h" +#include +#include +#include +#include "http/types.h" +#include "http/responsebuilder.h" +#include "http/irequesthandler.h" -class WebApplication; struct WebSession; +struct WebSessionData; -class AbstractRequestHandler +const char C_SID[] = "SID"; // name of session id cookie +const int BAN_TIME = 3600000; // 1 hour +const int INACTIVE_TIME = 900; // Session inactive time (in secs = 15 min.) +const int MAX_AUTH_FAILED_ATTEMPTS = 5; + +class AbstractWebApplication : public Http::ResponseBuilder, public Http::IRequestHandler { - friend class WebApplication; + Q_OBJECT + Q_DISABLE_COPY(AbstractWebApplication) public: - AbstractRequestHandler( - const HttpRequest& request, const HttpEnvironment& env, - WebApplication* app); + explicit AbstractWebApplication(QObject *parent = 0); + virtual ~AbstractWebApplication(); - HttpResponse run(); + Http::Response processRequest(const Http::Request &request, const Http::Environment &env); protected: virtual void processRequest() = 0; - void status(uint code, const QString& text); - void header(const QString& name, const QString& value); - void print(const QString& text, const QString& type = CONTENT_TYPE_HTML); - void print(const QByteArray& data, const QString& type = CONTENT_TYPE_HTML); - void printFile(const QString& path); - - // Session management - bool sessionActive() const { return session_ != 0; } - void sessionInitialize(); - void sessionStart(); - void sessionEnd(); - - // Ban management bool isBanned() const; int failedAttempts() const; void resetFailedAttempts(); void increaseFailedAttempts(); + void printFile(const QString &path); + + // Session management + bool sessionActive() const { return session_ != 0; } + bool sessionStart(); + bool sessionEnd(); + bool isAuthNeeded(); + bool readFile(const QString &path, QByteArray &data, QString &type); + // save data to temporary file on disk and return its name (or empty string if fails) - static QString saveTmpFile(const QByteArray& data); + static QString saveTmpFile(const QByteArray &data); + + WebSessionData *session(); + Http::Request request() const { return request_; } + Http::Environment env() const { return env_; } - inline WebSession* session() { return session_; } - inline HttpRequest request() const { return request_; } - inline HttpEnvironment env() const { return env_; } +private slots: + void UnbanTimerEvent(); + void removeInactiveSessions(); private: - WebApplication* app_; - WebSession* session_; - const HttpRequest request_; - const HttpEnvironment env_; - HttpResponse response_; + // Persistent data + QMap sessions_; + QHash clientFailedAttempts_; + QMap translatedFiles_; + + // Current data + WebSession *session_; + Http::Request request_; + Http::Environment env_; + + QString generateSid(); + bool sessionInitialize(); + + static void translateDocument(QString &data); - void print_impl(const QByteArray& data, const QString& type); + static const QStringMap CONTENT_TYPE_BY_EXT; + static QStringMap initializeContentTypeByExtMap(); }; -#endif // ABSTRACTREQUESTHANDLER_H +#endif // ABSTRACTWEBAPPLICATION_H diff --git a/src/webui/extra_translations.h b/src/webui/extra_translations.h index 840228afe..460531152 100644 --- a/src/webui/extra_translations.h +++ b/src/webui/extra_translations.h @@ -33,44 +33,44 @@ // Additional translations for Web UI static const char *__TRANSLATIONS__[] = { - QT_TRANSLATE_NOOP("HttpServer", "File"), - QT_TRANSLATE_NOOP("HttpServer", "Edit"), - QT_TRANSLATE_NOOP("HttpServer", "Help"), - QT_TRANSLATE_NOOP("HttpServer", "Download Torrents from their URL or Magnet link"), - QT_TRANSLATE_NOOP("HttpServer", "Only one link per line"), - QT_TRANSLATE_NOOP("HttpServer", "Download local torrent"), - QT_TRANSLATE_NOOP("HttpServer", "Torrent files were correctly added to download list."), - QT_TRANSLATE_NOOP("HttpServer", "Point to torrent file"), - QT_TRANSLATE_NOOP("HttpServer", "Download"), - QT_TRANSLATE_NOOP("HttpServer", "Are you sure you want to delete the selected torrents from the transfer list and hard disk?"), - QT_TRANSLATE_NOOP("HttpServer", "Download rate limit must be greater than 0 or disabled."), - QT_TRANSLATE_NOOP("HttpServer", "Upload rate limit must be greater than 0 or disabled."), - QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections limit must be greater than 0 or disabled."), - QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections per torrent limit must be greater than 0 or disabled."), - QT_TRANSLATE_NOOP("HttpServer", "Maximum number of upload slots per torrent limit must be greater than 0 or disabled."), - QT_TRANSLATE_NOOP("HttpServer", "Unable to save program preferences, qBittorrent is probably unreachable."), - QT_TRANSLATE_NOOP("HttpServer", "Language"), - QT_TRANSLATE_NOOP("HttpServer", "The port used for incoming connections must be greater than 1024 and less than 65535."), - QT_TRANSLATE_NOOP("HttpServer", "The port used for the Web UI must be greater than 1024 and less than 65535."), - QT_TRANSLATE_NOOP("HttpServer", "The Web UI username must be at least 3 characters long."), - QT_TRANSLATE_NOOP("HttpServer", "The Web UI password must be at least 3 characters long."), - QT_TRANSLATE_NOOP("HttpServer", "Save"), - QT_TRANSLATE_NOOP("HttpServer", "qBittorrent client is not reachable"), - QT_TRANSLATE_NOOP("HttpServer", "HTTP Server"), - QT_TRANSLATE_NOOP("HttpServer", "The following parameters are supported:"), - QT_TRANSLATE_NOOP("HttpServer", "Torrent path"), - QT_TRANSLATE_NOOP("HttpServer", "Torrent name"), - QT_TRANSLATE_NOOP("HttpServer", "qBittorrent has been shutdown."), - QT_TRANSLATE_NOOP("HttpServer", "Unable to log in, qBittorrent is probably unreachable."), - QT_TRANSLATE_NOOP("HttpServer", "Invalid Username or Password."), - QT_TRANSLATE_NOOP("HttpServer", "Password"), - QT_TRANSLATE_NOOP("HttpServer", "Login"), - QT_TRANSLATE_NOOP("HttpServer", "qBittorrent web User Interface"), - QT_TRANSLATE_NOOP("HttpServer", "Upload Failed!") + QT_TRANSLATE_NOOP("HttpServer", "File"), + QT_TRANSLATE_NOOP("HttpServer", "Edit"), + QT_TRANSLATE_NOOP("HttpServer", "Help"), + QT_TRANSLATE_NOOP("HttpServer", "Download Torrents from their URL or Magnet link"), + QT_TRANSLATE_NOOP("HttpServer", "Only one link per line"), + QT_TRANSLATE_NOOP("HttpServer", "Download local torrent"), + QT_TRANSLATE_NOOP("HttpServer", "Torrent files were correctly added to download list."), + QT_TRANSLATE_NOOP("HttpServer", "Point to torrent file"), + QT_TRANSLATE_NOOP("HttpServer", "Download"), + QT_TRANSLATE_NOOP("HttpServer", "Are you sure you want to delete the selected torrents from the transfer list and hard disk?"), + QT_TRANSLATE_NOOP("HttpServer", "Download rate limit must be greater than 0 or disabled."), + QT_TRANSLATE_NOOP("HttpServer", "Upload rate limit must be greater than 0 or disabled."), + QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections limit must be greater than 0 or disabled."), + QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections per torrent limit must be greater than 0 or disabled."), + QT_TRANSLATE_NOOP("HttpServer", "Maximum number of upload slots per torrent limit must be greater than 0 or disabled."), + QT_TRANSLATE_NOOP("HttpServer", "Unable to save program preferences, qBittorrent is probably unreachable."), + QT_TRANSLATE_NOOP("HttpServer", "Language"), + QT_TRANSLATE_NOOP("HttpServer", "The port used for incoming connections must be greater than 1024 and less than 65535."), + QT_TRANSLATE_NOOP("HttpServer", "The port used for the Web UI must be greater than 1024 and less than 65535."), + QT_TRANSLATE_NOOP("HttpServer", "The Web UI username must be at least 3 characters long."), + QT_TRANSLATE_NOOP("HttpServer", "The Web UI password must be at least 3 characters long."), + QT_TRANSLATE_NOOP("HttpServer", "Save"), + QT_TRANSLATE_NOOP("HttpServer", "qBittorrent client is not reachable"), + QT_TRANSLATE_NOOP("HttpServer", "HTTP Server"), + QT_TRANSLATE_NOOP("HttpServer", "The following parameters are supported:"), + QT_TRANSLATE_NOOP("HttpServer", "Torrent path"), + QT_TRANSLATE_NOOP("HttpServer", "Torrent name"), + QT_TRANSLATE_NOOP("HttpServer", "qBittorrent has been shutdown."), + QT_TRANSLATE_NOOP("HttpServer", "Unable to log in, qBittorrent is probably unreachable."), + QT_TRANSLATE_NOOP("HttpServer", "Invalid Username or Password."), + QT_TRANSLATE_NOOP("HttpServer", "Password"), + QT_TRANSLATE_NOOP("HttpServer", "Login"), + QT_TRANSLATE_NOOP("HttpServer", "qBittorrent web User Interface"), + QT_TRANSLATE_NOOP("HttpServer", "Upload Failed!") }; static const struct { const char *source; const char *comment; } __COMMENTED_TRANSLATIONS__[] = { - QT_TRANSLATE_NOOP3("HttpServer", "Downloaded", "Is the file downloaded or not?") + QT_TRANSLATE_NOOP3("HttpServer", "Downloaded", "Is the file downloaded or not?") }; #endif // EXTRA_TRANSLATIONS_H diff --git a/src/webui/httpconnection.cpp b/src/webui/httpconnection.cpp deleted file mode 100644 index 3a6208bdb..000000000 --- a/src/webui/httpconnection.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2006 Ishan Arora and Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include -#include "httptypes.h" -#include "httpserver.h" -#include "httprequestparser.h" -#include "httpresponsegenerator.h" -#include "webapplication.h" -#include "requesthandler.h" -#include "httpconnection.h" - -HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *httpserver) - : QObject(httpserver), m_socket(socket) -{ - m_socket->setParent(this); - connect(m_socket, SIGNAL(readyRead()), SLOT(read())); - connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater())); -} - -HttpConnection::~HttpConnection() -{ - delete m_socket; -} - -void HttpConnection::read() -{ - m_receivedData.append(m_socket->readAll()); - - HttpRequest request; - HttpRequestParser::ErrorCode err = HttpRequestParser::parse(m_receivedData, request); - switch (err) - { - case HttpRequestParser::IncompleteRequest: - // Partial request waiting for the rest - break; - case HttpRequestParser::BadRequest: - write(HttpResponse(400, "Bad Request")); - break; - case HttpRequestParser::NoError: - HttpEnvironment env; - env.clientAddress = m_socket->peerAddress(); - HttpResponse response = RequestHandler(request, env, WebApplication::instance()).run(); - if (acceptsGzipEncoding(request.headers["accept-encoding"])) - response.headers[HEADER_CONTENT_ENCODING] = "gzip"; - write(response); - break; - } -} - -void HttpConnection::write(const HttpResponse& response) -{ - m_socket->write(HttpResponseGenerator::generate(response)); - m_socket->disconnectFromHost(); -} - -bool HttpConnection::acceptsGzipEncoding(const QString& encoding) -{ - int pos = encoding.indexOf("gzip", 0, Qt::CaseInsensitive); - if (pos == -1) - return false; - - // Let's see if there's a qvalue of 0.0 following - if (encoding[pos + 4] != ';') //there isn't, so it accepts gzip anyway - return true; - - //So let's find = and the next comma - pos = encoding.indexOf("=", pos + 4, Qt::CaseInsensitive); - int comma_pos = encoding.indexOf(",", pos, Qt::CaseInsensitive); - - QString value; - if (comma_pos == -1) - value = encoding.mid(pos + 1, comma_pos); - else - value = encoding.mid(pos + 1, comma_pos - (pos + 1)); - - if (value.toDouble() == 0.0) - return false; - - return true; -} diff --git a/src/webui/httprequestparser.cpp b/src/webui/httprequestparser.cpp deleted file mode 100644 index 8d0894d32..000000000 --- a/src/webui/httprequestparser.cpp +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2006 Ishan Arora and Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include -//#include -#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) -#include -#endif -#include -#include -#include -#include "httprequestparser.h" - -const QByteArray EOL("\r\n"); -const QByteArray EOH("\r\n\r\n"); - -inline QString unquoted(const QString& str) -{ - if ((str[0] == '\"') && (str[str.length() - 1] == '\"')) - return str.mid(1, str.length() - 2); - - return str; -} - -HttpRequestParser::ErrorCode HttpRequestParser::parse(const QByteArray& data, HttpRequest& request, uint maxContentLength) -{ - return HttpRequestParser(maxContentLength).parseHttpRequest(data, request); -} - -HttpRequestParser::HttpRequestParser(uint maxContentLength) - : maxContentLength_(maxContentLength) -{ -} - -HttpRequestParser::ErrorCode HttpRequestParser::parseHttpRequest(const QByteArray& data, HttpRequest& request) -{ - request_ = HttpRequest(); - - // Parse HTTP request header - const int header_end = data.indexOf(EOH); - if (header_end < 0) - { - qDebug() << Q_FUNC_INFO << "incomplete request"; - return IncompleteRequest; - } - - if (!parseHttpHeader(data.left(header_end))) - { - qWarning() << Q_FUNC_INFO << "header parsing error"; - return BadRequest; - } - - // Parse HTTP request message - int content_length = 0; - if (request_.headers.contains("content-length")) - { - content_length = request_.headers["content-length"].toInt(); - if (content_length > static_cast(maxContentLength_)) - { - qWarning() << Q_FUNC_INFO << "bad request: message too long"; - return BadRequest; - } - - QByteArray content = data.mid(header_end + EOH.length(), content_length); - if (content.length() < content_length) - { - qDebug() << Q_FUNC_INFO << "incomplete request"; - return IncompleteRequest; - } - - if (!parseContent(content)) - { - qWarning() << Q_FUNC_INFO << "message parsing error"; - return BadRequest; - } - } - -// qDebug() << Q_FUNC_INFO; -// qDebug() << "HTTP Request header:"; -// qDebug() << data.left(header_end) << "\n"; - - request = request_; - return NoError; -} - -bool HttpRequestParser::parseStartingLine(const QString &line) -{ - const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$"); - - if (rx.indexIn(line.trimmed()) >= 0) - { - request_.method = rx.cap(1); - - QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1()); - request_.path = url.path(); // Path - - // Parse GET parameters -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - QListIterator > i(url.queryItems()); -#else - QListIterator > i(QUrlQuery(url).queryItems()); -#endif - while (i.hasNext()) - { - QPair pair = i.next(); - request_.gets[pair.first] = pair.second; - } - - return true; - } - - qWarning() << Q_FUNC_INFO << "invalid http header:" << line; - return false; -} - -bool HttpRequestParser::parseHeaderLine(const QString &line, QPair& out) -{ - int i = line.indexOf(QLatin1Char(':')); - if (i == -1) - { - qWarning() << Q_FUNC_INFO << "invalid http header:" << line; - return false; - } - - out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed()); - return true; -} - -bool HttpRequestParser::parseHttpHeader(const QByteArray &data) -{ - QString str = QString::fromUtf8(data); - QStringList lines = str.trimmed().split(EOL); - - QStringList headerLines; - foreach (const QString& line, lines) - { - if (line[0].isSpace()) // header line continuation - { - if (!headerLines.isEmpty()) // really continuation - { - headerLines.last() += QLatin1Char(' '); - headerLines.last() += line.trimmed(); - } - } - else - { - headerLines.append(line); - } - } - - if (headerLines.isEmpty()) - return false; // Empty header - - QStringList::Iterator it = headerLines.begin(); - if (!parseStartingLine(*it)) - return false; - - ++it; - for (; it != headerLines.end(); ++it) - { - QPair header; - if (!parseHeaderLine(*it, header)) - return false; - - request_.headers[header.first] = header.second; - } - - return true; -} - -QList HttpRequestParser::splitMultipartData(const QByteArray& data, const QByteArray& boundary) -{ - QList ret; - QByteArray sep = boundary + EOL; - const int sepLength = sep.size(); - - int start = 0, end = 0; - if ((end = data.indexOf(sep, start)) >= 0) - { - start = end + sepLength; // skip first boundary - - while ((end = data.indexOf(sep, start)) >= 0) - { - ret << data.mid(start, end - start); - start = end + sepLength; - } - - // last or single part - sep = boundary + "--" + EOL; - if ((end = data.indexOf(sep, start)) >= 0) - ret << data.mid(start, end - start); - } - - return ret; -} - -bool HttpRequestParser::parseContent(const QByteArray& data) -{ - // Parse message content - qDebug() << Q_FUNC_INFO << "Content-Length: " << request_.headers["content-length"]; - qDebug() << Q_FUNC_INFO << "data.size(): " << data.size(); - - // Parse url-encoded POST data - if (request_.headers["content-type"].startsWith("application/x-www-form-urlencoded")) - { - QUrl url; -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - url.setEncodedQuery(data); - QListIterator > i(url.queryItems()); -#else - url.setQuery(data); - QListIterator > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded)); -#endif - while (i.hasNext()) - { - QPair pair = i.next(); - request_.posts[pair.first.toLower()] = pair.second; - } - - return true; - } - - // Parse multipart/form data (torrent file) - /** - data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5") - ---cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 -Content-Disposition: form-data; name=\"Filename\" - -PB020344.torrent ---cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 -Content-Disposition: form-data; name=\"torrentfile"; filename=\"PB020344.torrent\" -Content-Type: application/x-bittorrent - -BINARY DATA IS HERE ---cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5 -Content-Disposition: form-data; name=\"Upload\" - -Submit Query ---cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5-- -**/ - QString content_type = request_.headers["content-type"]; - if (content_type.startsWith("multipart/form-data")) - { - const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\""); - const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)"); - - QByteArray boundary; - if (boundaryRegexQuoted.indexIn(content_type) < 0) - { - if (boundaryRegexNotQuoted.indexIn(content_type) < 0) - { - qWarning() << "Could not find boundary in multipart/form-data header!"; - return false; - } - else - { - boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1(); - } - } - else - { - boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1(); - } - - qDebug() << "Boundary is " << boundary; - QList parts = splitMultipartData(data, boundary); - qDebug() << parts.size() << "parts in data"; - - foreach (const QByteArray& part, parts) - { - if (!parseFormData(part)) - return false; - } - - return true; - } - - qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(content_type); - return false; -} - -bool HttpRequestParser::parseFormData(const QByteArray& data) -{ - // Parse form data header - const int header_end = data.indexOf(EOH); - if (header_end < 0) - { - qDebug() << "Invalid form data: \n" << data; - return false; - } - - QString header_str = QString::fromUtf8(data.left(header_end)); - QStringList lines = header_str.trimmed().split(EOL); - QStringMap headers; - foreach (const QString& line, lines) - { - QPair header; - if (!parseHeaderLine(line, header)) - return false; - - headers[header.first] = header.second; - } - - QStringMap disposition; - if (!headers.contains("content-disposition") || - !parseHeaderValue(headers["content-disposition"], disposition) || - !disposition.contains("name")) - { - qDebug() << "Invalid form data header: \n" << header_str; - return false; - } - - if (disposition.contains("filename")) - { - UploadedFile ufile; - ufile.filename = disposition["filename"]; - ufile.type = disposition["content-type"]; - ufile.data = data.mid(header_end + EOH.length()); - - request_.files[disposition["name"]] = ufile; - } - else - { - request_.posts[disposition["name"]] = QString::fromUtf8(data.mid(header_end + EOH.length())); - } - - return true; -} - -bool HttpRequestParser::parseHeaderValue(const QString& value, QStringMap& out) -{ - QStringList items = value.split(QLatin1Char(';')); - out[""] = items[0]; - - for (QStringList::size_type i = 1; i < items.size(); ++i) - { - int pos = items[i].indexOf("="); - if (pos < 0) - return false; - - out[items[i].left(pos).trimmed()] = unquoted(items[i].mid(pos + 1).trimmed()); - } - - return true; -} diff --git a/src/webui/httpresponsegenerator.cpp b/src/webui/httpresponsegenerator.cpp deleted file mode 100644 index 02842c514..000000000 --- a/src/webui/httpresponsegenerator.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2006 Ishan Arora and Christophe Dumez - * - * 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. - * - * Contact : chris@qbittorrent.org - */ - -#include -#include "httpresponsegenerator.h" - -bool gCompress(QByteArray data, QByteArray& dest_buffer); - -QByteArray HttpResponseGenerator::generate(HttpResponse response) -{ - if (response.headers[HEADER_CONTENT_ENCODING] == "gzip") - { - // A gzip seems to have 23 bytes overhead. - // Also "Content-Encoding: gzip\r\n" is 26 bytes long - // So we only benefit from gzip if the message is bigger than 23+26 = 49 - // If the message is smaller than 49 bytes we actually send MORE data if we gzip - QByteArray dest_buf; - if ((response.content.size() > 49) && (gCompress(response.content, dest_buf))) - { - response.content = dest_buf; - } - else - { - response.headers.remove(HEADER_CONTENT_ENCODING); - } - } - - if (response.content.length() > 0) - response.headers[HEADER_CONTENT_LENGTH] = QString::number(response.content.length()); - - QString ret(QLatin1String("HTTP/1.1 %1 %2\r\n%3\r\n")); - - QString header; - foreach (const QString& key, response.headers.keys()) - header += QString("%1: %2\r\n").arg(key).arg(response.headers[key]); - - ret = ret.arg(response.status.code).arg(response.status.text).arg(header); - -// qDebug() << Q_FUNC_INFO; -// qDebug() << "HTTP Response header:"; -// qDebug() << ret; - - return ret.toUtf8() + response.content; -} - -bool gCompress(QByteArray data, QByteArray& dest_buffer) -{ - static const int BUFSIZE = 128 * 1024; - char tmp_buf[BUFSIZE]; - int ret; - - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.next_in = reinterpret_cast(data.data()); - strm.avail_in = data.length(); - strm.next_out = reinterpret_cast(tmp_buf); - strm.avail_out = BUFSIZE; - - //windowBits = 15+16 to enable gzip - //From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits - //to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper. - ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15+16, 8, Z_DEFAULT_STRATEGY); - - if (ret != Z_OK) - return false; - - while (strm.avail_in != 0) - { - ret = deflate(&strm, Z_NO_FLUSH); - if (ret != Z_OK) - return false; - - if (strm.avail_out == 0) - { - dest_buffer.append(tmp_buf, BUFSIZE); - strm.next_out = reinterpret_cast(tmp_buf); - strm.avail_out = BUFSIZE; - } - } - - int deflate_res = Z_OK; - while (deflate_res == Z_OK) - { - if (strm.avail_out == 0) - { - dest_buffer.append(tmp_buf, BUFSIZE); - strm.next_out = reinterpret_cast(tmp_buf); - strm.avail_out = BUFSIZE; - } - - deflate_res = deflate(&strm, Z_FINISH); - } - - if (deflate_res != Z_STREAM_END) - return false; - - dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out); - deflateEnd(&strm); - - return true; -} diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index 23c2f7e59..61e52a3a2 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -333,6 +333,6 @@ void prefjson::setPreferences(const QString& json) pref->setDynDNSPassword(m["dyndns_password"].toString()); if (m.contains("dyndns_domain")) pref->setDynDomainName(m["dyndns_domain"].toString()); - // Reload preferences - QBtSession::instance()->configureSession(); + // Save preferences + pref->save(); } diff --git a/src/webui/requesthandler.cpp b/src/webui/requesthandler.cpp deleted file mode 100644 index 8f7eaf068..000000000 --- a/src/webui/requesthandler.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * Copyright (C) 2012, Christophe Dumez - * - * 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 -#ifdef DISABLE_GUI -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#ifndef DISABLE_GUI -#include "iconprovider.h" -#endif -#include "misc.h" -#include "fs_utils.h" -#include "preferences.h" -#include "btjson.h" -#include "prefjson.h" -#include "qbtsession.h" -#include "requesthandler.h" -#include "webapplication.h" - -using namespace libtorrent; - -static const int API_VERSION = 2; -static const int API_VERSION_MIN = 2; - -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] = &RequestHandler::action_##scope##_##action - -QMap > RequestHandler::initializeActions() -{ - QMap > 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, propertiesFiles); - ADD_ACTION(sync, maindata); - 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, getTorrentUpLimit); - ADD_ACTION(command, getTorrentDlLimit); - ADD_ACTION(command, setTorrentUpLimit); - ADD_ACTION(command, setTorrentDlLimit); - ADD_ACTION(command, alternativeSpeedLimitsEnabled); - ADD_ACTION(command, toggleAlternativeSpeedLimits); - ADD_ACTION(command, toggleSequentialDownload); - ADD_ACTION(command, toggleFirstLastPiecePrio); - 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, recheck); - ADD_ACTION(version, api); - ADD_ACTION(version, api_min); - ADD_ACTION(version, qbittorrent); - - return actions; -} - -#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; \ - } \ - } - -void RequestHandler::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 RequestHandler::action_public_webui() -{ - if (!sessionActive()) - printFile(PRIVATE_FOLDER + "login.html"); - else - printFile(PRIVATE_FOLDER + "index.html"); -} - -void RequestHandler::action_public_login() -{ - const Preferences* const pref = Preferences::instance(); - QCryptographicHash md5(QCryptographicHash::Md5); - - md5.addData(request().posts["password"].toLocal8Bit()); - QString pass = md5.result().toHex(); - - bool equalUser = misc::slowEquals(request().posts["username"].toUtf8(), pref->getWebUiUsername().toUtf8()); - bool equalPass = misc::slowEquals(pass.toUtf8(), pref->getWebUiPassword().toUtf8()); - - if (equalUser && equalPass) { - sessionStart(); - print(QByteArray("Ok."), CONTENT_TYPE_TXT); - } - else { - QString addr = env().clientAddress.toString(); - increaseFailedAttempts(); - qDebug("client IP: %s (%d failed attempts)", qPrintable(addr), failedAttempts()); - print(QByteArray("Fails."), CONTENT_TYPE_TXT); - } -} - -void RequestHandler::action_public_logout() -{ - CHECK_URI(0); - sessionEnd(); -} - -void RequestHandler::action_public_theme() -{ - if (args_.size() != 1) { - status(404, "Not Found"); - return; - } - -#ifdef DISABLE_GUI - QString url = ":/Icons/oxygen/" + args_.front() + ".png"; -#else - QString url = IconProvider::instance()->getIconPath(args_.front()); -#endif - qDebug() << Q_FUNC_INFO << "There icon:" << url; - - printFile(url); - header(HEADER_CACHE_CONTROL, MAX_AGE_MONTH); -} - -void RequestHandler::action_public_images() -{ - const QString path = ":/Icons/" + args_.join("/"); - printFile(path); - header(HEADER_CACHE_CONTROL, MAX_AGE_MONTH); -} - -// GET params: -// - filter (string): all, downloading, completed, paused, active, inactive -// - label (string): torrent label for filtering by it (empty string means "unlabeled"; no "label" param presented means "any label") -// - 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 RequestHandler::action_query_torrents() -{ - CHECK_URI(0); - const QStringMap& gets = request().gets; - - print(btjson::getTorrents( - gets["filter"], gets["label"], gets["sort"], gets["reverse"] == "true", - gets["limit"].toInt(), gets["offset"].toInt() - ), CONTENT_TYPE_JS); -} - -void RequestHandler::action_query_preferences() -{ - CHECK_URI(0); - print(prefjson::getPreferences(), CONTENT_TYPE_JS); -} - -void RequestHandler::action_query_transferInfo() -{ - CHECK_URI(0); - print(btjson::getTransferInfo(), CONTENT_TYPE_JS); -} - -void RequestHandler::action_query_propertiesGeneral() -{ - CHECK_URI(1); - print(btjson::getPropertiesForTorrent(args_.front()), CONTENT_TYPE_JS); -} - -void RequestHandler::action_query_propertiesTrackers() -{ - CHECK_URI(1); - print(btjson::getTrackersForTorrent(args_.front()), CONTENT_TYPE_JS); -} - -void RequestHandler::action_query_propertiesFiles() -{ - CHECK_URI(1); - print(btjson::getFilesForTorrent(args_.front()), CONTENT_TYPE_JS); -} - -// GET param: -// - rid (int): last response id -void RequestHandler::action_sync_maindata() -{ - CHECK_URI(0); - print(btjson::getSyncMainData(request().gets["rid"].toInt(), session()->syncMainDataLastResponse, session()->syncMainDataLastAcceptedResponse)); -} - -void RequestHandler::action_version_api() -{ - CHECK_URI(0); - print(QString::number(API_VERSION), CONTENT_TYPE_TXT); -} - -void RequestHandler::action_version_api_min() -{ - CHECK_URI(0); - print(QString::number(API_VERSION_MIN), CONTENT_TYPE_TXT); -} - -void RequestHandler::action_version_qbittorrent() -{ - CHECK_URI(0); - print(QString(VERSION), CONTENT_TYPE_TXT); -} - -void RequestHandler::action_command_shutdown() -{ - qDebug() << "Shutdown request from Web UI"; - // Special case handling for shutdown, we - // need to reply to the Web UI before - // actually shutting down. - - CHECK_URI(0); - QTimer::singleShot(0, qApp, SLOT(quit())); -} - -void RequestHandler::action_command_download() -{ - CHECK_URI(0); - CHECK_PARAMETERS("urls"); - QString urls = request().posts["urls"]; - QStringList list = urls.split('\n'); - - foreach (QString url, list) { - url = url.trimmed(); - if (!url.isEmpty()) { - if (url.startsWith("bc://bt/", Qt::CaseInsensitive)) { - qDebug("Converting bc link to magnet link"); - url = misc::bcLinkToMagnet(url); - } - else if (url.startsWith("magnet:", Qt::CaseInsensitive)) { - QBtSession::instance()->addMagnetSkipAddDlg(url); - } - else { - qDebug("Downloading url: %s", qPrintable(url)); - QBtSession::instance()->downloadUrlAndSkipDialog(url); - } - } - } -} - -void RequestHandler::action_command_upload() -{ - qDebug() << Q_FUNC_INFO; - CHECK_URI(0); - - foreach(const UploadedFile& torrent, request().files) { - QString filePath = saveTmpFile(torrent.data); - - if (!filePath.isEmpty()) { - QTorrentHandle h = QBtSession::instance()->addTorrent(filePath); - if (!h.is_valid()) { - status(415, "Internal Server Error"); - print(QObject::tr("Error: '%1' is not a valid torrent file.\n").arg(torrent.filename), CONTENT_TYPE_TXT); - } - // Clean up - fsutils::forceRemove(filePath); - } - else { - 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."), CONTENT_TYPE_TXT); - } - } -} - -void RequestHandler::action_command_addTrackers() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash" << "urls"); - QString hash = request().posts["hash"]; - - if (!hash.isEmpty()) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid() && h.has_metadata()) { - QString urls = request().posts["urls"]; - QStringList list = urls.split('\n'); - - foreach (const QString& url, list) { - announce_entry e(url.toStdString()); - h.add_tracker(e); - } - } - } -} - -void RequestHandler::action_command_resumeAll() -{ - CHECK_URI(0); - QBtSession::instance()->resumeAllTorrents(); -} - -void RequestHandler::action_command_pauseAll() -{ - CHECK_URI(0); - QBtSession::instance()->pauseAllTorrents(); -} - -void RequestHandler::action_command_resume() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash"); - QBtSession::instance()->resumeTorrent(request().posts["hash"]); -} - -void RequestHandler::action_command_pause() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash"); - QBtSession::instance()->pauseTorrent(request().posts["hash"]); -} - -void RequestHandler::action_command_setPreferences() -{ - CHECK_URI(0); - CHECK_PARAMETERS("json"); - prefjson::setPreferences(request().posts["json"]); -} - -void RequestHandler::action_command_setFilePrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash" << "id" << "priority"); - QString hash = request().posts["hash"]; - int file_id = request().posts["id"].toInt(); - int priority = request().posts["priority"].toInt(); - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid() && h.has_metadata()) - h.file_priority(file_id, priority); -} - -void RequestHandler::action_command_getGlobalUpLimit() -{ - CHECK_URI(0); - print(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit)); -} - -void RequestHandler::action_command_getGlobalDlLimit() -{ - CHECK_URI(0); - print(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit)); -} - -void RequestHandler::action_command_setGlobalUpLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("limit"); - qlonglong limit = request().posts["limit"].toLongLong(); - if (limit == 0) limit = -1; - - QBtSession::instance()->setUploadRateLimit(limit); - if (Preferences::instance()->isAltBandwidthEnabled()) - Preferences::instance()->setAltGlobalUploadLimit(limit / 1024.); - else - Preferences::instance()->setGlobalUploadLimit(limit / 1024.); -} - -void RequestHandler::action_command_setGlobalDlLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("limit"); - qlonglong limit = request().posts["limit"].toLongLong(); - if (limit == 0) limit = -1; - - QBtSession::instance()->setDownloadRateLimit(limit); - if (Preferences::instance()->isAltBandwidthEnabled()) - Preferences::instance()->setAltGlobalDownloadLimit(limit / 1024.); - else - Preferences::instance()->setGlobalDownloadLimit(limit / 1024.); -} - -void RequestHandler::action_command_getTorrentUpLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash"); - QString hash = request().posts["hash"]; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid()) - print(QByteArray::number(h.upload_limit())); -} - -void RequestHandler::action_command_getTorrentDlLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash"); - QString hash = request().posts["hash"]; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid()) - print(QByteArray::number(h.download_limit())); -} - -void RequestHandler::action_command_setTorrentUpLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash" << "limit"); - QString hash = request().posts["hash"]; - qlonglong limit = request().posts["limit"].toLongLong(); - if (limit == 0) limit = -1; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid()) - h.set_upload_limit(limit); -} - -void RequestHandler::action_command_setTorrentDlLimit() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash" << "limit"); - QString hash = request().posts["hash"]; - qlonglong limit = request().posts["limit"].toLongLong(); - if (limit == 0) limit = -1; - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (h.is_valid()) - h.set_download_limit(limit); -} - -void RequestHandler::action_command_toggleAlternativeSpeedLimits() -{ - CHECK_URI(0); - QBtSession::instance()->useAlternativeSpeedsLimit(!Preferences::instance()->isAltBandwidthEnabled()); -} - -void RequestHandler::action_command_alternativeSpeedLimitsEnabled() -{ - CHECK_URI(0); - print(QByteArray::number(Preferences::instance()->isAltBandwidthEnabled())); -} - -void RequestHandler::action_command_toggleSequentialDownload() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - h.toggleSequentialDownload(); - } - catch(invalid_handle&) {} - } -} - -void RequestHandler::action_command_toggleFirstLastPiecePrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - h.toggleFirstLastPiecePrio(); - } - catch(invalid_handle&) {} - } -} - -void RequestHandler::action_command_delete() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - foreach (const QString &hash, hashes) - QBtSession::instance()->deleteTorrent(hash, false); -} - -void RequestHandler::action_command_deletePerm() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - foreach (const QString &hash, hashes) - QBtSession::instance()->deleteTorrent(hash, true); -} - -void RequestHandler::action_command_increasePrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - - std::priority_queue, - std::vector >, - std::greater > > torrent_queue; - - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - catch(invalid_handle&) {} - } - - // Increase torrents priority (starting with the ones with highest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - - try { - h.queue_position_up(); - } - catch(invalid_handle&) {} - - torrent_queue.pop(); - } -} - -void RequestHandler::action_command_decreasePrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - QStringList hashes = request().posts["hashes"].split("|"); - - std::priority_queue, - std::vector >, - std::less > > torrent_queue; - - // Sort torrents by priority - foreach (const QString &hash, hashes) { - try { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - - if (!h.is_seed()) - torrent_queue.push(qMakePair(h.queue_position(), h)); - } - catch(invalid_handle&) {} - } - - // Decrease torrents priority (starting with the ones with lowest priority) - while(!torrent_queue.empty()) { - QTorrentHandle h = torrent_queue.top().second; - - try { - h.queue_position_down(); - } - catch(invalid_handle&) {} - - torrent_queue.pop(); - } -} - -void RequestHandler::action_command_topPrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - foreach (const QString &hash, request().posts["hashes"].split("|")) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) h.queue_position_top(); - } -} - -void RequestHandler::action_command_bottomPrio() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hashes"); - foreach (const QString &hash, request().posts["hashes"].split("|")) { - QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); - if (h.is_valid()) h.queue_position_bottom(); - } -} - -void RequestHandler::action_command_recheck() -{ - CHECK_URI(0); - CHECK_PARAMETERS("hash"); - QBtSession::instance()->recheckTorrent(request().posts["hash"]); -} - -bool RequestHandler::isPublicScope() -{ - return (scope_ == DEFAULT_SCOPE || scope_ == VERSION_INFO); -} - -void RequestHandler::processRequest() -{ - 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 RequestHandler::parsePath() -{ - if(request().path == "/") action_ = WEBUI_ACTION; - - // check action for requested path - QStringList pathItems = request().path.split('/', QString::SkipEmptyParts); - if (!pathItems.empty()) { - if (actions_.contains(pathItems.front())) { - scope_ = pathItems.front(); - pathItems.pop_front(); - } - } - - if (!pathItems.empty()) { - if (actions_[scope_].contains(pathItems.front())) { - action_ = pathItems.front(); - pathItems.pop_front(); - } - } - - args_ = pathItems; -} - -RequestHandler::RequestHandler(const HttpRequest &request, const HttpEnvironment &env, WebApplication *app) - : AbstractRequestHandler(request, env, app), scope_(DEFAULT_SCOPE), action_(DEFAULT_ACTION) -{ - parsePath(); -} - -QMap > RequestHandler::actions_ = RequestHandler::initializeActions(); diff --git a/src/webui/requesthandler.h b/src/webui/requesthandler.h deleted file mode 100644 index 015f245e8..000000000 --- a/src/webui/requesthandler.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014 Vladimir Golovnev - * - * 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. - */ - -#ifndef REQUESTHANDLER_H -#define REQUESTHANDLER_H - -#include -#include "httptypes.h" -#include "abstractrequesthandler.h" - -class WebApplication; - -class RequestHandler: public AbstractRequestHandler -{ -public: - RequestHandler( - const HttpRequest& request, const HttpEnvironment& env, - WebApplication* app); - -private: - // Actions - void action_public_webui(); - void action_public_index(); - void action_public_login(); - void action_public_logout(); - void action_public_theme(); - void action_public_images(); - void action_query_torrents(); - void action_query_preferences(); - void action_query_transferInfo(); - void action_query_propertiesGeneral(); - void action_query_propertiesTrackers(); - void action_query_propertiesFiles(); - void action_sync_maindata(); - void action_command_shutdown(); - void action_command_download(); - void action_command_upload(); - void action_command_addTrackers(); - void action_command_resumeAll(); - void action_command_pauseAll(); - void action_command_resume(); - void action_command_pause(); - void action_command_setPreferences(); - void action_command_setFilePrio(); - void action_command_getGlobalUpLimit(); - void action_command_getGlobalDlLimit(); - void action_command_setGlobalUpLimit(); - void action_command_setGlobalDlLimit(); - void action_command_getTorrentUpLimit(); - void action_command_getTorrentDlLimit(); - void action_command_setTorrentUpLimit(); - void action_command_setTorrentDlLimit(); - void action_command_alternativeSpeedLimitsEnabled(); - void action_command_toggleAlternativeSpeedLimits(); - void action_command_toggleSequentialDownload(); - void action_command_toggleFirstLastPiecePrio(); - void action_command_delete(); - void action_command_deletePerm(); - void action_command_increasePrio(); - void action_command_decreasePrio(); - void action_command_topPrio(); - void action_command_bottomPrio(); - void action_command_recheck(); - void action_version_api(); - void action_version_api_min(); - void action_version_qbittorrent(); - - typedef void (RequestHandler::*Action)(); - - QString scope_; - QString action_; - QStringList args_; - - void processRequest(); - - bool isPublicScope(); - void parsePath(); - - static QMap > initializeActions(); - static QMap > actions_; -}; - -#endif // REQUESTHANDLER_H diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 67ce61421..f7140e96c 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -26,285 +26,691 @@ * exception statement from your version. */ -#ifdef DISABLE_GUI +#include #include -#else -#include -#endif -#include #include -#include -#include +#include +#include +#include +#include +#ifndef DISABLE_GUI +// TODO: Drop GUI dependency! +#include "iconprovider.h" +#endif +#include "misc.h" +#include "fs_utils.h" #include "preferences.h" -#include "requesthandler.h" +#include "btjson.h" +#include "prefjson.h" +#include "qbtsession.h" +#include "websessiondata.h" #include "webapplication.h" -// UnbanTimer +using namespace libtorrent; -class UnbanTimer: public QTimer +static const int API_VERSION = 2; +static const int API_VERSION_MIN = 2; + +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 > WebApplication::initializeActions() { -public: - UnbanTimer(const QHostAddress& peer_ip, QObject *parent) - : QTimer(parent), m_peerIp(peer_ip) - { - setSingleShot(true); - setInterval(BAN_TIME); - } + QMap > 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, propertiesFiles); + ADD_ACTION(sync, maindata); + 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, getTorrentUpLimit); + ADD_ACTION(command, getTorrentDlLimit); + ADD_ACTION(command, setTorrentUpLimit); + ADD_ACTION(command, setTorrentDlLimit); + ADD_ACTION(command, alternativeSpeedLimitsEnabled); + ADD_ACTION(command, toggleAlternativeSpeedLimits); + ADD_ACTION(command, toggleSequentialDownload); + ADD_ACTION(command, toggleFirstLastPiecePrio); + 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, recheck); + ADD_ACTION(version, api); + ADD_ACTION(version, api_min); + ADD_ACTION(version, qbittorrent); + + return actions; +} - inline const QHostAddress& peerIp() const { return m_peerIp; } +#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; \ + } \ + } -private: - QHostAddress m_peerIp; -}; +void WebApplication::action_public_index() +{ + QString path; -// WebApplication + if (!args_.isEmpty()) { + if (args_.back() == "favicon.ico") + path = ":/icons/skin/qbittorrent16.png"; + else + path = WWW_FOLDER + args_.join("/"); + } -WebApplication::WebApplication(QObject *parent) - : QObject(parent) + printFile(path); +} + +void WebApplication::action_public_webui() { + if (!sessionActive()) + printFile(PRIVATE_FOLDER + "login.html"); + else + printFile(PRIVATE_FOLDER + "index.html"); } -WebApplication::~WebApplication() +void WebApplication::action_public_login() { - // cleanup sessions data - foreach (WebSession* session, sessions_.values()) - delete session; + const Preferences* const pref = Preferences::instance(); + QCryptographicHash md5(QCryptographicHash::Md5); + + md5.addData(request().posts["password"].toLocal8Bit()); + QString pass = md5.result().toHex(); + + bool equalUser = misc::slowEquals(request().posts["username"].toUtf8(), pref->getWebUiUsername().toUtf8()); + bool equalPass = misc::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)", qPrintable(addr), failedAttempts()); + print(QByteArray("Fails."), Http::CONTENT_TYPE_TXT); + } } -WebApplication *WebApplication::instance() +void WebApplication::action_public_logout() { - static WebApplication inst; - return &inst; + CHECK_URI(0); + sessionEnd(); } -void WebApplication::UnbanTimerEvent() +void WebApplication::action_public_theme() { - UnbanTimer* ubantimer = static_cast(sender()); - qDebug("Ban period has expired for %s", qPrintable(ubantimer->peerIp().toString())); - clientFailedAttempts_.remove(ubantimer->peerIp()); - ubantimer->deleteLater(); + if (args_.size() != 1) { + status(404, "Not Found"); + return; + } + +#ifdef DISABLE_GUI + QString url = ":/icons/oxygen/" + args_.front() + ".png"; +#else + QString url = IconProvider::instance()->getIconPath(args_.front()); +#endif + qDebug() << Q_FUNC_INFO << "There icon:" << url; + + printFile(url); + header(Http::HEADER_CACHE_CONTROL, MAX_AGE_MONTH); } -bool WebApplication::sessionInitialize(AbstractRequestHandler* _this) +void WebApplication::action_public_images() { - if (_this->session_ == 0) - { - QString cookie = _this->request_.headers.value("cookie"); - //qDebug() << Q_FUNC_INFO << "cookie: " << cookie; + const QString path = ":/icons/" + args_.join("/"); + printFile(path); + header(Http::HEADER_CACHE_CONTROL, MAX_AGE_MONTH); +} - QString sessionId; - const QString SID_START = C_SID + "="; - int pos = cookie.indexOf(SID_START); - if (pos >= 0) - { - pos += SID_START.length(); - int end = cookie.indexOf(QRegExp("[,;]"), pos); - sessionId = cookie.mid(pos, end >= 0 ? end - pos : end); - } +// GET params: +// - filter (string): all, downloading, completed, paused, active, inactive +// - label (string): torrent label for filtering by it (empty string means "unlabeled"; no "label" param presented means "any label") +// - 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; - // TODO: Additional session check - - if (!sessionId.isNull()) - { - if (sessions_.contains(sessionId)) - { - _this->session_ = sessions_[sessionId]; - return true; - } - else - { - qDebug() << Q_FUNC_INFO << "session does not exist!"; - } - } - } - - return false; -} - -bool WebApplication::readFile(const QString& path, QByteArray &data, QString &type) -{ - QString ext = ""; - int index = path.lastIndexOf('.') + 1; - if (index > 0) - ext = path.mid(index); - - // find translated file in cache - if (translatedFiles_.contains(path)) - { - data = translatedFiles_[path]; - } - else - { - QFile file(path); - if (!file.open(QIODevice::ReadOnly)) - { - qDebug("File %s was not found!", qPrintable(path)); - return false; - } + print(btjson::getTorrents( + gets["filter"], gets["label"], gets["sort"], gets["reverse"] == "true", + gets["limit"].toInt(), gets["offset"].toInt() + ), Http::CONTENT_TYPE_JS); +} + +void WebApplication::action_query_preferences() +{ + CHECK_URI(0); + print(prefjson::getPreferences(), Http::CONTENT_TYPE_JS); +} + +void WebApplication::action_query_transferInfo() +{ + CHECK_URI(0); + print(btjson::getTransferInfo(), Http::CONTENT_TYPE_JS); +} + +void WebApplication::action_query_propertiesGeneral() +{ + CHECK_URI(1); + print(btjson::getPropertiesForTorrent(args_.front()), Http::CONTENT_TYPE_JS); +} + +void WebApplication::action_query_propertiesTrackers() +{ + CHECK_URI(1); + print(btjson::getTrackersForTorrent(args_.front()), Http::CONTENT_TYPE_JS); +} + +void WebApplication::action_query_propertiesFiles() +{ + CHECK_URI(1); + print(btjson::getFilesForTorrent(args_.front()), Http::CONTENT_TYPE_JS); +} - data = file.readAll(); - file.close(); +// 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)); +} - // Translate the file - if ((ext == "html") || ((ext == "js") && !path.endsWith("excanvas-compressed.js"))) - { - QString dataStr = QString::fromUtf8(data.constData()); - translateDocument(dataStr); +void WebApplication::action_version_api() +{ + CHECK_URI(0); + print(QString::number(API_VERSION), Http::CONTENT_TYPE_TXT); +} - if (path.endsWith("about.html")) - { - dataStr.replace("${VERSION}", VERSION); - } +void WebApplication::action_version_api_min() +{ + CHECK_URI(0); + print(QString::number(API_VERSION_MIN), Http::CONTENT_TYPE_TXT); +} - data = dataStr.toUtf8(); - translatedFiles_[path] = data; // cashing translated file +void WebApplication::action_version_qbittorrent() +{ + CHECK_URI(0); + print(QString(VERSION), Http::CONTENT_TYPE_TXT); +} + +void WebApplication::action_command_shutdown() +{ + qDebug() << "Shutdown request from Web UI"; + // Special case handling for shutdown, we + // need to reply to the Web UI before + // actually shutting down. + + CHECK_URI(0); + QTimer::singleShot(0, qApp, SLOT(quit())); +} + +void WebApplication::action_command_download() +{ + CHECK_URI(0); + CHECK_PARAMETERS("urls"); + QString urls = request().posts["urls"]; + QStringList list = urls.split('\n'); + + foreach (QString url, list) { + url = url.trimmed(); + if (!url.isEmpty()) { + if (url.startsWith("bc://bt/", Qt::CaseInsensitive)) { + qDebug("Converting bc link to magnet link"); + url = misc::bcLinkToMagnet(url); + } + else if (url.startsWith("magnet:", Qt::CaseInsensitive)) { + QBtSession::instance()->addMagnetSkipAddDlg(url); + } + else { + qDebug("Downloading url: %s", qPrintable(url)); + QBtSession::instance()->downloadUrlAndSkipDialog(url); + } + } } - } - - type = CONTENT_TYPE_BY_EXT[ext]; - return true; -} - -QString WebApplication::generateSid() -{ - QString sid; - - qsrand(QDateTime::currentDateTime().toTime_t()); - do - { - const size_t size = 6; - quint32 tmp[size]; - - for (size_t i = 0; i < size; ++i) - tmp[i] = qrand(); - - sid = QByteArray::fromRawData(reinterpret_cast(tmp), sizeof(quint32) * size).toBase64(); - } - while (sessions_.contains(sid)); - - return sid; -} - -void WebApplication::translateDocument(QString& data) -{ - const QRegExp regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR"); - const QRegExp mnemonic("\\(?&([a-zA-Z]?\\))?"); - const std::string contexts[] = { - "TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", - "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", - "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel", - "PropTabBar", "TorrentModel", "downloadFromURL", "MainWindow", "misc", - "StatusBar" - }; - const size_t context_count = sizeof(contexts) / sizeof(contexts[0]); - int i = 0; - bool found = true; - - const QString locale = Preferences::instance()->getLocale(); - bool isTranslationNeeded = !locale.startsWith("en") || locale.startsWith("en_AU") || locale.startsWith("en_GB"); - - while(i < data.size() && found) - { - i = regex.indexIn(data, i); - if (i >= 0) - { - //qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data()); - QByteArray word = regex.cap(1).toUtf8(); - - QString translation = word; - if (isTranslationNeeded) - { - size_t context_index = 0; - while ((context_index < context_count) && (translation == word)) - { -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) - translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, QCoreApplication::UnicodeUTF8, 1); -#else - translation = qApp->translate(contexts[context_index].c_str(), word.constData(), 0, 1); -#endif - ++context_index; +} + +void WebApplication::action_command_upload() +{ + qDebug() << Q_FUNC_INFO; + CHECK_URI(0); + + foreach(const Http::UploadedFile& torrent, request().files) { + QString filePath = saveTmpFile(torrent.data); + + if (!filePath.isEmpty()) { + QTorrentHandle h = QBtSession::instance()->addTorrent(filePath); + if (!h.is_valid()) { + status(415, "Internal Server Error"); + print(QObject::tr("Error: '%1' is not a valid torrent file.\n").arg(torrent.filename), Http::CONTENT_TYPE_TXT); + } + // Clean up + fsutils::forceRemove(filePath); + } + else { + 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); } - } - // Remove keyboard shortcuts - translation.replace(mnemonic, ""); + } +} - data.replace(i, regex.matchedLength(), translation); - i += translation.length(); +void WebApplication::action_command_addTrackers() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash" << "urls"); + QString hash = request().posts["hash"]; + + if (!hash.isEmpty()) { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid() && h.has_metadata()) { + QString urls = request().posts["urls"]; + QStringList list = urls.split('\n'); + + foreach (const QString& url, list) { + announce_entry e(url.toStdString()); + h.add_tracker(e); + } + } } +} + +void WebApplication::action_command_resumeAll() +{ + CHECK_URI(0); + QBtSession::instance()->resumeAllTorrents(); +} + +void WebApplication::action_command_pauseAll() +{ + CHECK_URI(0); + QBtSession::instance()->pauseAllTorrents(); +} + +void WebApplication::action_command_resume() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash"); + QBtSession::instance()->resumeTorrent(request().posts["hash"]); +} + +void WebApplication::action_command_pause() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash"); + QBtSession::instance()->pauseTorrent(request().posts["hash"]); +} + +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 file_id = request().posts["id"].toInt(); + int priority = request().posts["priority"].toInt(); + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid() && h.has_metadata()) + h.file_priority(file_id, priority); +} + +void WebApplication::action_command_getGlobalUpLimit() +{ + CHECK_URI(0); + print(QByteArray::number(QBtSession::instance()->getSession()->settings().upload_rate_limit)); +} + +void WebApplication::action_command_getGlobalDlLimit() +{ + CHECK_URI(0); + print(QByteArray::number(QBtSession::instance()->getSession()->settings().download_rate_limit)); +} + +void WebApplication::action_command_setGlobalUpLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("limit"); + qlonglong limit = request().posts["limit"].toLongLong(); + if (limit == 0) limit = -1; + + QBtSession::instance()->setUploadRateLimit(limit); + if (Preferences::instance()->isAltBandwidthEnabled()) + Preferences::instance()->setAltGlobalUploadLimit(limit / 1024.); else - { - found = false; // no more translatable strings + Preferences::instance()->setGlobalUploadLimit(limit / 1024.); +} + +void WebApplication::action_command_setGlobalDlLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("limit"); + qlonglong limit = request().posts["limit"].toLongLong(); + if (limit == 0) limit = -1; + + QBtSession::instance()->setDownloadRateLimit(limit); + if (Preferences::instance()->isAltBandwidthEnabled()) + Preferences::instance()->setAltGlobalDownloadLimit(limit / 1024.); + else + Preferences::instance()->setGlobalDownloadLimit(limit / 1024.); +} + +void WebApplication::action_command_getTorrentUpLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash"); + QString hash = request().posts["hash"]; + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid()) + print(QByteArray::number(h.upload_limit())); +} + +void WebApplication::action_command_getTorrentDlLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash"); + QString hash = request().posts["hash"]; + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid()) + print(QByteArray::number(h.download_limit())); +} + +void WebApplication::action_command_setTorrentUpLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash" << "limit"); + QString hash = request().posts["hash"]; + qlonglong limit = request().posts["limit"].toLongLong(); + if (limit == 0) limit = -1; + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid()) + h.set_upload_limit(limit); +} + +void WebApplication::action_command_setTorrentDlLimit() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hash" << "limit"); + QString hash = request().posts["hash"]; + qlonglong limit = request().posts["limit"].toLongLong(); + if (limit == 0) limit = -1; + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (h.is_valid()) + h.set_download_limit(limit); +} + +void WebApplication::action_command_toggleAlternativeSpeedLimits() +{ + CHECK_URI(0); + QBtSession::instance()->useAlternativeSpeedsLimit(!Preferences::instance()->isAltBandwidthEnabled()); +} + +void WebApplication::action_command_alternativeSpeedLimitsEnabled() +{ + CHECK_URI(0); + print(QByteArray::number(Preferences::instance()->isAltBandwidthEnabled())); +} + +void WebApplication::action_command_toggleSequentialDownload() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + h.toggleSequentialDownload(); + } + catch(invalid_handle&) {} } - } } -bool WebApplication::isBanned(const AbstractRequestHandler *_this) const +void WebApplication::action_command_toggleFirstLastPiecePrio() { - return clientFailedAttempts_.value(_this->env_.clientAddress, 0) >= MAX_AUTH_FAILED_ATTEMPTS; + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + h.toggleFirstLastPiecePrio(); + } + catch(invalid_handle&) {} + } } -int WebApplication::failedAttempts(const AbstractRequestHandler* _this) const +void WebApplication::action_command_delete() { - return clientFailedAttempts_.value(_this->env_.clientAddress, 0); + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) + QBtSession::instance()->deleteTorrent(hash, false); } -void WebApplication::resetFailedAttempts(AbstractRequestHandler* _this) +void WebApplication::action_command_deletePerm() { - clientFailedAttempts_.remove(_this->env_.clientAddress); + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + foreach (const QString &hash, hashes) + QBtSession::instance()->deleteTorrent(hash, true); } -void WebApplication::increaseFailedAttempts(AbstractRequestHandler* _this) +void WebApplication::action_command_increasePrio() { - const int nb_fail = clientFailedAttempts_.value(_this->env_.clientAddress, 0) + 1; + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + + std::priority_queue, + std::vector >, + std::greater > > torrent_queue; + + // Sort torrents by priority + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + if (!h.is_seed()) + torrent_queue.push(qMakePair(h.queue_position(), h)); + } + catch(invalid_handle&) {} + } + + // Increase torrents priority (starting with the ones with highest priority) + while(!torrent_queue.empty()) { + QTorrentHandle h = torrent_queue.top().second; + + try { + h.queue_position_up(); + } + catch(invalid_handle&) {} + + torrent_queue.pop(); + } +} + +void WebApplication::action_command_decreasePrio() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + QStringList hashes = request().posts["hashes"].split("|"); + + std::priority_queue, + std::vector >, + std::less > > torrent_queue; + + // Sort torrents by priority + foreach (const QString &hash, hashes) { + try { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + + if (!h.is_seed()) + torrent_queue.push(qMakePair(h.queue_position(), h)); + } + catch(invalid_handle&) {} + } + + // Decrease torrents priority (starting with the ones with lowest priority) + while(!torrent_queue.empty()) { + QTorrentHandle h = torrent_queue.top().second; + + try { + h.queue_position_down(); + } + catch(invalid_handle&) {} + + torrent_queue.pop(); + } +} + +void WebApplication::action_command_topPrio() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + foreach (const QString &hash, request().posts["hashes"].split("|")) { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + if (h.is_valid()) h.queue_position_top(); + } +} - clientFailedAttempts_[_this->env_.clientAddress] = nb_fail; - if (nb_fail == MAX_AUTH_FAILED_ATTEMPTS) - { - // Max number of failed attempts reached - // Start ban period - UnbanTimer* ubantimer = new UnbanTimer(_this->env_.clientAddress, this); - connect(ubantimer, SIGNAL(timeout()), SLOT(UnbanTimerEvent())); - ubantimer->start(); - } +void WebApplication::action_command_bottomPrio() +{ + CHECK_URI(0); + CHECK_PARAMETERS("hashes"); + foreach (const QString &hash, request().posts["hashes"].split("|")) { + QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); + if (h.is_valid()) h.queue_position_bottom(); + } } -bool WebApplication::sessionStart(AbstractRequestHandler *_this) +void WebApplication::action_command_recheck() { - if (_this->session_ == 0) - { - _this->session_ = new WebSession(generateSid()); - sessions_[_this->session_->id] = _this->session_; - return true; - } + CHECK_URI(0); + CHECK_PARAMETERS("hash"); + QBtSession::instance()->recheckTorrent(request().posts["hash"]); +} - return false; +bool WebApplication::isPublicScope() +{ + return (scope_ == DEFAULT_SCOPE || scope_ == VERSION_INFO); } -bool WebApplication::sessionEnd(AbstractRequestHandler *_this) +void WebApplication::processRequest() { - if ((_this->session_ != 0) && (sessions_.contains(_this->session_->id))) - { - sessions_.remove(_this->session_->id); - delete _this->session_; - _this->session_ = 0; - return true; - } + 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; + } - return false; + 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; + } } -QStringMap WebApplication::initializeContentTypeByExtMap() +void WebApplication::parsePath() { - QStringMap map; + if(request().path == "/") action_ = WEBUI_ACTION; + + // check action for requested path + QStringList pathItems = request().path.split('/', QString::SkipEmptyParts); + if (!pathItems.empty()) { + if (actions_.contains(pathItems.front())) { + scope_ = pathItems.front(); + pathItems.pop_front(); + } + } - map["htm"] = CONTENT_TYPE_HTML; - map["html"] = CONTENT_TYPE_HTML; - map["css"] = CONTENT_TYPE_CSS; - map["gif"] = CONTENT_TYPE_GIF; - map["png"] = CONTENT_TYPE_PNG; - map["js"] = CONTENT_TYPE_JS; + if (!pathItems.empty()) { + if (actions_[scope_].contains(pathItems.front())) { + action_ = pathItems.front(); + pathItems.pop_front(); + } + } + + args_ = pathItems; +} - return map; +WebApplication::WebApplication(QObject *parent) + : AbstractWebApplication(parent) +{ } -const QStringMap WebApplication::CONTENT_TYPE_BY_EXT = WebApplication::initializeContentTypeByExtMap(); +QMap > WebApplication::actions_ = WebApplication::initializeActions(); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index f35f67009..b4b6b9b3f 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -29,61 +29,77 @@ #ifndef WEBAPPLICATION_H #define WEBAPPLICATION_H -#include -#include -#include -#include "httptypes.h" -#include +#include +#include "abstractwebapplication.h" -struct WebSession +class WebApplication: public AbstractWebApplication { - const QString id; - QVariantMap syncMainDataLastResponse; - QVariantMap syncMainDataLastAcceptedResponse; - WebSession(const QString& id): id(id) {} -}; - -const QString C_SID = "SID"; // name of session id cookie -const int BAN_TIME = 3600000; // 1 hour -const int MAX_AUTH_FAILED_ATTEMPTS = 5; - -class AbstractRequestHandler; - -class WebApplication: public QObject -{ - Q_OBJECT Q_DISABLE_COPY(WebApplication) public: - WebApplication(QObject* parent = 0); - virtual ~WebApplication(); - - static WebApplication* instance(); - - bool isBanned(const AbstractRequestHandler* _this) const; - int failedAttempts(const AbstractRequestHandler *_this) const; - void resetFailedAttempts(AbstractRequestHandler* _this); - void increaseFailedAttempts(AbstractRequestHandler* _this); - - bool sessionStart(AbstractRequestHandler* _this); - bool sessionEnd(AbstractRequestHandler* _this); - bool sessionInitialize(AbstractRequestHandler* _this); - - bool readFile(const QString &path, QByteArray& data, QString& type); - -private slots: - void UnbanTimerEvent(); + explicit WebApplication(QObject* parent = 0); private: - QMap sessions_; - QHash clientFailedAttempts_; - QMap translatedFiles_; - - QString generateSid(); - static void translateDocument(QString& data); - - static const QStringMap CONTENT_TYPE_BY_EXT; - static QStringMap initializeContentTypeByExtMap(); + // Actions + void action_public_webui(); + void action_public_index(); + void action_public_login(); + void action_public_logout(); + void action_public_theme(); + void action_public_images(); + void action_query_torrents(); + void action_query_preferences(); + void action_query_transferInfo(); + void action_query_propertiesGeneral(); + void action_query_propertiesTrackers(); + void action_query_propertiesFiles(); + void action_sync_maindata(); + void action_command_shutdown(); + void action_command_download(); + void action_command_upload(); + void action_command_addTrackers(); + void action_command_resumeAll(); + void action_command_pauseAll(); + void action_command_resume(); + void action_command_pause(); + void action_command_setPreferences(); + void action_command_setFilePrio(); + void action_command_getGlobalUpLimit(); + void action_command_getGlobalDlLimit(); + void action_command_setGlobalUpLimit(); + void action_command_setGlobalDlLimit(); + void action_command_getTorrentUpLimit(); + void action_command_getTorrentDlLimit(); + void action_command_setTorrentUpLimit(); + void action_command_setTorrentDlLimit(); + void action_command_alternativeSpeedLimitsEnabled(); + void action_command_toggleAlternativeSpeedLimits(); + void action_command_toggleSequentialDownload(); + void action_command_toggleFirstLastPiecePrio(); + void action_command_delete(); + void action_command_deletePerm(); + void action_command_increasePrio(); + void action_command_decreasePrio(); + void action_command_topPrio(); + void action_command_bottomPrio(); + void action_command_recheck(); + void action_version_api(); + void action_version_api_min(); + void action_version_qbittorrent(); + + typedef void (WebApplication::*Action)(); + + QString scope_; + QString action_; + QStringList args_; + + void processRequest(); + + bool isPublicScope(); + void parsePath(); + + static QMap > initializeActions(); + static QMap > actions_; }; #endif // WEBAPPLICATION_H diff --git a/src/webui/websessiondata.h b/src/webui/websessiondata.h new file mode 100644 index 000000000..fe7b4eeec --- /dev/null +++ b/src/webui/websessiondata.h @@ -0,0 +1,41 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef WEBSESSIONDATA +#define WEBSESSIONDATA + +#include + +struct WebSessionData +{ + QVariantMap syncMainDataLastResponse; + QVariantMap syncMainDataLastAcceptedResponse; +}; + +#endif // WEBSESSIONDATA + diff --git a/src/webui/webui.cpp b/src/webui/webui.cpp new file mode 100644 index 000000000..6cafe0b4b --- /dev/null +++ b/src/webui/webui.cpp @@ -0,0 +1,102 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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 "webui.h" +#include "http/server.h" +#include "webapplication.h" +#include "dnsupdater.h" +#include "preferences.h" +#include "logger.h" + +WebUI::WebUI(QObject *parent) : QObject(parent) +{ + init(); + connect(Preferences::instance(), SIGNAL(changed()), SLOT(init())); +} + +void WebUI::init() +{ + Preferences* const pref = Preferences::instance(); + Logger* const logger = Logger::instance(); + + if (pref->isWebUiEnabled()) { + const quint16 port = pref->getWebUiPort(); + + if (httpServer_) { + if (httpServer_->serverPort() != port) + httpServer_->close(); + } + else { + webapp_ = new WebApplication(this); + httpServer_ = new Http::Server(webapp_, this); + } + +#ifndef QT_NO_OPENSSL + if (pref->isWebUiHttpsEnabled()) { + QSslCertificate cert(pref->getWebUiHttpsCertificate()); + QSslKey key; + key = QSslKey(pref->getWebUiHttpsKey(), QSsl::Rsa); + if (!cert.isNull() && !key.isNull()) + httpServer_->enableHttps(cert, key); + else + httpServer_->disableHttps(); + } + else { + httpServer_->disableHttps(); + } +#endif + + if (!httpServer_->isListening()) { + bool success = httpServer_->listen(QHostAddress::Any, port); + if (success) + logger->addMessage(tr("The Web UI is listening on port %1").arg(port)); + else + logger->addMessage(tr("Web User Interface Error - Unable to bind Web UI to port %1").arg(port), Log::CRITICAL); + } + + // DynDNS + if (pref->isDynDNSEnabled()) { + if (!dynDNSUpdater_) + dynDNSUpdater_ = new DNSUpdater(this); + else + dynDNSUpdater_->updateCredentials(); + } + else { + if (dynDNSUpdater_) + delete dynDNSUpdater_; + } + } + else { + if (httpServer_) + delete httpServer_; + if (webapp_) + delete webapp_; + if (dynDNSUpdater_) + delete dynDNSUpdater_; + } +} diff --git a/src/webui/webui.h b/src/webui/webui.h new file mode 100644 index 000000000..2abc6b924 --- /dev/null +++ b/src/webui/webui.h @@ -0,0 +1,55 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2015 Vladimir Golovnev + * + * 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. + */ + +#ifndef WEBUI_H +#define WEBUI_H + +#include +#include + +namespace Http { class Server; } +class DNSUpdater; +class AbstractWebApplication; + +class WebUI : public QObject +{ + Q_OBJECT + +public: + explicit WebUI(QObject *parent = 0); + +private slots: + void init(); + +private: + QPointer httpServer_; + QPointer dynDNSUpdater_; + QPointer webapp_; +}; + +#endif // WEBUI_H diff --git a/src/webui/webui.pri b/src/webui/webui.pri index 26c009443..1c36c4a9c 100644 --- a/src/webui/webui.pri +++ b/src/webui/webui.pri @@ -1,33 +1,25 @@ INCLUDEPATH += $$PWD -HEADERS += $$PWD/httpserver.h \ - $$PWD/httpconnection.h \ - $$PWD/httprequestparser.h \ - $$PWD/httpresponsegenerator.h \ - $$PWD/btjson.h \ - $$PWD/prefjson.h \ - $$PWD/jsonutils.h \ - $$PWD/httptypes.h \ - $$PWD/extra_translations.h \ - $$PWD/webapplication.h \ - $$PWD/abstractrequesthandler.h \ - $$PWD/requesthandler.h \ - $$PWD/qtorrentfilter.h +HEADERS += \ + $$PWD/webui.h \ + $$PWD/btjson.h \ + $$PWD/prefjson.h \ + $$PWD/jsonutils.h \ + $$PWD/extra_translations.h \ + $$PWD/webapplication.h \ + $$PWD/qtorrentfilter.h \ + $$PWD/websessiondata.h \ + $$PWD/abstractwebapplication.h -SOURCES += $$PWD/httpserver.cpp \ - $$PWD/httpconnection.cpp \ - $$PWD/httprequestparser.cpp \ - $$PWD/httpresponsegenerator.cpp \ - $$PWD/btjson.cpp \ - $$PWD/prefjson.cpp \ - $$PWD/webapplication.cpp \ - $$PWD/abstractrequesthandler.cpp \ - $$PWD/requesthandler.cpp \ - $$PWD/qtorrentfilter.cpp +SOURCES += \ + $$PWD/webui.cpp \ + $$PWD/btjson.cpp \ + $$PWD/prefjson.cpp \ + $$PWD/webapplication.cpp \ + $$PWD/qtorrentfilter.cpp \ + $$PWD/abstractwebapplication.cpp # QJson JSON parser/serializer for using with Qt4 -lessThan(QT_MAJOR_VERSION, 5) { - include(qjson/qjson.pri) -} +lessThan(QT_MAJOR_VERSION, 5): include(qjson/qjson.pri) RESOURCES += $$PWD/webui.qrc diff --git a/unixconf.pri b/unixconf.pri index 6e1cfffa9..a93f50c93 100644 --- a/unixconf.pri +++ b/unixconf.pri @@ -10,17 +10,17 @@ exists($$OUT_PWD/../conf.pri) { } # COMPILATION SPECIFIC -!nox:dbus { - QT += dbus +!nogui:dbus { + QT += dbus } QMAKE_CXXFLAGS += -Wformat -Wformat-security !haiku { - QMAKE_LFLAGS_APP += -rdynamic + QMAKE_LFLAGS_APP += -rdynamic } # Man page -nox { +nogui { man.files = ../doc/qbittorrent-nox.1 } else { man.files = ../doc/qbittorrent.1 @@ -29,62 +29,61 @@ nox { man.path = $$MANPREFIX/man1 INSTALLS += man +DIST_PATH = ../dist/unix + # Menu Icon -!nox { - menuicon.files = Icons/qBittorrent.desktop - menuicon.path = $$PREFIX/share/applications/ - INSTALLS += menuicon - icon16.files = menuicons/16x16/apps/qbittorrent.png - icon16.path = $$PREFIX/share/icons/hicolor/16x16/apps/ - icon22.files = menuicons/22x22/apps/qbittorrent.png - icon22.path = $$PREFIX/share/icons/hicolor/22x22/apps/ - icon24.files = menuicons/24x24/apps/qbittorrent.png - icon24.path = $$PREFIX/share/icons/hicolor/24x24/apps/ - icon32.files = menuicons/32x32/apps/qbittorrent.png - icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps/ - icon36.files = menuicons/36x36/apps/qbittorrent.png - icon36.path = $$PREFIX/share/icons/hicolor/36x36/apps/ - icon48.files = menuicons/48x48/apps/qbittorrent.png - icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps/ - icon64.files = menuicons/64x64/apps/qbittorrent.png - icon64.path = $$PREFIX/share/icons/hicolor/64x64/apps/ - icon72.files = menuicons/72x72/apps/qbittorrent.png - icon72.path = $$PREFIX/share/icons/hicolor/72x72/apps/ - icon96.files = menuicons/96x96/apps/qbittorrent.png - icon96.path = $$PREFIX/share/icons/hicolor/96x96/apps/ - icon128.files = menuicons/128x128/apps/qbittorrent.png - icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps/ - icon192.files = menuicons/192x192/apps/qbittorrent.png - icon192.path = $$PREFIX/share/icons/hicolor/192x192/apps/ +!nogui { + menuicon.files = icons/qBittorrent.desktop + menuicon.path = $$PREFIX/share/applications/ + INSTALLS += menuicon - INSTALLS += icon16 \ - icon22 \ - icon24 \ - icon32 \ - icon36 \ - icon48 \ - icon64 \ - icon72 \ - icon96 \ - icon128 \ - icon192 + icon16.files = $$DIST_PATH/menuicons/16x16/apps/qbittorrent.png + icon16.path = $$PREFIX/share/icons/hicolor/16x16/apps/ + icon22.files = $$DIST_PATH/menuicons/22x22/apps/qbittorrent.png + icon22.path = $$PREFIX/share/icons/hicolor/22x22/apps/ + icon24.files = $$DIST_PATH/menuicons/24x24/apps/qbittorrent.png + icon24.path = $$PREFIX/share/icons/hicolor/24x24/apps/ + icon32.files = $$DIST_PATH/menuicons/32x32/apps/qbittorrent.png + icon32.path = $$PREFIX/share/icons/hicolor/32x32/apps/ + icon36.files = $$DIST_PATH/menuicons/36x36/apps/qbittorrent.png + icon36.path = $$PREFIX/share/icons/hicolor/36x36/apps/ + icon48.files = $$DIST_PATH/menuicons/48x48/apps/qbittorrent.png + icon48.path = $$PREFIX/share/icons/hicolor/48x48/apps/ + icon64.files = $$DIST_PATH/menuicons/64x64/apps/qbittorrent.png + icon64.path = $$PREFIX/share/icons/hicolor/64x64/apps/ + icon72.files = $$DIST_PATH/menuicons/72x72/apps/qbittorrent.png + icon72.path = $$PREFIX/share/icons/hicolor/72x72/apps/ + icon96.files = $$DIST_PATH/menuicons/96x96/apps/qbittorrent.png + icon96.path = $$PREFIX/share/icons/hicolor/96x96/apps/ + icon128.files = $$DIST_PATH/menuicons/128x128/apps/qbittorrent.png + icon128.path = $$PREFIX/share/icons/hicolor/128x128/apps/ + icon192.files = $$DIST_PATH/menuicons/192x192/apps/qbittorrent.png + icon192.path = $$PREFIX/share/icons/hicolor/192x192/apps/ + INSTALLS += \ + icon16 \ + icon22 \ + icon24 \ + icon32 \ + icon36 \ + icon48 \ + icon64 \ + icon72 \ + icon96 \ + icon128 \ + icon192 - pixmap.files = menuicons/128x128/apps/qbittorrent.png - pixmap.path = $$PREFIX/share/pixmaps/ - INSTALLS += pixmap + pixmap.files = $$DIST_PATH/menuicons/128x128/apps/qbittorrent.png + pixmap.path = $$PREFIX/share/pixmaps/ + INSTALLS += pixmap } # INSTALL target.path = $$PREFIX/bin/ INSTALLS += target -dbus { - include(src/qtnotify/qtnotify.pri) -} - -!nox { - # DEFINE added by configure - contains(DEFINES, WITH_GEOIP_EMBEDDED) { - message("You chose to embed GeoIP database in qBittorrent executable.") - } +!nogui { + # DEFINE added by configure + contains(DEFINES, WITH_GEOIP_EMBEDDED) { + message("You chose to embed GeoIP database in qBittorrent executable.") + } } diff --git a/winconf.pri b/winconf.pri index ec3b98fc1..0cc074f83 100644 --- a/winconf.pri +++ b/winconf.pri @@ -43,18 +43,11 @@ CONFIG(debug, debug|release) { #Enable backtrace support CONFIG += strace_win -strace_win:{ - DEFINES += STACKTRACE_WIN - FORMS += stacktrace_win_dlg.ui - HEADERS += stacktrace_win.h \ - stacktrace_win_dlg.h -} - win32-g++ { - include(winconf-mingw.pri) + include(winconf-mingw.pri) } else { - include(winconf-msvc.pri) + include(winconf-msvc.pri) } DEFINES += WITH_GEOIP_EMBEDDED