Compare commits

..

64 Commits

Author SHA1 Message Date
Christophe Dumez
dc91d7ca6c Tagged v2.0.6 release 2010-01-05 21:25:29 +00:00
Christophe Dumez
413dd60c56 - Bump to v2.0.6 2010-01-05 21:19:07 +00:00
Christophe Dumez
1c0d462785 COSMETIC: Improve torrent deletion confirmation dialog so that the text that not get truncated 2010-01-04 22:41:56 +00:00
Christophe Dumez
89f317dad2 BUGFIX: Force data recheck whenever a torrent is moved
BUGFIX: Detect existing torrent data even if incomplete torrents are saved to a different folder
2010-01-04 21:29:18 +00:00
Christophe Dumez
facbb650d0 - BUGFIX: Stop catching signals once one has been caught to avoid possible infinite loop 2010-01-03 14:41:28 +00:00
Christophe Dumez
893b7bf7f3 - Fix detection of invalid torrent files 2010-01-03 13:04:26 +00:00
Christophe Dumez
f39d3296ab - Use QDir::separator instead of / for Windows compatibility 2010-01-01 13:24:57 +00:00
Christophe Dumez
edbe67c4e6 - Updated spanish translation 2009-12-31 18:22:10 +00:00
Christophe Dumez
9836d4ec07 - Bump to v2.0.5 2009-12-31 16:53:20 +00:00
Christophe Dumez
b5b14d4d43 - BUGFIX: Fix crash with downloaded/availability bars when the torrent has too many pieces 2009-12-31 16:42:50 +00:00
Christophe Dumez
dff1666b6c BUGFIX: Fix possible crash when drawing piece downloaded/availability bars 2009-12-31 15:13:33 +00:00
Christophe Dumez
e6966bec31 - Updated release date 2009-12-30 16:53:19 +00:00
Christophe Dumez
e92f6a3d96 Display the correct DHT port in the log window 2009-12-30 16:49:38 +00:00
Christophe Dumez
f70aab3877 - Another fix for global download rate limiting in Web UI 2009-12-30 16:40:35 +00:00
Christophe Dumez
ec44efb261 BUGFIX: Fix global download rate limiting from Web UI 2009-12-30 16:38:46 +00:00
Christophe Dumez
d227ed8b59 - Use global maximum transfer rates as maximum values in per-torrent speed limiting dialogs (Web UI)
- Fix compilation error introduced in last commit
2009-12-30 16:35:45 +00:00
Christophe Dumez
395c2c862a - Use global maximum transfer rates as maximum values in per-torrent speed limit
ing dialogs
2009-12-30 16:21:04 +00:00
Christophe Dumez
e7eb61c0c3 - Updated French translation 2009-12-30 16:04:23 +00:00
Christophe Dumez
7cd2ec57d3 - qBittorrent no longer listens on a random port whenever it receives a listen_f
ailed_alert (because it may correspond to another network interface)
- Display a "disconnected" icon in status bar whenever qBittorrent fails to list
en on the selected port
2009-12-30 16:02:30 +00:00
Christophe Dumez
4941f24fff - Better Float JSON encoding fix for Qt 4.6 2009-12-29 22:58:32 +00:00
Christophe Dumez
94cb5fe0b6 - BUGFIX: Use Wildcard matching instead of full regex in RSS feed downloader 2009-12-29 21:42:19 +00:00
Christophe Dumez
4828ffa280 - Fix communication between qBittorrent and Web UI (broken by Qt 4.6) 2009-12-29 20:13:19 +00:00
Christophe Dumez
622d9701cb - Fix possible issues with DHT service port 2009-12-29 10:13:21 +00:00
Christophe Dumez
93c4b521bb - Make sure service port does not change 2009-12-28 21:23:51 +00:00
Christophe Dumez
9c7374e4a1 - Fix possible crash when closing a search engine tab that is not currently displayed 2009-12-28 20:56:46 +00:00
Christophe Dumez
1d58e7fd7f - Fix PeerGuardian .p2b file support 2009-12-28 19:36:42 +00:00
Christophe Dumez
ed9c68eea0 - Updated Catalan translator name 2009-12-23 20:08:15 +00:00
Christophe Dumez
1b8a87c54d - Updated spanish and catalan translations 2009-12-23 20:05:57 +00:00
Christophe Dumez
a33b978564 - Bump to v2.0.3 2009-12-23 08:53:21 +00:00
Christophe Dumez
f9cf937e20 - Minor french translation update 2009-12-22 21:35:28 +00:00
Christophe Dumez
7109685913 - Updated language files 2009-12-22 21:32:42 +00:00
Christophe Dumez
bd222ac8bd - BUGFIX: Use the save path set in program preferences as a default in torrent addition dialog 2009-12-22 19:22:33 +00:00
Christophe Dumez
b675f4ac58 - Fix issue with speed limiting (infinite value was not handled properly) 2009-12-22 17:37:45 +00:00
Christophe Dumez
9989ec79c9 - BUGFIX: Trackers are now displayed for torrents without metadata 2009-12-22 15:40:05 +00:00
Christophe Dumez
20167c2276 - Fix little typo 2009-12-22 14:21:03 +00:00
Christophe Dumez
e26a1d5342 - BUGFIX: Handle paths with [~, ., ..] properly 2009-12-22 10:39:38 +00:00
Christophe Dumez
3734e11879 - Update spanish translator name 2009-12-21 18:49:38 +00:00
Christophe Dumez
4632c6fda9 - Updated Spanish translation 2009-12-21 16:13:02 +00:00
Christophe Dumez
62ff08b6b5 - Fix "Temp path" button in program preferences 2009-12-19 20:46:51 +00:00
Christophe Dumez
f50b62624a - Fix to Serbian translation 2009-12-19 19:35:51 +00:00
Christophe Dumez
d380eb9958 - Updated Russian translation 2009-12-19 13:40:15 +00:00
Christophe Dumez
a71bdde022 - Updated Serbian translation 2009-12-19 07:50:26 +00:00
Christophe Dumez
e8e6894b5c - Minor cosmetic fix to program preferences 2009-12-18 20:02:55 +00:00
Christophe Dumez
d92f69fa0f - Prepare for v2.0.2 release 2009-12-18 15:24:13 +00:00
Christophe Dumez
77d7a1ec49 - Fix .qbittorrent folder not being created. This bug was introduced in v2.0.1 and it is very important. 2009-12-18 15:15:32 +00:00
Christophe Dumez
d5c174a6f8 - BUGFIX: Read RSS articles are remembered on restart for feeds with no torr
ents attached
2009-12-18 14:47:33 +00:00
Christophe Dumez
173999e504 - Fix Mininova search engine plugin 2009-12-18 14:16:19 +00:00
Christophe Dumez
1ed928bc39 - BUGFIX: Fix ThePirateBay.org search engine plugin 2009-12-18 14:00:39 +00:00
Christophe Dumez
b85d51ba79 - Do not use home folder as a fallback when the destination folder is not accessible 2009-12-17 20:01:18 +00:00
Christophe Dumez
779b53722b - Added referer parameter to download_file() helper function (required by some websites such as sumotorrent) 2009-12-14 21:50:21 +00:00
Christophe Dumez
76780c4c46 - Fix RSS downloader for feeds where torrents are not attached (but the links points to them) 2009-12-14 18:56:00 +00:00
Christophe Dumez
da74f24a71 - Update changelog 2009-12-13 17:53:04 +00:00
Christophe Dumez
01c56865db - Better checking of based32 encoded Magnet Links to increase robustness 2009-12-13 10:15:50 +00:00
Christophe Dumez
b541c9fa4c - Added Hex Magnet Link support (new standard, used for example by ThePirateBay) 2009-12-13 09:52:28 +00:00
Christophe Dumez
aac0fbcbe4 - Fix possible crash in torrent properties (files) 2009-12-13 00:44:47 +00:00
Christophe Dumez
b315551edd - Fix missing slot warning when using libtorrent v0.14 (Thanks Haypo) 2009-12-13 00:03:35 +00:00
Christophe Dumez
58a885cb87 - Updated version number to v2.0.1 2009-12-12 22:57:56 +00:00
Christophe Dumez
d19282285c - BUGFIX: ~/qBT_dir is created only when it is actually used 2009-12-12 22:39:29 +00:00
Christophe Dumez
e0d8ca39a5 - BUGFIX: Fix link to plugins.qbittorrent.org in plugins dialog 2009-12-12 22:17:51 +00:00
Christophe Dumez
ec3169c9b0 - Fix column hiding behavior when queueing system is disabled 2009-12-12 22:07:41 +00:00
Christophe Dumez
7bfd7e9cda - Disable debug mode 2009-12-11 13:05:46 +00:00
Christophe Dumez
459bb8c51d - Removed useless debug 2009-12-11 12:26:51 +00:00
Christophe Dumez
9159a9f25d - µTorrent is now also spoofed correctly 2009-12-11 12:22:41 +00:00
Christophe Dumez
8ea8f8a9f7 - Branched v2.0.x 2009-12-10 19:39:09 +00:00
277 changed files with 110182 additions and 94398 deletions

20
AUTHORS
View File

@@ -3,27 +3,15 @@ Author:
Contributors: Contributors:
* Stefanos Antaris <santaris@csd.auth.gr> * Stefanos Antaris <santaris@csd.auth.gr>
* Mohammad Dib <mdib@qbittorrent.org>
* Mirco Chinelli <infinity89@fastwebmail.it>
* Ishan Arora <ishan@qbittorrent.org> * Ishan Arora <ishan@qbittorrent.org>
* Arnaud Demaizière <arnaud@qbittorrent.org> * Arnaud Demaizière <arnaud@qbittorrent.org>
* Grigis Gaëtan <cipher16@gmail.com> * Grigis Gaëtan <cipher16@gmail.com>
* Christian Kandeler <zambesi@users.sourceforge.net>
* Silvan Scherrer <silvan.scherrer@aroa.ch>
Code from other projects: Code from other projects:
* files src/qtsingleapp/* src/lineedit/*
copyright: Nokia Corporation
license: LGPL
* files src/ico.cpp src/ico.h * files src/ico.cpp src/ico.h
copyright: Malte Starostik <malte@kde.org> copyright: Malte Starostik <malte@kde.org>
license: LGPL license: LGPL
* files src/search_engine/socks.py
copyright: Dan Haim <negativeiq@users.sourceforge.net>
license: BSD
Images Authors: Images Authors:
* files: src/Icons/*.png * files: src/Icons/*.png
copyright: Gnome Icon Theme copyright: Gnome Icon Theme
@@ -67,13 +55,11 @@ Images Authors:
Translations authors: Translations authors:
* files: src/lang/*.ts * files: src/lang/*.ts
copyright: copyright:
- Arabic: SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)
- Brazilian: Nick Marinho (nickmarinho@gmail.com) - Brazilian: Nick Marinho (nickmarinho@gmail.com)
- Bulgarian: Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net) - Bulgarian: Tsvetan & Boiko Bankov (emerge_life@users.sourceforge.net)
- Catalan: Francisco Luque Contreras (frannoe@ya.com) - Catalan: Francisco Luque Contreras (frannoe@ya.com)
- Chinese (Simplified): Guo Yue (yue.guo0418@gmail.com) - Chinese (Simplified): Guo Yue (yue.guo0418@gmail.com)
- Chinese (Traditional): Yi-Shun Wang (dnextstep@gmail.com) - Chinese (Traditional): Yi-Shun Wang (dnextstep@gmail.com)
- Croatian: Oliver Mucafir (oliver.untwist@gmail.com)
- Czech: Jirka Vilim (web@tets.cz) - Czech: Jirka Vilim (web@tets.cz)
- Danish: Mathias Nielsen (comoneo@gmail.com) - Danish: Mathias Nielsen (comoneo@gmail.com)
- Dutch: Joost Schipper (heavyjoost@users.sourceforge.net) - Dutch: Joost Schipper (heavyjoost@users.sourceforge.net)
@@ -83,7 +69,7 @@ Translations authors:
- German: Niels Hoffmann (zentralmaschine@users.sourceforge.net) - German: Niels Hoffmann (zentralmaschine@users.sourceforge.net)
- Greek: Tsvetan Bankov (emerge_life@users.sourceforge.net) and Stephanos Antaris (santaris@csd.auth.gr) - Greek: Tsvetan Bankov (emerge_life@users.sourceforge.net) and Stephanos Antaris (santaris@csd.auth.gr)
- Hungarian: Majoros Péter (majoros.j.p@t-online.hu) - Hungarian: Majoros Péter (majoros.j.p@t-online.hu)
- Italian: Matteo Sechi (bu17714@gmail.com) - Italian: Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)
- Japanese: Nardog (alphisation@gmail.com) - Japanese: Nardog (alphisation@gmail.com)
- Korean: Jin Woo Sin (jin828sin@users.sourceforge.net) - Korean: Jin Woo Sin (jin828sin@users.sourceforge.net)
- Norwegian: Lars-Erik Labori (hamil@users.sourceforge.net) - Norwegian: Lars-Erik Labori (hamil@users.sourceforge.net)
@@ -96,5 +82,5 @@ Translations authors:
- Spanish: Francisco Luque Contreras (frannoe@ya.com) - Spanish: Francisco Luque Contreras (frannoe@ya.com)
- Swedish: Daniel Nylander (po@danielnylander.se) - Swedish: Daniel Nylander (po@danielnylander.se)
- Turkish: Hasan Yilmaz (iletisim@hedefturkce.com) - Turkish: Hasan Yilmaz (iletisim@hedefturkce.com)
- Ukrainian: Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com) - Ukrainian: Andrey Shpachenko (masterfix@users.sourceforge.net)
license: GPLv2 license: GPLv2

190
Changelog
View File

@@ -1,192 +1,4 @@
* Wed Nov 10 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.10 * Tue Jan 5 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.6
- BUGFIX: Fix possible crash when selecting a RSS item (really closes #575624)
- BUGFIX: Improved IPv6 support (IP filter and Peer list)
- BUGFIX: Make IP filter more tolerant towards strangely formatted IPs
- BUGFIX: More reliable folder scanning
- BUGFIX: Do not create the torrent root folder at final destination if
torrent is in the temp dir (closes #673271)
- BUGFIX: Fix compilation with libnotify v0.7.0 (closes #671769)
- BUGFIX: Use a pointing cursor over status bar buttons
* Sun Oct 31 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.9
- BUGFIX: Fix crash when pressing enter in save path field in torrent addition dialog
- BUGFIX: Fix crash when deleting a torrent with no metadata (closes #667528)
- BUGFIX: Fix possible crash on clicking a RSS article (closes #575624)
- BUGFIX: Correctly update total number of torrents when a torrent is automatically removed (closes #668726)
- BUGFIX: Correctly display the hash of torrents with no metadata
- BUGFIX: Elide status bar text if it is too wide
- BUGFIX: Make sure the splash screen is displayed for 2 seconds
- BUGFIX: Make listening on a particular interface more reliable
- BUGFIX: Fix torrent size update in torrent addition dialog
- BUGFIX: Fix possible crash on qBittorrent shutdown
- BUGFIX: Fix and improve file priorities editing (closes #669084)
- I18N: Updated Arabic, Italian and Croatian translations
* Sun Oct 24 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.8
- BUGFIX: Fix possible crash on manual peer ban
- BUGFIX: Improved hostname resolution code
- BUGFIX: Several search plugins fixed
- BUGFIX: Auto-disable the shutdown feature
- BUGFIX: Remember the current property tab on startup
- BUGFIX: Fix status list widget height issue on style change
- BUGFIX: Fix rounding issue in torrent progress display
- BUGFIX: Fix issue when altering files priorities of a seeding torrent
- BUGFIX: Better fix for save path editing issues in torrent addition dialog
- BUGFIX: Peers can now be sorted by country
* Tue Oct 19 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.7
- BUGFIX: Display the priority column when the queueing system gets enabled
- BUGFIX: Fix encoding problem in file renaming
- BUGFIX: Delete uneeded files on torrent "soft" deletion
- BUGFIX: Fix issues when marking a file as 'not downloaded' causes the torrent to complete
- BUGFIX: Improved "Set Location" and "Change save path" dialogs
- BUGFIX: Fix display of queued seeding torrents
* Sun Oct 17 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.6
- BUGFIX: Fix "torrent seeding after creation" feature
- BUGFIX: The properties panel data would sometimes not match the selected torrent
- BUGFIX: Fix detection of files at final destination when temp dir is used
- BUGFIX: Fix moving of a torrent to an unexisting directory
* Tue Oct 12 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.5
- BUGFIX: Remember torrent completion date correctly
- BUGFIX: Fix feature to keep incomplete torrents in a separate folder
- BUGFIX: Fix display of URL seeds in the UI
- BUGFIX: Improved peer hostname resolution with caching
- BUGFIX: Piece availability/downloaded widgets performance improvement
* Fri Oct 1 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.4
- BUGFIX: Clean program exit on system shutdown/logout
- BUGFIX: Fix possible search engine plugin update
* Tue Sep 28 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.3
- BUGFIX: Fix encoding issue in command line parameters processing
- BUGFIX: Fix possible crash when changing the save path in addition dialog
- BUGFIX: Fix wrong mapping to source model
* Sun Sep 26 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.2
- BUGFIX: Fix display of torrent content in addition dialog
- BUGFIX: Really fix manual editing of save path in torrent addition dialog
* Sun Sep 26 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.1
- I18N: Updated Arabic translation
- I18N: Fixes to German translation
- BUGFIX: Save path can now be edited in torrent addition dialog
- BUGFIX: Fix save path encoding on non-utf8 systems
- BUGFIX: Fix saving to drive root on Windows
- BUGFIX: OGV can now be previewed
- BUGFIX: Maximum download limit is now 10MB/s
- BUGFIX: Fix 'download in scan dir' persistence
- BUGFIX: Add .torrent extension only when missing (torrent creator)
- BUGFIX: Fix possible issue with temporary download path persistence
- BUGFIX: Added support for | (OR) operator in RSS feed downloader
- BUGFIX: Fix Web UI for spanish users
- BUGFIX: Fix locale switching from Web UI
- BUGFIX: Use AND operator for torrentdownloads.net searches
- BUGFIX: Limit torrent addition dialog width to fit the screen
- COSMETIC: Fix progress bars style on Windows
* Tue Aug 24 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.0
- FEATURE: Added actions to "Move to top/bottom" of priority queue
- FEATURE: Auto-Shutdown on downloads completion
- FEATURE: Email notification on download completion
- FEATURE: Added button to password-lock the UI
- FEATURE: Added label-level Pause/Resume/Delete actions
- FEATURE: Torrents can now be filtered by name
- FEATURE: Run external program on torrent completion
- FEATURE: Detect executable updates in order to advise the user to restart
* Tue Jul 27 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.3.0
- FEATURE: Simplified torrent root folder renaming/truncating (< v2.3.0 is no longer forward compatible)
- FEATURE: Remember previous save paths in torrent addition dialog
- FEATURE: Max number of half-open connections can now be edited
- FEATURE: Added support for strict super seeding
- FEATURE: The user can force listening on a particular network interface
- FEATURE: Added cookie support for RSS feeds
- FEATURE: User can force tracker reannounce
- FEATURE: Added "No action" setting for double-click action
- FEATURE: Several torrents can be moved at once
- FEATURE: Added error state for torrents (error is displayed in a tooltip)
- FEATURE: Added filter for paused/error torrents
- FEATURE: Add Check/Uncheck all feature in Web UI
- FEATURE: Search engine can now be disabled
- FEATURE: Torrents can be automatically paused once they reach a given ratio
- FEATURE: Several files can now be disabled at once
- FEATURE: Added "Select All/None" buttons to files list
- FEATURE: Added support for BitComet links (bc://bt/...)
- BUGFIX: Hide seeding torrents files priorities in Web UI
- BUGFIX: The user can disable permanently recursive torrent download
- BUGFIX: Peer Exchange status is now correctly reported
- BUGFIX: Use an INI file instead of the registry on Windows (More reliable)
- BUGFIX: Removed client spoofing feature to avoid tracker blacklisting
- COSMETIC: Display peers country name in tooltip
- COSMETIC: Display number of torrents in transfers tab label
- COSMETIC: Simplified program preferences
- COSMETIC: Fix naming of actions opening new dialogs (use Name...)
* Sun Mar 14 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.2.0
- FEATURE: User can set alternative speed limits for fast toggling
- FEATURE: Bandwidth scheduler (automatically use alternative speed limits for a given period)
- FEATURE: Added "Added/Completed On" columns to transfer list
- FEATURE: Added "Upload/Download limit" columns to transfer list
- FEATURE: Torrent files can be exported to a given directory
- FEATURE: Outgoing ports range can be customized (for QoS)
- FEATURE: User can choose to apply transfer limits on LAN too
- FEATURE: User can choose to include the protocol overhead in transfer limits
- FEATURE: Torrents can be automatically rechecked on completion
- FEATURE: If 2 torrents have the same hash, add new trackers/URL seeds to the existing torrent
- FEATURE: Trackers can be added from Web UI
- FEATURE: Global transfer information are displayed in the new Web UI status bar
- FEATURE: Allow to change the priority of several files at once
- FEATURE: Support for multiple scan folders (Patch by Christian Kandeler)
- BUGFIX: Only one log window can be opened at a time
- BUGFIX: Optimized RSS module memory usage
- BUGFIX: Consider HTTP downloads >1MB as invalid .torrent files and abort
- BUGFIX: Fix Web UI authentication with some browsers
- BUGFIX: Set Web UI ban period to 1 hour
- COSMETIC: Improved style management
* Mon Jan 18 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.1.0
- FEATURE: Graphical User Interface can be disabled at compilation time (headless running)
- FEATURE: Torrents can be labeled/categorized
- FEATURE: Labeled torrent can be downloaded corresponding subfolders
- FEATURE: Disk cache size can be set from preferences
- FEATURE: Peer Exchange (PeX) can be disabled from preferences
- FEATURE: Append !.qB extension to incomplete files option (libtorrent >= v0.15 only)
- FEATURE: Torrent files/folders can be renamed (torrent addition dialog or files properties)
- FEATURE: uTorrent compatible tracker list support (use torrentz.com url as a default)
- FEATURE: Better proxy support and preferences remodeling
- FEATURE: qBittorrent can identify itself as uTorrent, Vuze or KTorrent (Any stable version)
- FEATURE: Torrents can be renamed in transfer list
- FEATURE: Display torrent addition dialog for magnet links too
- FEATURE: Files contained in a torrent are opened on double click (files panel)
- FEATURE: Added support for magnet links in search engine
- FEATURE: Added vertor.com and torrentdownloads.net search plugins
- FEATURE: Search engine can now use a SOCKS5 proxy
- FEATURE: HTTP proxy support for peer communication
- BUGFIX: Search engine loads new proxy settings without program restart
- BUGFIX: Use XDG folders (.cache, .local) instead of .qbittorrent
- BUGFIX: Added legal notice on startup that the user must accept
- BUGFIX: Protect Web UI authentication against brute forcing
- BUGFIX: Use HTTP digest mode for Web UI authentication (instead of Basic)
- BUGFIX: Properly display torrents with one file in subfolder(s)
- BUGFIX: Display Web UI favicon
- BUGFIX: File priority can be set for finished torrents that have filtered files
- COSMETIC: Use checkboxes to filter torrent content instead of comboboxes
- COSMETIC: Use alternating row colors in transfer list (set in program preferences)
- COSMETIC: Added a spin box to speed limiting dialog for manual input
* Mon Jan 11 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.0.7
- BUGFIX: Fix 'Add in pause' setting in torrent addition dialog
- BUGFIX: Update RSS feed as soon as feed downloader is enabled
- BUGFIX: RSS Feed downloader ignores articles above maximum number of articles
- BUGFIX: Fix possible bug when deleting a RSS folder
- BUGFIX: Remove persistant data when a RSS feed is deleted
- BUGFIX: RSS filters are now alphabetically sorted
- BUGFIX: Fix crash when renaming currently displayed RSS filter
- BUGFIX: Remove overwriting confirmation when exporting RSS filters since Qt takes care of it
* Tue Jan 5 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.0.6
- BUGFIX: Fix detection of invalid torrent files - BUGFIX: Fix detection of invalid torrent files
- BUGFIX: Stop catching signals once one has been caught to avoid possible infinite loop - BUGFIX: Stop catching signals once one has been caught to avoid possible infinite loop
- BUGFIX: Force data recheck whenever a torrent is moved - BUGFIX: Force data recheck whenever a torrent is moved

63
INSTALL
View File

@@ -1,57 +1,32 @@
qBittorrent - A BitTorrent client in C++ / Qt4 qBittorrent - A BitTorrent client in C++ / Qt4
------------------------------------------ ------------------------------------------
1) Compile and install qBittorrent with Qt4 Graphical Interface ./configure
make && make install
qbittorrent
$ ./configure will install and execute qBittorrent hopefully without any problems.
$ make && make install
$ qbittorrent
will install and execute qBittorrent hopefully without any problems. Dependencies:
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
Dependencies: - libtorrent-rasterbar by Arvid Norberg (>= 0.14.0 REQUIRED, >= v0.15.0 ADVISED)
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml) -> http://www.qbittorrent.org/download.php (advised)
-> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name.
- pkg-config executable - libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, compatible with v0.15.x) - python >= 2.3 (needed by search engine)
-> http://www.libtorrent.net * Run time only dependency
Be careful: another library (the one used by rTorrent) uses a similar name.
- libboost 1.34.x (libboost-filesystem, libboost-thread, libboost-date-time) + libasio - libnotify >= 0.4.2, glib-2.0 (optional)
or * Can be used for system notifications to replace standard Qt notifications
- libboost >= 1.35.x (libboost-system, libboost-filesystem, libboost-thread, libboost-date-time) so that it integrates better into the Desktop
- python >= 2.3 (needed by search engine)
* Run time only dependency
- libnotify >= 0.4.2, glib-2.0 (optional)
* Can be used for system notifications to replace standard Qt notifications
so that it integrates better into the Desktop
- geoip-database (optional)
* If qBittorrent cannot find this database, it will try to resolve countries using the Internet but it will be a lot slower.
* Run time only dependency
2) Compile and install qBittorrent without Qt4 Graphical interface
$ ./configure --disable-gui
$ make && make install
$ qbittorrent
will install and execute qBittorrent hopefully without any problems.
Dependencies:
- Qt >= 4.4.0 (libqt-devel, libqtcore, libqtnetwork)
- pkg-config executable
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, >= v0.15.0 ADVISED)
-> http://www.libtorrent.net
Be careful: another library (the one used by rTorrent) uses a similar name.
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
- geoip-database (optional)
* If qBittorrent cannot find this database, it will try to resolve countries using the Internet but it will be a lot slower.
* Run time only dependency
DOCUMENTATION: DOCUMENTATION:
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org. Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.

View File

@@ -1,62 +0,0 @@
qBittorrent - A BitTorrent client in Qt4
------------------------------------------
This is the eComStation (OS/2) qBittorrent part of the readme. See also README for more general information.
Building qBittorrent
********************
Requirements
============
- gcc based build env (recommended gcc v4.4.2 or greater)
- Qt4 for eCS (OS/2) dev package (see http://svn.netlabs.org/qt4 for more information)
- libtorrent-rasterbar for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
- boost for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
How to build
============
First you need to create the conf.pri file in the same dir as this readme.os2 is.
the conf.pri file has the following content:
##### conf.pri content beginn #####
PREFIX = .
BINDIR = ./bin
INCDIR = ./include
LIBDIR = ./lib
DATADIR = ./share
CONFIG += staticlib
INCLUDEPATH += x:/trees/libtorrent/trunk/include
LIBS += -Lx:/trees/libtorrent/trunk/src/.libs \
-Lx:/trees/boost/trunk/stage/lib \
-Lx:/trees/openssl \
-Lx:/extras/lib
##### conf.pri content end #####
Of course all the above path references have to be adjusted to your build env.
It should now be easy to build qBittorrent:
Simply type:
$ qmake
Followed by:
$ make
If all works fine you should get a working qbittorrent executable.
If you have any question regarding the eCS (OS/2) port of qBittorrent you can meet me (_diver) on IRC:
#netlabs on irc.freenode.net
------------------------------------------
Silvan Scherrer <silvan.scherrer@aroa.ch>

215
configure vendored
View File

@@ -18,24 +18,12 @@ Main options:
--help This help text. --help This help text.
Dependency options: Dependency options:
--disable-gui Disable qBittorrent --with-libboost-inc=[path] Path to libboost include files
Graphical user interface for --disable-libnotify Disable use of libnotify
headless running --disable-geoip-database Disable use of geoip-database
--with-libboost-inc=[path] Path to libboost include --with-geoip-database-embedded Geoip Database will be embedded in
files qBittorrent executable (please follow
--with-libboost-lib=[path] Path to libboost library instructions in src/geoip/README)
files
--disable-libnotify Disable use of libnotify
--disable-geoip-database Disable use of geoip-database
--with-geoip-database-embedded Geoip Database will be
embedded in qBittorrent
executable (please follow
instructions in
src/geoip/README)
--disable-qtsingleapplication Disable use of libboost
--with-qtsingleapplication=[system|shipped] Use the shipped
qtsingleapplication library
or the system one
EOT EOT
} }
@@ -152,21 +140,11 @@ while [ $# -gt 0 ]; do
shift shift
;; ;;
--disable-gui)
QC_DISABLE_GUI="Y"
shift
;;
--with-libboost-inc=*) --with-libboost-inc=*)
QC_WITH_LIBBOOST_INC=$optarg QC_WITH_LIBBOOST_INC=$optarg
shift shift
;; ;;
--with-libboost-lib=*)
QC_WITH_LIBBOOST_LIB=$optarg
shift
;;
--disable-libnotify) --disable-libnotify)
QC_DISABLE_libnotify="Y" QC_DISABLE_libnotify="Y"
shift shift
@@ -182,16 +160,6 @@ while [ $# -gt 0 ]; do
shift shift
;; ;;
--disable-qtsingleapplication)
QC_DISABLE_qtsingleapplication="Y"
shift
;;
--with-qtsingleapplication=*)
QC_WITH_QTSINGLEAPPLICATION=$optarg
shift
;;
--verbose) --verbose)
QC_VERBOSE="Y" QC_VERBOSE="Y"
shift shift
@@ -213,14 +181,10 @@ echo PREFIX=$PREFIX
echo BINDIR=$BINDIR echo BINDIR=$BINDIR
echo DATADIR=$DATADIR echo DATADIR=$DATADIR
echo EX_QTDIR=$EX_QTDIR echo EX_QTDIR=$EX_QTDIR
echo QC_DISABLE_GUI=$QC_DISABLE_GUI
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
echo QC_WITH_LIBBOOST_LIB=$QC_WITH_LIBBOOST_LIB
echo QC_DISABLE_libnotify=$QC_DISABLE_libnotify echo QC_DISABLE_libnotify=$QC_DISABLE_libnotify
echo QC_DISABLE_geoip_database=$QC_DISABLE_geoip_database echo QC_DISABLE_geoip_database=$QC_DISABLE_geoip_database
echo QC_WITH_GEOIP_DATABASE_EMBEDDED=$QC_WITH_GEOIP_DATABASE_EMBEDDED echo QC_WITH_GEOIP_DATABASE_EMBEDDED=$QC_WITH_GEOIP_DATABASE_EMBEDDED
echo QC_DISABLE_qtsingleapplication=$QC_DISABLE_qtsingleapplication
echo QC_WITH_QTSINGLEAPPLICATION=$QC_WITH_QTSINGLEAPPLICATION
echo echo
fi fi
@@ -326,7 +290,6 @@ cat >$1/modules.cpp <<EOT
/* /*
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: Qt >= 4.4 name: Qt >= 4.4
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
-----END QCMOD----- -----END QCMOD-----
*/ */
class qc_qt4 : public ConfObj class qc_qt4 : public ConfObj
@@ -337,27 +300,11 @@ public:
QString shortname() const { return "Qt 4.4"; } QString shortname() const { return "Qt 4.4"; }
bool exec() bool exec()
{ {
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) { if(QT_VERSION >= 0x040500) {
conf->addDefine("DISABLE_GUI"); conf->addDefine("QT_4_5");
} }
return(QT_VERSION >= 0x040400); return(QT_VERSION >= 0x040400);
}
};
#line 1 "pkg-config.qcm"
/*
-----BEGIN QCMOD-----
name: pkg-config
-----END QCMOD-----
*/
#include <QProcess>
class qc_pkg_config : public ConfObj
{
public:
qc_pkg_config(Conf *c) : ConfObj(c) {}
QString name() const { return "pkg-config executable"; }
QString shortname() const { return "pkg-config"; }
bool exec(){
return !conf->findProgram("pkg-config").isEmpty();
} }
}; };
#line 1 "libtorrent-rasterbar.qcm" #line 1 "libtorrent-rasterbar.qcm"
@@ -371,20 +318,24 @@ class qc_libtorrent_rasterbar : public ConfObj
{ {
public: public:
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {} qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
QString name() const { return "libtorrent-rasterbar >= 0.14.4"; } QString name() const { return "libtorrent-rasterbar >= 0.14.0 (>= 0.15.0 advised)"; }
QString shortname() const { return "libtorrent-rasterbar"; } QString shortname() const { return "libtorrent-rasterbar"; }
bool exec(){ bool exec(){
QStringList incs; QStringList incs;
QString req_ver = "0.14.4"; QString req_ver = "0.14.0";
QString adv_ver = "0.15.0";
QString version, libs, other; QString version, libs, other;
VersionMode mode = VersionMin; VersionMode mode = VersionMin;
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other)) if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
return false; return false;
for(int n = 0; n < incs.count(); ++n) for(int n = 0; n < incs.count(); ++n)
conf->addIncludePath(incs[n]); conf->addIncludePath(incs[n]);
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) { if(!libs.isEmpty())
conf->addLib("-lcrypto"); conf->addLib(libs);
} if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
printf("\nWarning: libtorrent-rasterbar v%s was detected.\nAlthough it will compile and run, you will be missing some features. Please consider updating to v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
else
conf->addDefine("LIBTORRENT_0_15");
return true; return true;
} }
}; };
@@ -393,39 +344,14 @@ public:
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: libboost name: libboost
arg: with-libboost-inc=[path], Path to libboost include files arg: with-libboost-inc=[path], Path to libboost include files
arg: with-libboost-lib=[path], Path to libboost library files
-----END QCMOD----- -----END QCMOD-----
*/ */
#include <boost/version.hpp>
class qc_libboost : public ConfObj class qc_libboost : public ConfObj
{ {
public: public:
qc_libboost(Conf *c) : ConfObj(c) {} qc_libboost(Conf *c) : ConfObj(c) {}
QString name() const { return "libboost"; } QString name() const { return "libboost"; }
QString shortname() const { return "libboost"; } QString shortname() const { return "libboost"; }
QString findBoostLib(QString path, QString lib) const {
QString name;
QDir libDir(path);
QStringList filters;
filters << "libboost_"+lib+"*-mt*.so";
QStringList result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
} else {
// Fall back to non -mt boost lib
filters.clear();
filters << "libboost_"+lib+"*.so";
result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
}
}
return name;
}
bool exec(){ bool exec(){
QString s; QString s;
s = conf->getenv("QC_WITH_LIBBOOST_INC"); s = conf->getenv("QC_WITH_LIBBOOST_INC");
@@ -467,43 +393,6 @@ public:
} }
} }
conf->addIncludePath(s); conf->addIncludePath(s);
// Find library
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
QStringList required_libs;
#if BOOST_VERSION >= 103500
required_libs << "system";
#endif
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
// Not required by nox
required_libs << "filesystem" << "thread";
}
QStringList libDirs;
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
foreach(const QString& lib, required_libs) {
if(!s.isEmpty()) {
QString detected_name = findBoostLib(s, lib);
if(detected_name.isEmpty()) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
} else {
conf->addLib("-l"+detected_name);
}
} else {
bool found = false;
foreach(const QString& libDir, libDirs) {
QString detected_name = findBoostLib(libDir, lib);
if(!detected_name.isEmpty()) {
conf->addLib("-l"+detected_name);
found = true;
break;
}
}
if(!found) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
}
}
}
return true; return true;
} }
}; };
@@ -520,15 +409,7 @@ public:
qc_libnotify(Conf *c) : ConfObj(c) {} qc_libnotify(Conf *c) : ConfObj(c) {}
QString name() const { return "libnotify >= 0.4.2 (optional)"; } QString name() const { return "libnotify >= 0.4.2 (optional)"; }
QString shortname() const { return "libnotify"; } QString shortname() const { return "libnotify"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec(){ bool exec(){
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
return false;
}
QStringList incs; QStringList incs;
QString req_ver = "0.4.2"; QString req_ver = "0.4.2";
QString version, libs, other; QString version, libs, other;
@@ -550,21 +431,10 @@ public:
} else { } else {
return false; return false;
} }
QStringList incs3;
QString req_ver3 = "2.0";
QString version3, libs3, other3;
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
for(int n = 0; n < incs3.count(); ++n)
conf->addIncludePath(incs3[n]);
if(!libs3.isEmpty())
conf->addLib(libs3);
} else {
return false;
}
} else { } else {
return false; return false;
} }
return true; return true;
} }
}; };
@@ -582,15 +452,7 @@ public:
qc_geoip_database(Conf *c) : ConfObj(c) {} qc_geoip_database(Conf *c) : ConfObj(c) {}
QString name() const { return "GeoIP Database (optional)"; } QString name() const { return "GeoIP Database (optional)"; }
QString shortname() const { return "GeoIP Database"; } QString shortname() const { return "GeoIP Database"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec() { bool exec() {
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
return false;
}
#ifdef Q_WS_X11 #ifdef Q_WS_X11
if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) { if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) {
#endif #endif
@@ -606,42 +468,12 @@ public:
#endif #endif
} }
}; };
#line 1 "qtsingleapplication.qcm"
/*
-----BEGIN QCMOD-----
name: libboost
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
-----END QCMOD-----
*/
class qc_qtsingleapplication : public ConfObj
{
public:
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
QString name() const { return "qtsingleapplication library"; }
QString shortname() const { return "qtsingleapplication"; }
bool exec(){
QString s;
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
if(s.compare("system", Qt::CaseInsensitive) == 0) {
// System
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
printf(" [system] ");
} else {
printf(" [shipped] ");
}
return true;
}
};
EOT EOT
cat >$1/modules_new.cpp <<EOT cat >$1/modules_new.cpp <<EOT
o = new qc_qt4(conf); o = new qc_qt4(conf);
o->required = true; o->required = true;
o->disabled = false; o->disabled = false;
o = new qc_pkg_config(conf);
o->required = true;
o->disabled = false;
o = new qc_libtorrent_rasterbar(conf); o = new qc_libtorrent_rasterbar(conf);
o->required = true; o->required = true;
o->disabled = false; o->disabled = false;
@@ -654,9 +486,6 @@ cat >$1/modules_new.cpp <<EOT
o = new qc_geoip_database(conf); o = new qc_geoip_database(conf);
o->required = false; o->required = false;
o->disabled = false; o->disabled = false;
o = new qc_qtsingleapplication(conf);
o->required = false;
o->disabled = false;
EOT EOT
cat >$1/conf4.h <<EOT cat >$1/conf4.h <<EOT
@@ -1602,14 +1431,10 @@ export PREFIX
export BINDIR export BINDIR
export DATADIR export DATADIR
export EX_QTDIR export EX_QTDIR
export QC_DISABLE_GUI
export QC_WITH_LIBBOOST_INC export QC_WITH_LIBBOOST_INC
export QC_WITH_LIBBOOST_LIB
export QC_DISABLE_libnotify export QC_DISABLE_libnotify
export QC_DISABLE_geoip_database export QC_DISABLE_geoip_database
export QC_WITH_GEOIP_DATABASE_EMBEDDED export QC_WITH_GEOIP_DATABASE_EMBEDDED
export QC_DISABLE_qtsingleapplication
export QC_WITH_QTSINGLEAPPLICATION
export QC_VERBOSE export QC_VERBOSE
rm -rf .qconftemp rm -rf .qconftemp
( (

View File

@@ -1,44 +0,0 @@
.\" This manpage has been automatically generated by docbook2man
.\" from a DocBook document. This tool can be found at:
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
.TH "QBITTORRENT\-NOX" "1" "January 16th 2010" "Command line Bittorrent client written in C++ / Qt4" ""
.SH "NAME"
qBittorrent\-nox \- a command line Bittorrent client written in C++ / Qt4
.SH "SYNOPSIS"
\fBqbittorrent\-nox\fR [\-\-webui-port=x] [TORRENT_FILE | URL]...
\fBqbittorrent\-nox\fR \-\-help
\fBqbittorrent\-nox\fR \-\-version
.PP
.SH "DESCRIPTION"
\fBqBittorrent-nox\fR is an advanced command-line Bittorrent client written in C++ / Qt4,
using the \fBlibtorrent-rasterbar\fR library by Arvid Norberg. qBittorrent\-nox aims
to be a good alternative to other command line bittorrent clients and provides features similar to popular graphical clients.
qBittorrent\-nox is fast, stable, light and it supports unicode.
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze compatible),
FAST extension (mainline) and PeX support (utorrent compatible).
qBittorrent\-nox is meant to be controlled via its feature-rich Web UI which is accessible as a default on http://localhost:8080. The Web UI access is secured and the default account user name is "admin" with "adminadmin" as a password.
.SH "OPTIONS"
\fB--help\fR Prints the command line options.
\fB--version\fR Prints qbittorrent program version number.
\fB--webui-port=x\fR Changes Web UI port to x (default: 8080).
.SH "BUGS"
If you find a bug, please report it at http://bugs.qbittorrent.org
.SH "AUTHOR"
Christophe Dumez <chris@qbittorrent.org>

View File

@@ -1,14 +1,14 @@
.\" This manpage has been automatically generated by docbook2man .\" This manpage has been automatically generated by docbook2man
.\" from a DocBook document. This tool can be found at: .\" from a DocBook document. This tool can be found at:
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>. .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
.TH "QBITTORRENT" "1" "January 16th 2010" "Bittorrent client written in C++ / Qt4" "" .TH "QBITTORRENT" "1" "September 30th 2009" "Bittorrent client written in C++ / Qt4" ""
.SH "NAME" .SH "NAME"
qBittorrent \- a Bittorrent client written in C++ / Qt4 qBittorrent \- a Bittorrent client written in C++ / Qt4
.SH "SYNOPSIS" .SH "SYNOPSIS"
\fBqbittorrent\fR [\-\-no-splash] [\-\-webui-port=x] [TORRENT_FILE | URL]... \fBqbittorrent\fR [\-\-no-splash] [TORRENT_FILE | URL]...
\fBqbittorrent\fR \-\-help \fBqbittorrent\fR \-\-help
@@ -18,9 +18,10 @@ qBittorrent \- a Bittorrent client written in C++ / Qt4
.SH "DESCRIPTION" .SH "DESCRIPTION"
\fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4, \fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4,
using the \fBlibtorrent-rasterbar\fR library by Arvid Norberg. qBittorrent is similar to uTorrent. qBittorrent using the \fBrblibtorrent\fR library by Arvid Norberg. qBittorrent aims
to be a good alternative to all other bittorrent clients out there. qBittorrent
is fast, stable, light, it supports unicode and it provides a good integrated search engine. is fast, stable, light, it supports unicode and it provides a good integrated search engine.
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze compatible), It also comes with UPnP port forwarding / NAT-PMP, encryption (Azureus compatible),
FAST extension (mainline) and PeX support (utorrent compatible). FAST extension (mainline) and PeX support (utorrent compatible).
.SH "OPTIONS" .SH "OPTIONS"
@@ -31,8 +32,6 @@ FAST extension (mainline) and PeX support (utorrent compatible).
\fB--no-splash\fR Disables splash screen on startup. \fB--no-splash\fR Disables splash screen on startup.
\fB--webui-port=x\fR Changes Web UI port to x (default: 8080).
.SH "BUGS" .SH "BUGS"
If you find a bug, please report it at http://bugs.qbittorrent.org If you find a bug, please report it at http://bugs.qbittorrent.org

View File

@@ -1,4 +1,6 @@
TEMPLATE = subdirs TEMPLATE = subdirs
include(conf.pri)
SUBDIRS += src SUBDIRS += src

View File

@@ -6,9 +6,6 @@
<dep type='qt4'> <dep type='qt4'>
<required/> <required/>
</dep> </dep>
<dep type='pkg-config'>
<required/>
</dep>
<dep type='libtorrent-rasterbar'> <dep type='libtorrent-rasterbar'>
<required/> <required/>
</dep> </dep>
@@ -19,6 +16,4 @@
</dep> </dep>
<dep type='geoip-database'> <dep type='geoip-database'>
</dep> </dep>
<dep type='qtsingleapplication'>
</dep>
</qconf> </qconf>

View File

@@ -11,15 +11,7 @@ public:
qc_geoip_database(Conf *c) : ConfObj(c) {} qc_geoip_database(Conf *c) : ConfObj(c) {}
QString name() const { return "GeoIP Database (optional)"; } QString name() const { return "GeoIP Database (optional)"; }
QString shortname() const { return "GeoIP Database"; } QString shortname() const { return "GeoIP Database"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec() { bool exec() {
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
return false;
}
#ifdef Q_WS_X11 #ifdef Q_WS_X11
if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) { if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) {
#endif #endif

View File

@@ -2,39 +2,14 @@
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: libboost name: libboost
arg: with-libboost-inc=[path], Path to libboost include files arg: with-libboost-inc=[path], Path to libboost include files
arg: with-libboost-lib=[path], Path to libboost library files
-----END QCMOD----- -----END QCMOD-----
*/ */
#include <boost/version.hpp>
class qc_libboost : public ConfObj class qc_libboost : public ConfObj
{ {
public: public:
qc_libboost(Conf *c) : ConfObj(c) {} qc_libboost(Conf *c) : ConfObj(c) {}
QString name() const { return "libboost"; } QString name() const { return "libboost"; }
QString shortname() const { return "libboost"; } QString shortname() const { return "libboost"; }
QString findBoostLib(QString path, QString lib) const {
QString name;
QDir libDir(path);
QStringList filters;
filters << "libboost_"+lib+"*-mt*.so";
QStringList result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
} else {
// Fall back to non -mt boost lib
filters.clear();
filters << "libboost_"+lib+"*.so";
result = libDir.entryList(filters, QDir::Files);
if(!result.empty()) {
name = result.first().mid(3);
// Remove .so
name.chop(3);
}
}
return name;
}
bool exec(){ bool exec(){
QString s; QString s;
s = conf->getenv("QC_WITH_LIBBOOST_INC"); s = conf->getenv("QC_WITH_LIBBOOST_INC");
@@ -76,43 +51,6 @@ public:
} }
} }
conf->addIncludePath(s); conf->addIncludePath(s);
// Find library
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
QStringList required_libs;
#if BOOST_VERSION >= 103500
required_libs << "system";
#endif
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
// Not required by nox
required_libs << "filesystem" << "thread";
}
QStringList libDirs;
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
foreach(const QString& lib, required_libs) {
if(!s.isEmpty()) {
QString detected_name = findBoostLib(s, lib);
if(detected_name.isEmpty()) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
} else {
conf->addLib("-l"+detected_name);
}
} else {
bool found = false;
foreach(const QString& libDir, libDirs) {
QString detected_name = findBoostLib(libDir, lib);
if(!detected_name.isEmpty()) {
conf->addLib("-l"+detected_name);
found = true;
break;
}
}
if(!found) {
printf("Could not find boost %s library!\n", qPrintable(lib));
return false;
}
}
}
return true; return true;
} }
}; };

View File

@@ -10,15 +10,7 @@ public:
qc_libnotify(Conf *c) : ConfObj(c) {} qc_libnotify(Conf *c) : ConfObj(c) {}
QString name() const { return "libnotify >= 0.4.2 (optional)"; } QString name() const { return "libnotify >= 0.4.2 (optional)"; }
QString shortname() const { return "libnotify"; } QString shortname() const { return "libnotify"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec(){ bool exec(){
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
return false;
}
QStringList incs; QStringList incs;
QString req_ver = "0.4.2"; QString req_ver = "0.4.2";
QString version, libs, other; QString version, libs, other;
@@ -40,21 +32,10 @@ public:
} else { } else {
return false; return false;
} }
QStringList incs3;
QString req_ver3 = "2.0";
QString version3, libs3, other3;
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
for(int n = 0; n < incs3.count(); ++n)
conf->addIncludePath(incs3[n]);
if(!libs3.isEmpty())
conf->addLib(libs3);
} else {
return false;
}
} else { } else {
return false; return false;
} }
return true; return true;
} }
}; };

View File

@@ -8,20 +8,24 @@ class qc_libtorrent_rasterbar : public ConfObj
{ {
public: public:
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {} qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
QString name() const { return "libtorrent-rasterbar >= 0.14.4"; } QString name() const { return "libtorrent-rasterbar >= 0.14.0 (>= 0.15.0 advised)"; }
QString shortname() const { return "libtorrent-rasterbar"; } QString shortname() const { return "libtorrent-rasterbar"; }
bool exec(){ bool exec(){
QStringList incs; QStringList incs;
QString req_ver = "0.14.4"; QString req_ver = "0.14.0";
QString adv_ver = "0.15.0";
QString version, libs, other; QString version, libs, other;
VersionMode mode = VersionMin; VersionMode mode = VersionMin;
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other)) if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
return false; return false;
for(int n = 0; n < incs.count(); ++n) for(int n = 0; n < incs.count(); ++n)
conf->addIncludePath(incs[n]); conf->addIncludePath(incs[n]);
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) { if(!libs.isEmpty())
conf->addLib("-lcrypto"); conf->addLib(libs);
} if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
printf("\nWarning: libtorrent-rasterbar v%s was detected.\nAlthough it will compile and run, you will be missing some features. Please consider updating to v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
else
conf->addDefine("LIBTORRENT_0_15");
return true; return true;
} }
}; };

View File

@@ -1,16 +0,0 @@
/*
-----BEGIN QCMOD-----
name: pkg-config
-----END QCMOD-----
*/
#include <QProcess>
class qc_pkg_config : public ConfObj
{
public:
qc_pkg_config(Conf *c) : ConfObj(c) {}
QString name() const { return "pkg-config executable"; }
QString shortname() const { return "pkg-config"; }
bool exec(){
return !conf->findProgram("pkg-config").isEmpty();
}
};

View File

@@ -1,7 +1,6 @@
/* /*
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: Qt >= 4.4 name: Qt >= 4.4
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
-----END QCMOD----- -----END QCMOD-----
*/ */
class qc_qt4 : public ConfObj class qc_qt4 : public ConfObj
@@ -12,9 +11,10 @@ public:
QString shortname() const { return "Qt 4.4"; } QString shortname() const { return "Qt 4.4"; }
bool exec() bool exec()
{ {
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) { if(QT_VERSION >= 0x040500) {
conf->addDefine("DISABLE_GUI"); conf->addDefine("QT_4_5");
} }
return(QT_VERSION >= 0x040400); return(QT_VERSION >= 0x040400);
} }
}; };

View File

@@ -1,26 +0,0 @@
/*
-----BEGIN QCMOD-----
name: libboost
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
-----END QCMOD-----
*/
class qc_qtsingleapplication : public ConfObj
{
public:
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
QString name() const { return "qtsingleapplication library"; }
QString shortname() const { return "qtsingleapplication"; }
bool exec(){
QString s;
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
if(s.compare("system", Qt::CaseInsensitive) == 0) {
// System
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
printf(" [system] ");
} else {
printf(" [shipped] ");
}
return true;
}
};

File diff suppressed because it is too large Load Diff

219
src/GUI.h
View File

@@ -37,10 +37,13 @@
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
enum TabIndex{TAB_TRANSFER, TAB_SEARCH, TAB_RSS};
class Bittorrent; class Bittorrent;
class QTimer; class QTimer;
class downloadFromURL; class downloadFromURL;
class SearchEngine; class SearchEngine;
class QLocalServer;
class QCloseEvent; class QCloseEvent;
class RSSImp; class RSSImp;
class QShortcut; class QShortcut;
@@ -52,138 +55,108 @@ class TransferListFiltersWidget;
class QSplitter; class QSplitter;
class PropertiesWidget; class PropertiesWidget;
class StatusBar; class StatusBar;
class consoleDlg;
class about;
class createtorrent;
class downloadFromURL;
class HidableTabWidget;
class LineEdit;
class QFileSystemWatcher;
class GUI : public QMainWindow, private Ui::MainWindow{ class GUI : public QMainWindow, private Ui::MainWindow{
Q_OBJECT Q_OBJECT
public: private:
// Construct / Destruct // Bittorrent
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList()); Bittorrent *BTSession;
~GUI(); QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed?
// Methods // GUI related
QWidget* getCurrentTabWidget() const; QTimer *guiUpdater;
TransferListWidget* getTransferList() const { return transferList; } QTabWidget *tabs;
QMenu* getTrayIconMenu(); StatusBar *status_bar;
PropertiesWidget *getProperties() const { return properties; } QPointer<options_imp> options;
QPointer<QSystemTrayIcon> systrayIcon;
QPointer<QTimer> systrayCreator;
QMenu *myTrayIconMenu;
TransferListWidget *transferList;
TransferListFiltersWidget *transferListFilters;
PropertiesWidget *properties;
bool displaySpeedInTitle;
bool force_exit;
// Keyboard shortcuts
QShortcut *switchSearchShortcut;
QShortcut *switchSearchShortcut2;
QShortcut *switchTransferShortcut;
QShortcut *switchRSSShortcut;
// Widgets
QAction *prioSeparator;
QAction *prioSeparator2;
QSplitter *hSplitter;
QSplitter *vSplitter;
// Search
SearchEngine *searchEngine;
// RSS
QPointer<RSSImp> rssWidget;
// Misc
QLocalServer *localServer;
public slots: protected slots:
void trackerAuthenticationRequired(QTorrentHandle& h); // GUI related slots
void setTabText(int index, QString text) const; void dropEvent(QDropEvent *event);
void showNotificationBaloon(QString title, QString msg) const; void dragEnterEvent(QDragEnterEvent *event);
void downloadFromURLList(const QStringList& urls); void toggleVisibility(QSystemTrayIcon::ActivationReason e);
void updateAltSpeedsBtn(bool alternative); void on_actionAbout_triggered();
void updateNbTorrents(unsigned int nb_downloading, unsigned int nb_seeding, unsigned int nb_active, unsigned int nb_inactive, unsigned int nb_paused); void on_actionCreate_torrent_triggered();
void deleteBTSession(); void on_actionWebsite_triggered() const;
void on_actionBugReport_triggered() const;
void on_actionShow_console_triggered();
void readParamsOnSocket();
void acceptConnection();
void balloonClicked();
void writeSettings();
void readSettings();
void on_actionExit_triggered();
void createTrayIcon();
void fullDiskError(QTorrentHandle& h, QString msg) const;
void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
void tab_changed(int);
// Keyboard shortcuts
void createKeyboardShortcuts();
void displayTransferTab() const;
void displaySearchTab() const;
void displayRSSTab() const;
// Torrent actions
void on_actionSet_global_upload_limit_triggered();
void on_actionSet_global_download_limit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void updateGUI();
void loadPreferences(bool configure_session=true);
void processParams(const QStringList& params);
void addTorrent(QString path);
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
void processDownloadedFiles(QString path, QString url);
void downloadFromURLList(const QStringList& urls);
void finishedTorrent(QTorrentHandle& h) const;
// Options slots
void on_actionOptions_triggered();
void optionsSaved();
// HTTP slots
void on_actionDownload_from_URL_triggered();
protected slots:
// GUI related slots
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void toggleVisibility(QSystemTrayIcon::ActivationReason e);
void on_actionAbout_triggered();
void on_actionCreate_torrent_triggered();
void on_actionWebsite_triggered() const;
void on_actionBugReport_triggered() const;
void on_actionShow_console_triggered();
void balloonClicked();
void writeSettings();
void readSettings();
void on_actionExit_triggered();
void createTrayIcon();
void fullDiskError(QTorrentHandle& h, QString msg) const;
void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
void tab_changed(int);
void on_actionLock_qBittorrent_triggered();
void defineUILockPassword();
bool unlockUI();
void notifyOfUpdate(QString);
// Keyboard shortcuts
void createKeyboardShortcuts();
void displayTransferTab() const;
void displaySearchTab() const;
void displayRSSTab() const;
// Torrent actions
void on_actionSet_global_upload_limit_triggered();
void on_actionSet_global_download_limit_triggered();
void on_actionDocumentation_triggered() const;
void on_actionOpen_triggered();
void updateGUI();
void loadPreferences(bool configure_session=true);
void processParams(const QString& params);
void processParams(const QStringList& params);
void addTorrent(QString path);
void addUnauthenticatedTracker(const QPair<QTorrentHandle,QString> &tracker);
void processDownloadedFiles(QString path, QString url);
void finishedTorrent(QTorrentHandle& h) const;
void askRecursiveTorrentDownloadConfirmation(QTorrentHandle &h);
// Options slots
void on_actionOptions_triggered();
void optionsSaved();
// HTTP slots
void on_actionDownload_from_URL_triggered();
protected: public slots:
void closeEvent(QCloseEvent *); void trackerAuthenticationRequired(QTorrentHandle& h);
void showEvent(QShowEvent *); void setTabText(int index, QString text) const;
bool event(QEvent * event); void showNotificationBaloon(QString title, QString msg) const;
void displayRSSTab(bool enable);
void displaySearchTab(bool enable);
private: protected:
QFileSystemWatcher *executable_watcher; void closeEvent(QCloseEvent *);
// Bittorrent void showEvent(QShowEvent *);
Bittorrent *BTSession; bool event(QEvent * event);
QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed? void displayRSSTab(bool enable);
// GUI related
QTimer *guiUpdater;
HidableTabWidget *tabs;
StatusBar *status_bar;
QPointer<options_imp> options;
QPointer<consoleDlg> console;
QPointer<about> aboutDlg;
QPointer<createtorrent> createTorrentDlg;
QPointer<downloadFromURL> downloadFromURLDialog;
QPointer<QSystemTrayIcon> systrayIcon;
QPointer<QTimer> systrayCreator;
QPointer<QMenu> myTrayIconMenu;
TransferListWidget *transferList;
TransferListFiltersWidget *transferListFilters;
PropertiesWidget *properties;
bool displaySpeedInTitle;
bool force_exit;
bool ui_locked;
LineEdit *search_filter;
// Keyboard shortcuts
QShortcut *switchSearchShortcut;
QShortcut *switchSearchShortcut2;
QShortcut *switchTransferShortcut;
QShortcut *switchRSSShortcut;
// Widgets
QAction *prioSeparator;
QAction *prioSeparator2;
QSplitter *hSplitter;
QSplitter *vSplitter;
QMenu *lockMenu;
// Search
QPointer<SearchEngine> searchEngine;
// RSS
QPointer<RSSImp> rssWidget;
private slots: public:
void on_actionSearch_engine_triggered(); // Construct / Destruct
void on_actionRSS_Reader_triggered(); GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
void on_actionSpeed_in_title_bar_triggered(); ~GUI();
void on_actionTop_tool_bar_triggered(); // Methods
void on_actionShutdown_when_downloads_complete_triggered(); int getCurrentTabIndex() const;
void on_actionDonate_money_triggered(); QPoint screenCenter() const;
}; };
#endif #endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 B

BIN
src/Icons/locale.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 B

BIN
src/Icons/oxygen/wallet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

View File

@@ -1,9 +1,8 @@
[Desktop Entry] [Desktop Entry]
Categories=Qt;Network;P2P; Categories=Qt;Network;P2P;
Comment=V2.4.10 Comment=V2.0.6
Exec=qbittorrent %f Exec=qbittorrent %f
GenericName=Bittorrent client GenericName=Bittorrent client
GenericName[ar]=العميل Bittorrent
GenericName[bg]=Торент клиент GenericName[bg]=Торент клиент
GenericName[cs]=Bittorrent klient GenericName[cs]=Bittorrent klient
GenericName[de]=Bittorren Client GenericName[de]=Bittorren Client
@@ -11,7 +10,6 @@ GenericName[el]=Bittorrent πελάτης
GenericName[es]=Cliente Bittorrent GenericName[es]=Cliente Bittorrent
GenericName[fi]=Bittorrent-ohjelma GenericName[fi]=Bittorrent-ohjelma
GenericName[fr]=Client Bittorrent GenericName[fr]=Client Bittorrent
GenericName[hr]=Bittorrent klijent
GenericName[hu]=Bittorrent kliens GenericName[hu]=Bittorrent kliens
GenericName[it]=Client Bittorrent GenericName[it]=Client Bittorrent
GenericName[ja]=Bittorrent クライアント GenericName[ja]=Bittorrent クライアント

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 B

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/Icons/skin/pausedUP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/Icons/skin/queuedUP.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
src/Icons/skin/stalled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -1,60 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>torrent</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>qBitTorrentDocument</string>
<key>CFBundleTypeName</key>
<string>BitTorrent Document</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>application/x-bittorrent</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>org.bittorrent.torrent</string>
</array>
<key>LSIsAppleDefaultForType</key>
<true/>
</dict>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>magnet</string>
</array>
<key>CFBundleURLName</key>
<string>BitTorrent Magnet URL</string>
</dict>
</array>
<key>CFBundleIconFile</key>
<string>qbittorrent_mac.icns</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleGetInfoString</key>
<string>2.4.10</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
<string>qbittorrent</string>
<key>CFBundleIdentifier</key>
<string>org.qbittorrent</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
</dict>
</plist>

View File

@@ -1,6 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>gpl.html</file>
</qresource>
</RCC>

View File

@@ -32,7 +32,7 @@
#define ABOUT_H #define ABOUT_H
#include "ui_about.h" #include "ui_about.h"
#include <QFile> #include <QScrollBar>
class about : public QDialog, private Ui::AboutDlg{ class about : public QDialog, private Ui::AboutDlg{
Q_OBJECT Q_OBJECT
@@ -47,56 +47,399 @@ class about : public QDialog, private Ui::AboutDlg{
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
// Set icons // Set icons
logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png"))); logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png")));
mascot_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/mascot.png")));
//Title //Title
lb_name->setText(QString::fromUtf8("<b><h1>")+tr("qBittorrent")+QString::fromUtf8(" "VERSION"</h1></b>")); lb_name->setText(QString::fromUtf8("<b><h1>")+tr("qBittorrent")+QString::fromUtf8(" "VERSION"</h1></b>"));
// Thanks // Thanks
QString thanks_txt; te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
thanks_txt += QString::fromUtf8("<p>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</p>"); te_thanks->append(QString::fromUtf8("<ul><li>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</li>"));
thanks_txt += QString::fromUtf8("<p>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</p>"); te_thanks->append(QString::fromUtf8("<li>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</li>"));
thanks_txt += QString::fromUtf8("<p>I also want to thank Στέφανος Αντάρης (santaris@csd.auth.gr) and Mirco Chinelli (infinity89@fastwebmail.it) for working on Mac OS X packaging.</p>"); te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, for his help and support since the beginning of this project.</li>"));
thanks_txt += QString::fromUtf8("<p>I am grateful to Peter Koeleman (peter@qbittorrent.org) and Mohammad Dib (mdib@qbittorrent.org) for working on qBittorrent port to Windows.</p>"); te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) for working on qBittorrent port to Windows.</li>"));
thanks_txt += QString::fromUtf8("<p>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</p>"); te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
te_thanks->setHtml(thanks_txt); te_thanks->scrollToAnchor(QString::fromUtf8("top"));
// Translation // Translation
QString trans_txt = "<p>"+tr("I would like to thank the following people who volunteered to translate qBittorrent:")+"</p>"; te_translation->append(QString::fromUtf8("<a name='top'></a>"));
trans_txt += QString::fromUtf8("<ul><li><u>Arabic:</u> SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)</li>\ te_translation->append(tr("I would like to thank the following people who volunteered to translate qBittorrent:")+QString::fromUtf8("<br>"));
<li><u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)</li>\ te_translation->append(QString::fromUtf8(
<li><u>Bulgarian:</u> Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)</li>\ "<i>- <u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
<li><u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\ - <u>Bulgarian:</u> Tsvetan & Boiko Bankov (emerge_life@users.sourceforge.net)<br>\
<li><u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)</li>\ - <u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)<br>\
<li><u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)</li>\ - <u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)<br>\
<li><u>Croatian:</u> Oliver Mucafir (oliver.untwist@gmail.com)</li>\ - <u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)<br>\
<li><u>Czech:</u> Jirka Vilim (web@tets.cz)</li>\ - <u>Czech:</u> Jirka Vilim (web@tets.cz)<br>\
<li><u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)</li>\ - <u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)<br>\
<li><u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)</li>\ - <u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)<br>\
<li><u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)</li>\ - <u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)<br>\
<li><u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)</li>\ - <u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)<br>\
<li><u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)</li>\ - <u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)<br>\
<li><u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)</li>\ - <u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)<br>\
<li><u>Italian:</u> Matteo Sechi (bu17714@gmail.com)</li>\ - <u>Italian:</u> Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)<br>\
<li><u>Japanese:</u> Nardog (alphisation@gmail.com)</li>\ - <u>Japanese:</u> Nardog (alphisation@gmail.com)<br>\
<li><u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)</li>\ - <u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)<br>\
<li><u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)</li>\ - <u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)<br>\
<li><u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)</li>\ - <u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)<br>\
<li><u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)</li>\ - <u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
<li><u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)</li>\ - <u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)<br>\
<li><u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)</li>\ - <u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)<br>\
<li><u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)</li>\ - <u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)<br>\
<li><u>Slovak:</u> helix84</li>\ - <u>Slovak:</u> helix84<br>\
<li><u>Spanish:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\ - <u>Spanish:</u> Francisco Luque Contreras (frannoe@ya.com)<br>\
<li><u>Swedish:</u> Daniel Nylander (po@danielnylander.se)</li>\ - <u>Swedish:</u> Daniel Nylander (po@danielnylander.se)<br>\
<li><u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)</li>\ - <u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)<br>\
<li><u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com)</li></ul>"); - <u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net)<br><br>"));
trans_txt += "<p>"+tr("Please contact me if you would like to translate qBittorrent into your own language.")+"</p>"; te_translation->append(tr("Please contact me if you would like to translate qBittorrent into your own language."));
te_translation->setHtml(trans_txt); te_translation->scrollToAnchor(QString::fromUtf8("top"));
// License // License
te_license->append(QString::fromUtf8("<a name='top'></a>")); te_license->append(QString::fromUtf8("<a name='top'></a>"));
QFile licensefile(":/gpl.html"); te_license->append(QString::fromUtf8("qBittorrent is licensed under the GNU General Public License version 2 with the\
if(licensefile.open(QIODevice::ReadOnly|QIODevice::Text)) { addition of the following special exception:\
te_license->setHtml(licensefile.readAll()); <br><br>\
licensefile.close(); <i>In addition, as a special exception, the copyright holders give permission to\
} link this program with the OpenSSL project\'s \"OpenSSL\" library (or with\
show(); 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.</i>\
<br><br>\
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
<center>Version 2, June 1991</center><br>\
Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br>\
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
Everyone is permitted to copy and distribute verbatim copies<br>\
of this license document, but changing it is not allowed.<br>\
<br>\
<center><b>Preamble</b></center><br>\
The licenses for most software are designed to take away your<br>\
freedom to share and change it. By contrast, the GNU General Public<br>\
License is intended to guarantee your freedom to share and change free<br>\
software--to make sure the software is free for all its users. This<br>\
General Public License applies to most of the Free Software<br>\
Foundation's software and to any other program whose authors commit to<br>\
using it. (Some other Free Software Foundation software is covered by<br>\
the GNU Library General Public License instead.) You can apply it to<br>\
your programs, too.<br>\
<br>\
When we speak of free software, we are referring to freedom, not<br>\
price. Our General Public Licenses are designed to make sure that you<br>\
have the freedom to distribute copies of free software (and charge for<br>\
this service if you wish), that you receive source code or can get it<br>\
if you want it, that you can change the software or use pieces of it<br>\
in new free programs; and that you know you can do these things.<br>\
<br>\
To protect your rights, we need to make restrictions that forbid<br>\
anyone to deny you these rights or to ask you to surrender the rights.<br>\
These restrictions translate to certain responsibilities for you if you<br>\
distribute copies of the software, or if you modify it.<br>\
<br>\
For example, if you distribute copies of such a program, whether<br>\
gratis or for a fee, you must give the recipients all the rights that<br>\
you have. You must make sure that they, too, receive or can get the<br>\
source code. And you must show them these terms so they know their<br>\
rights.<br>\
<br>\
We protect your rights with two steps: (1) copyright the software, and<br>\
(2) offer you this license which gives you legal permission to copy,<br>\
distribute and/or modify the software.<br>\
<br>\
Also, for each author's protection and ours, we want to make certain<br>\
that everyone understands that there is no warranty for this free<br>\
software. If the software is modified by someone else and passed on, we<br>\
want its recipients to know that what they have is not the original, so<br>\
that any problems introduced by others will not reflect on the original<br>\
authors' reputations.<br>\
<br>\
Finally, any free program is threatened constantly by software<br>\
patents. We wish to avoid the danger that redistributors of a free<br>\
program will individually obtain patent licenses, in effect making the<br>\
program proprietary. To prevent this, we have made it clear that any<br>\
patent must be licensed for everyone's free use or not licensed at all.<br>\
<br>\
The precise terms and conditions for copying, distribution and<br>\
modification follow.<br>\
<br>\
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
<center><b>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</b></center><br>\
0. This License applies to any program or other work which contains<br>\
a notice placed by the copyright holder saying it may be distributed<br>\
under the terms of this General Public License. The 'Program', below,<br>\
refers to any such program or work, and a 'work based on the Program'<br>\
means either the Program or any derivative work under copyright law:<br>\
that is to say, a work containing the Program or a portion of it,<br>\
either verbatim or with modifications and/or translated into another<br>\
language. (Hereinafter, translation is included without limitation in<br>\
the term 'modification'.) Each licensee is addressed as 'you'.<br>\
<br>\
Activities other than copying, distribution and modification are not<br>\
covered by this License; they are outside its scope. The act of<br>\
running the Program is not restricted, and the output from the Program<br>\
is covered only if its contents constitute a work based on the<br>\
Program (independent of having been made by running the Program).<br>\
Whether that is true depends on what the Program does.<br>\
<br>\
1. You may copy and distribute verbatim copies of the Program's<br>\
source code as you receive it, in any medium, provided that you<br>\
conspicuously and appropriately publish on each copy an appropriate<br>\
copyright notice and disclaimer of warranty; keep intact all the<br>\
notices that refer to this License and to the absence of any warranty;<br>\
and give any other recipients of the Program a copy of this License<br>\
along with the Program.<br>\
<br>\
You may charge a fee for the physical act of transferring a copy, and<br>\
you may at your option offer warranty protection in exchange for a fee.<br>\
<br>\
2. You may modify your copy or copies of the Program or any portion<br>\
of it, thus forming a work based on the Program, and copy and<br>\
distribute such modifications or work under the terms of Section 1<br>\
above, provided that you also meet all of these conditions:<br>\
<br>\
a) You must cause the modified files to carry prominent notices<br>\
stating that you changed the files and the date of any change.<br>\
<br>\
b) You must cause any work that you distribute or publish, that in<br>\
whole or in part contains or is derived from the Program or any<br>\
part thereof, to be licensed as a whole at no charge to all third<br>\
parties under the terms of this License.<br>\
<br>\
c) If the modified program normally reads commands interactively<br>\
when run, you must cause it, when started running for such<br>\
interactive use in the most ordinary way, to print or display an<br>\
announcement including an appropriate copyright notice and a<br>\
notice that there is no warranty (or else, saying that you provide<br>\
a warranty) and that users may redistribute the program under<br>\
these conditions, and telling the user how to view a copy of this<br>\
License. (Exception: if the Program itself is interactive but<br>\
does not normally print such an announcement, your work based on<br>\
the Program is not required to print an announcement.)<br>\
<br>\
These requirements apply to the modified work as a whole. If<br>\
identifiable sections of that work are not derived from the Program,<br>\
and can be reasonably considered independent and separate works in<br>\
themselves, then this License, and its terms, do not apply to those<br>\
sections when you distribute them as separate works. But when you<br>\
distribute the same sections as part of a whole which is a work based<br>\
on the Program, the distribution of the whole must be on the terms of<br>\
this License, whose permissions for other licensees extend to the<br>\
entire whole, and thus to each and every part regardless of who wrote it.<br>\
<br>\
Thus, it is not the intent of this section to claim rights or contest<br>\
your rights to work written entirely by you; rather, the intent is to<br>\
exercise the right to control the distribution of derivative or<br>\
collective works based on the Program.<br>\
<br>\
In addition, mere aggregation of another work not based on the Program<br>\
with the Program (or with a work based on the Program) on a volume of<br>\
a storage or distribution medium does not bring the other work under<br>\
the scope of this License.<br>\
<br>\
3. You may copy and distribute the Program (or a work based on it,<br>\
under Section 2) in object code or executable form under the terms of<br>\
Sections 1 and 2 above provided that you also do one of the following:<br>\
<br>\
a) Accompany it with the complete corresponding machine-readable<br>\
source code, which must be distributed under the terms of Sections<br>\
1 and 2 above on a medium customarily used for software interchange; or,<br>\
<br>\
b) Accompany it with a written offer, valid for at least three<br>\
years, to give any third party, for a charge no more than your<br>\
cost of physically performing source distribution, a complete<br>\
machine-readable copy of the corresponding source code, to be<br>\
distributed under the terms of Sections 1 and 2 above on a medium<br>\
customarily used for software interchange; or,<br>\
<br>\
c) Accompany it with the information you received as to the offer<br>\
to distribute corresponding source code. (This alternative is<br>\
allowed only for noncommercial distribution and only if you<br>\
received the program in object code or executable form with such<br>\
an offer, in accord with Subsection b above.)<br>\
<br>\
The source code for a work means the preferred form of the work for<br>\
making modifications to it. For an executable work, complete source<br>\
code means all the source code for all modules it contains, plus any<br>\
associated interface definition files, plus the scripts used to<br>\
control compilation and installation of the executable. However, as a<br>\
special exception, the source code distributed need not include<br>\
anything that is normally distributed (in either source or binary<br>\
form) with the major components (compiler, kernel, and so on) of the<br>\
operating system on which the executable runs, unless that component<br>\
itself accompanies the executable.<br>\
<br>\
If distribution of executable or object code is made by offering<br>\
access to copy from a designated place, then offering equivalent<br>\
access to copy the source code from the same place counts as<br>\
distribution of the source code, even though third parties are not<br>\
compelled to copy the source along with the object code.<br>\
<br>\
4. You may not copy, modify, sublicense, or distribute the Program<br>\
except as expressly provided under this License. Any attempt<br>\
otherwise to copy, modify, sublicense or distribute the Program is<br>\
void, and will automatically terminate your rights under this License.<br>\
However, parties who have received copies, or rights, from you under<br>\
this License will not have their licenses terminated so long as such<br>\
parties remain in full compliance.<br>\
<br>\
5. You are not required to accept this License, since you have not<br>\
signed it. However, nothing else grants you permission to modify or<br>\
distribute the Program or its derivative works. These actions are<br>\
prohibited by law if you do not accept this License. Therefore, by<br>\
modifying or distributing the Program (or any work based on the<br>\
Program), you indicate your acceptance of this License to do so, and<br>\
all its terms and conditions for copying, distributing or modifying<br>\
the Program or works based on it.<br>\
<br>\
6. Each time you redistribute the Program (or any work based on the<br>\
Program), the recipient automatically receives a license from the<br>\
original licensor to copy, distribute or modify the Program subject to<br>\
these terms and conditions. You may not impose any further<br>\
restrictions on the recipients' exercise of the rights granted herein.<br>\
You are not responsible for enforcing compliance by third parties to<br>\
this License.<br>\
<br>\
7. If, as a consequence of a court judgment or allegation of patent<br>\
infringement or for any other reason (not limited to patent issues),<br>\
conditions are imposed on you (whether by court order, agreement or<br>\
otherwise) that contradict the conditions of this License, they do not<br>\
excuse you from the conditions of this License. If you cannot<br>\
distribute so as to satisfy simultaneously your obligations under this<br>\
License and any other pertinent obligations, then as a consequence you<br>\
may not distribute the Program at all. For example, if a patent<br>\
license would not permit royalty-free redistribution of the Program by<br>\
all those who receive copies directly or indirectly through you, then<br>\
the only way you could satisfy both it and this License would be to<br>\
refrain entirely from distribution of the Program.<br>\
<br>\
If any portion of this section is held invalid or unenforceable under<br>\
any particular circumstance, the balance of the section is intended to<br>\
apply and the section as a whole is intended to apply in other<br>\
circumstances.<br>\
<br>\
It is not the purpose of this section to induce you to infringe any<br>\
patents or other property right claims or to contest validity of any<br>\
such claims; this section has the sole purpose of protecting the<br>\
integrity of the free software distribution system, which is<br>\
implemented by public license practices. Many people have made<br>\
generous contributions to the wide range of software distributed<br>\
through that system in reliance on consistent application of that<br>\
system; it is up to the author/donor to decide if he or she is willing<br>\
to distribute software through any other system and a licensee cannot<br>\
impose that choice.<br>\
<br>\
This section is intended to make thoroughly clear what is believed to<br>\
be a consequence of the rest of this License.<br>\
<br>\
8. If the distribution and/or use of the Program is restricted in<br>\
certain countries either by patents or by copyrighted interfaces, the<br>\
original copyright holder who places the Program under this License<br>\
may add an explicit geographical distribution limitation excluding<br>\
those countries, so that distribution is permitted only in or among<br>\
countries not thus excluded. In such case, this License incorporates<br>\
the limitation as if written in the body of this License.<br>\
<br>\
9. The Free Software Foundation may publish revised and/or new versions<br>\
of the General Public License from time to time. Such new versions will<br>\
be similar in spirit to the present version, but may differ in detail to<br>\
address new problems or concerns.<br>\
<br>\
Each version is given a distinguishing version number. If the Program<br>\
specifies a version number of this License which applies to it and 'any<br>\
later version', you have the option of following the terms and conditions<br>\
either of that version or of any later version published by the Free<br>\
Software Foundation. If the Program does not specify a version number of<br>\
this License, you may choose any version ever published by the Free Software<br>\
Foundation.<br>\
<br>\
10. If you wish to incorporate parts of the Program into other free<br>\
programs whose distribution conditions are different, write to the author<br>\
to ask for permission. For software which is copyrighted by the Free<br>\
Software Foundation, write to the Free Software Foundation; we sometimes<br>\
make exceptions for this. Our decision will be guided by the two goals<br>\
of preserving the free status of all derivatives of our free software and<br>\
of promoting the sharing and reuse of software generally.<br>\
<br>\
<center><b>NO WARRANTY</b></center><br>\
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY<br>\
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN<br>\
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES<br>\
PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED<br>\
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF<br>\
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS<br>\
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE<br>\
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,<br>\
REPAIR OR CORRECTION.<br>\
<br>\
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING<br>\
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR<br>\
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,<br>\
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING<br>\
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED<br>\
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY<br>\
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER<br>\
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<br>\
POSSIBILITY OF SUCH DAMAGES.<br>\
<br>\
<center><b>END OF TERMS AND CONDITIONS</b></center><br>\
<center>How to Apply These Terms to Your New Programs</center><br>\
If you develop a new program, and you want it to be of the greatest<br>\
possible use to the public, the best way to achieve this is to make it<br>\
free software which everyone can redistribute and change under these terms.<br>\
<br>\
To do so, attach the following notices to the program. It is safest<br>\
to attach them to the start of each source file to most effectively<br>\
convey the exclusion of warranty; and each file should have at least<br>\
the 'copyright' line and a pointer to where the full notice is found.<br>\
<br>\
<one line to give the program's name and a brief idea of what it does.><br>\
Copyright (C) <year> <name of author><br>\
<br>\
This program is free software; you can redistribute it and/or modify<br>\
it under the terms of the GNU General Public License as published by<br>\
the Free Software Foundation; either version 2 of the License, or<br>\
(at your option) any later version.<br>\
<br>\
This program is distributed in the hope that it will be useful,<br>\
but WITHOUT ANY WARRANTY; without even the implied warranty of<br>\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>\
GNU General Public License for more details.<br>\
<br>\
You should have received a copy of the GNU General Public License<br>\
along with this program; if not, write to the Free Software<br>\
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
<br>\
<br>\
Also add information on how to contact you by electronic and paper mail.<br>\
<br>\
If the program is interactive, make it output a short notice like this<br>\
when it starts in an interactive mode:<br>\
<br>\
Gnomovision version 69, Copyright (C) year name of author<br>\
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.<br>\
This is free software, and you are welcome to redistribute it<br>\
under certain conditions; type `show c' for details.<br>\
<br>\
The hypothetical commands `show w' and `show c' should show the appropriate<br>\
parts of the General Public License. Of course, the commands you use may<br>\
be called something other than `show w' and `show c'; they could even be<br>\
mouse-clicks or menu items--whatever suits your program.<br>\
<br>\
You should also get your employer (if you work as a programmer) or your<br>\
school, if any, to sign a 'copyright disclaimer' for the program, if<br>\
necessary. Here is a sample; alter the names:<br>\
<br>\
Yoyodyne, Inc., hereby disclaims all copyright interest in the program<br>\
`Gnomovision' (which makes passes at compilers) written by James Hacker.<br>\
<br>\
'signature of Ty Coon', 1 April 1989<br>\
Ty Coon, President of Vice<br>\
<br>\
This General Public License does not permit incorporating your program into<br>\
proprietary programs. If your program is a subroutine library, you may<br>\
consider it more useful to permit linking proprietary applications with the<br>\
library. If this is what you want to do, use the GNU Library General<br>\
Public License instead of this License.<br>"));
te_license->scrollToAnchor(QString::fromUtf8("top"));
show();
} }
}; };

View File

@@ -1,208 +0,0 @@
#ifndef ADVANCEDSETTINGS_H
#define ADVANCEDSETTINGS_H
#include <QTableWidget>
#include <QHeaderView>
#include <QSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include <QNetworkInterface>
#include <libtorrent/version.hpp>
#include "preferences.h"
enum AdvSettingsCols {PROPERTY, VALUE};
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, PROGRAM_NOTIFICATIONS };
#define ROW_COUNT 13
class AdvancedSettings: public QTableWidget {
Q_OBJECT
private:
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh, *spin_maxhalfopen;
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts, *cb_super_seeding, *cb_program_notifications;
QComboBox *combo_iface;
public:
AdvancedSettings(QWidget *parent=0): QTableWidget(parent) {
// Set visual appearance
setEditTriggers(QAbstractItemView::NoEditTriggers);
setAlternatingRowColors(true);
setColumnCount(2);
QStringList header;
header << tr("Property") << tr("Value");
setHorizontalHeaderLabels(header);
setColumnWidth(0, width()/2);
horizontalHeader()->setStretchLastSection(true);
verticalHeader()->setVisible(false);
setRowCount(ROW_COUNT);
// Load settings
loadAdvancedSettings();
}
~AdvancedSettings() {
delete spin_cache;
delete outgoing_ports_min;
delete outgoing_ports_max;
delete cb_ignore_limits_lan;
delete cb_count_overhead;
delete cb_recheck_completed;
delete spin_list_refresh;
delete cb_resolve_countries;
delete cb_resolve_hosts;
delete spin_maxhalfopen;
delete cb_super_seeding;
delete combo_iface;
delete cb_program_notifications;
}
public slots:
void saveAdvancedSettings() {
// Disk write cache
Preferences::setDiskCacheSize(spin_cache->value());
// Outgoing ports
Preferences::setOutgoingPortsMin(outgoing_ports_min->value());
Preferences::setOutgoingPortsMax(outgoing_ports_max->value());
// Ignore limits on LAN
Preferences::ignoreLimitsOnLAN(cb_ignore_limits_lan->isChecked());
// Include protocol overhead in transfer limits
Preferences::includeOverheadInLimits(cb_count_overhead->isChecked());
// Recheck torrents on completion
Preferences::recheckTorrentsOnCompletion(cb_recheck_completed->isChecked());
// Transfer list refresh interval
Preferences::setRefreshInterval(spin_list_refresh->value());
// Peer resolution
Preferences::resolvePeerCountries(cb_resolve_countries->isChecked());
Preferences::resolvePeerHostNames(cb_resolve_hosts->isChecked());
// Max Half-Open connections
Preferences::setMaxHalfOpenConnections(spin_maxhalfopen->value());
#if LIBTORRENT_VERSION_MINOR > 14
// Super seeding
Preferences::enableSuperSeeding(cb_super_seeding->isChecked());
#endif
// Network interface
if(combo_iface->currentIndex() == 0) {
// All interfaces (default)
Preferences::setNetworkInterface(QString::null);
} else {
Preferences::setNetworkInterface(combo_iface->currentText());
}
// Program notification
Preferences::useProgramNotification(cb_program_notifications->isChecked());
}
protected slots:
void loadAdvancedSettings() {
// Disk write cache
setItem(DISK_CACHE, PROPERTY, new QTableWidgetItem(tr("Disk write cache size")));
spin_cache = new QSpinBox();
connect(spin_cache, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
spin_cache->setMinimum(1);
spin_cache->setMaximum(200);
spin_cache->setValue(Preferences::diskCacheSize());
spin_cache->setSuffix(tr(" MiB"));
setCellWidget(DISK_CACHE, VALUE, spin_cache);
// Outgoing port Min
setItem(OUTGOING_PORT_MIN, PROPERTY, new QTableWidgetItem(tr("Outgoing ports (Min) [0: Disabled]")));
outgoing_ports_min = new QSpinBox();
connect(outgoing_ports_min, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
outgoing_ports_min->setMinimum(0);
outgoing_ports_min->setMaximum(65535);
outgoing_ports_min->setValue(Preferences::outgoingPortsMin());
setCellWidget(OUTGOING_PORT_MIN, VALUE, outgoing_ports_min);
// Outgoing port Min
setItem(OUTGOING_PORT_MAX, PROPERTY, new QTableWidgetItem(tr("Outgoing ports (Max) [0: Disabled]")));
outgoing_ports_max = new QSpinBox();
connect(outgoing_ports_max, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
outgoing_ports_max->setMinimum(0);
outgoing_ports_max->setMaximum(65535);
outgoing_ports_max->setValue(Preferences::outgoingPortsMax());
setCellWidget(OUTGOING_PORT_MAX, VALUE, outgoing_ports_max);
// Ignore transfer limits on local network
setItem(IGNORE_LIMIT_LAN, PROPERTY, new QTableWidgetItem(tr("Ignore transfer limits on local network")));
cb_ignore_limits_lan = new QCheckBox();
connect(cb_ignore_limits_lan, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_ignore_limits_lan->setChecked(Preferences::ignoreLimitsOnLAN());
setCellWidget(IGNORE_LIMIT_LAN, VALUE, cb_ignore_limits_lan);
// Consider protocol overhead in transfer limits
setItem(COUNT_OVERHEAD, PROPERTY, new QTableWidgetItem(tr("Include TCP/IP overhead in transfer limits")));
cb_count_overhead = new QCheckBox();
connect(cb_count_overhead, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_count_overhead->setChecked(Preferences::includeOverheadInLimits());
setCellWidget(COUNT_OVERHEAD, VALUE, cb_count_overhead);
// Recheck completed torrents
setItem(RECHECK_COMPLETED, PROPERTY, new QTableWidgetItem(tr("Recheck torrents on completion")));
cb_recheck_completed = new QCheckBox();
connect(cb_recheck_completed, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_recheck_completed->setChecked(Preferences::recheckTorrentsOnCompletion());
setCellWidget(RECHECK_COMPLETED, VALUE, cb_recheck_completed);
// Transfer list refresh interval
setItem(LIST_REFRESH, PROPERTY, new QTableWidgetItem(tr("Transfer list refresh interval")));
spin_list_refresh = new QSpinBox();
connect(spin_list_refresh, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
spin_list_refresh->setMinimum(30);
spin_list_refresh->setMaximum(99999);
spin_list_refresh->setValue(Preferences::getRefreshInterval());
spin_list_refresh->setSuffix(tr(" ms", " milliseconds"));
setCellWidget(LIST_REFRESH, VALUE, spin_list_refresh);
// Resolve Peer countries
setItem(RESOLVE_COUNTRIES, PROPERTY, new QTableWidgetItem(tr("Resolve peer countries (GeoIP)")));
cb_resolve_countries = new QCheckBox();
connect(cb_resolve_countries, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_resolve_countries->setChecked(Preferences::resolvePeerCountries());
setCellWidget(RESOLVE_COUNTRIES, VALUE, cb_resolve_countries);
// Resolve peer hosts
setItem(RESOLVE_HOSTS, PROPERTY, new QTableWidgetItem(tr("Resolve peer host names")));
cb_resolve_hosts = new QCheckBox();
connect(cb_resolve_hosts, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_resolve_hosts->setChecked(Preferences::resolvePeerHostNames());
setCellWidget(RESOLVE_HOSTS, VALUE, cb_resolve_hosts);
// Max Half Open connections
setItem(MAX_HALF_OPEN, PROPERTY, new QTableWidgetItem(tr("Maximum number of half-open connections [0: Disabled]")));
spin_maxhalfopen = new QSpinBox();
connect(spin_maxhalfopen, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
spin_maxhalfopen->setMinimum(0);
spin_maxhalfopen->setMaximum(99999);
spin_maxhalfopen->setValue(Preferences::getMaxHalfOpenConnections());
setCellWidget(MAX_HALF_OPEN, VALUE, spin_maxhalfopen);
// Super seeding
setItem(SUPER_SEEDING, PROPERTY, new QTableWidgetItem(tr("Strict super seeding")));
cb_super_seeding = new QCheckBox();
connect(cb_super_seeding, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
#if LIBTORRENT_VERSION_MINOR > 14
cb_super_seeding->setChecked(Preferences::isSuperSeedingEnabled());
#else
cb_super_seeding->setEnabled(false);
#endif
setCellWidget(SUPER_SEEDING, VALUE, cb_super_seeding);
// Network interface
setItem(NETWORK_IFACE, PROPERTY, new QTableWidgetItem(tr("Network Interface (requires restart)")));
combo_iface = new QComboBox;
combo_iface->addItem(tr("Any interface", "i.e. Any network interface"));
const QString current_iface = Preferences::getNetworkInterface();
int i = 1;
foreach(const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
if(iface.name() == "lo") continue;
combo_iface->addItem(iface.name());
if(!current_iface.isEmpty() && iface.name() == current_iface)
combo_iface->setCurrentIndex(i);
++i;
}
connect(combo_iface, SIGNAL(currentIndexChanged(int)), this, SLOT(emitSettingsChanged()));
setCellWidget(NETWORK_IFACE, VALUE, combo_iface);
// Program notifications
setItem(PROGRAM_NOTIFICATIONS, PROPERTY, new QTableWidgetItem(tr("Display program notification baloons")));
cb_program_notifications = new QCheckBox();
connect(cb_program_notifications, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
cb_program_notifications->setChecked(Preferences::useProgramNotification());
setCellWidget(PROGRAM_NOTIFICATIONS, VALUE, cb_program_notifications);
}
void emitSettingsChanged() {
emit settingsChanged();
}
signals:
void settingsChanged();
};
#endif // ADVANCEDSETTINGS_H

View File

@@ -1,111 +0,0 @@
#ifndef BANDWIDTHSCHEDULER_H
#define BANDWIDTHSCHEDULER_H
#include <QTimer>
#include <QTime>
#include <QDateTime>
#include "preferences.h"
#include <iostream>
class BandwidthScheduler: public QTimer {
Q_OBJECT
private:
bool in_alternative_mode;
public:
BandwidthScheduler(QObject *parent): QTimer(parent), in_alternative_mode(false) {
Q_ASSERT(Preferences::isSchedulerEnabled());
// Signal shot, we call start() again manually
setSingleShot(true);
// Connect Signals/Slots
connect(this, SIGNAL(timeout()), this, SLOT(switchMode()));
}
public slots:
void start() {
Q_ASSERT(Preferences::isSchedulerEnabled());
QTime startAltSpeeds = Preferences::getSchedulerStartTime();
QTime endAltSpeeds = Preferences::getSchedulerEndTime();
if(startAltSpeeds == endAltSpeeds) {
std::cerr << "Error: bandwidth scheduler have the same start time and end time." << std::endl;
std::cerr << "The bandwidth scheduler will be disabled" << std::endl;
stop();
emit switchToAlternativeMode(false);
return;
}
// Determine what the closest QTime is
QTime now = QTime::currentTime();
uint time_to_start = secsTo(now, startAltSpeeds);
uint time_to_end = secsTo(now, endAltSpeeds);
if(time_to_end < time_to_start) {
// We should be in alternative mode
in_alternative_mode = true;
// Start counting
QTimer::start(time_to_end*1000);
} else {
// We should be in normal mode
in_alternative_mode = false;
// Start counting
QTimer::start(time_to_start*1000);
}
// Send signal to notify BTSession
emit switchToAlternativeMode(in_alternative_mode);
}
void switchMode() {
// Get the day this mode was started (either today or yesterday)
QDate current_date = QDateTime::currentDateTime().toLocalTime().date();
int day = current_date.dayOfWeek();
if(in_alternative_mode) {
// It is possible that starttime was yesterday
if(QTime::currentTime().secsTo(Preferences::getSchedulerStartTime()) > 0) {
current_date.addDays(-1); // Go to yesterday
day = current_date.day();
}
}
// Check if the day is in scheduler days
// Notify BTSession only if necessary
switch(Preferences::getSchedulerDays()) {
case EVERY_DAY:
emit switchToAlternativeMode(!in_alternative_mode);
break;
case WEEK_ENDS:
if(day == Qt::Saturday || day == Qt::Sunday)
emit switchToAlternativeMode(!in_alternative_mode);
break;
case WEEK_DAYS:
if(day != Qt::Saturday && day != Qt::Sunday)
emit switchToAlternativeMode(!in_alternative_mode);
break;
default:
// Convert our enum index to Qt enum index
int scheduler_day = ((int)Preferences::getSchedulerDays()) - 2;
if(day == scheduler_day)
emit switchToAlternativeMode(!in_alternative_mode);
break;
}
// Call start again
start();
}
signals:
void switchToAlternativeMode(bool alternative);
private:
// Qt function can return negative values and we
// don't want that
uint secsTo(QTime now, QTime t) {
int diff = now.secsTo(t);
if(diff < 0) {
// 86400 seconds in a day
diff += 86400;
}
Q_ASSERT(diff >= 0);
return diff;
}
};
#endif // BANDWIDTHSCHEDULER_H

File diff suppressed because it is too large Load Diff

View File

@@ -34,16 +34,10 @@
#include <QMap> #include <QMap>
#include <QUrl> #include <QUrl>
#include <QStringList> #include <QStringList>
#ifdef DISABLE_GUI
#include <QCoreApplication>
#else
#include <QApplication> #include <QApplication>
#include <QPalette> #include <QPalette>
#endif
#include <QPointer> #include <QPointer>
#include <QTimer>
#include <libtorrent/version.hpp>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp> #include <libtorrent/ip_filter.hpp>
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
@@ -54,17 +48,16 @@ using namespace libtorrent;
class downloadThread; class downloadThread;
class QTimer; class QTimer;
class FileSystemWatcher;
class FilterParserThread; class FilterParserThread;
class HttpServer; class HttpServer;
class BandwidthScheduler;
class ScanFoldersModel;
class TrackerInfos { class TrackerInfos {
public: public:
QString name_or_url; QString name_or_url;
QString last_message; QString last_message;
unsigned long num_peers; unsigned long num_peers;
#if LIBTORRENT_VERSION_MINOR < 15 #ifndef LIBTORRENT_0_15
bool verified; bool verified;
uint fail_count; uint fail_count;
#endif #endif
@@ -75,13 +68,13 @@ public:
Q_ASSERT(!name_or_url.isEmpty()); Q_ASSERT(!name_or_url.isEmpty());
last_message = b.last_message; last_message = b.last_message;
num_peers = b.num_peers; num_peers = b.num_peers;
#if LIBTORRENT_VERSION_MINOR < 15 #ifndef LIBTORRENT_0_15
verified = b.verified; verified = b.verified;
fail_count = b.fail_count; fail_count = b.fail_count;
#endif #endif
} }
TrackerInfos(QString name_or_url): name_or_url(name_or_url), last_message(""), num_peers(0) { TrackerInfos(QString name_or_url): name_or_url(name_or_url), last_message(""), num_peers(0) {
#if LIBTORRENT_VERSION_MINOR < 15 #ifndef LIBTORRENT_0_15
fail_count = 0; fail_count = 0;
verified = false; verified = false;
#endif #endif
@@ -91,6 +84,52 @@ public:
class Bittorrent : public QObject { class Bittorrent : public QObject {
Q_OBJECT Q_OBJECT
private:
// Bittorrent
session *s;
QPointer<QTimer> timerAlerts;
QMap<QUrl, QString> savepath_fromurl;
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
// Ratio
QPointer<QTimer> BigRatioTimer;
// HTTP
QPointer<downloadThread> downloader;
// File System
QPointer<FileSystemWatcher> FSWatcher;
// Console / Log
QStringList consoleMessages;
QStringList peerBanMessages;
// Settings
bool preAllocateAll;
bool addInPause;
float ratio_limit;
bool UPnPEnabled;
bool NATPMPEnabled;
bool LSDEnabled;
bool DHTEnabled;
int current_dht_port;
bool queueingEnabled;
QString defaultSavePath;
QString defaultTempPath;
// GeoIP
bool resolve_countries;
bool geoipDBLoaded;
// ETA Computation
QPointer<QTimer> timerETA;
QHash<QString, QList<int> > ETA_samples;
// IP filtering
QPointer<FilterParserThread> filterParser;
QString filterPath;
// Web UI
QPointer<HttpServer> httpServer;
QList<QUrl> url_skippingDlg;
// Fast exit (async)
bool exiting;
protected:
QString getSavePath(QString hash);
bool initWebUi(QString username, QString password, int port);
public: public:
// Constructor / Destructor // Constructor / Destructor
Bittorrent(); Bittorrent();
@@ -108,7 +147,6 @@ public:
session* getSession() const; session* getSession() const;
QHash<QString, TrackerInfos> getTrackersInfo(QString hash) const; QHash<QString, TrackerInfos> getTrackersInfo(QString hash) const;
bool hasActiveTorrents() const; bool hasActiveTorrents() const;
bool hasDownloadingTorrents() const;
bool isQueueingEnabled() const; bool isQueueingEnabled() const;
int getMaximumActiveDownloads() const; int getMaximumActiveDownloads() const;
int getMaximumActiveTorrents() const; int getMaximumActiveTorrents() const;
@@ -118,31 +156,30 @@ public:
qlonglong getETA(QString hash); qlonglong getETA(QString hash);
bool useTemporaryFolder() const; bool useTemporaryFolder() const;
QString getDefaultSavePath() const; QString getDefaultSavePath() const;
ScanFoldersModel* getScanFoldersModel() const;
bool isPexEnabled() const;
#if LIBTORRENT_VERSION_MINOR < 15
void saveDHTEntry();
#endif
public slots: public slots:
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false); QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false); QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false);
void importOldTorrents();
void applyFormerAttributeFiles(QTorrentHandle h);
void importOldTempData(QString torrent_path);
void loadSessionState(); void loadSessionState();
void saveSessionState(); void saveSessionState();
void downloadFromUrl(QString url); void downloadFromUrl(QString url);
void deleteTorrent(QString hash, bool delete_local_files = false); void deleteTorrent(QString hash, bool delete_local_files = false);
void startUpTorrents(); void startUpTorrents();
session_proxy asyncDeletion(); session_proxy asyncDeletion();
void recheckTorrent(QString hash);
void useAlternativeSpeedsLimit(bool alternative);
/* Needed by Web UI */ /* Needed by Web UI */
void pauseAllTorrents(); void pauseAllTorrents();
void pauseTorrent(QString hash); void pauseTorrent(QString hash);
void resumeTorrent(QString hash); void resumeTorrent(QString hash);
void resumeAllTorrents(); void resumeAllTorrents();
/* End Web UI */ /* End Web UI */
void saveDHTEntry();
void preAllocateAllFiles(bool b); void preAllocateAllFiles(bool b);
void saveFastResumeData(); void saveFastResumeData();
void enableDirectoryScanning(QString scan_dir);
void disableDirectoryScanning();
void enableIPFilter(QString filter); void enableIPFilter(QString filter);
void disableIPFilter(); void disableIPFilter();
void setQueueingEnabled(bool enable); void setQueueingEnabled(bool enable);
@@ -155,20 +192,13 @@ public slots:
void setMaxUploadsPerTorrent(int max); void setMaxUploadsPerTorrent(int max);
void setDownloadRateLimit(long rate); void setDownloadRateLimit(long rate);
void setUploadRateLimit(long rate); void setUploadRateLimit(long rate);
void setMaxRatio(float ratio); void setGlobalRatio(float ratio);
void setDeleteRatio(float ratio);
void setDHTPort(int dht_port); void setDHTPort(int dht_port);
void setPeerProxySettings(const proxy_settings &proxySettings); void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
void setHTTPProxySettings(const proxy_settings &proxySettings); void setSessionSettings(session_settings sessionSettings);
void setSessionSettings(const session_settings &sessionSettings);
void startTorrentsInPause(bool b); void startTorrentsInPause(bool b);
void setDefaultTempPath(QString temppath); void setDefaultTempPath(QString temppath);
void setAppendLabelToSavePath(bool append);
void appendLabelToTorrentSavePath(QTorrentHandle &h);
void changeLabelInTorrentSavePath(QTorrentHandle &h, QString old_label, QString new_label);
#if LIBTORRENT_VERSION_MINOR > 14
void appendqBextensionToTorrent(QTorrentHandle &h, bool append);
void setAppendqBExtension(bool append);
#endif
void applyEncryptionSettings(pe_settings se); void applyEncryptionSettings(pe_settings se);
void setDownloadLimit(QString hash, long val); void setDownloadLimit(QString hash, long val);
void setUploadLimit(QString hash, long val); void setUploadLimit(QString hash, long val);
@@ -176,33 +206,19 @@ public slots:
void enableNATPMP(bool b); void enableNATPMP(bool b);
void enableLSD(bool b); void enableLSD(bool b);
bool enableDHT(bool b); bool enableDHT(bool b);
#ifdef DISABLE_GUI
void addConsoleMessage(QString msg, QString color=QString::null);
#else
void addConsoleMessage(QString msg, QColor color=QApplication::palette().color(QPalette::WindowText)); void addConsoleMessage(QString msg, QColor color=QApplication::palette().color(QPalette::WindowText));
#endif
void addPeerBanMessage(QString msg, bool from_ipfilter); void addPeerBanMessage(QString msg, bool from_ipfilter);
void processDownloadedFile(QString, QString); void processDownloadedFile(QString, QString);
void addMagnetSkipAddDlg(QString uri); void addMagnetSkipAddDlg(QString uri);
void downloadFromURLList(const QStringList& urls); void downloadFromURLList(const QStringList& urls);
void configureSession(); void configureSession();
void banIP(QString ip); void banIP(QString ip);
void recursiveTorrentDownload(const QTorrentHandle &h);
protected:
QString getSavePath(QString hash, bool fromScanDir = false, QString filePath = QString::null, QString root_folder=QString::null);
bool initWebUi(QString username, QString password, int port);
protected slots: protected slots:
void addTorrentsFromScanFolder(QStringList&); void addTorrentsFromScanFolder(QStringList&);
void readAlerts(); void readAlerts();
void processBigRatios(); void deleteBigRatios();
void takeETASamples(); void takeETASamples();
void exportTorrentFiles(QString path);
void saveTempFastResumeData();
void sendNotificationEmail(QTorrentHandle h);
void autoRunExternalProgram(QTorrentHandle h, bool async=true);
void cleanUpAutoRunProcess(int);
signals: signals:
void addedTorrent(QTorrentHandle& h); void addedTorrent(QTorrentHandle& h);
@@ -218,66 +234,6 @@ signals:
void downloadFromUrlFailure(QString url, QString reason); void downloadFromUrlFailure(QString url, QString reason);
void torrentFinishedChecking(QTorrentHandle& h); void torrentFinishedChecking(QTorrentHandle& h);
void metadataReceived(QTorrentHandle &h); void metadataReceived(QTorrentHandle &h);
void savePathChanged(QTorrentHandle &h);
void newConsoleMessage(QString msg);
void alternativeSpeedsModeChanged(bool alternative);
void recursiveTorrentDownloadPossible(QTorrentHandle &h);
private:
// Bittorrent
session *s;
QPointer<QTimer> timerAlerts;
QPointer<BandwidthScheduler> bd_scheduler;
QMap<QUrl, QString> savepath_fromurl;
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
QHash<QString, QString> savePathsToRemove;
QStringList torrentsToPausedAfterChecking;
QTimer resumeDataTimer;
// Ratio
QPointer<QTimer> BigRatioTimer;
// HTTP
QPointer<downloadThread> downloader;
// File System
ScanFoldersModel *m_scanFolders;
// Console / Log
QStringList consoleMessages;
QStringList peerBanMessages;
// Settings
bool preAllocateAll;
bool addInPause;
float ratio_limit;
int high_ratio_action;
bool UPnPEnabled;
bool NATPMPEnabled;
bool LSDEnabled;
bool DHTEnabled;
int current_dht_port;
bool PeXEnabled;
bool queueingEnabled;
bool appendLabelToSavePath;
bool torrentExport;
#if LIBTORRENT_VERSION_MINOR > 14
bool appendqBExtension;
#endif
QString defaultSavePath;
QString defaultTempPath;
// ETA Computation
QPointer<QTimer> timerETA;
QHash<QString, QList<int> > ETA_samples;
// IP filtering
QPointer<FilterParserThread> filterParser;
QString filterPath;
// Web UI
QPointer<HttpServer> httpServer;
QList<QUrl> url_skippingDlg;
// Fast exit (async)
bool exiting;
// GeoIP
#ifndef DISABLE_GUI
bool geoipDBLoaded;
bool resolve_countries;
#endif
}; };
#endif #endif

View File

@@ -46,7 +46,6 @@ class consoleDlg : public QDialog, private Ui_ConsoleDlg{
consoleDlg(QWidget *parent, Bittorrent* _BTSession) : QDialog(parent) { consoleDlg(QWidget *parent, Bittorrent* _BTSession) : QDialog(parent) {
setupUi(this); setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
BTSession = _BTSession; BTSession = _BTSession;
textConsole->setHtml(BTSession->getConsoleMessages().join("<br>")); textConsole->setHtml(BTSession->getConsoleMessages().join("<br>"));
textBannedPeers->setHtml(BTSession->getPeerBanMessages().join("<br>")); textBannedPeers->setHtml(BTSession->getPeerBanMessages().join("<br>"));

View File

@@ -1,95 +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 arnaud@qbittorrent.org
*/
#include "cookiesdlg.h"
#include "ui_cookiesdlg.h"
#include <QNetworkCookie>
enum CookiesCols { COOKIE_KEY, COOKIE_VALUE};
CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) :
QDialog(parent),
ui(new Ui::CookiesDlg)
{
ui->setupUi(this);
ui->infos_lbl->setText(tr("Common keys for cookies are : '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass"));
foreach(const QByteArray &raw_cookie, raw_cookies) {
QList<QByteArray> cookie_parts = raw_cookie.split('=');
if(cookie_parts.size() != 2) continue;
const int i = ui->cookiesTable->rowCount();
ui->cookiesTable->setRowCount(i+1);
ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(cookie_parts.first().data()));
ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(cookie_parts.last().data()));
}
}
CookiesDlg::~CookiesDlg()
{
delete ui;
}
void CookiesDlg::on_add_btn_clicked() {
ui->cookiesTable->setRowCount(ui->cookiesTable->rowCount()+1);
// Edit first column
ui->cookiesTable->editItem(ui->cookiesTable->item(ui->cookiesTable->rowCount()-1, COOKIE_KEY));
}
void CookiesDlg::on_del_btn_clicked() {
// Get selected cookie
QList<QTableWidgetItem*> selection = ui->cookiesTable->selectedItems();
if(!selection.isEmpty()) {
ui->cookiesTable->removeRow(selection.first()->row());
}
}
QList<QByteArray> CookiesDlg::getCookies() const {
QList<QByteArray> ret;
for(int i=0; i<ui->cookiesTable->rowCount(); ++i) {
QString key = ui->cookiesTable->item(i, COOKIE_KEY)->text().trimmed();
QString value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed();
if(!key.isEmpty() && !value.isEmpty()) {
const QString raw_cookie = key+"="+value;
qDebug("Cookie: %s", qPrintable(raw_cookie));
ret << raw_cookie.toLocal8Bit();
}
}
return ret;
}
QList<QByteArray> CookiesDlg::askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok) {
CookiesDlg dlg(parent, raw_cookies);
if(dlg.exec()) {
*ok = true;
return dlg.getCookies();
}
*ok = false;
return QList<QByteArray>();
}

View File

@@ -1,58 +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 arnaud@qbittorrent.org
*/
#ifndef COOKIESDLG_H
#define COOKIESDLG_H
#include <QDialog>
namespace Ui {
class CookiesDlg;
}
class CookiesDlg : public QDialog
{
Q_OBJECT
public:
explicit CookiesDlg(QWidget *parent = 0, const QList<QByteArray> &raw_cookies = QList<QByteArray>());
~CookiesDlg();
QList<QByteArray> getCookies() const;
static QList<QByteArray> askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok);
protected slots:
void on_add_btn_clicked();
void on_del_btn_clicked();
private:
Ui::CookiesDlg *ui;
};
#endif // COOKIESDLG_H

View File

@@ -37,7 +37,6 @@
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <libtorrent/version.hpp>
#include <libtorrent/entry.hpp> #include <libtorrent/entry.hpp>
#include <libtorrent/bencode.hpp> #include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_info.hpp> #include <libtorrent/torrent_info.hpp>
@@ -50,7 +49,6 @@
#include "torrentpersistentdata.h" #include "torrentpersistentdata.h"
#include "createtorrent_imp.h" #include "createtorrent_imp.h"
#include "misc.h" #include "misc.h"
#include "qinisettings.h"
using namespace libtorrent; using namespace libtorrent;
using namespace boost::filesystem; using namespace boost::filesystem;
@@ -67,9 +65,8 @@ bool file_filter(boost::filesystem::path const& filename)
createtorrent::createtorrent(QWidget *parent): QDialog(parent){ createtorrent::createtorrent(QWidget *parent): QDialog(parent){
setupUi(this); setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
setModal(true);
creatorThread = new torrentCreatorThread(this); creatorThread = new torrentCreatorThread(this);
connect(creatorThread, SIGNAL(creationSuccess(QString, QString)), this, SLOT(handleCreationSuccess(QString, QString))); connect(creatorThread, SIGNAL(creationSuccess(QString, const char*)), this, SLOT(handleCreationSuccess(QString, const char*)));
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString))); connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int))); connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
path::default_name_check(no_check); path::default_name_check(no_check);
@@ -81,29 +78,15 @@ createtorrent::~createtorrent() {
} }
void createtorrent::on_addFolder_button_clicked(){ void createtorrent::on_addFolder_button_clicked(){
QIniSettings settings("qBittorrent", "qBittorrent"); QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
QString last_path = settings.value("CreateTorrent/last_add_path", QDir::homePath()).toString(); if(!dir.isEmpty())
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), last_path, QFileDialog::ShowDirsOnly);
if(!dir.isEmpty()) {
settings.setValue("CreateTorrent/last_add_path", dir);
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
dir = dir.replace("/", "\\");
#endif
textInputPath->setText(dir); textInputPath->setText(dir);
}
} }
void createtorrent::on_addFile_button_clicked(){ void createtorrent::on_addFile_button_clicked(){
QIniSettings settings("qBittorrent", "qBittorrent"); QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
QString last_path = settings.value("CreateTorrent/last_add_path", QDir::homePath()).toString(); if(!file.isEmpty())
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), last_path);
if(!file.isEmpty()) {
settings.setValue("CreateTorrent/last_add_path", misc::removeLastPathPart(file));
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
file = file.replace("/", "\\");
#endif
textInputPath->setText(file); textInputPath->setText(file);
}
} }
void createtorrent::on_removeTracker_button_clicked() { void createtorrent::on_removeTracker_button_clicked() {
@@ -184,59 +167,39 @@ void createtorrent::on_createButton_clicked(){
return; return;
} }
QStringList trackers = allItems(trackers_list); QStringList trackers = allItems(trackers_list);
/*if(!trackers.size()){
QIniSettings settings("qBittorrent", "qBittorrent"); QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
QString last_path = settings.value("CreateTorrent/last_save_path", QDir::homePath()).toString(); return;
}*/
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), last_path, tr("Torrent Files")+QString::fromUtf8(" (*.torrent)")); QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!destination.isEmpty()) { if(!destination.isEmpty()) {
settings.setValue("CreateTorrent/last_save_path", misc::removeLastPathPart(destination)); if(!destination.endsWith(QString::fromUtf8(".torrent")))
if(!destination.toUpper().endsWith(".TORRENT"))
destination += QString::fromUtf8(".torrent"); destination += QString::fromUtf8(".torrent");
} else { } else {
return; return;
} }
// Disable dialog
setEnabled(false);
// Set busy cursor
setCursor(QCursor(Qt::WaitCursor));
// Actually create the torrent
QStringList url_seeds = allItems(URLSeeds_list); QStringList url_seeds = allItems(URLSeeds_list);
QString comment = txt_comment->toPlainText(); QString comment = txt_comment->toPlainText();
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize()); creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
} }
void createtorrent::handleCreationFailure(QString msg) { void createtorrent::handleCreationFailure(QString msg) {
// Enable dialog
setEnabled(true);
// Remove busy cursor
setCursor(QCursor(Qt::ArrowCursor));
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg)); QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
} }
void createtorrent::handleCreationSuccess(QString path, QString branch_path) { void createtorrent::handleCreationSuccess(QString path, const char* branch_path) {
// Enable Dialog
setEnabled(true);
// Remove busy cursor
setCursor(QCursor(Qt::ArrowCursor));
if(checkStartSeeding->isChecked()) { if(checkStartSeeding->isChecked()) {
QString root_folder;
// Create save path temp data // Create save path temp data
boost::intrusive_ptr<torrent_info> t; boost::intrusive_ptr<torrent_info> t;
try { try {
t = new torrent_info(path.toUtf8().data()); t = new torrent_info(path.toLocal8Bit().data());
root_folder = misc::truncateRootFolder(t);
} catch(std::exception&) { } catch(std::exception&) {
QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list.")); QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list."));
return; return;
} }
QString hash = misc::toQString(t->info_hash()); QString hash = misc::toQString(t->info_hash());
QString save_path = branch_path; TorrentTempData::setSavePath(hash, QString(branch_path));
if(!root_folder.isEmpty()) { #ifdef LIBTORRENT_0_15
save_path = QDir(save_path).absoluteFilePath(root_folder);
}
TorrentTempData::setSavePath(hash, save_path);
#if LIBTORRENT_VERSION_MINOR > 14
// Enable seeding mode (do not recheck the files) // Enable seeding mode (do not recheck the files)
TorrentTempData::setSeedingMode(hash, true); TorrentTempData::setSeedingMode(hash, true);
#endif #endif
@@ -246,18 +209,6 @@ void createtorrent::handleCreationSuccess(QString path, QString branch_path) {
close(); close();
} }
void createtorrent::on_cancelButton_clicked() {
// End torrent creation thread
if(creatorThread->isRunning()) {
creatorThread->abortCreation();
creatorThread->terminate();
// Wait for termination
creatorThread->wait();
}
// Close the dialog
reject();
}
void createtorrent::updateProgressBar(int progress) { void createtorrent::updateProgressBar(int progress) {
progressBar->setValue(progress); progressBar->setValue(progress);
} }
@@ -291,7 +242,8 @@ void torrentCreatorThread::run() {
char const* creator_str = "qBittorrent "VERSION; char const* creator_str = "qBittorrent "VERSION;
try { try {
file_storage fs; file_storage fs;
path full_path = complete(path(input_path.toUtf8().constData())); file_pool fp;
path full_path = complete(path(input_path.toLocal8Bit().data()));
// Adding files to the torrent // Adding files to the torrent
add_files(fs, full_path, file_filter); add_files(fs, full_path, file_filter);
if(abort) return; if(abort) return;
@@ -311,15 +263,15 @@ void torrentCreatorThread::run() {
// Set qBittorrent as creator and add user comment to // Set qBittorrent as creator and add user comment to
// torrent_info structure // torrent_info structure
t.set_creator(creator_str); t.set_creator(creator_str);
t.set_comment((const char*)comment.toUtf8()); t.set_comment((const char*)comment.toLocal8Bit());
// Is private ? // Is private ?
t.set_priv(is_private); t.set_priv(is_private);
if(abort) return; if(abort) return;
// create the torrent and print it to out // create the torrent and print it to out
ofstream out(complete(path((const char*)save_path.toUtf8())), std::ios_base::binary); ofstream out(complete(path((const char*)save_path.toLocal8Bit())), std::ios_base::binary);
bencode(std::ostream_iterator<char>(out), t.generate()); bencode(std::ostream_iterator<char>(out), t.generate());
emit updateProgress(100); emit updateProgress(100);
emit creationSuccess(save_path, QString::fromUtf8(full_path.branch_path().string().c_str())); emit creationSuccess(save_path, full_path.branch_path().string().c_str());
} }
catch (std::exception& e){ catch (std::exception& e){
emit creationFailure(QString::fromUtf8(e.what())); emit creationFailure(QString::fromUtf8(e.what()));

View File

@@ -58,14 +58,13 @@ class torrentCreatorThread : public QThread {
} }
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size); void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
void sendProgressSignal(int progress); void sendProgressSignal(int progress);
void abortCreation() { abort = true; }
protected: protected:
void run(); void run();
signals: signals:
void creationFailure(QString msg); void creationFailure(QString msg);
void creationSuccess(QString path, QString branch_path); void creationSuccess(QString path, const char* branch_path);
signals: signals:
void updateProgress(int progress); void updateProgress(int progress);
@@ -88,7 +87,6 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
public slots: public slots:
void updateProgressBar(int progress); void updateProgressBar(int progress);
void on_cancelButton_clicked();
protected slots: protected slots:
void on_createButton_clicked(); void on_createButton_clicked();
@@ -99,7 +97,7 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
void on_addURLSeed_button_clicked(); void on_addURLSeed_button_clicked();
void on_removeURLSeed_button_clicked(); void on_removeURLSeed_button_clicked();
void handleCreationFailure(QString msg); void handleCreationFailure(QString msg);
void handleCreationSuccess(QString path, QString branch_path); void handleCreationSuccess(QString path, const char* branch_path);
}; };
#endif #endif

View File

@@ -33,8 +33,6 @@
#include <QDialog> #include <QDialog>
#include "ui_confirmdeletiondlg.h" #include "ui_confirmdeletiondlg.h"
#include "preferences.h"
#include "misc.h"
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg { class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
Q_OBJECT Q_OBJECT
@@ -42,10 +40,6 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
public: public:
DeletionConfirmationDlg(QWidget *parent=0): QDialog(parent) { DeletionConfirmationDlg(QWidget *parent=0): QDialog(parent) {
setupUi(this); setupUi(this);
move(misc::screenCenter(this));
checkPermDelete->setChecked(Preferences::deleteTorrentFilesAsDefault());
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
buttonBox->setFocus();
} }
bool shouldDeleteLocalFiles() const { bool shouldDeleteLocalFiles() const {
@@ -61,15 +55,6 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
return false; return false;
} }
private slots:
void updateRememberButtonState() {
rememberBtn->setEnabled(checkPermDelete->isChecked() != Preferences::deleteTorrentFilesAsDefault());
}
void on_rememberBtn_clicked() {
Preferences::setDeleteTorrentFilesAsDefault(checkPermDelete->isChecked());
rememberBtn->setEnabled(false);
}
}; };
#endif // DELETIONCONFIRMATIONDLG_H #endif // DELETIONCONFIRMATIONDLG_H

View File

@@ -53,69 +53,46 @@ public:
setFixedHeight(BAR_HEIGHT); setFixedHeight(BAR_HEIGHT);
} }
void setProgress(const bitfield &pieces, const bitfield &downloading_pieces) { void setProgress(bitfield pieces) {
if(pieces.empty()) { if(pieces.empty()) {
// Empty bar // Empty bar
QPixmap pix = QPixmap(1, 1); QPixmap pix = QPixmap(1, 1);
pix.fill(); pix.fill();
pixmap = pix; pixmap = pix;
} else { } else {
const qulonglong nb_pieces = pieces.size(); int nb_pieces = pieces.size();
// Reduce the number of pieces before creating the pixmap // Reduce the number of pieces before creating the pixmap
// otherwise it can crash when there are too many pieces // otherwise it can crash when there are too many pieces
const uint w = width(); if(nb_pieces > width()) {
if(nb_pieces > w) { int ratio = floor(nb_pieces/(double)width());
const uint ratio = floor(nb_pieces/(double)w); QVector<bool> scaled_pieces;
bitfield scaled_pieces(ceil(nb_pieces/(double)ratio), false); for(int i=0; i<nb_pieces; i+= ratio) {
bitfield scaled_downloading(ceil(nb_pieces/(double)ratio), false);
uint scaled_index = 0;
for(qulonglong i=0; i<nb_pieces; i+= ratio) {
bool have = true; bool have = true;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) { for(int j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(!pieces[i]) { have = false; break; } if(!pieces[i]) { have = false; break; }
} }
if(have) { scaled_pieces << have;
scaled_pieces.set_bit(scaled_index);
} else {
bool downloading = false;
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
if(downloading_pieces[i]) { downloading = true; break; }
}
if(downloading)
scaled_downloading.set_bit(scaled_index);
}
++scaled_index;
} }
QPixmap pix = QPixmap(scaled_pieces.size(), 1); QPixmap pix = QPixmap(scaled_pieces.size(), 1);
//pix.fill(); pix.fill();
QPainter painter(&pix); QPainter painter(&pix);
for(uint i=0; i<scaled_pieces.size(); ++i) { for(int i=0; i<scaled_pieces.size(); ++i) {
if(scaled_pieces[i]) { if(scaled_pieces[i])
painter.setPen(Qt::blue); painter.setPen(Qt::blue);
} else { else
if(scaled_downloading[i]) { painter.setPen(Qt::white);
painter.setPen(Qt::yellow);
} else {
painter.setPen(Qt::white);
}
}
painter.drawPoint(i,0); painter.drawPoint(i,0);
} }
pixmap = pix; pixmap = pix;
} else { } else {
QPixmap pix = QPixmap(pieces.size(), 1); QPixmap pix = QPixmap(pieces.size(), 1);
//pix.fill(); pix.fill();
QPainter painter(&pix); QPainter painter(&pix);
for(uint i=0; i<pieces.size(); ++i) { for(uint i=0; i<pieces.size(); ++i) {
if(pieces[i]) { if(pieces[i])
painter.setPen(Qt::blue); painter.setPen(Qt::blue);
} else { else
if(downloading_pieces[i]) { painter.setPen(Qt::white);
painter.setPen(Qt::yellow);
} else {
painter.setPen(Qt::white);
}
}
painter.drawPoint(i,0); painter.drawPoint(i,0);
} }
pixmap = pix; pixmap = pix;

View File

@@ -47,11 +47,10 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL{
setupUi(this); setupUi(this);
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
icon_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/url.png"))); icon_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/url.png")));
setModal(true);
show(); show();
// Paste clipboard if there is an URL in it // Paste clipboard if there is an URL in it
QString clip_txt = qApp->clipboard()->text(); QString clip_txt = qApp->clipboard()->text();
if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive) || clip_txt.startsWith("bc://bt/", Qt::CaseInsensitive)) { if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive)) {
textUrls->setText(clip_txt); textUrls->setText(clip_txt);
} }
} }

View File

@@ -28,71 +28,51 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include "downloadthread.h"
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QSettings>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QNetworkCookie>
#include <QNetworkCookieJar>
#include "downloadthread.h"
#include "preferences.h"
#include "qinisettings.h"
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5};
/** Download Thread **/ /** Download Thread **/
downloadThread::downloadThread(QObject* parent) : QObject(parent) { downloadThread::downloadThread(QObject* parent) : QObject(parent) {
connect(&networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*))); networkManager = new QNetworkAccessManager(this);
#ifndef QT_NO_OPENSSL connect(networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
connect(&networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList<QSslError>))); }
#endif
downloadThread::~downloadThread(){
qDebug("Deleting network manager");
delete networkManager;
qDebug("Deleted network manager");
} }
void downloadThread::processDlFinished(QNetworkReply* reply) { void downloadThread::processDlFinished(QNetworkReply* reply) {
QString url = reply->url().toEncoded().data(); QString url = reply->url().toString();
qDebug("Download finished: %s", qPrintable(url));
if(reply->error() != QNetworkReply::NoError) { if(reply->error() != QNetworkReply::NoError) {
// Failure // Failure
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error())));
emit downloadFailure(url, errorCodeToString(reply->error())); emit downloadFailure(url, errorCodeToString(reply->error()));
} else { } else {
QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(redirection.isValid()) {
// We should redirect
qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(redirection.toUrl().toString()));
redirect_mapping.insert(redirection.toUrl().toString(), url);
downloadUrl(redirection.toUrl().toString());
return;
}
// Checking if it was redirecting, restoring initial URL
if(redirect_mapping.contains(url)) {
url = redirect_mapping.take(url);
}
// Success // Success
QString filePath; QString filePath;
QTemporaryFile *tmpfile = new QTemporaryFile; QTemporaryFile tmpfile;
tmpfile->setAutoRemove(false); tmpfile.setAutoRemove(false);
if (tmpfile->open()) { if (tmpfile.open()) {
filePath = tmpfile->fileName(); filePath = tmpfile.fileName();
qDebug("Temporary filename is: %s", qPrintable(filePath)); qDebug("Temporary filename is: %s", filePath.toLocal8Bit().data());
if(reply->open(QIODevice::ReadOnly)) { if(reply->open(QIODevice::ReadOnly)) {
// TODO: Support GZIP compression tmpfile.write(reply->readAll());
tmpfile->write(reply->readAll());
reply->close(); reply->close();
tmpfile->close(); tmpfile.close();
delete tmpfile;
// Send finished signal // Send finished signal
emit downloadFinished(url, filePath); emit downloadFinished(url, filePath);
} else { } else {
// Error when reading the request // Error when reading the request
tmpfile->close(); tmpfile.close();
delete tmpfile;
emit downloadFailure(url, tr("I/O Error")); emit downloadFailure(url, tr("I/O Error"));
} }
} else { } else {
delete tmpfile;
emit downloadFailure(url, tr("I/O Error")); emit downloadFailure(url, tr("I/O Error"));
} }
} }
@@ -100,87 +80,32 @@ void downloadThread::processDlFinished(QNetworkReply* reply) {
reply->deleteLater(); reply->deleteLater();
} }
void downloadThread::loadCookies(const QString &host_name, QString url) { void downloadThread::downloadUrl(QString url){
const QList<QByteArray> raw_cookies = Preferences::getHostNameCookies(host_name);
QNetworkCookieJar *cookie_jar = networkManager.cookieJar();
QList<QNetworkCookie> cookies;
qDebug("Loading cookies for host name: %s", qPrintable(host_name));
foreach(const QByteArray& raw_cookie, raw_cookies) {
QList<QByteArray> cookie_parts = raw_cookie.split('=');
if(cookie_parts.size() == 2) {
qDebug("Loading cookie: %s", raw_cookie.constData());
cookies << QNetworkCookie(cookie_parts.first(), cookie_parts.last());
}
}
cookie_jar->setCookiesFromUrl(cookies, url);
networkManager.setCookieJar(cookie_jar);
}
void downloadThread::downloadTorrentUrl(QString url){
// Load cookies
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
if(!host_name.isEmpty())
loadCookies(host_name, url);
// Process request
QNetworkReply *reply = downloadUrl(url);
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
}
QNetworkReply* downloadThread::downloadUrl(QString url){
// Update proxy settings // Update proxy settings
applyProxySettings(); applyProxySettings();
// Load cookies
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
if(!host_name.isEmpty())
loadCookies(host_name, url);
// Process download request // Process download request
qDebug("url is %s", qPrintable(url)); QNetworkRequest request;
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit()); request.setUrl(QUrl::fromEncoded(url.toLocal8Bit()));
QNetworkRequest request(qurl);
// Spoof Firefox 3.5 user agent to avoid // Spoof Firefox 3.5 user agent to avoid
// Web server banning // Web server banning
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5"); request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5");
qDebug("Downloading %s...", request.url().toEncoded().data()); qDebug("Downloading %s...", request.url().toString().toLocal8Bit().data());
qDebug("%d cookies for this URL", networkManager.cookieJar()->cookiesForUrl(url).size()); networkManager->get(request);
for(int i=0; i<networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
qDebug("%s=%s", networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data());
qDebug("Domain: %s, Path: %s", qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).path()));
}
return networkManager.get(request);
}
void downloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) {
if(bytesTotal > 0) {
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
// Total number of bytes is available
if(bytesTotal > 1048576) {
// More than 1MB, this is probably not a torrent file, aborting...
reply->abort();
} else {
disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
}
} else {
if(bytesReceived > 1048576) {
// More than 1MB, this is probably not a torrent file, aborting...
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
reply->abort();
}
}
} }
void downloadThread::applyProxySettings() { void downloadThread::applyProxySettings() {
QNetworkProxy proxy; QNetworkProxy proxy;
QIniSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt(); int intValue = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt();
if(intValue > 0) { if(intValue > 0) {
// Proxy enabled // Proxy enabled
QString IP = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString(); QString IP = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString();
proxy.setHostName(IP); proxy.setHostName(IP);
QString port = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toString(); QString port = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toString();
qDebug("Using proxy: %s", qPrintable(IP)); qDebug("Using proxy: %s", (IP+QString(":")+port).toLocal8Bit().data());
proxy.setPort(port.toUShort()); proxy.setPort(port.toUShort());
// Default proxy type is HTTP, we must change if it is SOCKS5 // Default proxy type is HTTP, we must change if it is SOCKS5
if(intValue == SOCKS5 || intValue == SOCKS5_PW) { if(intValue%2==0) {
qDebug("Proxy is SOCKS5, not HTTP"); qDebug("Proxy is SOCKS5, not HTTP");
proxy.setType(QNetworkProxy::Socks5Proxy); proxy.setType(QNetworkProxy::Socks5Proxy);
} else { } else {
@@ -198,7 +123,7 @@ void downloadThread::applyProxySettings() {
} else { } else {
proxy.setType(QNetworkProxy::NoProxy); proxy.setType(QNetworkProxy::NoProxy);
} }
networkManager.setProxy(proxy); networkManager->setProxy(proxy);
} }
QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) { QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
@@ -249,11 +174,3 @@ QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
return tr("Unknown error"); return tr("Unknown error");
} }
} }
#ifndef QT_NO_OPENSSL
void downloadThread::ignoreSslErrors(QNetworkReply* reply,QList<QSslError> errors) {
Q_UNUSED(errors)
// Ignore all SSL errors
reply->ignoreSslErrors();
}
#endif

View File

@@ -33,8 +33,6 @@
#include <QNetworkReply> #include <QNetworkReply>
#include <QObject> #include <QObject>
#include <QHash>
#include <QSslError>
class QNetworkAccessManager; class QNetworkAccessManager;
@@ -42,8 +40,7 @@ class downloadThread : public QObject {
Q_OBJECT Q_OBJECT
private: private:
QNetworkAccessManager networkManager; QNetworkAccessManager *networkManager;
QHash<QString, QString> redirect_mapping;
signals: signals:
void downloadFinished(QString url, QString file_path); void downloadFinished(QString url, QString file_path);
@@ -51,21 +48,16 @@ signals:
public: public:
downloadThread(QObject* parent); downloadThread(QObject* parent);
QNetworkReply* downloadUrl(QString url); ~downloadThread();
void downloadTorrentUrl(QString url); void downloadUrl(QString url);
//void setProxy(QString IP, int port, QString username, QString password); //void setProxy(QString IP, int port, QString username, QString password);
protected: protected:
QString errorCodeToString(QNetworkReply::NetworkError status); QString errorCodeToString(QNetworkReply::NetworkError status);
void applyProxySettings(); void applyProxySettings();
void loadCookies(const QString &host_name, QString url);
protected slots: protected slots:
void processDlFinished(QNetworkReply* reply); void processDlFinished(QNetworkReply* reply);
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
#ifndef QT_NO_OPENSSL
void ignoreSslErrors(QNetworkReply*,QList<QSslError>);
#endif
}; };

View File

@@ -36,6 +36,7 @@
#include "pluginsource.h" #include "pluginsource.h"
#include <QProcess> #include <QProcess>
#include <QHeaderView> #include <QHeaderView>
#include <QSettings>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QFileDialog> #include <QFileDialog>
@@ -44,7 +45,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID}; enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID};
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/branches/v2_4_x/src/search_engine/engines/" #define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/trunk/src/search_engine/engines/"
engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) { engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) {
setupUi(this); setupUi(this);
@@ -80,14 +81,9 @@ void engineSelectDlg::dropEvent(QDropEvent *event) {
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n")); QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
QString file; QString file;
foreach(file, files) { foreach(file, files) {
qDebug("dropped %s", qPrintable(file)); qDebug("dropped %s", file.toLocal8Bit().data());
#ifdef Q_WS_WIN
file = file.replace("file:///", "");
#else
file = file.replace("file://", ""); file = file.replace("file://", "");
#endif
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) { if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(file); downloader->downloadUrl(file);
continue; continue;
} }
@@ -103,7 +99,7 @@ void engineSelectDlg::dropEvent(QDropEvent *event) {
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) { void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
QString mime; QString mime;
foreach(mime, event->mimeData()->formats()){ foreach(mime, event->mimeData()->formats()){
qDebug("mimeData: %s", qPrintable(mime)); qDebug("mimeData: %s", mime.toLocal8Bit().data());
} }
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) { if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
event->acceptProposedAction(); event->acceptProposedAction();
@@ -112,7 +108,6 @@ void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
void engineSelectDlg::on_updateButton_clicked() { void engineSelectDlg::on_updateButton_clicked() {
// Download version file from update server on sourceforge // Download version file from update server on sourceforge
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt"); downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt");
} }
@@ -130,17 +125,18 @@ void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
void engineSelectDlg::displayContextMenu(const QPoint&) { void engineSelectDlg::displayContextMenu(const QPoint&) {
QMenu myContextMenu(this); QMenu myContextMenu(this);
QModelIndex index;
// Enable/disable pause/start action given the DL state // Enable/disable pause/start action given the DL state
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems(); QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
bool has_enable = false, has_disable = false; bool has_enable = false, has_disable = false;
QTreeWidgetItem *item; QTreeWidgetItem *item;
foreach(item, items) { foreach(item, items) {
QString id = item->text(ENGINE_ID); QString id = item->text(ENGINE_ID);
if(supported_engines->value(id)->isEnabled() && !has_disable) { if(supported_engines->value(id)->isEnabled() and !has_disable) {
myContextMenu.addAction(actionDisable); myContextMenu.addAction(actionDisable);
has_disable = true; has_disable = true;
} }
if(!supported_engines->value(id)->isEnabled() && !has_enable) { if(!supported_engines->value(id)->isEnabled() and !has_enable) {
myContextMenu.addAction(actionEnable); myContextMenu.addAction(actionEnable);
has_enable = true; has_enable = true;
} }
@@ -174,7 +170,7 @@ void engineSelectDlg::on_actionUninstall_triggered() {
}else { }else {
// Proceed with uninstall // Proceed with uninstall
// remove it from hard drive // remove it from hard drive
QDir enginesFolder(misc::searchEngineLocation()+QDir::separator()+"engines"); QDir enginesFolder(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines");
QStringList filters; QStringList filters;
filters << id+".*"; filters << id+".*";
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted); QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
@@ -249,28 +245,27 @@ QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id){
} }
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const { bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
float old_version = SearchEngine::getPluginVersion(misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py"); float old_version = SearchEngine::getPluginVersion(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version); qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
return (new_version > old_version); return (new_version > old_version);
} }
void engineSelectDlg::installPlugin(QString path, QString plugin_name) { void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
qDebug("Asked to install plugin at %s", qPrintable(path)); qDebug("Asked to install plugin at %s", path.toLocal8Bit().data());
float new_version = SearchEngine::getPluginVersion(path); float new_version = SearchEngine::getPluginVersion(path);
qDebug("Version to be installed: %.2f", new_version); qDebug("Version to be installed: %.2f", new_version);
if(!isUpdateNeeded(plugin_name, new_version)) { if(!isUpdateNeeded(plugin_name, new_version)) {
qDebug("Apparently update is not needed, we have a more recent version"); qDebug("Apparently update is not needed, we have a more recent version");
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
return; return;
} }
// Process with install // Process with install
QString dest_path = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py"; QString dest_path = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
bool update = false; bool update = false;
if(QFile::exists(dest_path)) { if(QFile::exists(dest_path)) {
// Backup in case install fails // Backup in case install fails
QFile::copy(dest_path, dest_path+".bak"); QFile::copy(dest_path, dest_path+".bak");
misc::safeRemove(dest_path); QFile::remove(dest_path);
misc::safeRemove(dest_path+"c");
update = true; update = true;
} }
// Copy the plugin // Copy the plugin
@@ -281,28 +276,28 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
if(!supported_engines->contains(plugin_name)) { if(!supported_engines->contains(plugin_name)) {
if(update) { if(update) {
// Remove broken file // Remove broken file
misc::safeRemove(dest_path); QFile::remove(dest_path);
// restore backup // restore backup
QFile::copy(dest_path+".bak", dest_path); QFile::copy(dest_path+".bak", dest_path);
misc::safeRemove(dest_path+".bak"); QFile::remove(dest_path+".bak");
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
return; return;
} else { } else {
// Remove broken file // Remove broken file
misc::safeRemove(dest_path); QFile::remove(dest_path);
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
return; return;
} }
} }
// Install was successful, remove backup // Install was successful, remove backup
if(update) { if(update) {
misc::safeRemove(dest_path+".bak"); QFile::remove(dest_path+".bak");
} }
if(update) { if(update) {
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
return; return;
} else { } else {
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
return; return;
} }
} }
@@ -329,12 +324,12 @@ void engineSelectDlg::addNewEngine(QString engine_name) {
setRowColor(pluginsTree->indexOfTopLevelItem(item), "red"); setRowColor(pluginsTree->indexOfTopLevelItem(item), "red");
} }
// Handle icon // Handle icon
QString iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".png"; QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".png";
if(QFile::exists(iconPath)) { if(QFile::exists(iconPath)) {
// Good, we already have the icon // Good, we already have the icon
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
} else { } else {
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".ico"; iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".ico";
if(QFile::exists(iconPath)) { // ICO support if(QFile::exists(iconPath)) { // ICO support
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
} else { } else {
@@ -355,10 +350,8 @@ void engineSelectDlg::askForPluginUrl() {
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"), QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
tr("URL:"), QLineEdit::Normal, tr("URL:"), QLineEdit::Normal,
"http://", &ok); "http://", &ok);
if (ok && !url.isEmpty()) { if (ok && !url.isEmpty())
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(url); downloader->downloadUrl(url);
}
} }
void engineSelectDlg::askForLocalPlugin() { void engineSelectDlg::askForLocalPlugin() {
@@ -397,24 +390,23 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
plugin_name.chop(1); // remove trailing ':' plugin_name.chop(1); // remove trailing ':'
bool ok; bool ok;
float version = list.last().toFloat(&ok); float version = list.last().toFloat(&ok);
qDebug("read line %s: %.2f", qPrintable(plugin_name), version); qDebug("read line %s: %.2f", plugin_name.toLocal8Bit().data(), version);
if(!ok) continue; if(!ok) continue;
file_correct = true; file_correct = true;
if(isUpdateNeeded(plugin_name, version)) { if(isUpdateNeeded(plugin_name, version)) {
qDebug("Plugin: %s is outdated", qPrintable(plugin_name)); qDebug("Plugin: %s is outdated", plugin_name.toLocal8Bit().data());
// Downloading update // Downloading update
setCursor(QCursor(Qt::WaitCursor));
downloader->downloadUrl(UPDATE_URL+plugin_name+".py"); downloader->downloadUrl(UPDATE_URL+plugin_name+".py");
//downloader->downloadUrl(UPDATE_URL+plugin_name+".png"); //downloader->downloadUrl(UPDATE_URL+plugin_name+".png");
updated = true; updated = true;
}else { }else {
qDebug("Plugin: %s is up to date", qPrintable(plugin_name)); qDebug("Plugin: %s is up to date", plugin_name.toLocal8Bit().data());
} }
} }
// Close file // Close file
versions.close(); versions.close();
// Clean up tmp file // Clean up tmp file
misc::safeRemove(versions_file); QFile::remove(versions_file);
if(file_correct && !updated) { if(file_correct && !updated) {
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date.")); QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
} }
@@ -422,8 +414,7 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
} }
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) { void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
setCursor(QCursor(Qt::ArrowCursor)); qDebug("engineSelectDlg received %s", url.toLocal8Bit().data());
qDebug("engineSelectDlg received %s", qPrintable(url));
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){ if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
// Icon downloaded // Icon downloaded
QImage fileIcon; QImage fileIcon;
@@ -436,37 +427,36 @@ void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
QFile icon(filePath); QFile icon(filePath);
icon.open(QIODevice::ReadOnly); icon.open(QIODevice::ReadOnly);
if(ICOHandler::canRead(&icon)) if(ICOHandler::canRead(&icon))
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+id+".ico"; iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".ico";
else else
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+id+".png"; iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
QFile::copy(filePath, iconPath); QFile::copy(filePath, iconPath);
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath))); item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
} }
} }
// Delete tmp file // Delete tmp file
misc::safeRemove(filePath); QFile::remove(filePath);
return; return;
} }
if(url.endsWith("versions.txt")) { if(url.endsWith("versions.txt")) {
if(!parseVersionsFile(filePath)) { if(!parseVersionsFile(filePath)) {
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable.")); QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
} }
misc::safeRemove(filePath); QFile::remove(filePath);
return; return;
} }
if(url.endsWith(".py", Qt::CaseInsensitive)) { if(url.endsWith(".py", Qt::CaseInsensitive)) {
QString plugin_name = url.split('/').last(); QString plugin_name = url.split('/').last();
plugin_name.replace(".py", ""); plugin_name.replace(".py", "");
installPlugin(filePath, plugin_name); installPlugin(filePath, plugin_name);
misc::safeRemove(filePath); QFile::remove(filePath);
return; return;
} }
} }
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) { void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
setCursor(QCursor(Qt::ArrowCursor));
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){ if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
qDebug("Could not download favicon: %s, reason: %s", qPrintable(url), qPrintable(reason)); qDebug("Could not download favicon: %s, reason: %s", url.toLocal8Bit().data(), reason.toLocal8Bit().data());
return; return;
} }
if(url.endsWith("versions.txt")) { if(url.endsWith("versions.txt")) {
@@ -477,6 +467,6 @@ void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
// a plugin update download has been failed // a plugin update download has been failed
QString plugin_name = url.split('/').last(); QString plugin_name = url.split('/').last();
plugin_name.replace(".py", "", Qt::CaseInsensitive); plugin_name.replace(".py", "", Qt::CaseInsensitive);
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name)); QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
} }
} }

View File

@@ -29,19 +29,16 @@
*/ */
#include <libtorrent/version.hpp>
#include "eventmanager.h" #include "eventmanager.h"
#include "bittorrent.h" #include "bittorrent.h"
#include "scannedfoldersmodel.h"
#include "misc.h" #include "misc.h"
#include "preferences.h" #include "preferences.h"
//#include "proplistdelegate.h" #include "proplistdelegate.h"
#include "torrentpersistentdata.h" #include "torrentpersistentdata.h"
#include <QDebug> #include <QDebug>
#include <QTranslator>
EventManager::EventManager(QObject *parent, Bittorrent *BTSession) EventManager::EventManager(QObject *parent, Bittorrent *BTSession)
: QObject(parent), BTSession(BTSession) : QObject(parent), BTSession(BTSession)
{ {
} }
@@ -62,7 +59,7 @@ QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
tracker["url"] = tracker_url; tracker["url"] = tracker_url;
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
QString error_message = data.last_message.trimmed(); QString error_message = data.last_message.trimmed();
#if LIBTORRENT_VERSION_MINOR > 14 #ifdef LIBTORRENT_0_15
if(it->verified) { if(it->verified) {
tracker["status"] = tr("Working"); tracker["status"] = tr("Working");
} else { } else {
@@ -106,237 +103,33 @@ QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
int i=0; int i=0;
for(fi=t.begin_files(); fi != t.end_files(); fi++) { for(fi=t.begin_files(); fi != t.end_files(); fi++) {
QVariantMap file; QVariantMap file;
QString path = QDir::cleanPath(misc::toQStringU(fi->path.string())); if(h.num_files() == 1) {
QString name = path.split('/').last(); file["name"] = h.name();
file["name"] = name; } else {
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
QString name = path.split('/').last();
file["name"] = name;
}
file["size"] = misc::friendlyUnit((double)fi->size); file["size"] = misc::friendlyUnit((double)fi->size);
if(fi->size > 0) if(fi->size > 0)
file["progress"] = fp[i]/(double)fi->size; file["progress"] = fp[i]/(double)fi->size;
else else
file["progress"] = 1.; // Empty file... file["progress"] = 1.; // Empty file...
file["priority"] = priorities[i]; file["priority"] = priorities[i];
if(i == 0)
file["is_seed"] = h.is_seed();
files << file; files << file;
++i; ++i;
} }
return files; return files;
} }
void EventManager::setGlobalPreferences(QVariantMap m) const {
// UI
if(m.contains("locale")) {
QString locale = m["locale"].toString();
if(Preferences::getLocale() != locale) {
QTranslator *translator = new QTranslator;
if(translator->load(QString::fromUtf8(":/lang/qbittorrent_") + locale)){
qDebug("%s locale recognized, using translation.", qPrintable(locale));
}else{
qDebug("%s locale unrecognized, using default (en_GB).", qPrintable(locale));
}
qApp->installTranslator(translator);
}
Preferences::setLocale(locale);
}
// Downloads
if(m.contains("save_path"))
Preferences::setSavePath(m["save_path"].toString());
if(m.contains("temp_path_enabled"))
Preferences::setTempPathEnabled(m["temp_path_enabled"].toBool());
if(m.contains("temp_path"))
Preferences::setTempPath(m["temp_path"].toString());
if(m.contains("scan_dirs") && m.contains("download_in_scan_dirs")) {
QVariantList download_at_path_tmp = m["download_in_scan_dirs"].toList();
QList<bool> download_at_path;
foreach(QVariant var, download_at_path_tmp) {
download_at_path << var.toBool();
}
QStringList old_folders = Preferences::getScanDirs();
QStringList new_folders = m["scan_dirs"].toStringList();
if(download_at_path.size() == new_folders.size()) {
Preferences::setScanDirs(new_folders);
Preferences::setDownloadInScanDirs(download_at_path);
foreach(const QString &old_folder, old_folders) {
// Update deleted folders
if(!new_folders.contains(old_folder)) {
BTSession->getScanFoldersModel()->removePath(old_folder);
}
}
int i = 0;
foreach(const QString &new_folder, new_folders) {
qDebug("New watched folder: %s", qPrintable(new_folder));
// Update new folders
if(!old_folders.contains(new_folder)) {
BTSession->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i));
}
++i;
}
}
}
if(m.contains("export_dir"))
Preferences::setExportDir(m["export_dir"].toString());
if(m.contains("mail_notification_enabled"))
Preferences::setMailNotificationEnabled(m["mail_notification_enabled"].toBool());
if(m.contains("mail_notification_email"))
Preferences::setMailNotificationEmail(m["mail_notification_email"].toString());
if(m.contains("mail_notification_smtp"))
Preferences::setMailNotificationSMTP(m["mail_notification_smtp"].toString());
if(m.contains("autorun_enabled"))
Preferences::setAutoRunEnabled(m["autorun_enabled"].toBool());
if(m.contains("autorun_program"))
Preferences::setAutoRunProgram(m["autorun_program"].toString());
if(m.contains("preallocate_all"))
Preferences::preAllocateAllFiles(m["preallocate_all"].toBool());
if(m.contains("queueing_enabled"))
Preferences::setQueueingSystemEnabled(m["queueing_enabled"].toBool());
if(m.contains("max_active_downloads"))
Preferences::setMaxActiveDownloads(m["max_active_downloads"].toInt());
if(m.contains("max_active_torrents"))
Preferences::setMaxActiveTorrents(m["max_active_torrents"].toInt());
if(m.contains("max_active_uploads"))
Preferences::setMaxActiveUploads(m["max_active_uploads"].toInt());
#if LIBTORRENT_VERSION_MINOR > 14
if(m.contains("incomplete_files_ext"))
Preferences::useIncompleteFilesExtension(m["incomplete_files_ext"].toBool());
#endif
// Connection
if(m.contains("listen_port"))
Preferences::setSessionPort(m["listen_port"].toInt());
if(m.contains("upnp"))
Preferences::setUPnPEnabled(m["upnp"].toBool());
if(m.contains("natpmp"))
Preferences::setNATPMPEnabled(m["natpmp"].toBool());
if(m.contains("dl_limit"))
Preferences::setGlobalDownloadLimit(m["dl_limit"].toInt());
if(m.contains("up_limit"))
Preferences::setGlobalUploadLimit(m["up_limit"].toInt());
if(m.contains("max_connec"))
Preferences::setMaxConnecs(m["max_connec"].toInt());
if(m.contains("max_connec_per_torrent"))
Preferences::setMaxConnecsPerTorrent(m["max_connec_per_torrent"].toInt());
if(m.contains("max_uploads_per_torrent"))
Preferences::setMaxUploadsPerTorrent(m["max_uploads_per_torrent"].toInt());
// Bittorrent
if(m.contains("dht"))
Preferences::setDHTEnabled(m["dht"].toBool());
if(m.contains("dhtSameAsBT"))
Preferences::setDHTPortSameAsBT(m["dhtSameAsBT"].toBool());
if(m.contains("dht_port"))
Preferences::setDHTPort(m["dht_port"].toInt());
if(m.contains("pex"))
Preferences::setPeXEnabled(m["pex"].toBool());
qDebug("Pex support: %d", (int)m["pex"].toBool());
if(m.contains("lsd"))
Preferences::setLSDEnabled(m["lsd"].toBool());
if(m.contains("encryption"))
Preferences::setEncryptionSetting(m["encryption"].toInt());
// Proxy
if(m.contains("proxy_type"))
Preferences::setPeerProxyType(m["proxy_type"].toInt());
if(m.contains("proxy_ip"))
Preferences::setPeerProxyIp(m["proxy_ip"].toString());
if(m.contains("proxy_port"))
Preferences::setPeerProxyPort(m["proxy_port"].toUInt());
if(m.contains("proxy_auth_enabled"))
Preferences::setPeerProxyAuthEnabled(m["proxy_auth_enabled"].toBool());
if(m.contains("proxy_username"))
Preferences::setPeerProxyUsername(m["proxy_username"].toString());
if(m.contains("proxy_password"))
Preferences::setPeerProxyPassword(m["proxy_password"].toString());
if(m.contains("http_proxy_type"))
Preferences::setHTTPProxyType(m["http_proxy_type"].toInt());
if(m.contains("http_proxy_ip"))
Preferences::setHTTPProxyIp(m["http_proxy_ip"].toString());
if(m.contains("http_proxy_port"))
Preferences::setHTTPProxyPort(m["http_proxy_port"].toUInt());
if(m.contains("http_proxy_auth_enabled"))
Preferences::setHTTPProxyAuthEnabled(m["http_proxy_auth_enabled"].toBool());
if(m.contains("http_proxy_username"))
Preferences::setHTTPProxyUsername(m["http_proxy_username"].toString());
if(m.contains("http_proxy_password"))
Preferences::setHTTPProxyPassword(m["http_proxy_password"].toString());
// IP Filter
if(m.contains("ip_filter_enabled"))
Preferences::setFilteringEnabled(m["ip_filter_enabled"].toBool());
if(m.contains("ip_filter_path"))
Preferences::setFilter(m["ip_filter_path"].toString());
// Web UI
if(m.contains("web_ui_port"))
Preferences::setWebUiPort(m["web_ui_port"].toUInt());
if(m.contains("web_ui_username"))
Preferences::setWebUiUsername(m["web_ui_username"].toString());
if(m.contains("web_ui_password"))
Preferences::setWebUiPassword(m["web_ui_password"].toString());
// Reload preferences
BTSession->configureSession();
}
QVariantMap EventManager::getGlobalPreferences() const { QVariantMap EventManager::getGlobalPreferences() const {
QVariantMap data; QVariantMap data;
// UI
data["locale"] = Preferences::getLocale();
// Downloads
data["save_path"] = Preferences::getSavePath();
data["temp_path_enabled"] = Preferences::isTempPathEnabled();
data["temp_path"] = Preferences::getTempPath();
data["scan_dirs"] = Preferences::getScanDirs();
QVariantList var_list;
foreach(bool b, Preferences::getDownloadInScanDirs()) {
var_list << b;
}
data["download_in_scan_dirs"] = var_list;
data["export_dir_enabled"] = Preferences::isTorrentExportEnabled();
data["export_dir"] = Preferences::getExportDir();
data["mail_notification_enabled"] = Preferences::isMailNotificationEnabled();
data["mail_notification_email"] = Preferences::getMailNotificationEmail();
data["mail_notification_smtp"] = Preferences::getMailNotificationSMTP();
data["autorun_enabled"] = Preferences::isAutoRunEnabled();
data["autorun_program"] = Preferences::getAutoRunProgram();
data["preallocate_all"] = Preferences::preAllocateAllFiles();
data["queueing_enabled"] = Preferences::isQueueingSystemEnabled();
data["max_active_downloads"] = Preferences::getMaxActiveDownloads();
data["max_active_torrents"] = Preferences::getMaxActiveTorrents();
data["max_active_uploads"] = Preferences::getMaxActiveUploads();
#if LIBTORRENT_VERSION_MINOR > 14
data["incomplete_files_ext"] = Preferences::useIncompleteFilesExtension();
#endif
// Connection
data["listen_port"] = Preferences::getSessionPort();
data["upnp"] = Preferences::isUPnPEnabled();
data["natpmp"] = Preferences::isNATPMPEnabled();
data["dl_limit"] = Preferences::getGlobalDownloadLimit(); data["dl_limit"] = Preferences::getGlobalDownloadLimit();
data["up_limit"] = Preferences::getGlobalUploadLimit(); data["up_limit"] = Preferences::getGlobalUploadLimit();
data["dht"] = Preferences::isDHTEnabled();
data["max_connec"] = Preferences::getMaxConnecs(); data["max_connec"] = Preferences::getMaxConnecs();
data["max_connec_per_torrent"] = Preferences::getMaxConnecsPerTorrent(); data["max_connec_per_torrent"] = Preferences::getMaxConnecsPerTorrent();
data["max_uploads_per_torrent"] = Preferences::getMaxUploadsPerTorrent(); data["max_uploads_per_torrent"] = Preferences::getMaxUploadsPerTorrent();
// Bittorrent
data["dht"] = Preferences::isDHTEnabled();
data["dhtSameAsBT"] = Preferences::isDHTPortSameAsBT();
data["dht_port"] = Preferences::getDHTPort();
data["pex"] = Preferences::isPeXEnabled();
data["lsd"] = Preferences::isLSDEnabled();
data["encryption"] = Preferences::getEncryptionSetting();
// Proxy
data["proxy_type"] = Preferences::getPeerProxyType();
data["proxy_ip"] = Preferences::getPeerProxyIp();
data["proxy_port"] = Preferences::getPeerProxyPort();
data["proxy_auth_enabled"] = Preferences::isPeerProxyAuthEnabled();
data["proxy_username"] = Preferences::getPeerProxyUsername();
data["proxy_password"] = Preferences::getPeerProxyPassword();
data["http_proxy_type"] = Preferences::getHTTPProxyType();
data["http_proxy_ip"] = Preferences::getHTTPProxyIp();
data["http_proxy_port"] = Preferences::getHTTPProxyPort();
data["http_proxy_auth_enabled"] = Preferences::isHTTPProxyAuthEnabled();
data["http_proxy_username"] = Preferences::getHTTPProxyUsername();
data["http_proxy_password"] = Preferences::getHTTPProxyPassword();
// IP Filter
data["ip_filter_enabled"] = Preferences::isFilteringEnabled();
data["ip_filter_path"] = Preferences::getFilter();
// Web UI
data["web_ui_port"] = Preferences::getWebUiPort();
data["web_ui_username"] = Preferences::getWebUiUsername();
data["web_ui_password"] = Preferences::getWebUiPassword();
return data; return data;
} }
@@ -345,36 +138,34 @@ QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) { if(h.is_valid() && h.has_metadata()) {
// Save path // Save path
QString p = TorrentPersistentData::getSavePath(hash); data["save_path"] = TorrentPersistentData::getSavePath(hash);
if(p.isEmpty()) p = h.save_path();
data["save_path"] = p;
// Creation date // Creation date
data["creation_date"] = h.creation_date(); data["creation_date"] = h.creation_date();
// Comment // Comment
data["comment"] = h.comment(); data["comment"] = h.comment();
data["total_wasted"] = QVariant(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes())); data["total_wasted"] = misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes());
data["total_uploaded"] = QVariant(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")"); data["total_uploaded"] = misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")";
data["total_downloaded"] = QVariant(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")"); data["total_downloaded"] = misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")";
if(h.upload_limit() <= 0) if(h.upload_limit() <= 0)
data["up_limit"] = QString::fromUtf8(""); data["up_limit"] = QString::fromUtf8("");
else else
data["up_limit"] = QVariant(misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)")); data["up_limit"] = misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)");
if(h.download_limit() <= 0) if(h.download_limit() <= 0)
data["dl_limit"] = QString::fromUtf8(""); data["dl_limit"] = QString::fromUtf8("");
else else
data["dl_limit"] = QVariant(misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)")); data["dl_limit"] = misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)");
QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
if(h.is_seed()) { if(h.is_seed()) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
} }
data["time_elapsed"] = elapsed_txt; data["time_elapsed"] = elapsed_txt;
data["nb_connections"] = QVariant(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")"); data["nb_connections"] = QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")";
// Update ratio info // Update ratio info
double ratio = BTSession->getRealRatio(h.hash()); double ratio = BTSession->getRealRatio(h.hash());
if(ratio > 100.) if(ratio > 100.)
data["share_ratio"] = QString::fromUtf8(""); data["share_ratio"] = QString::fromUtf8("");
else else
data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1)); data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1));
} }
return data; return data;
} }
@@ -395,14 +186,10 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
QVariantMap event; QVariantMap event;
event["eta"] = QVariant(QString::fromUtf8("")); event["eta"] = QVariant(QString::fromUtf8(""));
if(h.is_paused()) { if(h.is_paused()) {
if(h.has_error()) { if(h.is_seed())
event["state"] = QVariant("error"); event["state"] = QVariant("pausedUP");
} else { else
if(h.is_seed()) event["state"] = QVariant("pausedDL");
event["state"] = QVariant("pausedUP");
else
event["state"] = QVariant("pausedDL");
}
} else { } else {
if(BTSession->isQueueingEnabled() && h.is_queued()) { if(BTSession->isQueueingEnabled() && h.is_queued()) {
if(h.is_seed()) if(h.is_seed())

View File

@@ -55,7 +55,6 @@ class EventManager : public QObject
QList<QVariantMap> getPropTrackersInfo(QString hash) const; QList<QVariantMap> getPropTrackersInfo(QString hash) const;
QList<QVariantMap> getPropFilesInfo(QString hash) const; QList<QVariantMap> getPropFilesInfo(QString hash) const;
QVariantMap getGlobalPreferences() const; QVariantMap getGlobalPreferences() const;
void setGlobalPreferences(QVariantMap m) const;
public slots: public slots:
void addedTorrent(QTorrentHandle& h); void addedTorrent(QTorrentHandle& h);

View File

@@ -28,16 +28,15 @@ public:
setColumnCount(1); setColumnCount(1);
QTreeWidgetItem *___qtreewidgetitem = headerItem(); QTreeWidgetItem *___qtreewidgetitem = headerItem();
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8)); ___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
unread_item = new QTreeWidgetItem(this); unread_item = new QTreeWidgetItem(this);
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")")); unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png"))); unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
itemAdded(unread_item, rssmanager); itemAdded(unread_item, rssmanager);
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
setCurrentItem(unread_item); setCurrentItem(unread_item);
} }
~FeedList() { ~FeedList() {
disconnect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
delete unread_item; delete unread_item;
} }
@@ -48,16 +47,10 @@ public:
} }
} }
void itemAboutToBeRemoved(QTreeWidgetItem *item) { void itemRemoved(QTreeWidgetItem *item) {
RssFile* file = mapping.take(item); RssFile* file = mapping.take(item);
if(file->getType() == RssFile::STREAM) { if(file->getType() == RssFile::STREAM)
feeds_items.remove(file->getID()); feeds_items.remove(file->getID());
} else {
QList<RssStream*> feeds = ((RssFolder*)file)->getAllFeeds();
foreach(RssStream* feed, feeds) {
feeds_items.remove(feed->getID());
}
}
} }
bool hasFeed(QString url) const { bool hasFeed(QString url) const {
@@ -122,19 +115,19 @@ public:
} }
RssFile* getRSSItem(QTreeWidgetItem *item) const { RssFile* getRSSItem(QTreeWidgetItem *item) const {
return mapping.value(item, 0); return mapping[item];
} }
RssFile::FileType getItemType(QTreeWidgetItem *item) const { RssFile::FileType getItemType(QTreeWidgetItem *item) const {
return mapping.value(item)->getType(); return mapping[item]->getType();
} }
QString getItemID(QTreeWidgetItem *item) const { QString getItemID(QTreeWidgetItem *item) const {
return mapping.value(item)->getID(); return mapping[item]->getID();
} }
QTreeWidgetItem* getTreeItemFromUrl(QString url) const{ QTreeWidgetItem* getTreeItemFromUrl(QString url) const{
return feeds_items.value(url, 0); return feeds_items[url];
} }
RssStream* getRSSItemFromUrl(QString url) const { RssStream* getRSSItemFromUrl(QString url) const {
@@ -155,9 +148,7 @@ signals:
protected slots: protected slots:
void updateCurrentFeed(QTreeWidgetItem* new_item) { void updateCurrentFeed(QTreeWidgetItem* new_item) {
if(!new_item) return; if((new_item && getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
if(!mapping.contains(new_item)) return;
if((getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
current_feed = new_item; current_feed = new_item;
} }

View File

@@ -32,6 +32,7 @@
#define FEEDDOWNLOADER_H #define FEEDDOWNLOADER_H
#include <QString> #include <QString>
#include <QSettings>
#include <QListWidget> #include <QListWidget>
#include <QListWidgetItem> #include <QListWidgetItem>
#include <QInputDialog> #include <QInputDialog>
@@ -44,9 +45,8 @@
#include "bittorrent.h" #include "bittorrent.h"
#include "ui_feeddownloader.h" #include "ui_feeddownloader.h"
#include "qinisettings.h"
#if QT_VERSION >= 0x040500 #ifdef QT_4_5
#include <QHash> #include <QHash>
#else #else
#include <QMap> #include <QMap>
@@ -68,10 +68,8 @@ public:
if(token.isEmpty() || token == "") if(token.isEmpty() || token == "")
continue; continue;
QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard); QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard);
//reg.setMinimal(false);
if(reg.indexIn(s) < 0) return false; if(reg.indexIn(s) < 0) return false;
} }
qDebug("Checking not matching tokens");
// Checking not matching // Checking not matching
QStringList notmatch_tokens = getNotMatchingTokens(); QStringList notmatch_tokens = getNotMatchingTokens();
foreach(const QString& token, notmatch_tokens) { foreach(const QString& token, notmatch_tokens) {
@@ -105,7 +103,7 @@ public:
QStringList getNotMatchingTokens() const { QStringList getNotMatchingTokens() const {
QString notmatching = this->value("not", "").toString(); QString notmatching = this->value("not", "").toString();
return notmatching.split(QRegExp("[\\s|]")); return notmatching.split(" ");
} }
QString getNotMatchingTokens_str() const { QString getNotMatchingTokens_str() const {
@@ -168,20 +166,20 @@ public:
} }
bool isDownloadingEnabled() const { bool isDownloadingEnabled() const {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
return feeds_w_downloader.value(feed_url, false).toBool(); return feeds_w_downloader.value(feed_url, false).toBool();
} }
void setDownloadingEnabled(bool enabled) { void setDownloadingEnabled(bool enabled) {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
feeds_w_downloader[feed_url] = enabled; feeds_w_downloader[feed_url] = enabled;
qBTRSS.setValue("downloader_on", feeds_w_downloader); qBTRSS.setValue("downloader_on", feeds_w_downloader);
} }
static FeedFilters getFeedFilters(QString url) { static FeedFilters getFeedFilters(QString url) {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
return FeedFilters(url, all_feeds_filters.value(url, QHash<QString, QVariant>()).toHash()); return FeedFilters(url, all_feeds_filters.value(url, QHash<QString, QVariant>()).toHash());
} }
@@ -222,9 +220,9 @@ public:
} }
void save() { void save() {
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss"); QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash(); QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
qDebug("Saving filters for feed: %s (%d filters)", qPrintable(feed_url), (*this).size()); qDebug("Saving filters for feed: %s (%d filters)", feed_url.toLocal8Bit().data(), (*this).size());
all_feeds_filters[feed_url] = *this; all_feeds_filters[feed_url] = *this;
qBTRSS.setValue("feed_filters", all_feeds_filters); qBTRSS.setValue("feed_filters", all_feeds_filters);
} }
@@ -269,8 +267,6 @@ public:
} }
~FeedDownloaderDlg() { ~FeedDownloaderDlg() {
if(enableDl_cb->isChecked())
emit filteringEnabled();
// Make sure we save everything // Make sure we save everything
saveCurrentFilterSettings(); saveCurrentFilterSettings();
filters.save(); filters.save();
@@ -297,7 +293,7 @@ protected slots:
default_path = QDir::homePath(); default_path = QDir::homePath();
} }
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath()); QString dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
if(!dir.isNull() && QDir(dir).exists()) { if(!dir.isNull() and QDir(dir).exists()) {
savepath_line->setText(dir); savepath_line->setText(dir);
} }
} }
@@ -378,8 +374,6 @@ protected slots:
QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use.")); QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use."));
} }
}while(!validated); }while(!validated);
// Save the current filter
saveCurrentFilterSettings();
// Rename the filter // Rename the filter
filters.rename(current_name, new_name); filters.rename(current_name, new_name);
if(selected_filter == current_name) if(selected_filter == current_name)
@@ -493,19 +487,16 @@ protected slots:
// Append file extension // Append file extension
if(!destination.endsWith(".filters")) if(!destination.endsWith(".filters"))
destination += ".filters"; destination += ".filters";
/*if(QFile::exists(destination)) { if(QFile::exists(destination)) {
int ret = QMessageBox::question(0, tr("Overwriting confirmation"), tr("Are you sure you want to overwrite existing file?"), QMessageBox::Yes|QMessageBox::No); int ret = QMessageBox::question(0, tr("Overwriting confirmation"), tr("Are you sure you want to overwrite existing file?"), QMessageBox::Yes|QMessageBox::No);
if(ret != QMessageBox::Yes) return; if(ret != QMessageBox::Yes) return;
}*/ }
if(filters.serialize(destination)) if(filters.serialize(destination))
QMessageBox::information(0, tr("Export successful"), tr("Filters export was successful.")); QMessageBox::information(0, tr("Export successful"), tr("Filters export was successful."));
else else
QMessageBox::warning(0, tr("Export failure"), tr("Filters could not be exported due to an I/O error.")); QMessageBox::warning(0, tr("Export failure"), tr("Filters could not be exported due to an I/O error."));
} }
signals:
void filteringEnabled();
}; };
#undef QHash #undef QHash

View File

@@ -2,10 +2,10 @@
#define FILESYSTEMWATCHER_H #define FILESYSTEMWATCHER_H
#include <QFileSystemWatcher> #include <QFileSystemWatcher>
#include <QDir>
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
#include <QTimer> #include <QTimer>
#include <QDir>
#include <QPointer> #include <QPointer>
#include <QStringList> #include <QStringList>
#include <QSet> #include <QSet>
@@ -19,8 +19,6 @@
#endif #endif
#endif #endif
#include "misc.h"
#ifndef CIFS_MAGIC_NUMBER #ifndef CIFS_MAGIC_NUMBER
#define CIFS_MAGIC_NUMBER 0xFF534D42 #define CIFS_MAGIC_NUMBER 0xFF534D42
#endif #endif
@@ -29,9 +27,6 @@
#define NFS_SUPER_MAGIC 0x6969 #define NFS_SUPER_MAGIC 0x6969
#endif #endif
const int WATCH_INTERVAL = 10000; // 10 sec
const int MAX_PARTIAL_RETRIES = 5;
/* /*
* Subclassing QFileSystemWatcher in order to support Network File * Subclassing QFileSystemWatcher in order to support Network File
* System watching (NFS, CIFS) on Linux and Mac OS. * System watching (NFS, CIFS) on Linux and Mac OS.
@@ -39,28 +34,23 @@ const int MAX_PARTIAL_RETRIES = 5;
class FileSystemWatcher: public QFileSystemWatcher { class FileSystemWatcher: public QFileSystemWatcher {
Q_OBJECT Q_OBJECT
private:
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
QList<QDir> watched_folders; private:
QDir watched_folder;
QPointer<QTimer> watch_timer; QPointer<QTimer> watch_timer;
#endif QStringList filters;
QStringList m_filters;
// Partial torrents
QHash<QString, int> m_partialTorrents;
QPointer<QTimer> m_partialTorrentTimer;
#ifndef Q_WS_WIN protected:
private: bool isNetworkFileSystem(QString path) {
static bool isNetworkFileSystem(QString path) {
QString file = path; QString file = path;
if(!file.endsWith(QDir::separator())) if(!file.endsWith(QDir::separator()))
file += QDir::separator(); file += QDir::separator();
file += "."; file += ".";
struct statfs buf; struct statfs buf;
if(!statfs(file.toLocal8Bit().constData(), &buf)) { if(!statfs(file.toLocal8Bit().data(), &buf)) {
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC); return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC);
} else { } else {
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl; std::cerr << "Error: statfs() call failed for " << file.toLocal8Bit().data() << ". Supposing it is a local folder..." << std::endl;
switch(errno) { switch(errno) {
case EACCES: case EACCES:
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl; std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
@@ -107,8 +97,14 @@ private:
public: public:
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) { FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
m_filters << "*.torrent"; filters << "*.torrent";
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString))); connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder()));
}
FileSystemWatcher(QString path, QObject *parent): QFileSystemWatcher(parent) {
filters << "*.torrent";
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder()));
addPath(path);
} }
~FileSystemWatcher() { ~FileSystemWatcher() {
@@ -116,45 +112,36 @@ public:
if(watch_timer) if(watch_timer)
delete watch_timer; delete watch_timer;
#endif #endif
if(m_partialTorrentTimer)
delete m_partialTorrentTimer;
} }
QStringList directories() const { QStringList directories() const {
QStringList dirs;
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
if(watch_timer) { if(watch_timer)
foreach (const QDir &dir, watched_folders) return QStringList(watched_folder.path());
dirs << dir.canonicalPath();
}
#endif #endif
dirs << QFileSystemWatcher::directories(); return QFileSystemWatcher::directories();
return dirs;
} }
void addPath(const QString & path) { void addPath(const QString & path) {
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
QDir dir(path); watched_folder = QDir(path);
if (!dir.exists()) if(!watched_folder.exists()) return;
return;
// Check if the path points to a network file system or not // Check if the path points to a network file system or not
if(isNetworkFileSystem(path)) { if(isNetworkFileSystem(path)) {
// Network mode // Network mode
qDebug("Network folder detected: %s", qPrintable(path)); Q_ASSERT(!watch_timer);
qDebug("Network folder detected: %s", path.toLocal8Bit().data());
qDebug("Using file polling mode instead of inotify..."); qDebug("Using file polling mode instead of inotify...");
watched_folders << dir;
// Set up the watch timer // Set up the watch timer
if (!watch_timer) { watch_timer = new QTimer(this);
watch_timer = new QTimer(this); connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanFolder()));
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders())); watch_timer->start(5000); // 5 sec
watch_timer->start(WATCH_INTERVAL); // 5 sec
}
} else { } else {
#endif #endif
// Normal mode // Normal mode
qDebug("FS Watching is watching %s in normal mode", qPrintable(path)); qDebug("FS Watching is watching %s in normal mode", path.toLocal8Bit().data());
QFileSystemWatcher::addPath(path); QFileSystemWatcher::addPath(path);
scanLocalFolder(path); scanFolder();
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
} }
#endif #endif
@@ -162,118 +149,38 @@ public:
void removePath(const QString & path) { void removePath(const QString & path) {
#ifndef Q_WS_WIN #ifndef Q_WS_WIN
QDir dir(path); if(watch_timer) {
for (int i = 0; i < watched_folders.count(); ++i) { // Network mode
if (QDir(watched_folders.at(i)) == dir) { if(QDir(path) == watched_folder) {
watched_folders.removeAt(i); delete watch_timer;
if (watched_folders.isEmpty())
delete watch_timer;
return;
} }
} else {
#endif
// Normal mode
QFileSystemWatcher::removePath(path);
#ifndef Q_WS_WIN
} }
#endif #endif
// Normal mode
QFileSystemWatcher::removePath(path);
} }
protected slots: protected slots:
void scanLocalFolder(QString path) { // XXX: Does not detect file size changes to improve performance.
qDebug("scanLocalFolder(%s) called", qPrintable(path)); void scanFolder() {
qDebug("Scan folder was called");
QStringList torrents; QStringList torrents;
// Local folders scan if(watch_timer) {
addTorrentsFromDir(QDir(path), torrents); torrents = watched_folder.entryList(filters, QDir::Files, QDir::Unsorted);
// Report detected torrent files
if(!torrents.empty()) {
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
emit torrentsAdded(torrents);
}
}
void scanNetworkFolders() {
#ifndef Q_WS_WIN
qDebug("scanNetworkFolders() called");
QStringList torrents;
// Network folders scan
foreach (const QDir &dir, watched_folders) {
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
addTorrentsFromDir(dir, torrents);
}
// Report detected torrent files
if(!torrents.empty()) {
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
emit torrentsAdded(torrents);
}
#endif
}
void processPartialTorrents() {
QStringList no_longer_partial;
// Check which torrents are still partial
foreach(const QString& torrent_path, m_partialTorrents.keys()) {
if(!QFile::exists(torrent_path)) {
m_partialTorrents.remove(torrent_path);
continue;
}
if(misc::isValidTorrentFile(torrent_path)) {
no_longer_partial << torrent_path;
m_partialTorrents.remove(torrent_path);
} else {
if(m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) {
m_partialTorrents.remove(torrent_path);
QFile::rename(torrent_path, torrent_path+".invalid");
} else {
m_partialTorrents[torrent_path]++;
}
}
}
// Stop the partial timer if necessary
if(m_partialTorrents.empty()) {
m_partialTorrentTimer->stop();
m_partialTorrentTimer->deleteLater();
qDebug("No longer any partial torrent.");
} else { } else {
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count()); torrents = QDir(QFileSystemWatcher::directories().first()).entryList(filters, QDir::Files, QDir::Unsorted);
m_partialTorrentTimer->start(WATCH_INTERVAL); qDebug("FSWatcher: Polling manually folder %s", QFileSystemWatcher::directories().first().toLocal8Bit().data());
} }
// Notify of new torrents if(!torrents.empty())
if(!no_longer_partial.isEmpty()) emit torrentsAdded(torrents);
emit torrentsAdded(no_longer_partial);
} }
signals: signals:
void torrentsAdded(QStringList &pathList); void torrentsAdded(QStringList &pathList);
private:
void startPartialTorrentTimer() {
Q_ASSERT(!m_partialTorrents.isEmpty());
if(!m_partialTorrentTimer) {
m_partialTorrentTimer = new QTimer();
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
m_partialTorrentTimer->setSingleShot(true);
m_partialTorrentTimer->start(WATCH_INTERVAL);
}
}
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
foreach(const QString &file, files) {
const QString file_abspath = dir.absoluteFilePath(file);
if(misc::isValidTorrentFile(file_abspath)) {
torrents << file_abspath;
} else {
if(!m_partialTorrents.contains(file_abspath)) {
qDebug("Partial torrent detected at: %s", qPrintable(file_abspath));
qDebug("Delay the file's processing...");
m_partialTorrents.insert(file_abspath, 0);
}
}
}
if(!m_partialTorrents.empty())
startPartialTorrentTimer();
}
}; };
#endif // FILESYSTEMWATCHER_H #endif // FILESYSTEMWATCHER_H

View File

@@ -34,8 +34,7 @@
#include <QThread> #include <QThread>
#include <QFile> #include <QFile>
#include <QDataStream> #include <QDataStream>
#include <QStringList> #include <QMessageBox>
#include <QHostAddress>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp> #include <libtorrent/ip_filter.hpp>
@@ -46,364 +45,373 @@ using namespace std;
// P2B Stuff // P2B Stuff
#include <string.h> #include <string.h>
#ifdef Q_WS_WIN #ifdef Q_WS_WIN
#include <Winsock2.h> #include <Winsock2.h>
#else #else
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
// End of P2B stuff // End of P2B stuff
class FilterParserThread : public QThread { class FilterParserThread : public QThread {
Q_OBJECT Q_OBJECT
private: private:
session *s; session *s;
ip_filter filter; ip_filter filter;
bool abort; bool abort;
QString filePath; QString filePath;
protected: protected:
QString cleanupIPAddress(QString _ip) { void run(){
QHostAddress ip(_ip.trimmed()); qDebug("Processing filter file");
if(ip.isNull()) { if(filePath.endsWith(".dat", Qt::CaseInsensitive)) {
return QString(); // eMule DAT file
} parseDATFilterFile(filePath);
return ip.toString();
}
void run(){
qDebug("Processing filter file");
if(filePath.endsWith(".dat", Qt::CaseInsensitive)) {
// eMule DAT file
parseDATFilterFile(filePath);
} else {
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
// PeerGuardian p2p file
parseP2PFilterFile(filePath);
} else { } else {
if(filePath.endsWith(".p2b", Qt::CaseInsensitive)) { if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
// PeerGuardian p2b file // PeerGuardian p2p file
parseP2BFilterFile(filePath); parseP2PFilterFile(filePath);
} else { } else {
// Default: eMule DAT format if(filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
parseDATFilterFile(filePath); // PeerGuardian p2b file
parseP2BFilterFile(filePath);
} else {
// Default: eMule DAT format
parseDATFilterFile(filePath);
}
} }
} }
s->set_ip_filter(filter);
qDebug("IP Filter thread: finished parsing, filter applied");
} }
s->set_ip_filter(filter);
qDebug("IP Filter thread: finished parsing, filter applied"); public:
} FilterParserThread(QObject* parent, session *s) : QThread(parent), s(s), abort(false) {
public:
FilterParserThread(QObject* parent, session *s) : QThread(parent), s(s), abort(false) {
}
~FilterParserThread(){
abort = true;
wait();
}
// Parser for eMule ip filter in DAT format
void parseDATFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line should be splitted by commas
QList<QByteArray> partsList = line.split(',');
const uint nbElem = partsList.size();
// IP Range should be splitted by a dash
QList<QByteArray> IPs = partsList.first().split('-');
if(IPs.size() != 2) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Line was %s", line.constData());
continue;
}
boost::system::error_code ec;
const QString strStartIP = cleanupIPAddress(IPs.at(0));
if(strStartIP.isEmpty()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
if(ec) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
continue;
}
const QString strEndIP = cleanupIPAddress(IPs.at(1));
if(strEndIP.isEmpty()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
continue;
}
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
if(ec) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
continue;
}
if(startAddr.is_v4() != endAddr.is_v4()) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
qDebug("One IP is IPv4 and the other is IPv6!");
continue;
}
// Check if there is an access value (apparently not mandatory)
int nbAccess = 0;
if(nbElem > 1) {
// There is possibly one
nbAccess = partsList.at(1).trimmed().toInt();
}
if(nbAccess > 127) {
// Ignoring this rule because access value is too high
continue;
}
// Now Add to the filter
try {
filter.add_rule(startAddr, endAddr, ip_filter::blocked);
}catch(exception){
qDebug("Bad line in filter file, avoided crash...");
}
}
file.close();
} }
}
~FilterParserThread(){
// Parser for PeerGuardian ip filter in p2p format
void parseP2PFilterFile(QString filePath) {
QFile file(filePath);
QStringList IP;
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine().trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is splitted by :
QList<QByteArray> partsList = line.split(':');
if(partsList.size() < 2){
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
// Get IP range
QList<QByteArray> IPs = partsList.last().split('-');
if(IPs.size() != 2) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("line was: %s", line.constData());
continue;
}
boost::system::error_code ec;
QString strStartIP = cleanupIPAddress(IPs.at(0));
if(strStartIP.isEmpty()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
if(ec) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
continue;
}
QString strEndIP = cleanupIPAddress(IPs.at(1));
if(strEndIP.isEmpty()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
continue;
}
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
if(ec) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
continue;
}
if(startAddr.is_v4() != endAddr.is_v4()) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Line was: %s", line.constData());
continue;
}
try {
filter.add_rule(startAddr, endAddr, ip_filter::blocked);
} catch(std::exception&) {
qDebug("p2p file: line %d is malformed.", nbLine);
qDebug("Line was: %s", line.constData());
continue;
}
}
file.close();
}
}
int getlineInStream(QDataStream& stream, string& name, char delim) {
char c;
int total_read = 0;
int read;
do {
read = stream.readRawData(&c, 1);
total_read += read;
if(read > 0) {
if(c != delim) {
name += c;
} else {
// Delim found
return total_read;
}
}
} while(read > 0);
return total_read;
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2BFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly)){
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
return;
}
QDataStream stream(&file);
// Read header
char buf[7];
unsigned char version;
if(
!stream.readRawData(buf, sizeof(buf)) ||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
!stream.readRawData((char*)&version, sizeof(version))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(version==1 || version==2) {
qDebug ("p2b version 1 or 2");
unsigned int start, end;
string name;
while(getlineInStream(stream, name, '\0') && !abort) {
if(
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
}
}
else if(version==3) {
qDebug ("p2b version 3");
unsigned int namecount;
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
namecount=ntohl(namecount);
// Reading names although, we don't really care about them
for(unsigned int i=0; i<namecount; i++) {
string name;
if(!getlineInStream(stream, name, '\0')) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
if(abort) return;
}
// Reading the ranges
unsigned int rangecount;
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
rangecount=ntohl(rangecount);
unsigned int name, start, end;
for(unsigned int i=0; i<rangecount; i++) {
if(
!stream.readRawData((char*)&name, sizeof(name)) ||
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
if(abort) return;
}
} else {
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
return;
}
file.close();
}
}
// Process ip filter file
// Supported formats:
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
void processFilterFile(QString _filePath){
// First, import current filter
filter = s->get_ip_filter();
if(isRunning()) {
// Already parsing a filter, abort first
abort = true; abort = true;
wait(); wait();
} }
abort = false;
filePath = _filePath; // Parser for eMule ip filter in DAT format
// Run it void parseDATFilterFile(QString filePath) {
start(); const QRegExp is_ipv6(QString::fromUtf8("^[0-9a-f]{4}(:[0-9a-f]{4}){7}$"), Qt::CaseInsensitive, QRegExp::RegExp);
} const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
QString strStartIP, strEndIP;
static void processFilterList(session *s, QStringList IPs) { bool IPv4 = true;
// First, import current filter QFile file(filePath);
ip_filter filter = s->get_ip_filter(); if (file.exists()){
foreach(const QString &ip, IPs) { if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData()); QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
boost::system::error_code ec; return;
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().constData(), ec); }
Q_ASSERT(!ec); unsigned int nbLine = 0;
if(!ec) while (!file.atEnd() && !abort) {
filter.add_rule(addr, addr, ip_filter::blocked); ++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is not commented
QList<QByteArray> partsList = line.split(',');
unsigned int nbElem = partsList.size();
// IP Range can be splitted by a dash or a comma...
// Check if there is a dash in first part
QByteArray firstPart = partsList.at(0);
int nbAccess = 0;
if(firstPart.contains('-')) {
// Range is splitted by a dash
QList<QByteArray> IPs = firstPart.split('-');
if(IPs.size() != 2) {
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
strStartIP = IPs.at(0).trimmed();
strEndIP = IPs.at(1).trimmed();
// Check if IPs are correct
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
IPv4 = true;
} else {
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
IPv4 = false;
} else {
// Could not determine IP format
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
}
// Check if there is an access value (apparently not mandatory)
if(nbElem > 1) {
// There is possibly one
bool ok;
nbAccess = partsList.at(1).trimmed().toInt(&ok);
if(!ok){
nbAccess = 0;
}
}
} else {
// Range is probably splitted by a comma
unsigned int nbElem = partsList.size();
if(nbElem > 1) {
strStartIP = firstPart.trimmed();
strEndIP = partsList.at(1).trimmed();
// Check if IPs are correct
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
IPv4 = true;
} else {
if(strStartIP.contains(is_ipv6) && strEndIP.contains(is_ipv6)) {
IPv4 = false;
} else {
// Could not determine IP format
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
continue;
}
}
// Check if there is an access value (apparently not mandatory)
if(nbElem > 2) {
// There is possibly one
bool ok;
nbAccess = partsList.at(2).trimmed().toInt(&ok);
if(!ok){
nbAccess = 0;
}
}
}
}
if(nbAccess > 127) {
// Ignoring this rule because access value is too high
continue;
}
// Now Add to the filter
QStringList IP;
try {
if(IPv4) {
//IPv4 addresses
IP = strStartIP.split('.');
if(IP.size() != 4)
throw exception();
address_v4 start((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
IP = strEndIP.split('.');
if(IP.size() != 4)
throw exception();
address_v4 last((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
} else {
// IPv6, ex : 1fff:0000:0a88:85a3:0000:0000:ac1f:8001
IP = strStartIP.split(':');
address_v6 start = address_v6::from_string(strStartIP.remove(':', 0).toLocal8Bit().data());
IP = strEndIP.split(':');
address_v6 last = address_v6::from_string(strEndIP.remove(':', 0).toLocal8Bit().data());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
}
}catch(exception){
qDebug("Bad line in filter file, avoided crash...");
}
}
file.close();
}
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2PFilterFile(QString filePath) {
const QRegExp is_ipv4(QString::fromUtf8("^(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))(\\.(([0-1]?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))){3}$"), Qt::CaseInsensitive, QRegExp::RegExp);
QFile file(filePath);
QStringList IP;
if (file.exists()){
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
return;
}
unsigned int nbLine = 0;
while (!file.atEnd() && !abort) {
++nbLine;
QByteArray line = file.readLine();
// Ignoring empty lines
line = line.trimmed();
if(line.isEmpty()) continue;
// Ignoring commented lines
if(line.startsWith('#') || line.startsWith("//")) continue;
// Line is not commented
QList<QByteArray> partsList = line.split(':');
if(partsList.size() < 2){
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
// Get IP range
QList<QByteArray> IPs = partsList.last().split('-');
if(IPs.size() != 2) {
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
QString strStartIP = IPs.at(0).trimmed();
QString strEndIP = IPs.at(1).trimmed();
// Check IPs format (IPv4 only)
if(strStartIP.contains(is_ipv4) && strEndIP.contains(is_ipv4)) {
// IPv4
IP = strStartIP.split('.');
address_v4 start((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
IP = strEndIP.split('.');
address_v4 last((IP.at(0).toUInt() << 24) + (IP.at(1).toUInt() << 16) + (IP.at(2).toUInt() << 8) + IP.at(3).toUInt());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
} else {
qDebug("p2p file: line %d is malformed.", nbLine);
continue;
}
}
file.close();
}
}
int getlineInStream(QDataStream& stream, string& name, char delim) {
char c;
int total_read = 0;
int read;
do {
read = stream.readRawData(&c, 1);
total_read += read;
if(read > 0) {
if(c != delim) {
name += c;
} else {
// Delim found
return total_read;
}
}
} while(read > 0);
return total_read;
}
// Parser for PeerGuardian ip filter in p2p format
void parseP2BFilterFile(QString filePath) {
QFile file(filePath);
if (file.exists()){
if(!file.open(QIODevice::ReadOnly)){
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
return;
}
QDataStream stream(&file);
// Read header
char buf[7];
unsigned char version;
if(
!stream.readRawData(buf, sizeof(buf)) ||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
!stream.readRawData((char*)&version, sizeof(version))
) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
if(version==1 || version==2) {
qDebug ("p2b version 1 or 2");
unsigned int start, end;
string name;
while(getlineInStream(stream, name, '\0') && !abort) {
if(
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
}
}
else if(version==3) {
qDebug ("p2b version 3");
unsigned int namecount;
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
namecount=ntohl(namecount);
// Reading names although, we don't really care about them
for(unsigned int i=0; i<namecount; i++) {
string name;
if(!getlineInStream(stream, name, '\0')) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
if(abort) return;
}
// Reading the ranges
unsigned int rangecount;
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
rangecount=ntohl(rangecount);
unsigned int name, start, end;
for(unsigned int i=0; i<rangecount; i++) {
if(
!stream.readRawData((char*)&name, sizeof(name)) ||
!stream.readRawData((char*)&start, sizeof(start)) ||
!stream.readRawData((char*)&end, sizeof(end))
) {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
// Network byte order to Host byte order
// asio address_v4 contructor expects it
// that way
address_v4 first(ntohl(start));
address_v4 last(ntohl(end));
// Apply to bittorrent session
filter.add_rule(first, last, ip_filter::blocked);
if(abort) return;
}
} else {
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
return;
}
file.close();
}
}
// Process ip filter file
// Supported formats:
// * eMule IP list (DAT): http://wiki.phoenixlabs.org/wiki/DAT_Format
// * PeerGuardian Text (P2P): http://wiki.phoenixlabs.org/wiki/P2P_Format
// * PeerGuardian Binary (P2B): http://wiki.phoenixlabs.org/wiki/P2B_Format
void processFilterFile(QString _filePath){
// First, import current filter
filter = s->get_ip_filter();
if(isRunning()) {
// Already parsing a filter, abort first
abort = true;
wait();
}
abort = false;
filePath = _filePath;
// Run it
start();
}
static void processFilterList(session *s, QStringList IPs) {
// First, import current filter
ip_filter filter = s->get_ip_filter();
foreach(const QString &ip, IPs) {
qDebug("Manual ban of peer %s", ip.toLocal8Bit().data());
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().data());
filter.add_rule(addr, addr, ip_filter::blocked);
}
s->set_ip_filter(filter);
} }
s->set_ip_filter(filter);
}
}; };

View File

@@ -40,14 +40,13 @@
using namespace libtorrent; using namespace libtorrent;
class GeoIP : public QObject { class GeoIP {
Q_OBJECT
protected: protected:
#ifdef WITH_GEOIP_EMBEDDED #ifdef WITH_GEOIP_EMBEDDED
static QString geoipFolder(bool embedded=false) { static QString geoipFolder(bool embedded=false) {
if(embedded) if(embedded)
return ":/geoip/"; return ":/geoip/";
return misc::QDesktopServicesDataLocation()+"geoip"+QDir::separator(); return misc::qBittorrentPath()+"geoip"+QDir::separator();
#else #else
static QString geoipFolder(bool) { static QString geoipFolder(bool) {
if(QFile::exists("/usr/local/share/GeoIP/GeoIP.dat")) if(QFile::exists("/usr/local/share/GeoIP/GeoIP.dat"))
@@ -69,19 +68,13 @@ protected:
// Create geoip folder is necessary // Create geoip folder is necessary
QDir gfolder(geoipFolder(false)); QDir gfolder(geoipFolder(false));
if(!gfolder.exists()) { if(!gfolder.exists()) {
if(!gfolder.mkpath(geoipFolder(false))) { if(!gfolder.mkpath(geoipFolder(false))) return;
std::cerr << "Failed to create geoip folder at " << qPrintable(geoipFolder(false)) << std::endl;
return;
}
} }
// Remove destination files // Remove destination files
if(QFile::exists(geoipDBpath(false))) if(QFile::exists(geoipDBpath(false)))
misc::safeRemove(geoipDBpath(false)); QFile::remove(geoipDBpath(false));
// Copy from executable to hard disk // Copy from executable to hard disk
qDebug("%s -> %s", qPrintable(geoipDBpath(true)), qPrintable(geoipDBpath(false))); QFile::copy(geoipDBpath(true), geoipDBpath(false));
if(!QFile::copy(geoipDBpath(true), geoipDBpath(false))) {
std::cerr << "ERROR: Failed to copy geoip.dat from executable to hard disk" << std::endl;
}
qDebug("Local Geoip database was updated"); qDebug("Local Geoip database was updated");
} }
} }
@@ -93,9 +86,9 @@ public:
exportEmbeddedDb(); exportEmbeddedDb();
#endif #endif
if(QFile::exists(geoipDBpath(false))) { if(QFile::exists(geoipDBpath(false))) {
qDebug("Loading GeoIP database from %s...", qPrintable(geoipDBpath(false))); qDebug("Loading GeoIP database from %s...", geoipDBpath(false).toLocal8Bit().data());
if(!s->load_country_db(geoipDBpath(false).toLocal8Bit().constData())) { if(!s->load_country_db(geoipDBpath(false).toLocal8Bit().data())) {
std::cerr << "Failed to load Geoip Database at " << qPrintable(geoipDBpath(false)) << std::endl; std::cerr << "Failed to load Geoip Database at " << geoipDBpath(false).toLocal8Bit().data() << std::endl;
} }
} else { } else {
qDebug("ERROR: Impossible to find local Geoip Database"); qDebug("ERROR: Impossible to find local Geoip Database");
@@ -104,7 +97,7 @@ public:
// TODO: Support more countries // TODO: Support more countries
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm // http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
static QIcon CountryISOCodeToIcon(const char* iso, QString &country_name) { static QIcon CountryISOCodeToIcon(char* iso) {
switch(iso[0]) { switch(iso[0]) {
case 0: case 0:
case '-': case '-':
@@ -112,107 +105,106 @@ public:
//qDebug("Not returning any icon because iso is invalid: %s", iso); //qDebug("Not returning any icon because iso is invalid: %s", iso);
return QIcon(); return QIcon();
case 'A': case 'A':
if(iso[1] == 'U') { country_name = tr("Australia"); return QIcon(":/Icons/flags/australia.png"); } if(iso[1] == 'U') return QIcon(":/Icons/flags/australia.png");
if(iso[1] == 'R') { country_name = tr("Argentina"); return QIcon(":/Icons/flags/argentina.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/argentina.png");
if(iso[1] == 'T') { country_name = tr("Austria"); return QIcon(":/Icons/flags/austria.png"); } if(iso[1] == 'T') return QIcon(":/Icons/flags/austria.png");
if(iso[1] == 'E') { country_name = tr("United Arab Emirates"); return QIcon(":/Icons/flags/united_arab_emirates.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/united_arab_emirates.png");
break; break;
case 'B': case 'B':
if(iso[1] == 'R') { country_name = tr("Brazil"); return QIcon(":/Icons/flags/brazil.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/brazil.png");
if(iso[1] == 'G') { country_name = tr("Bulgaria"); return QIcon(":/Icons/flags/bulgaria.png"); } if(iso[1] == 'G') return QIcon(":/Icons/flags/bulgaria.png");
if(iso[1] == 'Y') { country_name = tr("Belarus"); return QIcon(":/Icons/flags/belarus.png"); } if(iso[1] == 'Y') return QIcon(":/Icons/flags/belarus.png");
if(iso[1] == 'E') { country_name = tr("Belgium"); return QIcon(":/Icons/flags/belgium.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/belgium.png");
if(iso[1] == 'A') { country_name = tr("Bosnia"); return QIcon(":/Icons/flags/bosnia.png"); } if(iso[1] == 'A') return QIcon(":/Icons/flags/bosnia.png");
break; break;
case 'C': case 'C':
if(iso[1] == 'A') { country_name = tr("Canada"); return QIcon(":/Icons/flags/canada.png"); } if(iso[1] == 'A') return QIcon(":/Icons/flags/canada.png");
if(iso[1] == 'Z') { country_name = tr("Czech Republic"); return QIcon(":/Icons/flags/czech.png"); } if(iso[1] == 'Z') return QIcon(":/Icons/flags/czech.png");
if(iso[1] == 'N') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); } if(iso[1] == 'N') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') { country_name = tr("Costa Rica"); return QIcon(":/Icons/flags/costa_rica.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/costa_rica.png");
if(iso[1] == 'H') { country_name = tr("Switzerland"); return QIcon(":/Icons/flags/suisse.png"); } if(iso[1] == 'H') return QIcon(":/Icons/flags/suisse.png");
break; break;
case 'D': case 'D':
if(iso[1] == 'E') { country_name = tr("Germany"); return QIcon(":/Icons/flags/germany.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/germany.png");
if(iso[1] == 'K') { country_name = tr("Denmark"); return QIcon(":/Icons/flags/denmark.png"); } if(iso[1] == 'K') return QIcon(":/Icons/flags/denmark.png");
if(iso[1] == 'Z') { country_name = tr("Algeria"); return QIcon(":/Icons/flags/algeria.png"); } if(iso[1] == 'Z') return QIcon(":/Icons/flags/algeria.png");
break; break;
case 'E': case 'E':
if(iso[1] == 'S') { country_name = tr("Spain"); return QIcon(":/Icons/flags/spain.png"); } if(iso[1] == 'S') return QIcon(":/Icons/flags/spain.png");
if(iso[1] == 'G') { country_name = tr("Egypt"); return QIcon(":/Icons/flags/egypt.png"); } if(iso[1] == 'G') return QIcon(":/Icons/flags/egypt.png");
break; break;
case 'F': case 'F':
if(iso[1] == 'I') { country_name = tr("Finland"); return QIcon(":/Icons/flags/finland.png"); } if(iso[1] == 'I') return QIcon(":/Icons/flags/finland.png");
if(iso[1] == 'R') { country_name = tr("France"); return QIcon(":/Icons/flags/france.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/france.png");
break; break;
case 'G': case 'G':
if(iso[1] == 'B') { country_name = tr("United Kingdom"); return QIcon(":/Icons/flags/united_kingdom.png"); } if(iso[1] == 'B') return QIcon(":/Icons/flags/united_kingdom.png");
if(iso[1] == 'R') { country_name = tr("Greece"); return QIcon(":/Icons/flags/greece.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/greece.png");
if(iso[1] == 'E') { country_name = tr("Georgia"); return QIcon(":/Icons/flags/georgia.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/georgia.png");
break; break;
case 'H': case 'H':
if(iso[1] == 'U') { country_name = tr("Hungary"); return QIcon(":/Icons/flags/hungary.png"); } if(iso[1] == 'U') return QIcon(":/Icons/flags/hungary.png");
if(iso[1] == 'K') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); } if(iso[1] == 'K') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') { country_name = tr("Croatia"); return QIcon(":/Icons/flags/croatia.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/croatia.png");
break; break;
case 'I': case 'I':
if(iso[1] == 'T') { country_name = tr("Italy"); return QIcon(":/Icons/flags/italy.png"); } if(iso[1] == 'T') return QIcon(":/Icons/flags/italy.png");
if(iso[1] == 'N') { country_name = tr("India"); return QIcon(":/Icons/flags/india.png"); } if(iso[1] == 'N') return QIcon(":/Icons/flags/india.png");
if(iso[1] == 'L') { country_name = tr("Israel"); return QIcon(":/Icons/flags/israel.png"); } if(iso[1] == 'L') return QIcon(":/Icons/flags/israel.png");
if(iso[1] == 'E') { country_name = tr("Ireland"); return QIcon(":/Icons/flags/ireland.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/ireland.png");
if(iso[1] == 'S') { country_name = tr("Iceland"); return QIcon(":/Icons/flags/iceland.png"); } if(iso[1] == 'S') return QIcon(":/Icons/flags/iceland.png");
if(iso[1] == 'D') { country_name = tr("Indonesia"); return QIcon(":/Icons/flags/indonesia.png"); } if(iso[1] == 'D') return QIcon(":/Icons/flags/indonesia.png");
break; break;
case 'J': case 'J':
if(iso[1] == 'P') { country_name = tr("Japan"); return QIcon(":/Icons/flags/japan.png"); } if(iso[1] == 'P') return QIcon(":/Icons/flags/japan.png");
break; break;
case 'K': case 'K':
if(iso[1] == 'R') { country_name = tr("South Korea"); return QIcon(":/Icons/flags/south_korea.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/south_korea.png");
break; break;
case 'L': case 'L':
if(iso[1] == 'U') { country_name = tr("Luxembourg"); return QIcon(":/Icons/flags/luxembourg.png"); } if(iso[1] == 'U') return QIcon(":/Icons/flags/luxembourg.png");
break; break;
case 'M': case 'M':
if(iso[1] == 'Y') { country_name = tr("Malaysia"); return QIcon(":/Icons/flags/malaysia.png"); } if(iso[1] == 'Y') return QIcon(":/Icons/flags/malaysia.png");
if(iso[1] == 'X') { country_name = tr("Mexico"); return QIcon(":/Icons/flags/mexico.png"); } if(iso[1] == 'X') return QIcon(":/Icons/flags/mexico.png");
if(iso[1] == 'E') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/serbia.png");
if(iso[1] == 'A') { country_name = tr("Morocco"); return QIcon(":/Icons/flags/morocco.png"); } if(iso[1] == 'A') return QIcon(":/Icons/flags/morocco.png");
break; break;
case 'N': case 'N':
if(iso[1] == 'L') { country_name = tr("Netherlands"); return QIcon(":/Icons/flags/netherlands.png"); } if(iso[1] == 'L') return QIcon(":/Icons/flags/netherlands.png");
if(iso[1] == 'O') { country_name = tr("Norway"); return QIcon(":/Icons/flags/norway.png"); } if(iso[1] == 'O') return QIcon(":/Icons/flags/norway.png");
if(iso[1] == 'Z') { country_name = tr("New Zealand"); return QIcon(":/Icons/flags/newzealand.png"); } if(iso[1] == 'Z') return QIcon(":/Icons/flags/newzealand.png");
break; break;
case 'P': case 'P':
if(iso[1] == 'T') { country_name = tr("Portugal"); return QIcon(":/Icons/flags/portugal.png"); } if(iso[1] == 'T') return QIcon(":/Icons/flags/portugal.png");
if(iso[1] == 'L') { country_name = tr("Poland"); return QIcon(":/Icons/flags/poland.png"); } if(iso[1] == 'L') return QIcon(":/Icons/flags/poland.png");
if(iso[1] == 'K') { country_name = tr("Pakistan"); return QIcon(":/Icons/flags/pakistan.png"); } if(iso[1] == 'K') return QIcon(":/Icons/flags/pakistan.png");
if(iso[1] == 'H') { country_name = tr("Philippines"); return QIcon(":/Icons/flags/philippines.png"); } if(iso[1] == 'H') return QIcon(":/Icons/flags/philippines.png");
break; break;
case 'R': case 'R':
if(iso[1] == 'U') { country_name = tr("Russia"); return QIcon(":/Icons/flags/russia.png"); } if(iso[1] == 'U') return QIcon(":/Icons/flags/russia.png");
if(iso[1] == 'O') { country_name = tr("Romania"); return QIcon(":/Icons/flags/romania.png"); } if(iso[1] == 'O') return QIcon(":/Icons/flags/romania.png");
if(iso[1] == 'E') { country_name = tr("France (Reunion Island)"); return QIcon(":/Icons/flags/france.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/france.png");
if(iso[1] == 'S') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); } if(iso[1] == 'S') return QIcon(":/Icons/flags/serbia.png");
break; break;
case 'S': case 'S':
if(iso[1] == 'A') { country_name = tr("Saudi Arabia"); return QIcon(":/Icons/flags/saoudi_arabia.png"); } if(iso[1] == 'E') return QIcon(":/Icons/flags/sweden.png");
if(iso[1] == 'E') { country_name = tr("Sweden"); return QIcon(":/Icons/flags/sweden.png"); } if(iso[1] == 'K') return QIcon(":/Icons/flags/slovakia.png");
if(iso[1] == 'K') { country_name = tr("Slovakia"); return QIcon(":/Icons/flags/slovakia.png"); } if(iso[1] == 'G') return QIcon(":/Icons/flags/singapore.png");
if(iso[1] == 'G') { country_name = tr("Singapore"); return QIcon(":/Icons/flags/singapore.png"); } if(iso[1] == 'I') return QIcon(":/Icons/flags/slovenia.png");
if(iso[1] == 'I') { country_name = tr("Slovenia"); return QIcon(":/Icons/flags/slovenia.png"); }
break; break;
case 'T': case 'T':
if(iso[1] == 'W') { country_name = tr("Taiwan"); return QIcon(":/Icons/flags/china.png"); } if(iso[1] == 'W') return QIcon(":/Icons/flags/china.png");
if(iso[1] == 'R') { country_name = tr("Turkey"); return QIcon(":/Icons/flags/turkey.png"); } if(iso[1] == 'R') return QIcon(":/Icons/flags/turkey.png");
if(iso[1] == 'H') { country_name = tr("Thailand"); return QIcon(":/Icons/flags/thailand.png"); } if(iso[1] == 'H') return QIcon(":/Icons/flags/thailand.png");
break; break;
case 'U': case 'U':
if(iso[1] == 'S') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); } if(iso[1] == 'S') return QIcon(":/Icons/flags/usa.png");
if(iso[1] == 'M') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); } if(iso[1] == 'M') return QIcon(":/Icons/flags/usa.png");
if(iso[1] == 'A') { country_name = tr("Ukraine"); return QIcon(":/Icons/flags/ukraine.png"); } if(iso[1] == 'A') return QIcon(":/Icons/flags/ukraine.png");
break; break;
case 'Z': case 'Z':
if(iso[1] == 'A') { country_name = tr("South Africa"); return QIcon(":/Icons/flags/south_africa.png"); } if(iso[1] == 'A') return QIcon(":/Icons/flags/south_africa.png");
break; break;
} }
qDebug("Unrecognized country code: %c%c", iso[0], iso[1]); qDebug("Unrecognized country code: %c%c", iso[0], iso[1]);

View File

@@ -1,5 +1,5 @@
<RCC> <RCC>
<qresource> <qresource prefix="/" >
<file>geoip/GeoIP.dat</file> <file>geoip/GeoIP.dat</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -1,508 +0,0 @@
<p>qBittorrent is licensed under the GNU General Public License version 2 with the
addition of the following special exception:</p>
<p>
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.</p>
----------
<h2>GNU General Public License, version 2</h2>
<hr>
<h3>Table of Contents</h3>
<ul>
<li><a name="TOC1" href="#SEC1">GNU GENERAL PUBLIC
LICENSE<!--TRANSLATORS: Don't translate the license; copy msgid's
verbatim!--></a>
<ul>
<li><a name="TOC2" href="#SEC2">Preamble</a></li>
<li><a name="TOC3" href="#SEC3">TERMS AND CONDITIONS
FOR COPYING, DISTRIBUTION AND MODIFICATION</a></li>
<li><a name="TOC4" href="#SEC4">How to Apply These
Terms to Your New Programs</a></li>
</ul></li>
</ul>
<hr>
<h3><a name="SEC1" href="#TOC1">GNU GENERAL PUBLIC LICENSE</a></h3>
<p>
Version 2, June 1991
</p>
<pre>
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
</pre>
<h3><a name="preamble"></a><a name="SEC2" href="#TOC2">Preamble</a></h3>
<p>
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
</p>
<p>
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
</p>
<p>
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
</p>
<p>
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
</p>
<p>
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
</p>
<p>
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
</p>
<p>
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
</p>
<p>
The precise terms and conditions for copying, distribution and
modification follow.
</p>
<h3><a name="terms"></a><a name="SEC3" href="#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
<a name="section0"></a><p>
<strong>0.</strong>
This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
</p>
<p>
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
</p>
<a name="section1"></a><p>
<strong>1.</strong>
You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
</p>
<p>
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
</p>
<a name="section2"></a><p>
<strong>2.</strong>
You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
</p>
<dl>
<dt></dt>
<dd>
<strong>a)</strong>
You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
</dd>
<dt></dt>
<dd>
<strong>b)</strong>
You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
</dd>
<dt></dt>
<dd>
<strong>c)</strong>
If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
</dd>
</dl>
<p>
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
</p>
<p>
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
</p>
<p>
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
</p>
<a name="section3"></a><p>
<strong>3.</strong>
You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
</p>
<!-- we use this doubled UL to get the sub-sections indented, -->
<!-- while making the bullets as unobvious as possible. -->
<dl>
<dt></dt>
<dd>
<strong>a)</strong>
Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
</dd>
<dt></dt>
<dd>
<strong>b)</strong>
Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
</dd>
<dt></dt>
<dd>
<strong>c)</strong>
Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
</dd>
</dl>
<p>
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
</p>
<p>
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
</p>
<a name="section4"></a><p>
<strong>4.</strong>
You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
</p>
<a name="section5"></a><p>
<strong>5.</strong>
You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
</p>
<a name="section6"></a><p>
<strong>6.</strong>
Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
</p>
<a name="section7"></a><p>
<strong>7.</strong>
If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
</p>
<p>
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
</p>
<p>
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
</p>
<p>
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
</p>
<a name="section8"></a><p>
<strong>8.</strong>
If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
</p>
<a name="section9"></a><p>
<strong>9.</strong>
The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
</p>
<p>
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
</p>
<a name="section10"></a><p>
<strong>10.</strong>
If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
</p>
<a name="section11"></a><p><strong>NO WARRANTY</strong></p>
<p>
<strong>11.</strong>
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
</p>
<a name="section12"></a><p>
<strong>12.</strong>
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
</p>
<h3>END OF TERMS AND CONDITIONS</h3>
<h3><a name="howto"></a><a name="SEC4" href="#TOC4">How to Apply These Terms to Your New Programs</a></h3>
<p>
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
</p>
<p>
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
</p>
<pre>
<var>one line to give the program's name and an idea of what it does.</var>
Copyright (C) <var>yyyy</var> <var>name of author</var>
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.
</pre>
<p>
Also add information on how to contact you by electronic and paper mail.
</p>
<p>
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
</p>
<pre>
Gnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var>
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
type `show w'. This is free software, and you are welcome
to redistribute it under certain conditions; type `show c'
for details.
</pre>
<p>
The hypothetical commands <samp>`show w'</samp> and <samp>`show c'</samp> should show
the appropriate parts of the General Public License. Of course, the
commands you use may be called something other than <samp>`show w'</samp> and
<samp>`show c'</samp>; they could even be mouse-clicks or menu items--whatever
suits your program.
</p>
<p>
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
</p>
<pre>
Yoyodyne, Inc., hereby disclaims all copyright
interest in the program `Gnomovision'
(which makes passes at compilers) written
by James Hacker.
<var>signature of Ty Coon</var>, 1 April 1989
Ty Coon, President of Vice
</pre>
<p>
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the
<a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
instead of this License.
</p>

View File

@@ -1,110 +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 <QObject>
#include <QCoreApplication>
#include "preferences.h"
#include "bittorrent.h"
class HeadlessLoader: public QObject {
Q_OBJECT
public:
HeadlessLoader(QStringList torrentCmdLine) {
// Enable Web UI
Preferences::setWebUiEnabled(true);
// Instanciate Bittorrent Object
BTSession = new Bittorrent();
connect(BTSession, SIGNAL(newConsoleMessage(QString)), this, SLOT(displayConsoleMessage(QString)));
// Resume unfinished torrents
BTSession->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(Preferences::getWebUiPort()))) << std::endl;
std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(Preferences::getWebUiUsername())) << std::endl;
if(Preferences::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;
}
}
~HeadlessLoader() {
delete BTSession;
}
public slots:
// Call this function to exit qBittorrent headless loader
// and return to prompt (object will be deleted by main)
void exit() {
qApp->quit();
}
void displayConsoleMessage(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 = param.trimmed();
if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
BTSession->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)) {
BTSession->addMagnetUri(param);
} else {
BTSession->addTorrent(param);
}
}
}
}
private:
Bittorrent *BTSession;
};
#endif

View File

@@ -1,44 +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
*/
#ifndef HIDABLETABWIDGET_H
#define HIDABLETABWIDGET_H
#include <QTabWidget>
#include <QTabBar>
class HidableTabWidget : public QTabWidget {
public:
void showTabBar(bool show) {
tabBar()->setVisible(show);
}
};
#endif // HIDABLETABWIDGET_H

View File

@@ -35,7 +35,6 @@
#include "preferences.h" #include "preferences.h"
#include "json.h" #include "json.h"
#include "bittorrent.h" #include "bittorrent.h"
#include "misc.h"
#include <QTcpSocket> #include <QTcpSocket>
#include <QDateTime> #include <QDateTime>
#include <QStringList> #include <QStringList>
@@ -47,7 +46,7 @@
#include <QTemporaryFile> #include <QTemporaryFile>
HttpConnection::HttpConnection(QTcpSocket *socket, Bittorrent *BTSession, HttpServer *parent) HttpConnection::HttpConnection(QTcpSocket *socket, Bittorrent *BTSession, HttpServer *parent)
: QObject(parent), socket(socket), parent(parent), BTSession(BTSession) : QObject(parent), socket(socket), parent(parent), BTSession(BTSession)
{ {
socket->setParent(this); socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(read())); connect(socket, SIGNAL(readyRead()), this, SLOT(read()));
@@ -104,12 +103,12 @@ void HttpConnection::write()
} }
QString HttpConnection::translateDocument(QString data) { QString HttpConnection::translateDocument(QString data) {
std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel"}; std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", "options_imp"};
int i=0; int i=0;
bool found = false; bool found = false;
do { do {
found = false; found = false;
QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)")); QRegExp regex("_\\(([\\w\\s?!:\\/\\(\\)\\.]+)\\)");
i = regex.indexIn(data, i); i = regex.indexIn(data, i);
if(i >= 0) { if(i >= 0) {
//qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data()); //qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
@@ -117,11 +116,9 @@ QString HttpConnection::translateDocument(QString data) {
QString translation = word; QString translation = word;
int context_index= 0; int context_index= 0;
do { do {
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().constData(), 0, QCoreApplication::UnicodeUTF8, 1); translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().data(), 0, QCoreApplication::UnicodeUTF8, 1);
++context_index; ++context_index;
}while(translation == word && context_index < 13); }while(translation == word && context_index < 10);
// Remove keyboard shortcuts
translation = translation.replace("&", "");
//qDebug("Translation is %s", translation.toUtf8().data()); //qDebug("Translation is %s", translation.toUtf8().data());
data = data.replace(i, regex.matchedLength(), translation); data = data.replace(i, regex.matchedLength(), translation);
i += translation.length(); i += translation.length();
@@ -131,54 +128,18 @@ QString HttpConnection::translateDocument(QString data) {
return data; return data;
} }
void HttpConnection::respond() { void HttpConnection::respond()
{
//qDebug("Respond called"); //qDebug("Respond called");
const QString peer_ip = socket->peerAddress().toString(); QStringList auth = parser.value("Authorization").split(" ", QString::SkipEmptyParts);
const int nb_fail = parent->NbFailedAttemptsForIp(peer_ip); if (auth.size() != 2 || QString::compare(auth[0], "Basic", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth[1].toLocal8Bit()))
if(nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) { {
generator.setStatusLine(403, "Forbidden");
generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
write();
return;
}
QString auth = parser.value("Authorization");
if(auth.isEmpty()) {
// Return unauthorized header
qDebug("Auth is Empty...");
generator.setStatusLine(401, "Unauthorized"); generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\""); generator.setValue("WWW-Authenticate", "Basic realm=\"you know what\"");
write(); write();
return; return;
} }
//qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) {
// Update failed attempt counter
parent->increaseNbFailedAttemptsForIp(peer_ip);
qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail+1);
// Return unauthorized header
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
write();
return;
}
// Client successfully authenticated, reset number of failed attempts
parent->resetNbFailedAttemptsForIp(peer_ip);
QString url = parser.url(); QString url = parser.url();
// Favicon
if(url.endsWith("favicon.ico")) {
qDebug("Returning favicon");
QFile favicon(":/Icons/skin/qbittorrent16.png");
if(favicon.open(QIODevice::ReadOnly)) {
QByteArray data = favicon.readAll();
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("png");
generator.setMessage(data);
write();
} else {
respondNotFound();
}
return;
}
QStringList list = url.split('/', QString::SkipEmptyParts); QStringList list = url.split('/', QString::SkipEmptyParts);
if (list.contains(".") || list.contains("..")) if (list.contains(".") || list.contains(".."))
{ {
@@ -215,10 +176,6 @@ void HttpConnection::respond() {
} else { } else {
if(list[1] == "preferences") { if(list[1] == "preferences") {
respondPreferencesJson(); respondPreferencesJson();
} else {
if(list[1] == "transferInfo") {
respondGlobalTransferInfoJson();
}
} }
} }
} }
@@ -239,7 +196,6 @@ void HttpConnection::respond() {
QFile file(url); QFile file(url);
if(!file.open(QIODevice::ReadOnly)) if(!file.open(QIODevice::ReadOnly))
{ {
qDebug("File %s was not found!", qPrintable(url));
respondNotFound(); respondNotFound();
return; return;
} }
@@ -312,18 +268,6 @@ void HttpConnection::respondPreferencesJson() {
write(); write();
} }
void HttpConnection::respondGlobalTransferInfoJson() {
QVariantMap info;
session_status sessionStatus = BTSession->getSessionStatus();
info["DlInfos"] = tr("D: %1/s - T: %2", "Download speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_download_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_download));
info["UpInfos"] = tr("U: %1/s - T: %2", "Upload speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_upload_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_upload));
QString string = json::toJson(info);
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondCommand(QString command) void HttpConnection::respondCommand(QString command)
{ {
if(command == "download") if(command == "download")
@@ -333,10 +277,6 @@ void HttpConnection::respondCommand(QString command)
foreach(QString url, list){ foreach(QString url, list){
url = url.trimmed(); url = url.trimmed();
if(!url.isEmpty()){ if(!url.isEmpty()){
if(url.startsWith("bc://bt/", Qt::CaseInsensitive)) {
qDebug("Converting bc link to magnet link");
url = misc::bcLinkToMagnet(url);
}
if(url.startsWith("magnet:", Qt::CaseInsensitive)) { if(url.startsWith("magnet:", Qt::CaseInsensitive)) {
emit MagnetReadyToBeDownloaded(url); emit MagnetReadyToBeDownloaded(url);
} else { } else {
@@ -347,20 +287,6 @@ void HttpConnection::respondCommand(QString command)
} }
return; return;
} }
if(command == "addTrackers") {
QString hash = parser.post("hash");
if(!hash.isEmpty()) {
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) {
QString urls = parser.post("urls");
QStringList list = urls.split('\n');
foreach(QString url, list) {
announce_entry e(url.toStdString());
h.add_tracker(e);
}
}
}
}
if(command == "upload") if(command == "upload")
{ {
QByteArray torrentfile = parser.torrent(); QByteArray torrentfile = parser.torrent();
@@ -403,16 +329,44 @@ void HttpConnection::respondCommand(QString command)
return; return;
} }
if(command == "setPreferences") { if(command == "setPreferences") {
QString json_str = parser.post("json"); bool ok = false;
EventManager* manager = parent->eventManager(); int dl_limit = parser.post("dl_limit").toInt(&ok);
manager->setGlobalPreferences(json::fromJson(json_str)); if(ok) {
BTSession->setDownloadRateLimit(dl_limit*1024);
Preferences::setGlobalDownloadLimit(dl_limit);
}
int up_limit = parser.post("up_limit").toInt(&ok);
if(ok) {
BTSession->setUploadRateLimit(up_limit*1024);
Preferences::setGlobalUploadLimit(up_limit);
}
int dht_state = parser.post("dht").toInt(&ok);
if(ok) {
BTSession->enableDHT(dht_state == 1);
Preferences::setDHTEnabled(dht_state == 1);
}
int max_connec = parser.post("max_connec").toInt(&ok);
if(ok) {
BTSession->setMaxConnections(max_connec);
Preferences::setMaxConnecs(max_connec);
}
int max_connec_per_torrent = parser.post("max_connec_per_torrent").toInt(&ok);
if(ok) {
BTSession->setMaxConnectionsPerTorrent(max_connec_per_torrent);
Preferences::setMaxConnecsPerTorrent(max_connec_per_torrent);
}
int max_uploads_per_torrent = parser.post("max_uploads_per_torrent").toInt(&ok);
if(ok) {
BTSession->setMaxUploadsPerTorrent(max_uploads_per_torrent);
Preferences::setMaxUploadsPerTorrent(max_uploads_per_torrent);
}
} }
if(command == "setFilePrio") { if(command == "setFilePrio") {
QString hash = parser.post("hash"); QString hash = parser.post("hash");
int file_id = parser.post("id").toInt(); int file_id = parser.post("id").toInt();
int priority = parser.post("priority").toInt(); int priority = parser.post("priority").toInt();
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid() && h.has_metadata()) { if(h.is_valid() && h.has_metadata() && !h.get_torrent_handle().is_seed()) {
h.file_priority(file_id, priority); h.file_priority(file_id, priority);
} }
} }
@@ -466,18 +420,6 @@ void HttpConnection::respondCommand(QString command)
h.set_download_limit(limit); h.set_download_limit(limit);
} }
} }
if(command == "setGlobalUpLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
BTSession->getSession()->set_upload_rate_limit(limit);
Preferences::setGlobalUploadLimit(limit/1024.);
}
if(command == "setGlobalDlLimit") {
qlonglong limit = parser.post("limit").toLongLong();
if(limit == 0) limit = -1;
BTSession->getSession()->set_download_rate_limit(limit);
Preferences::setGlobalDownloadLimit(limit/1024.);
}
if(command == "pause") { if(command == "pause") {
emit pauseTorrent(parser.post("hash")); emit pauseTorrent(parser.post("hash"));
return; return;
@@ -500,16 +442,6 @@ void HttpConnection::respondCommand(QString command)
if(h.is_valid()) h.queue_position_down(); if(h.is_valid()) h.queue_position_down();
return; return;
} }
if(command == "topPrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_top();
return;
}
if(command == "bottomPrio") {
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
if(h.is_valid()) h.queue_position_bottom();
return;
}
if(command == "recheck"){ if(command == "recheck"){
recheckTorrent(parser.post("hash")); recheckTorrent(parser.post("hash"));
return; return;
@@ -522,8 +454,8 @@ void HttpConnection::respondCommand(QString command)
void HttpConnection::recheckTorrent(QString hash) { void HttpConnection::recheckTorrent(QString hash) {
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid()){ if(h.is_valid() && !h.is_paused()){
BTSession->recheckTorrent(h.hash()); h.force_recheck();
} }
} }
@@ -532,7 +464,7 @@ void HttpConnection::recheckAllTorrents() {
std::vector<torrent_handle>::iterator torrentIT; std::vector<torrent_handle>::iterator torrentIT;
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) { for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
QTorrentHandle h = QTorrentHandle(*torrentIT); QTorrentHandle h = QTorrentHandle(*torrentIT);
if(h.is_valid()) if(h.is_valid() && !h.is_paused())
BTSession->recheckTorrent(h.hash()); h.force_recheck();
} }
} }

View File

@@ -54,13 +54,12 @@ class HttpConnection : public QObject
protected slots: protected slots:
void write(); void write();
void respond(); virtual void respond();
void respondJson(); void respondJson();
void respondGenPropertiesJson(QString hash); void respondGenPropertiesJson(QString hash);
void respondTrackersPropertiesJson(QString hash); void respondTrackersPropertiesJson(QString hash);
void respondFilesPropertiesJson(QString hash); void respondFilesPropertiesJson(QString hash);
void respondPreferencesJson(); void respondPreferencesJson();
void respondGlobalTransferInfoJson();
void respondCommand(QString command); void respondCommand(QString command);
void respondNotFound(); void respondNotFound();
void processDownloadedFile(QString, QString); void processDownloadedFile(QString, QString);

View File

@@ -39,8 +39,7 @@ void HttpResponseGenerator::setMessage(const QByteArray message)
void HttpResponseGenerator::setMessage(const QString message) void HttpResponseGenerator::setMessage(const QString message)
{ {
// This must be UTF-8! setMessage(message.QString::toLocal8Bit());
setMessage(message.toUtf8());
} }
void HttpResponseGenerator::stripMessage() void HttpResponseGenerator::stripMessage()

View File

@@ -44,7 +44,7 @@ class HttpResponseGenerator : public QHttpResponseHeader
void setMessage(const QString message); void setMessage(const QString message);
void stripMessage(); void stripMessage();
void setContentTypeByExt(const QString ext); void setContentTypeByExt(const QString ext);
QByteArray toByteArray() const; virtual QByteArray toByteArray() const;
}; };
#endif #endif

View File

@@ -33,56 +33,13 @@
#include "httpconnection.h" #include "httpconnection.h"
#include "eventmanager.h" #include "eventmanager.h"
#include "bittorrent.h" #include "bittorrent.h"
#include "preferences.h"
#include <QTimer> #include <QTimer>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QTime>
#include <QRegExp>
#include <QTimer>
const int BAN_TIME = 3600000; // 1 hour
class UnbanTimer: public QTimer {
public:
UnbanTimer(QObject *parent, QString peer_ip): QTimer(parent), peer_ip(peer_ip){
setSingleShot(true);
setInterval(BAN_TIME);
}
~UnbanTimer() {
qDebug("||||||||||||Deleting ban timer|||||||||||||||");
}
QString peer_ip;
};
void HttpServer::UnbanTimerEvent() {
UnbanTimer* ubantimer = static_cast<UnbanTimer*>(sender());
qDebug("Ban period has expired for %s", qPrintable(ubantimer->peer_ip));
client_failed_attempts.remove(ubantimer->peer_ip);
ubantimer->deleteLater();
}
int HttpServer::NbFailedAttemptsForIp(QString ip) const {
return client_failed_attempts.value(ip, 0);
}
void HttpServer::increaseNbFailedAttemptsForIp(QString ip) {
const int nb_fail = client_failed_attempts.value(ip, 0);
client_failed_attempts.insert(ip, nb_fail+1);
if(nb_fail == MAX_AUTH_FAILED_ATTEMPTS-1) {
// Max number of failed attempts reached
// Start ban period
UnbanTimer* ubantimer = new UnbanTimer(this, ip);
connect(ubantimer, SIGNAL(timeout()), this, SLOT(UnbanTimerEvent()));
ubantimer->start();
}
}
void HttpServer::resetNbFailedAttemptsForIp(QString ip) {
client_failed_attempts.remove(ip);
}
HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) { HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) {
username = Preferences::getWebUiUsername().toLocal8Bit(); username = Preferences::getWebUiUsername().toLocal8Bit();
password_ha1 = Preferences::getWebUiPassword().toLocal8Bit(); password_md5 = Preferences::getWebUiPassword().toLocal8Bit();
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection())); connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
BTSession = _BTSession; BTSession = _BTSession;
manager = new EventManager(this, BTSession); manager = new EventManager(this, BTSession);
@@ -119,12 +76,6 @@ HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcp
a = tr("Maximum number of connections per torrent limit must be greater than 0 or disabled."); a = tr("Maximum number of connections per torrent limit must be greater than 0 or disabled.");
a = tr("Maximum number of upload slots per torrent limit must be greater than 0 or disabled."); a = tr("Maximum number of upload slots per torrent limit must be greater than 0 or disabled.");
a = tr("Unable to save program preferences, qBittorrent is probably unreachable."); a = tr("Unable to save program preferences, qBittorrent is probably unreachable.");
a = tr("Language");
a = tr("Downloaded", "Is the file downloaded or not?");
a = tr("The port used for incoming connections must be greater than 1024 and less than 65535.");
a = tr("The port used for the Web UI must be greater than 1024 and less than 65535.");
a = tr("The Web UI username must be at least 3 characters long.");
a = tr("The Web UI password must be at least 3 characters long.");
} }
HttpServer::~HttpServer() HttpServer::~HttpServer()
@@ -161,106 +112,21 @@ void HttpServer::onTimer() {
} }
} }
QString HttpServer::generateNonce() const { void HttpServer::setAuthorization(QString _username, QString _password_md5) {
QCryptographicHash md5(QCryptographicHash::Md5);
md5.addData(QTime::currentTime().toString("hhmmsszzz").toLocal8Bit());
md5.addData(":");
md5.addData(QBT_REALM);
return md5.result().toHex();
}
void HttpServer::setAuthorization(QString _username, QString _password_ha1) {
username = _username.toLocal8Bit(); username = _username.toLocal8Bit();
password_ha1 = _password_ha1.toLocal8Bit(); password_md5 = _password_md5.toLocal8Bit();
} }
// Parse HTTP AUTH string bool HttpServer::isAuthorized(QByteArray auth) const {
// http://tools.ietf.org/html/rfc2617 // Decode Auth
bool HttpServer::isAuthorized(QByteArray auth, QString method) const { QByteArray decoded = QByteArray::fromBase64(auth);
//qDebug("AUTH string is %s", auth.data()); QList<QByteArray> creds = decoded.split(':');
// Get user name if(creds.size() != 2) return false;
QRegExp regex_user(".*username=\"([^\"]+)\".*"); // Must be a quoted string QByteArray prop_username = creds.first();
if(regex_user.indexIn(auth) < 0) return false; if(prop_username != username) return false;
QString prop_user = regex_user.cap(1); QCryptographicHash md5(QCryptographicHash::Md5);
//qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data()); md5.addData(creds.last());
if(prop_user != username) { return (password_md5 == md5.result().toHex());
// User name is invalid, we can reject already
qDebug("AUTH-PROB: Username is invalid");
return false;
}
// Get realm
QRegExp regex_realm(".*realm=\"([^\"]+)\".*"); // Must be a quoted string
if(regex_realm.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing realm");
return false;
}
QByteArray prop_realm = regex_realm.cap(1).toLocal8Bit();
if(prop_realm != QBT_REALM) {
qDebug("AUTH-PROB: Wrong realm");
return false;
}
// get nonce
QRegExp regex_nonce(".*nonce=[\"]?([\\w=]+)[\"]?.*");
if(regex_nonce.indexIn(auth) < 0) {
qDebug("AUTH-PROB: missing nonce");
return false;
}
QByteArray prop_nonce = regex_nonce.cap(1).toLocal8Bit();
qDebug("prop nonce is: %s", prop_nonce.data());
// get uri
QRegExp regex_uri(".*uri=\"([^\"]+)\".*");
if(regex_uri.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing uri");
return false;
}
QByteArray prop_uri = regex_uri.cap(1).toLocal8Bit();
qDebug("prop uri is: %s", prop_uri.data());
// get response
QRegExp regex_response(".*response=[\"]?([\\w=]+)[\"]?.*");
if(regex_response.indexIn(auth) < 0) {
qDebug("AUTH-PROB: Missing response");
return false;
}
QByteArray prop_response = regex_response.cap(1).toLocal8Bit();
qDebug("prop response is: %s", prop_response.data());
// Compute correct reponse
QCryptographicHash md5_ha2(QCryptographicHash::Md5);
md5_ha2.addData(method.toLocal8Bit() + ":" + prop_uri);
QByteArray ha2 = md5_ha2.result().toHex();
QByteArray response = "";
if(auth.contains("qop=")) {
QCryptographicHash md5_ha(QCryptographicHash::Md5);
// Get nc
QRegExp regex_nc(".*nc=[\"]?([\\w=]+)[\"]?.*");
if(regex_nc.indexIn(auth) < 0) {
qDebug("AUTH-PROB: qop but missing nc");
return false;
}
QByteArray prop_nc = regex_nc.cap(1).toLocal8Bit();
qDebug("prop nc is: %s", prop_nc.data());
QRegExp regex_cnonce(".*cnonce=[\"]?([\\w=]+)[\"]?.*");
if(regex_cnonce.indexIn(auth) < 0) {
qDebug("AUTH-PROB: qop but missing cnonce");
return false;
}
QByteArray prop_cnonce = regex_cnonce.cap(1).toLocal8Bit();
qDebug("prop cnonce is: %s", prop_cnonce.data());
QRegExp regex_qop(".*qop=[\"]?(\\w+)[\"]?.*");
if(regex_qop.indexIn(auth) < 0) {
qDebug("AUTH-PROB: missing qop");
return false;
}
QByteArray prop_qop = regex_qop.cap(1).toLocal8Bit();
qDebug("prop qop is: %s", prop_qop.data());
md5_ha.addData(password_ha1+":"+prop_nonce+":"+prop_nc+":"+prop_cnonce+":"+prop_qop+":"+ha2);
response = md5_ha.result().toHex();
} else {
QCryptographicHash md5_ha(QCryptographicHash::Md5);
md5_ha.addData(password_ha1+":"+prop_nonce+":"+ha2);
response = md5_ha.result().toHex();
}
qDebug("AUTH: comparing reponses: (%d)", static_cast<int>(prop_response == response));
return prop_response == response;
} }
EventManager* HttpServer::eventManager() const EventManager* HttpServer::eventManager() const

View File

@@ -35,41 +35,31 @@
#include <QPair> #include <QPair>
#include <QTcpServer> #include <QTcpServer>
#include <QByteArray> #include <QByteArray>
#include <QHash>
#include "preferences.h"
class Bittorrent; class Bittorrent;
class QTimer; class QTimer;
class EventManager; class EventManager;
const int MAX_AUTH_FAILED_ATTEMPTS = 5;
class HttpServer : public QTcpServer { class HttpServer : public QTcpServer {
Q_OBJECT Q_OBJECT
public: private:
HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0); QByteArray username;
~HttpServer(); QByteArray password_md5;
void setAuthorization(QString username, QString password_ha1); Bittorrent *BTSession;
bool isAuthorized(QByteArray auth, QString method) const; EventManager *manager;
EventManager *eventManager() const; QTimer *timer;
QString generateNonce() const;
int NbFailedAttemptsForIp(QString ip) const;
void increaseNbFailedAttemptsForIp(QString ip);
void resetNbFailedAttemptsForIp(QString ip);
private slots: public:
void newHttpConnection(); HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
void onTimer(); ~HttpServer();
void UnbanTimerEvent(); void setAuthorization(QString username, QString password_md5);
bool isAuthorized(QByteArray auth) const;
EventManager *eventManager() const;
private: private slots:
QByteArray username; void newHttpConnection();
QByteArray password_ha1; void onTimer();
Bittorrent *BTSession;
EventManager *manager;
QTimer *timer;
QHash<QString, int> client_failed_attempts;
}; };
#endif #endif

View File

@@ -1,184 +1,174 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file>Icons/url.png</file>
<file>Icons/loading.png</file>
<file>Icons/rss16.png</file>
<file>Icons/rss32.png</file> <file>Icons/rss32.png</file>
<file>Icons/3-state-checkbox.gif</file>
<file>Icons/uparrow.png</file>
<file>Icons/sphere.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/slow_off.png</file>
<file>Icons/sphere2.png</file> <file>Icons/sphere2.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/url.png</file>
<file>Icons/locale.png</file>
<file>Icons/loading.png</file>
<file>Icons/magnet.png</file> <file>Icons/magnet.png</file>
<file>Icons/slow.png</file> <file>Icons/sphere.png</file>
<file>Icons/L.gif</file> <file>Icons/uparrow.png</file>
<file>Icons/skin/delete_perm22.png</file> <file>Icons/rss16.png</file>
<file>Icons/skin/seeding.png</file> <file>Icons/skin/checkingUP.png</file>
<file>Icons/skin/splash.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/tabs.gif</file>
<file>Icons/skin/delete.png</file>
<file>Icons/skin/increase.png</file>
<file>Icons/skin/qbittorrent16.png</file>
<file>Icons/skin/connected.png</file>
<file>Icons/skin/url.png</file>
<file>Icons/skin/qbittorrent22.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/mascot.png</file>
<file>Icons/skin/info.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/slider-area.gif</file>
<file>Icons/skin/pause22.png</file>
<file>Icons/skin/stalledDL.png</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/delete_all.png</file>
<file>Icons/skin/delete_perm.png</file>
<file>Icons/skin/qb_question.png</file>
<file>Icons/skin/error.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/handle-icon-horizontal.gif</file>
<file>Icons/skin/pause.png</file>
<file>Icons/skin/qbittorrent32.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/knob.gif</file>
<file>Icons/skin/open.png</file>
<file>Icons/skin/pause_all.png</file>
<file>Icons/skin/filterall.png</file>
<file>Icons/skin/uploading.png</file>
<file>Icons/skin/queued.png</file>
<file>Icons/skin/checking.png</file>
<file>Icons/skin/handle-icon.gif</file>
<file>Icons/skin/arrow-right.gif</file>
<file>Icons/skin/filterinactive.png</file>
<file>Icons/skin/decrease.png</file>
<file>Icons/skin/play22.png</file>
<file>Icons/skin/firewalled.png</file>
<file>Icons/skin/delete22.png</file>
<file>Icons/skin/new.png</file>
<file>Icons/skin/downloading.png</file>
<file>Icons/skin/play_all.png</file>
<file>Icons/skin/toolbox-divider.gif</file>
<file>Icons/skin/stalledUP.png</file>
<file>Icons/skin/filteractive.png</file>
<file>Icons/skin/bg-handle-horizontal.gif</file> <file>Icons/skin/bg-handle-horizontal.gif</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/qbittorrent22.png</file>
<file>Icons/skin/queuedDL.png</file>
<file>Icons/skin/new.png</file>
<file>Icons/skin/queuedUP.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/stalled.png</file>
<file>Icons/skin/delete.png</file>
<file>Icons/skin/handle-icon-horizontal.gif</file>
<file>Icons/skin/handle-icon.gif</file>
<file>Icons/skin/knob.gif</file>
<file>Icons/skin/url.png</file>
<file>Icons/skin/stalledUP.png</file>
<file>Icons/skin/delete_perm22.png</file>
<file>Icons/skin/filteractive.png</file>
<file>Icons/skin/connected.png</file>
<file>Icons/skin/pausedDL.png</file>
<file>Icons/skin/mascot.png</file>
<file>Icons/skin/pausedUP.png</file>
<file>Icons/skin/seeding.png</file>
<file>Icons/skin/increase.png</file>
<file>Icons/skin/qbittorrent32.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/toolbox-divider.gif</file>
<file>Icons/skin/stalledDL.png</file>
<file>Icons/skin/qb_question.png</file>
<file>Icons/skin/download.png</file> <file>Icons/skin/download.png</file>
<file>Icons/flags/pakistan.png</file> <file>Icons/skin/open.png</file>
<file>Icons/flags/argentina.png</file> <file>Icons/skin/play22.png</file>
<file>Icons/flags/netherlands.png</file> <file>Icons/skin/qbittorrent16.png</file>
<file>Icons/flags/australia.png</file> <file>Icons/skin/slider-area.gif</file>
<file>Icons/flags/finland.png</file> <file>Icons/skin/downloading.png</file>
<file>Icons/flags/croatia.png</file> <file>Icons/skin/filterinactive.png</file>
<file>Icons/flags/indonesia.png</file> <file>Icons/skin/pause22.png</file>
<file>Icons/flags/greece.png</file> <file>Icons/skin/pause_all.png</file>
<file>Icons/flags/belarus.png</file> <file>Icons/skin/delete22.png</file>
<file>Icons/flags/morocco.png</file> <file>Icons/skin/play_all.png</file>
<file>Icons/flags/portugal.png</file> <file>Icons/skin/pause.png</file>
<file>Icons/flags/egypt.png</file> <file>Icons/skin/firewalled.png</file>
<file>Icons/flags/georgia.png</file> <file>Icons/skin/properties.png</file>
<file>Icons/flags/costa_rica.png</file> <file>Icons/skin/info.png</file>
<file>Icons/flags/denmark.png</file> <file>Icons/skin/tabs.gif</file>
<file>Icons/flags/bosnia.png</file> <file>Icons/skin/delete_perm.png</file>
<file>Icons/flags/newzealand.png</file> <file>Icons/skin/checkingDL.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/delete_all.png</file>
<file>Icons/skin/splash.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/decrease.png</file>
<file>Icons/skin/uploading.png</file>
<file>Icons/skin/filterall.png</file>
<file>Icons/flags/czech.png</file>
<file>Icons/flags/serbia.png</file> <file>Icons/flags/serbia.png</file>
<file>Icons/flags/singapore.png</file> <file>Icons/flags/iceland.png</file>
<file>Icons/flags/italy.png</file> <file>Icons/flags/china.png</file>
<file>Icons/flags/brazil.png</file> <file>Icons/flags/luxembourg.png</file>
<file>Icons/flags/newzealand.png</file>
<file>Icons/flags/austria.png</file>
<file>Icons/flags/indonesia.png</file>
<file>Icons/flags/united_arab_emirates.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/india.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/australia.png</file>
<file>Icons/flags/netherlands.png</file>
<file>Icons/flags/south_africa.png</file>
<file>Icons/flags/belarus.png</file>
<file>Icons/flags/georgia.png</file>
<file>Icons/flags/taiwan.png</file> <file>Icons/flags/taiwan.png</file>
<file>Icons/flags/south_korea.png</file> <file>Icons/flags/sweden.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/ireland.png</file>
<file>Icons/flags/singapore.png</file>
<file>Icons/flags/israel.png</file>
<file>Icons/flags/belgium.png</file>
<file>Icons/flags/usa.png</file>
<file>Icons/flags/costa_rica.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/suisse.png</file>
<file>Icons/flags/croatia.png</file>
<file>Icons/flags/mexico.png</file>
<file>Icons/flags/denmark.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/slovenia.png</file>
<file>Icons/flags/ukraine.png</file>
<file>Icons/flags/turkey.png</file> <file>Icons/flags/turkey.png</file>
<file>Icons/flags/algeria.png</file> <file>Icons/flags/algeria.png</file>
<file>Icons/flags/japan.png</file> <file>Icons/flags/greece.png</file>
<file>Icons/flags/luxembourg.png</file> <file>Icons/flags/morocco.png</file>
<file>Icons/flags/poland.png</file> <file>Icons/flags/argentina.png</file>
<file>Icons/flags/iceland.png</file>
<file>Icons/flags/spain.png</file> <file>Icons/flags/spain.png</file>
<file>Icons/flags/saoudi_arabia.png</file> <file>Icons/flags/portugal.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/russia.png</file> <file>Icons/flags/russia.png</file>
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/philippines.png</file>
<file>Icons/flags/thailand.png</file>
<file>Icons/flags/israel.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/czech.png</file>
<file>Icons/flags/usa.png</file>
<file>Icons/flags/malaysia.png</file>
<file>Icons/flags/belgium.png</file>
<file>Icons/flags/south_africa.png</file>
<file>Icons/flags/slovenia.png</file>
<file>Icons/flags/china.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/ukraine.png</file>
<file>Icons/flags/india.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/united_kingdom.png</file> <file>Icons/flags/united_kingdom.png</file>
<file>Icons/flags/united_arab_emirates.png</file> <file>Icons/flags/thailand.png</file>
<file>Icons/flags/sweden.png</file> <file>Icons/flags/poland.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/canada.png</file> <file>Icons/flags/canada.png</file>
<file>Icons/flags/mexico.png</file> <file>Icons/flags/brazil.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/france.png</file> <file>Icons/flags/france.png</file>
<file>Icons/flags/ireland.png</file> <file>Icons/flags/slovakia.png</file>
<file>Icons/flags/austria.png</file> <file>Icons/flags/pakistan.png</file>
<file>Icons/flags/suisse.png</file> <file>Icons/flags/egypt.png</file>
<file>Icons/oxygen/peer.png</file> <file>Icons/flags/italy.png</file>
<file>Icons/oxygen/unavailable.png</file> <file>Icons/flags/south_korea.png</file>
<file>Icons/oxygen/subscribe.png</file> <file>Icons/flags/bosnia.png</file>
<file>Icons/oxygen/list-remove.png</file> <file>Icons/flags/japan.png</file>
<file>Icons/oxygen/dialog-warning.png</file> <file>Icons/flags/malaysia.png</file>
<file>Icons/oxygen/mail-folder-inbox.png</file> <file>Icons/flags/philippines.png</file>
<file>Icons/oxygen/folder.png</file> <file>Icons/oxygen/mail-queue.png</file>
<file>Icons/oxygen/edit-copy.png</file>
<file>Icons/oxygen/folder-documents.png</file>
<file>Icons/oxygen/urlseed.png</file>
<file>Icons/oxygen/go-up.png</file>
<file>Icons/oxygen/edit-cut.png</file>
<file>Icons/oxygen/gear32.png</file>
<file>Icons/oxygen/go-bottom.png</file>
<file>Icons/oxygen/user-group-delete.png</file>
<file>Icons/oxygen/unsubscribe.png</file>
<file>Icons/oxygen/tab-close.png</file>
<file>Icons/oxygen/file.png</file>
<file>Icons/oxygen/services.png</file>
<file>Icons/oxygen/view-refresh.png</file> <file>Icons/oxygen/view-refresh.png</file>
<file>Icons/oxygen/feed-subscribe.png</file> <file>Icons/oxygen/file.png</file>
<file>Icons/oxygen/remove.png</file>
<file>Icons/oxygen/chronometer.png</file>
<file>Icons/oxygen/filter.png</file>
<file>Icons/oxygen/run-build.png</file>
<file>Icons/oxygen/button_ok.png</file>
<file>Icons/oxygen/user-group-new.png</file>
<file>Icons/oxygen/cookies.png</file>
<file>Icons/oxygen/network-server.png</file>
<file>Icons/oxygen/unsubscribe16.png</file>
<file>Icons/oxygen/encrypted32.png</file>
<file>Icons/oxygen/list-add.png</file>
<file>Icons/oxygen/edit-paste.png</file>
<file>Icons/oxygen/folder-remote.png</file>
<file>Icons/oxygen/help-about.png</file>
<file>Icons/oxygen/encrypted.png</file>
<file>Icons/oxygen/folder-remote16.png</file> <file>Icons/oxygen/folder-remote16.png</file>
<file>Icons/oxygen/go-top.png</file> <file>Icons/oxygen/mail-folder-inbox.png</file>
<file>Icons/oxygen/emblem-favorite.png</file>
<file>Icons/oxygen/edit_clear.png</file>
<file>Icons/oxygen/bug.png</file>
<file>Icons/oxygen/gear.png</file>
<file>Icons/oxygen/connection.png</file>
<file>Icons/oxygen/document-new.png</file>
<file>Icons/oxygen/browse.png</file>
<file>Icons/oxygen/proxy.png</file>
<file>Icons/oxygen/button_cancel.png</file>
<file>Icons/oxygen/preferences-desktop.png</file>
<file>Icons/oxygen/bt_settings.png</file>
<file>Icons/oxygen/go-down.png</file>
<file>Icons/oxygen/subscribe16.png</file>
<file>Icons/oxygen/download.png</file>
<file>Icons/oxygen/log.png</file>
<file>Icons/oxygen/edit-find.png</file> <file>Icons/oxygen/edit-find.png</file>
<file>Icons/oxygen/edit-clear.png</file>
<file>Icons/oxygen/webui.png</file>
<file>Icons/oxygen/folder-new.png</file> <file>Icons/oxygen/folder-new.png</file>
<file>Icons/oxygen/folder-remote.png</file>
<file>Icons/oxygen/edit-paste.png</file>
<file>Icons/oxygen/run-build.png</file>
<file>Icons/oxygen/proxy.png</file>
<file>Icons/oxygen/user-group-delete.png</file>
<file>Icons/oxygen/user-group-new.png</file>
<file>Icons/oxygen/log.png</file>
<file>Icons/oxygen/unavailable.png</file>
<file>Icons/oxygen/button_ok.png</file>
<file>Icons/oxygen/button_cancel.png</file>
<file>Icons/oxygen/edit-clear.png</file>
<file>Icons/oxygen/filter.png</file>
<file>Icons/oxygen/encrypted.png</file>
<file>Icons/oxygen/edit_clear.png</file>
<file>Icons/oxygen/download.png</file>
<file>Icons/oxygen/gear.png</file>
<file>Icons/oxygen/remove.png</file>
<file>Icons/oxygen/dialog-warning.png</file>
<file>Icons/oxygen/peer.png</file>
<file>Icons/oxygen/browse.png</file>
<file>Icons/oxygen/unsubscribe16.png</file>
<file>Icons/oxygen/subscribe.png</file>
<file>Icons/oxygen/edit-copy.png</file>
<file>Icons/oxygen/bt_settings.png</file>
<file>Icons/oxygen/document-new.png</file>
<file>Icons/oxygen/preferences-desktop.png</file>
<file>Icons/oxygen/tab-close.png</file>
<file>Icons/oxygen/wallet.png</file>
<file>Icons/oxygen/webui.png</file>
<file>Icons/oxygen/list-remove.png</file>
<file>Icons/oxygen/connection.png</file>
<file>Icons/oxygen/bug.png</file>
<file>Icons/oxygen/help-about.png</file>
<file>Icons/oxygen/list-add.png</file>
<file>Icons/oxygen/network-server.png</file>
<file>Icons/oxygen/folder.png</file>
<file>Icons/oxygen/urlseed.png</file>
<file>Icons/oxygen/edit-cut.png</file>
<file>Icons/oxygen/unsubscribe.png</file>
<file>Icons/oxygen/subscribe16.png</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -41,23 +41,15 @@ namespace json {
return "null"; return "null";
switch(v.type()) switch(v.type())
{ {
case QVariant::Bool: case QVariant::Bool:
case QVariant::Double: case QVariant::Double:
case QVariant::Int: case QVariant::Int:
case QVariant::LongLong: case QVariant::LongLong:
case QVariant::UInt: case QVariant::UInt:
case QVariant::ULongLong: case QVariant::ULongLong:
//case QMetaType::Float: case QMetaType::Float:
return v.value<QString>(); return v.value<QString>();
case QVariant::StringList: case QVariant::String:
case QVariant::List: {
QStringList strList;
foreach(const QVariant &var, v.toList()) {
strList << toJson(var);
}
return "["+strList.join(",")+"]";
}
case QVariant::String:
{ {
QString s = v.value<QString>(); QString s = v.value<QString>();
QString result = "\""; QString result = "\"";
@@ -66,36 +58,35 @@ namespace json {
QChar ch = s[i]; QChar ch = s[i];
switch(ch.toAscii()) switch(ch.toAscii())
{ {
case '\b': case '\b':
result += "\\b"; result += "\\b";
break; break;
case '\f': case '\f':
result += "\\f"; result += "\\f";
break; break;
case '\n': case '\n':
result += "\\n"; result += "\\n";
break; break;
case '\r': case '\r':
result += "\\r"; result += "\\r";
break; break;
case '\t': case '\t':
result += "\\t"; result += "\\t";
break; break;
case '\"': case '\"':
case '\'': case '\'':
case '\\': case '\\':
case '&': case '&':
result += '\\'; result += '\\';
case '\0': case '\0':
default: default:
result += ch; result += ch;
} }
} }
result += "\""; result += "\"";
return result; return result;
} }
default: default:
qDebug("Unknown QVariantType: %d", (int)v.type());
return "undefined"; return "undefined";
} }
} }
@@ -108,72 +99,6 @@ namespace json {
return "{"+vlist.join(",")+"}"; return "{"+vlist.join(",")+"}";
} }
QVariantMap fromJson(QString json) {
qDebug("JSON is %s", qPrintable(json));
QVariantMap m;
if(json.startsWith("{") && json.endsWith("}")) {
json.chop(1);
json = json.replace(0, 1, "");
QStringList couples;
QString tmp = "";
bool in_list = false;
foreach(QChar c, json) {
if(c == ',' && !in_list) {
couples << tmp;
tmp = "";
} else {
if(c == '[') {
in_list = true;
} else {
if(c == ']') {
in_list = false;
}
}
tmp += c;
}
}
if(!tmp.isEmpty()) couples << tmp;
foreach(QString couple, couples) {
QStringList parts = couple.split(":");
if(parts.size() != 2) continue;
QString key = parts.first();
if(key.startsWith("\"") && key.endsWith("\"")) {
key.chop(1);
key = key.replace(0, 1, "");
}
QString value_str = parts.last();
QVariant value;
if(value_str.startsWith("[") && value_str.endsWith("]")) {
value_str.chop(1);
value_str.replace(0, 1, "");
QStringList list_elems = value_str.split(",", QString::SkipEmptyParts);
QVariantList varlist;
foreach(QString list_val, list_elems) {
if(list_val.startsWith("\"") && list_val.endsWith("\"")) {
list_val.chop(1);
list_val = list_val.replace(0, 1, "");
varlist << list_val;
} else {
varlist << list_val.toInt();
}
}
value = varlist;
} else {
if(value_str.startsWith("\"") && value_str.endsWith("\"")) {
value_str.chop(1);
value_str = value_str.replace(0, 1, "");
value = value_str;
} else {
value = value_str.toInt();
}
}
m.insert(key,value);
qDebug("%s:%s", key.toLocal8Bit().data(), value_str.toLocal8Bit().data());
}
}
return m;
}
QString toJson(QList<QVariantMap> v) { QString toJson(QList<QVariantMap> v) {
QStringList res; QStringList res;
foreach(QVariantMap m, v) { foreach(QVariantMap m, v) {

View File

@@ -1,34 +1,32 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file>lang/qbittorrent_nl.qm</file> <file>lang/qbittorrent_es.qm</file>
<file>lang/qbittorrent_hu.qm</file>
<file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_zh_TW.qm</file>
<file>lang/qbittorrent_tr.qm</file>
<file>lang/qbittorrent_fi.qm</file>
<file>lang/qbittorrent_sk.qm</file> <file>lang/qbittorrent_sk.qm</file>
<file>lang/qbittorrent_ja.qm</file> <file>lang/qbittorrent_zh_TW.qm</file>
<file>lang/qbittorrent_pt.qm</file>
<file>lang/qbittorrent_sv.qm</file>
<file>lang/qbittorrent_pl.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_ko.qm</file>
<file>lang/qbittorrent_en.qm</file>
<file>lang/qbittorrent_ro.qm</file>
<file>lang/qbittorrent_bg.qm</file>
<file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_nl.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_fi.qm</file>
<file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_cs.qm</file>
<file>lang/qbittorrent_pt_BR.qm</file>
<file>lang/qbittorrent_el.qm</file> <file>lang/qbittorrent_el.qm</file>
<file>lang/qbittorrent_ca.qm</file> <file>lang/qbittorrent_ca.qm</file>
<file>lang/qbittorrent_pt.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_fr.qm</file>
<file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_zh.qm</file>
<file>lang/qbittorrent_ko.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_sv.qm</file>
<file>lang/qbittorrent_de.qm</file>
<file>lang/qbittorrent_sr.qm</file> <file>lang/qbittorrent_sr.qm</file>
<file>lang/qbittorrent_pt_BR.qm</file> <file>lang/qbittorrent_hu.qm</file>
<file>lang/qbittorrent_da.qm</file> <file>lang/qbittorrent_da.qm</file>
<file>lang/qbittorrent_cs.qm</file> <file>lang/qbittorrent_de.qm</file>
<file>lang/qbittorrent_pl.qm</file> <file>lang/qbittorrent_zh.qm</file>
<file>lang/qbittorrent_bg.qm</file> <file>lang/qbittorrent_ja.qm</file>
<file>lang/qbittorrent_ar.qm</file> <file>lang/qbittorrent_tr.qm</file>
<file>lang/qbittorrent_es.qm</file> <file>lang/qbittorrent_fr.qm</file>
<file>lang/qbittorrent_en.qm</file>
<file>lang/qbittorrent_hr.qm</file>
<file>lang/qbittorrent_ro.qm</file>
</qresource> </qresource>
</RCC> </RCC>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More