Compare commits

...

279 Commits

Author SHA1 Message Date
Christophe Dumez
429ff34e46 - Tagged beta2 release 2008-06-22 17:41:47 +00:00
Christophe Dumez
54bf7e61f7 - libmagick configure improvement 2008-04-11 16:47:55 +00:00
Christophe Dumez
8422cb395a - Fixed unlimited download limit problem (trayicon) 2008-03-14 20:54:02 +00:00
Christophe Dumez
313b9b9bc0 - Fixed mistake in french translation 2008-03-14 20:46:07 +00:00
Christophe Dumez
9d3fcb10d5 - Updated language files 2008-03-14 20:27:40 +00:00
Christophe Dumez
34725dc26b - Fixed index problem in preview selection (closes #192495) 2008-02-17 13:33:43 +00:00
Christophe Dumez
8b576fcc50 - Attempt to fix compilation on vc++ 2008-02-03 10:43:12 +00:00
Christophe Dumez
1b7449ef4e - Fixed torrent names in mininova search engine 2008-01-28 12:26:44 +00:00
Christophe Dumez
596ec6ae4c - Updated slovak translation 2008-01-11 23:35:09 +00:00
Christophe Dumez
569b69689a - updated translation files 2008-01-04 20:53:56 +00:00
Christophe Dumez
07c195f475 - Catch more exceptions when adding a torrent 2008-01-04 20:43:28 +00:00
Christophe Dumez
5e86c8a14d - Fixed systray integration when qbittorrent is launched on system startup 2008-01-04 20:09:37 +00:00
Christophe Dumez
85796d9c2c - Use 16px tray icon on windows 2008-01-04 19:48:26 +00:00
Christophe Dumez
f4120088c6 Updated language files 2008-01-03 16:18:37 +00:00
Christophe Dumez
8d1f1512b0 - Fixed UPnP messages 2008-01-03 16:17:01 +00:00
Christophe Dumez
c6abdacadd - Threadified torrent creation
- Added a progress bar in torrent creation
- Display if UPnP/NAT-PMP was successful or not
2007-12-31 16:57:35 +00:00
Christophe Dumez
fa00d745b2 - Sharemonkey filename fix (Thanks Keiron Waites) 2007-12-30 21:03:05 +00:00
Christophe Dumez
1e1313dffc - Fixed torrent creation from a folder
- Fixed start seeding directly after torrent creation
2007-12-30 18:06:51 +00:00
Christophe Dumez
00b3677867 - Fixed btjunkie search engine 2007-12-28 09:57:05 +00:00
Ishan Arora
8a9a23866f Corrections in RealProgressBarThread
- Fixed a bug related to the size of the array
- Removed the much hated goto statements with loops :)
2007-12-26 16:40:20 +00:00
Christophe Dumez
91328de30f - Disabled libcurl verbosity 2007-12-12 22:20:36 +00:00
Christophe Dumez
204d9ff496 - Fixed eol problems on Windows 2007-12-12 22:00:41 +00:00
Christophe Dumez
5d64373798 should fix eol problems on windows 2007-12-12 21:45:44 +00:00
Christophe Dumez
699d4b1161 - Forgot to call a python script with python executable 2007-12-12 18:17:09 +00:00
Christophe Dumez
0fe9d68be1 - Forgot to call one script with python 2007-12-12 17:55:16 +00:00
Christophe Dumez
b2f9394d6e - Call python scripts using python executable (fix for windows) 2007-12-12 17:14:15 +00:00
Christophe Dumez
56965d444a - Attempt to fix search engine on Windows 2007-12-12 16:56:32 +00:00
Christophe Dumez
07d7a44731 - DownloadThread now write in binary mode (should fix problems on Windows) 2007-12-12 12:23:38 +00:00
Christophe Dumez
f85aa72c72 - Disable libcurl debug 2007-12-11 20:33:57 +00:00
Christophe Dumez
2f5b4a190e - downloads from urls now follow redirections 2007-12-11 20:20:43 +00:00
Christophe Dumez
aada4f3c03 - Forgot to remove temporary download file when addition failed 2007-12-11 20:00:43 +00:00
Christophe Dumez
9540d45c1d - Fixed torrentreactor search engine 2007-12-11 19:32:09 +00:00
Christophe Dumez
cfd7a1bf0b Updated Japanese translation 2007-12-11 17:11:27 +00:00
Arnaud Demaiziere
3e13bd1381 BUGFIX : when shutting down the system, qbt simply quits 2007-12-09 16:35:51 +00:00
Christophe Dumez
3af23ae8a9 - Fixed compilation warning 2007-12-09 15:51:09 +00:00
Christophe Dumez
f1aa1f993d - Fixed preview when path contain spaces 2007-12-09 09:30:06 +00:00
Christophe Dumez
82b1b29906 - Fixed space problem for "open destination folder" action 2007-12-09 09:28:11 +00:00
Christophe Dumez
14882fe38f - Added our sharemonkey campaign id
- Updated hungarian translation
2007-12-08 22:17:01 +00:00
Christophe Dumez
82f60f1b89 - Updated version in menu entry 2007-12-07 22:59:12 +00:00
Christophe Dumez
dab617458a - Added comment for sharemonkey campaign id 2007-12-07 22:56:36 +00:00
Christophe Dumez
1832d518d6 - Updated splash screen for v1.1.0
- Version Bump to v1.1.0alpha2
2007-12-07 22:51:25 +00:00
Christophe Dumez
5ed8517cde - Added an icon for the new "Buy it" action
- Updated language files
2007-12-07 22:48:51 +00:00
Christophe Dumez
b2950afd5c - FEATURE: Allow to buy downloads using ShareMonkey 2007-12-07 22:33:19 +00:00
Christophe Dumez
a84c686090 - Fixed a problem when closing to systray, then closing the app and cancel 2007-12-04 12:04:53 +00:00
Christophe Dumez
efb8315a38 - Fixed nbResult != 0 when clearing results while a search is running 2007-12-03 19:29:57 +00:00
Christophe Dumez
56b018fb95 - Attempt to fix downloadFromUrl on Windows 2007-11-30 16:05:20 +00:00
Christophe Dumez
731438a19a - Updated search engine plugins in order to limit to 10 pages 2007-11-30 11:54:05 +00:00
Christophe Dumez
fd4ce53eb1 - Added a 3 minutes search timeout 2007-11-30 10:48:00 +00:00
Christophe Dumez
b4f0fbdbc4 Updated polish translation 2007-11-29 18:16:45 +00:00
Christophe Dumez
26ae2af198 - Fixed proxy host max length in preferences 2007-11-28 12:50:08 +00:00
Christophe Dumez
bdb625a615 - Fixed compilation on windows (closes #172391) 2007-11-27 19:37:45 +00:00
Christophe Dumez
f68c5114b2 Updated japanese translation 2007-11-25 19:55:19 +00:00
Christophe Dumez
83282bf835 - Updated polish translation 2007-11-25 14:55:16 +00:00
Christophe Dumez
054351e3a4 - Updated translation files 2007-11-25 13:38:21 +00:00
Christophe Dumez
a5dac03956 - Same as last commit but for gnome looks 2007-11-24 23:47:44 +00:00
Christophe Dumez
a760913482 - Same as last commit but for infoBar this time 2007-11-24 23:43:57 +00:00
Christophe Dumez
f3c8889865 - Should fixed text color for stalled torrents when using a dark theme 2007-11-24 23:39:44 +00:00
Christophe Dumez
b24a1e9123 - COSMETIC: Do not display progress bar in seeding list (always 100%) 2007-11-24 22:24:01 +00:00
Christophe Dumez
c4b7622a17 - Do not compute data for hidden columns in order to save CPU 2007-11-24 22:16:00 +00:00
Christophe Dumez
f7fa66c65d - Improved a lot ETA calculation algorithm 2007-11-24 21:55:19 +00:00
Christophe Dumez
a91d2028d8 - Rewrote part of Arnaud's code for column hiding to debug and optimize it 2007-11-24 19:04:28 +00:00
Christophe Dumez
adea644c04 - Removed "Resize all columns" action 2007-11-24 18:33:17 +00:00
Christophe Dumez
31679f0d6d Tagged rc9 release 2007-11-24 12:11:12 +00:00
Arnaud Demaiziere
0d90ad8f7c bufixes on hidden columns in transfert lists 2007-11-24 00:14:19 +00:00
Christophe Dumez
8ab6aeafcc - Fixed compilation warning in Ishan's code 2007-11-23 23:48:05 +00:00
Christophe Dumez
a56baf5b58 - Added some errors code for libcurl
- Added HTTPS download support
2007-11-23 23:06:38 +00:00
Christophe Dumez
61eb921e44 - Moved from libcommoncpp to libcurl for downloads from urls
- Added SOCKS5 proxy support when downloading from urls
2007-11-23 22:57:49 +00:00
Christophe Dumez
8ecf79e91c - Do not use SOCKS5 proxies in downloadThread.cpp (does not work) 2007-11-23 20:48:47 +00:00
Christophe Dumez
349e58fa22 - Fixed minor version number 2007-11-23 17:22:48 +00:00
Christophe Dumez
436f5bbd7e - Fixed HTTP proxy support in downloadThread 2007-11-23 15:54:18 +00:00
Christophe Dumez
64bfdf930c - Fixed not destructing libtorrent session bug 2007-11-23 15:20:26 +00:00
Christophe Dumez
8858518a14 - fixed proxy layout in preferences 2007-11-23 00:23:55 +00:00
Christophe Dumez
def19ef88b - Added h.is_valid check 2007-11-22 21:53:42 +00:00
Christophe Dumez
4f989b674f - Do not save fastresume data for paused torrents anymore 2007-11-22 21:50:22 +00:00
Christophe Dumez
f0ef21e409 - Fixed proxy auth disable problem when disabling proxy 2007-11-22 21:32:30 +00:00
Christophe Dumez
6b0ae4f575 - Some bug fixes (Proxy) 2007-11-22 10:49:10 +00:00
Christophe Dumez
3926b0d787 - Fixed compilation warning 2007-11-21 22:46:13 +00:00
Christophe Dumez
a721e6d943 - Improved proxy support (search, rss, downloadFromUrl...) 2007-11-21 22:30:33 +00:00
Ishan Arora
9c05148a24 few more corrections on RealProgressBar
- removed counting incomplete pieces
- replaced few Q_ASSERT with corrective statements
2007-11-21 21:12:03 +00:00
Christophe Dumez
725ef52cfe - Fixed compilation warning 2007-11-21 20:35:31 +00:00
Christophe Dumez
7982011d0b - Do not save fastresume data regularly for seeding torrents 2007-11-21 20:33:30 +00:00
Christophe Dumez
0a3bb0cfcd - BUGFIX: title bar is now reset when "Display speed in title" is disabled 2007-11-21 20:17:01 +00:00
Christophe Dumez
f75501f781 - Fixed overflow handling in ETA calculation 2007-11-21 18:59:49 +00:00
Christophe Dumez
70f8a00c20 - Save fastresume data every minute instead of every 10 sec 2007-11-21 16:50:26 +00:00
Christophe Dumez
2691677650 - Fixed segfault that would happen when unfiltering files in torrent addition dialog (closes #163379) 2007-11-19 21:42:58 +00:00
Christophe Dumez
be34bed61a - Fixed compilation warning 2007-11-19 21:33:07 +00:00
Christophe Dumez
721ef1d236 - Fixed useless variable in properties 2007-11-19 21:31:48 +00:00
Christophe Dumez
c8b944508b - Saving trackers file only when necessary
- Avoid code duplication for showProperties()
2007-11-19 21:10:57 +00:00
Christophe Dumez
f3f3f2822b - Do not save fastresume data for checking torrents anymore 2007-11-19 20:43:54 +00:00
Christophe Dumez
3ef0c82a8c - Do no pause torrents before saving fastresume data anymore (no longer needed)
- Save fast resume data regularly (every 10 seconds) to avoid downloading from s
cratch when qBittorrent restart
2007-11-19 20:33:31 +00:00
Arnaud Demaiziere
b8200fd7b2 feature : allow to hide/show columns 2007-11-19 00:58:14 +00:00
Ishan Arora
9e62780a6d Added files missing in last revision 2007-11-18 19:40:49 +00:00
Christophe Dumez
58c78fa6f6 - FEATURE: Real progress bar in torrent properties that displays downloaded pieces (contribution by
Ishan Arora)
2007-11-18 18:06:44 +00:00
Christophe Dumez
5484ec923d - Improved proxy support in search engine 2007-11-16 19:59:36 +00:00
Christophe Dumez
54225cd42f - Made properties window a bit larger
- Remember properties window size and position on startup
2007-11-16 19:33:38 +00:00
Christophe Dumez
dc651ffed3 - Updated TODO List 2007-11-16 19:21:24 +00:00
Christophe Dumez
476a7d7be0 Updated language files 2007-11-16 18:54:52 +00:00
Christophe Dumez
c580285fe8 - Allow to open destination folder on right click on a torrent 2007-11-16 18:53:21 +00:00
Christophe Dumez
5b6166ee15 - Simplified file preview using QDesktopServices 2007-11-16 18:36:57 +00:00
Christophe Dumez
18ee43225e - Allow to open destination folder on double-click 2007-11-16 18:26:00 +00:00
Christophe Dumez
b47073901f - Removed a fedora 7 workaround 2007-11-13 10:32:42 +00:00
Christophe Dumez
d45c44868e - Removed Fedora workaround because Fedora 8 is working propery now 2007-11-10 09:25:51 +00:00
Christophe Dumez
4401b60d51 - cleaned up arnaud's code for double-click action 2007-11-08 18:31:41 +00:00
Christophe Dumez
0ce114a7f8 - Fixed double-click default action 2007-11-08 18:22:34 +00:00
Christophe Dumez
38ca306499 - Fixed start minimized 2007-11-07 12:30:20 +00:00
Christophe Dumez
6cb636f85d - Fixed Changelog 2007-11-07 12:19:12 +00:00
Christophe Dumez
7563b09ead - Updated TODO/Changelog 2007-11-07 11:20:28 +00:00
Arnaud Demaiziere
76783c819c clear the results of a search stops it 2007-11-07 08:41:34 +00:00
Arnaud Demaiziere
42312b982b i forgot the changelog ... oups 2007-11-06 21:16:49 +00:00
Arnaud Demaiziere
a329a59719 - Start minimized option in program preferences
*** - In finished list, replace "Seeds/Leechs" column by "Leechers" 
because Seeds are always 0.
- Allow to change action on double-click
  -> in download list
  -> in seeding list
2007-11-06 21:11:09 +00:00
Christophe Dumez
82e548a2fd - Fixed a bug in torrent files filtering (closes #158846) 2007-11-06 20:55:23 +00:00
Christophe Dumez
5bbdb9e119 - Renabled debug
- Bumped to v1.1.0alpha1
2007-11-06 17:40:44 +00:00
Christophe Dumez
68cbcaeab9 - Fixed a little problem with 16x16px icon install 2007-11-04 09:18:31 +00:00
Christophe Dumez
3c88657e0f rc7 release 2007-11-03 11:57:00 +00:00
Christophe Dumez
f836be6736 - BUGFIX: Don't reload seeding torrents anymore (no point) 2007-11-03 00:19:24 +00:00
Christophe Dumez
85fe900afe - Disable debug mode 2007-11-02 22:56:33 +00:00
Christophe Dumez
052ccf2302 - BUGFIX: Don't reload all torrents everytime settings are saved 2007-11-02 22:56:07 +00:00
Christophe Dumez
ae384da609 - Updated TODO 2007-11-02 13:32:24 +00:00
Christophe Dumez
0fc2d289eb - BUGFIX: Pause/Start All now affect all tabs, not only the current one 2007-11-02 13:12:31 +00:00
Christophe Dumez
9ca02aad88 - BUGFIX: Fixed a bug in children update when changing files priorities 2007-10-30 21:29:15 +00:00
Christophe Dumez
8ea34135e4 - BUGFIX: Catching DHT exception in case there is a problem 2007-10-27 20:53:09 +00:00
Christophe Dumez
b66be5b64b - Removed build dependency on Python 2007-10-27 20:24:17 +00:00
Christophe Dumez
ebc738e3a5 - Updated Turkish translation 2007-10-19 22:27:00 +00:00
Christophe Dumez
713474bc43 - Bump to rc6 2007-10-19 21:25:17 +00:00
Christophe Dumez
265a8cada8 - Fixed ETA display 2007-10-19 20:38:00 +00:00
Christophe Dumez
138af31ef9 - BUGFIX: Remember scan directory in program preferences now 2007-10-19 16:08:05 +00:00
Christophe Dumez
a5f6663e65 - BUGFIX: Fixed deletion of subfolders when deleting torrents from hard drive 2007-10-19 16:00:42 +00:00
Christophe Dumez
1519bca46d - Fixed folder progress calculation in torrent properties (closes #154387) 2007-10-19 15:51:13 +00:00
Christophe Dumez
03719cbb87 - Simplified code 2007-10-18 18:30:04 +00:00
Christophe Dumez
98825613fa - Fixed spanish translation 2007-10-17 16:18:40 +00:00
Christophe Dumez
47abadfc67 - Fixed a typo in french translation 2007-10-17 15:47:49 +00:00
Christophe Dumez
a220e01e2e - FEATURE: Added a button to clear search pattern
- I18N: Fixed swedish translation
2007-10-14 12:21:51 +00:00
Christophe Dumez
dea9237aa0 - BUGFIX: Fixed minimize to tray feature 2007-10-14 09:23:29 +00:00
Christophe Dumez
6099544da5 - BUGFIX: configure doesn't require paths with a terminal "/" anymore 2007-10-14 09:15:02 +00:00
Christophe Dumez
902116cbc8 - BUGFIX: configure looks for libraries in lib64 folders too 2007-10-14 09:09:19 +00:00
Christophe Dumez
96a6f450d0 - Make use of this: added option to delete files from disk as a torrent is removed (libtorrent) 2007-10-13 08:54:46 +00:00
Christophe Dumez
6ed565a06b - COSMETIC: Improved progress bar text rendering 2007-10-13 08:32:31 +00:00
Christophe Dumez
1fbbee794c - BUGFIX: Fixed "Automatically start seeding" feature in torrent creation tool 2007-10-12 18:28:30 +00:00
Christophe Dumez
bfab6389b5 - Commented fast resume rejected alert until #182 is fixed in libtorrent 2007-10-12 11:54:29 +00:00
Christophe Dumez
26a4040003 - BUGFIX: Fixed "Missing Input path" error when creating a torrent
- BUGFIX: Fixed some notification messages for torrent addition dialog
2007-10-12 11:50:02 +00:00
Christophe Dumez
e7ac721b3c - Updated TODO 2007-10-11 18:34:14 +00:00
Christophe Dumez
48f5436f6f - Improved ETA calculation code a little and fixed a little typo in the code
- Updated TODO (waiting for a bug fix in libtorrent)
2007-10-10 20:29:07 +00:00
Christophe Dumez
f754e34e35 - BUGFIX: Fixed possible overflow in ETA calculation 2007-10-10 19:57:24 +00:00
Christophe Dumez
9bb18e1a07 - reverted src.pro 2007-10-10 19:17:25 +00:00
Christophe Dumez
0245bbf5ab - BUGFIX: Fixed an ETA calculation problem when the torrent has filtered files 2007-10-10 19:10:34 +00:00
Christophe Dumez
0a0c1f3529 - BUGFIX: AddInPause setting doesn't pause downloads on startup anymore (real fix this time...) 2007-10-10 17:54:09 +00:00
Christophe Dumez
be94c86350 - Revert a change in src.pro I didn't mean to commit 2007-10-10 17:36:02 +00:00
Christophe Dumez
a26333bc65 - BUGFIX: Now filtered don't appear on hard drive anymore (libtorrent >= r1659 REQUIRED)
- BUGFIX: AddInPause setting doesn't pause downloads on startup anymore
2007-10-10 17:34:52 +00:00
Christophe Dumez
4c862597ae - Updated Japanese translation
- rc4 release
2007-10-06 20:00:13 +00:00
Christophe Dumez
937d45d850 - Use system's default style as a default instead of Plastique style 2007-10-03 19:18:55 +00:00
Christophe Dumez
20b6579392 - BUGFIX: Updated INSTALL file
- BUGFIX: Optimized torrent real size calculation
2007-10-01 19:43:24 +00:00
Christophe Dumez
4f25e6c769 - Another attempt to fix compilation on FreeBSD
- Fixed IP Filter preferences (closes #147532)
2007-10-01 09:47:00 +00:00
Christophe Dumez
ce3b8733d1 BUGFIX: Fixed a proxy problem causing connections to be rejected by trackers 2007-09-30 17:46:47 +00:00
Christophe Dumez
b0140fbdbe - We have an optimized way to get the torrent actual size using libtorrent but this is buggy. Hence we don't use it yet. 2007-09-29 08:32:22 +00:00
Christophe Dumez
abcd2b7600 - Ok, Now I'm pretty sure I fixed the compilation problem on FreeBSD 2007-09-29 08:13:20 +00:00
Christophe Dumez
f9998957cd - attempt to fix compilation on FreeBSD 2007-09-29 08:00:14 +00:00
Christophe Dumez
acb8d5679f - rc2 release 2007-09-27 19:15:41 +00:00
Christophe Dumez
c3d3156ec7 - BUGFIX: Fixed search engine plugins update 2007-09-27 19:09:32 +00:00
Christophe Dumez
09abd303bd - Updated plugin version in text file 2007-09-27 18:51:06 +00:00
Christophe Dumez
0678e2e2bd - Fixed mininova search engie plugin 2007-09-27 18:47:26 +00:00
Christophe Dumez
00dbf7ba38 - Updated italian and korean translations
- Fixed infoBar text when adding a torrent
2007-09-27 18:25:37 +00:00
Christophe Dumez
42cacabccd - BUGFIX: Fixed a unsupported character in man page 2007-09-26 18:32:17 +00:00
Christophe Dumez
3d04a944b6 - rc1 release
- disabled debug mode
2007-09-25 17:50:04 +00:00
Christophe Dumez
b02e2c2f9b Updated Hungarian translation 2007-09-24 21:01:46 +00:00
Christophe Dumez
5c0f17bf31 - Fixed a problem in log text color (introduced very recently) 2007-09-24 18:56:14 +00:00
Christophe Dumez
ac9a81985f - Fixed error in last commit 2007-09-24 18:35:35 +00:00
Christophe Dumez
a46c63d883 - Updated spanish translation
- Log text default color uses skin foreground color instead of black now
2007-09-24 18:08:38 +00:00
Christophe Dumez
09a696fc27 updated translations 2007-09-22 13:32:31 +00:00
Christophe Dumez
dedc54d26f - Updated TODO 2007-09-21 20:06:04 +00:00
Christophe Dumez
c0ffb8fa5c - BUGFIX: Fixed overflow in ratio 2007-09-21 07:45:28 +00:00
Christophe Dumez
3e350c5935 - Updated some translation
- Added -NDEBUG to src.pro to fix compilation problem with libtorrent svn
2007-09-20 20:57:41 +00:00
Christophe Dumez
da244d7ef2 - Added more debug when we get a file_error 2007-09-18 22:41:23 +00:00
Christophe Dumez
2fc7039cc7 - COSMETIC: Progress bar text now uses style foreground color 2007-09-18 22:09:06 +00:00
Christophe Dumez
654497cd4a - Disable debug in libtorrent when qBittorrent is in release mode 2007-09-18 20:01:31 +00:00
Christophe Dumez
bdf50483df - Attempt to make progress bar text color dynamic 2007-09-18 19:08:09 +00:00
Christophe Dumez
8a0681744e - Improved tracker deletion code slightly 2007-09-18 17:27:01 +00:00
Christophe Dumez
2699c76afd Updated japanese 2007-09-18 06:52:41 +00:00
Christophe Dumez
773064a081 - beta7 release 2007-09-17 19:50:47 +00:00
Christophe Dumez
7718403419 - Perfected chinese translation 2007-09-17 19:42:31 +00:00
Christophe Dumez
26b4d31810 - Updated a lot of translations 2007-09-17 19:16:12 +00:00
Christophe Dumez
f8dfda0893 - Updated resource files 2007-09-16 19:59:54 +00:00
Christophe Dumez
e8897ae159 - Fixed configure 2007-09-16 19:58:07 +00:00
Christophe Dumez
2f71f9f51a - Updated russian translation 2007-09-16 18:37:24 +00:00
Christophe Dumez
fc595f49d8 - Updated slovak translation 2007-09-16 16:51:56 +00:00
Christophe Dumez
92444fccf4 - Updated swedish translation 2007-09-16 15:15:44 +00:00
Christophe Dumez
6d0aebe9cb - Edit a bit of code in torrent permanent deletion to make sure all files are actually deleted 2007-09-16 15:13:32 +00:00
Christophe Dumez
ef7af6bad6 - Fixed torrent permanent deletion from hd
- Updated Romanian translation
2007-09-16 15:10:25 +00:00
Christophe Dumez
c1a8a58080 - Updated TODO 2007-09-16 14:27:13 +00:00
Christophe Dumez
76047f4ef2 - Fixed transfers list interval setting 2007-09-16 14:25:24 +00:00
Christophe Dumez
9062266a84 - Fixed a ratio assert in libtorrent because we sent it -1 for unlimited instead of 0 2007-09-16 14:16:42 +00:00
Christophe Dumez
9cdcd53234 - Completed french translation 2007-09-16 14:02:06 +00:00
Christophe Dumez
419b94f042 - A lot of improvement and bug fixes in new torrent content selection 2007-09-16 13:33:41 +00:00
Christophe Dumez
3cb34ed7ee - Fixed an overflow that could cause ETA to become negative sometimes (hitted an assert) 2007-09-16 12:30:41 +00:00
Christophe Dumez
925ecb3464 - Fixed search engines plugins saving 2007-09-16 12:08:26 +00:00
Christophe Dumez
488bd90303 - Fixed a bug in torrent content selection when there is only one file in the torrent 2007-09-16 09:41:05 +00:00
Christophe Dumez
c478ba59ac - new torrent content selection (as a tree). Merge from the new-torrent-selection branch 2007-09-16 09:19:22 +00:00
Christophe Dumez
fe7c0db425 - remove ratio should be applied to finished torrent only (and disabled as a default!) - FIXED 2007-09-13 19:50:14 +00:00
Christophe Dumez
b2bf7047d9 - Fixed search resource file 2007-09-13 19:34:21 +00:00
Christophe Dumez
e943449b42 - Updated TODO, spotted a bug 2007-09-13 18:48:52 +00:00
Christophe Dumez
69e2355ff4 Update language files 2007-09-11 19:32:37 +00:00
Christophe Dumez
b7ea2fb51a - Allow to download plugins from their url 2007-09-11 19:31:21 +00:00
Christophe Dumez
4c880fea09 - Improved tooltip title style 2007-09-11 18:49:24 +00:00
Christophe Dumez
a0e895641c - Improved systray tooltip a little 2007-09-11 11:55:58 +00:00
Christophe Dumez
484f1c36c9 - Improved systray style 2007-09-10 20:25:31 +00:00
Christophe Dumez
982ce5c0ad - Updated TODO 2007-09-09 20:12:54 +00:00
Christophe Dumez
7ac5da0acc - Updated TODO 2007-09-09 20:10:49 +00:00
Christophe Dumez
9733716136 - Updated language files 2007-09-09 20:08:53 +00:00
Christophe Dumez
eebdc26e5a - FEATURE: Articles in a RSS feed are now ordered by date (newer at the top)
- FEATURE: Read articles in a feed are not resetted when the feed is refreshed anymore
2007-09-09 20:02:49 +00:00
Christophe Dumez
1f8b9378a3 - FEATURE: Display RSS article date and author if available 2007-09-09 19:19:33 +00:00
Christophe Dumez
b99b838827 - Fix in rss refresh and mark all as read 2007-09-09 17:02:12 +00:00
Christophe Dumez
c4d143c2c0 - Added some RSS preferences 2007-09-09 15:46:23 +00:00
Christophe Dumez
db1bd53f44 - Fixed a typo in RSS 2007-09-09 12:23:29 +00:00
Christophe Dumez
07dc182053 - FEATURE: Added 'Mark all as read' function for RSS feeds 2007-09-09 11:31:51 +00:00
Christophe Dumez
563055e891 Updated language files 2007-09-09 09:47:59 +00:00
Christophe Dumez
b3de2ccc72 - Small fix for proxy options 2007-09-09 09:42:30 +00:00
Christophe Dumez
f4d5ceb3c4 - Fixed a unit problem in last commit 2007-09-09 09:36:10 +00:00
Christophe Dumez
70beef9f94 - FEATURE: Added an option to display current transfer speeds in title bar 2007-09-09 09:35:30 +00:00
Christophe Dumez
47c1908519 - Added some more debug 2007-09-09 09:20:01 +00:00
Christophe Dumez
e1ead940be - Added some more debug 2007-09-09 09:15:29 +00:00
Christophe Dumez
862d24ac48 - FEATURE: Added an option to automatically delete torrents when they reach a given ratio (>= 1.0) 2007-09-09 09:09:24 +00:00
Christophe Dumez
078c80c81d - FEATURE: Added an option to set the max number of connections per torrent
- FEATURE: Added an option to set the max number of uploads per torrent
2007-09-09 07:44:22 +00:00
Christophe Dumez
6ba4588e62 Updated language files 2007-09-08 17:11:08 +00:00
Christophe Dumez
cd70843ee9 - Totally redesigned program preferences
- Added some options, the following are already implemented:
  * Start/Stop LSD/NAT-PMP/UPnP
	* Force preallocation of all files
	* Force new torrents status to pause
- Other new features are going to be implemented soon
2007-09-08 17:07:29 +00:00
Christophe Dumez
cb8ecb74bf - Fixed a bug introduced recently in torrent addition dialog 2007-09-04 04:27:52 +00:00
Christophe Dumez
9f36d521a4 - BUGFIX: Fixed deprecation warning with latest libtorrent svn
- FEATURE: Redesigned torrent creation dialog
- FEATURE: Allow to set piece size when creating a torrent
- improved new options dialog a little
2007-09-04 04:18:51 +00:00
Christophe Dumez
2c2c1093c3 - Reworked options UI file. Now I need to rewrite the code 2007-09-04 02:20:28 +00:00
Christophe Dumez
0e81f3731f - Dropped Qt4.2 support, it becomes too difficult to maintain for me and Trolltech broke backward compatibility for UI files... 2007-09-03 22:59:46 +00:00
Christophe Dumez
f1114387ae - Updated TODO 2007-09-03 22:54:19 +00:00
Christophe Dumez
54516ac231 - In torrent content selection, check if all files are filtered before saving and display an error if it is the case. I removed this checking on right click menu in case people want to filter all torrents, then add one, then save. 2007-09-03 22:52:58 +00:00
Christophe Dumez
913f93ba82 - Updated language files
- Fixed a typo in french translation
- Updated Hungarian translation
2007-09-03 22:38:37 +00:00
Christophe Dumez
11c7255da6 - Added a lot of stuff to TODO list 2007-09-03 22:33:37 +00:00
Christophe Dumez
69fa916e26 - FEATURE: Added zip support in search plugins manager (can put .py & .png inside) 2007-09-03 22:05:40 +00:00
Christophe Dumez
d86335a58e - Rename fake zip files to pyqBT because we will add real zip support soon 2007-09-03 17:58:23 +00:00
Christophe Dumez
24dced2411 - BUGFIX: the function that set the rows color doesn't handle hidden columns anymore
- BUGFIX: improved search engine plugin manager code and fixed bugs
2007-09-03 17:45:16 +00:00
Christophe Dumez
1b71a4a4a4 - Updated TODO 2007-09-03 17:18:31 +00:00
Christophe Dumez
ffedffcead - Updated Itralian, greek and bolgarian translations 2007-09-03 11:14:11 +00:00
Christophe Dumez
1f64e14195 updated french translation 2007-09-02 17:08:55 +00:00
Christophe Dumez
d6a5b1d321 updated language files 2007-09-02 17:05:26 +00:00
Christophe Dumez
792101731f - BUGFIX: Fixed torrent create (can only one file or one folder) 2007-09-02 17:03:33 +00:00
Christophe Dumez
3e783873ec - BUGFIX: Made pause/resume all function affect both (dl/up) tabs when window is hidden 2007-09-02 14:57:07 +00:00
Christophe Dumez
ffb262f0a1 - Fixed log context menu position
- Updated Changelog
2007-09-02 14:42:09 +00:00
Christophe Dumez
4727c1bdca - Updated remove icon 2007-09-02 14:29:43 +00:00
Christophe Dumez
1f1e37ce76 - Updated bug report icon 2007-09-02 14:14:22 +00:00
Christophe Dumez
f40337d2e3 - Updated cryptographie icon 2007-09-02 13:53:34 +00:00
Christophe Dumez
ecc5106f07 - Updated TODO 2007-09-02 13:32:14 +00:00
Christophe Dumez
1b93f4eaf1 - Fixed a little bug in last commit 2007-09-02 13:23:02 +00:00
Christophe Dumez
685ceafc5b - second attempt for drag n drop 2007-09-02 13:19:30 +00:00
Christophe Dumez
cf6128a222 - Fixed drag'ndrop on non-KDE systems 2007-09-02 13:06:42 +00:00
Christophe Dumez
f78c74c1b5 - Updated checking icon 2007-09-02 12:50:38 +00:00
Christophe Dumez
faeee49517 - Use RSS (un)subscribe icons 2007-09-02 12:44:09 +00:00
Christophe Dumez
1918969e28 - Updated RSS icon with one from Oxygen (KDE4 icons) 2007-09-02 12:12:51 +00:00
Christophe Dumez
fa9e3bf564 Updated french translation 2007-09-02 09:37:18 +00:00
Christophe Dumez
8eac154c05 Updated language files 2007-09-02 09:32:59 +00:00
Christophe Dumez
ff12c76a62 - Updated TODO 2007-09-02 09:12:26 +00:00
Christophe Dumez
2c27f952e2 - FEATURE: Allow to drag'n drop plugin to list for install/update
- Added some debug
2007-09-02 09:01:22 +00:00
Christophe Dumez
e3214a9b6a - Updated TODO 2007-09-02 08:23:59 +00:00
Christophe Dumez
4a02d171b4 - BUGFIX: 'Unknown' is now displayed in search results columns where value is -1
- BUGFIX: Improved search engine core a little
- BUGFIX: Forgot to remove *.pyc files when uninstalling a search plugin
2007-09-02 07:58:25 +00:00
Christophe Dumez
7a16a1d8f1 - updated language files 2007-09-02 06:53:55 +00:00
Christophe Dumez
ecc41fbc60 - Display "Unknown" in search engine result columns where value is -1 2007-09-02 06:39:55 +00:00
Christophe Dumez
0125a2b521 - Updated polish translation 2007-09-01 23:05:56 +00:00
Christophe Dumez
d328b93c1a - Updated swedish and German translations 2007-09-01 23:01:15 +00:00
Christophe Dumez
ab588b741e Paused torrents could be displayed as connected for a sec after checking 2007-09-01 22:57:50 +00:00
Christophe Dumez
1588cd5d3d - Updated Ukrainian translation 2007-09-01 12:24:40 +00:00
Christophe Dumez
a597f067e2 - Updated french and dutch translations
- Fixed some typos in engineSelect
- Made search engine plugin install more reliable
- Added some comments
2007-09-01 10:23:39 +00:00
Christophe Dumez
0778f2a19e - Updated Russian translation 2007-08-31 21:32:30 +00:00
Christophe Dumez
6c0d4b9439 - Dropped Hong Kong flag 2007-08-31 17:39:41 +00:00
Christophe Dumez
98d91f7697 - beta6 release 2007-08-31 17:15:38 +00:00
Christophe Dumez
f7ac4ec652 - Updated Italian translation 2007-08-31 16:56:21 +00:00
Christophe Dumez
43b96ea4f2 - Updated slovak translation 2007-08-31 16:55:00 +00:00
Christophe Dumez
91bd5f1d8e - Updated Changelog 2007-08-31 16:51:02 +00:00
Christophe Dumez
f0edd7dcf7 - Added multipage support for mininova 2007-08-31 16:39:29 +00:00
Christophe Dumez
e3f2480fe5 - Added multipage support to btjunkie plugin 2007-08-31 16:15:39 +00:00
Christophe Dumez
26e5785754 - Fixed plugin update && fixed btjunkie search plugin 2007-08-31 16:02:01 +00:00
Christophe Dumez
db12fb292a - Updated French translation 2007-08-31 14:25:07 +00:00
Christophe Dumez
798ee52de1 - Added some more debug 2007-08-31 14:08:47 +00:00
Christophe Dumez
b1f562d7c0 - Fixed a bug in previous commit
- Added some debug
2007-08-31 13:39:27 +00:00
Christophe Dumez
41daeb4c19 - Added a messagebox when plugin are already up to date 2007-08-31 13:35:55 +00:00
Christophe Dumez
d66b788e5f - Update translation files 2007-08-31 12:11:38 +00:00
Christophe Dumez
ff4ab915a2 - Merged custom-search branch. New search plugins management system 2007-08-31 12:06:31 +00:00
202 changed files with 51573 additions and 21902 deletions

View File

@@ -1,5 +1,6 @@
Author: Author:
* Christophe Dumez <chris@qbittorrent.org> * Christophe Dumez <chris@qbittorrent.org>
Other developers: Contributors:
* Arnaud Demaizière <arnaud@qbittorrent.org> * Arnaud Demaizière <arnaud@qbittorrent.org>
* Ishan Arora <ishanarora@gmail.com>

View File

@@ -1,10 +1,37 @@
* Unknown - Christophe Dumez <chris@qbittorrent.org> - v1.0.0 * Unknown - Christophe Dumez <chris@qbittorrent.org> - v1.1.0
- FEATURE: Web interface to control qbittorrent (Ishan Arora)
- FEATURE: Can spoof Azureus peer id to avoid ban
- FEATURE: Allow to hide/show some columns in download and seeding lists
- FEATURE: Option to start qBittorrent minimized in systray
- FEATURE: Allow to define double-click actions in torrents lists
- FEATURE: Allow to open torrent destination folder
- FEATURE: Real progress bar in torrent properties that displays downloaded pieces
- FEATURE: Allow to buy downloads using ShareMonkey
- FEATURE: Display if UPnP/NAT-PMP was successful or not
- FEATURE: Threadified torrent creation
- FEATURE: Improved eMule DAT ip filter parser
- FEATURE: Added support for PeerGuardian p2p filters (text)
- FEATURE: Added support for PeerGuardian p2b filters (binary)
- FEATURE: Allow to customize folder scan interval
- FEATURE: Allow to add several trackers at once
- BUGFIX: Do not display seeds number in seeding list (always 0)
- BUGFIX: Threadified IP filter file parser to avoid GUI freeze
- COSMETIC: Do not display progress bar in seeding list (always 100%)
- COSMETIC: Added a progress bar for torrent creation
- COSMETIC: Display tracker errors in a cleaner way
- COSMETIC: Display "unpaused/total_torrent" in download/upload tabs
- COSMETIC: Allow to resize RSS column
- COSMETIC: Global UP/DL speeds and ratio are displayed above tabs
* Fri Apr 11 2008 - Christophe Dumez <chris@qbittorrent.org> - v1.0.0
- FEATURE: Based on new libtorrent v0.13 - FEATURE: Based on new libtorrent v0.13
- FEATURE: Added UPnP / NAT-PMP port forwarding support - FEATURE: Added UPnP / NAT-PMP port forwarding support
- FEATURE: Added encryption support (compatible with Azureus) - FEATURE: Added encryption support (compatible with Azureus)
- FEATURE: Bittorrent FAST extension support - FEATURE: Bittorrent FAST extension support
- FEATURE: Added RSS support - FEATURE: Added RSS support
- FEATURE: Support files prioritizing in a torrent - FEATURE: Support files prioritizing in a torrent
- FEATURE: Brand new search engine plugins system
- FEATURE: Filtered files don't appear on hard disk anymore
- FEATURE: Finished torrents are now moved to another tab for seeding - FEATURE: Finished torrents are now moved to another tab for seeding
- FEATURE: Display more infos about the torrent in its properties - FEATURE: Display more infos about the torrent in its properties
- FEATURE: Allow the user to edit torrents' trackers - FEATURE: Allow the user to edit torrents' trackers
@@ -30,6 +57,15 @@
- FEATURE: User is now warned when fast resume data was rejected - FEATURE: User is now warned when fast resume data was rejected
- FEATURE: Url seeds are now displayed in torrent properties and are editable - FEATURE: Url seeds are now displayed in torrent properties and are editable
- FEATURE: Allow to drag 'n drop urls on the main window - FEATURE: Allow to drag 'n drop urls on the main window
- FEATURE: Improved search engine (multipage support in all plugins)
- FEATURE: Added BTJunkie search engine plugin
- FEATURE: Added an option to force full disk allocation for all torrents
- FEATURE: Added an option to add torrents in paused state
- FEATURE: Added an option to set the max number of connections per torrent
- FEATURE: Added an option to set the max number of uploads per torrent
- FEATURE: Added an option to automatically delete torrents when they reach a given ratio (>= 1.0)
- FEATURE: Added an option to display current transfer speeds in title bar
- FEATURE: Torrent content is now displayed as a tree
- I18N: Added Hungarian translation - I18N: Added Hungarian translation
- I18N: Added Brazilian translation - I18N: Added Brazilian translation
- BUGFIX: Progress of paused torrents is now correct on restart - BUGFIX: Progress of paused torrents is now correct on restart
@@ -52,11 +88,16 @@
- BUGFIX: Made torrent deletion from hard-drive safer - BUGFIX: Made torrent deletion from hard-drive safer
- BUGFIX: Prevent downloadFromUrl flooding - BUGFIX: Prevent downloadFromUrl flooding
- BUGFIX: ETA was wrong for torrents with filtered files - BUGFIX: ETA was wrong for torrents with filtered files
- BUGFIX: Fixed drag'n drop on non-KDE systems
- BUGFIX: Removed build dependency on Python
- BUGFIX: Catching DHT exception in case there is a problem
- COSMETIC: Redesigned torrent properties a little - COSMETIC: Redesigned torrent properties a little
- COSMETIC: Redesigned options a little - COSMETIC: Totally redesigned program preferences
- COSMETIC: Display more logs messages concerning features - COSMETIC: Display more logs messages concerning features
- COSMETIC: Improved lists renderers - COSMETIC: Improved lists renderers
- COSMETIC: Use a different icon for torrents being checked and for connecting ones - COSMETIC: Use a different icon for torrents being checked and for connecting ones
- COSMETIC: Improved some icons
- COSMETIC: Improved systray tooltip style
* Mon May 07 2007 - Christophe Dumez <chris@qbittorrent.org> - v0.9.3 * Mon May 07 2007 - Christophe Dumez <chris@qbittorrent.org> - v0.9.3
- BUGFIX: Fixed pause toggle on double-click in download list - BUGFIX: Fixed pause toggle on double-click in download list

23
INSTALL
View File

@@ -1,4 +1,4 @@
qBittorrent - A BitTorrent client in C++ / Qt4.2 qBittorrent - A BitTorrent client in C++ / Qt4
------------------------------------------ ------------------------------------------
*** Necessary if qt3 is default on your system *** *** Necessary if qt3 is default on your system ***
@@ -14,25 +14,30 @@ qbittorrent
will install and execute qBittorrent hopefully without any problems. will install and execute qBittorrent hopefully without any problems.
Dependencies: Dependencies:
- Qt >= 4.2 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml) - Qt >= 4.3.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
- libtorrent by Arvid Norberg (>= v0.13 REQUIRED) - rblibtorrent by Arvid Norberg (>= v0.13 REQUIRED)
-> http://www.qbittorrent.org/download.php (advised)
-> http://www.libtorrent.net -> http://www.libtorrent.net
Be carefull: another library (the one used by rtorrent) use the same name. Be careful: another library (the one used by rTorrent) use the same name.
These are TWO different libraries and qBittorrent will only work with the one provided These are TWO different libraries and qBittorrent will only work with the one provided
on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other. on sourceforge (created by Arvid Norberg). The two libraries conflicts with each other.
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization - libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
- libcommoncpp2 - libcurl
- python >= 2.3 (previous might work - not tested): needed by search engine. - python >= 2.3 (needed by search engine)
- libmagick++ (advised, not required) - libmagick++ (advised, not required)
* Needed for favicons support (RSS / Search plugins)
NOTE FOR GNOME USERS: - libzzip (advised, not required)
- qt4-qtconfig package is advised when using Plastique style (default) * Needed for zip support (Search plugins)
or just change qBittorrent style to Cleanlooks (GNOME like)
NOTE FOR NON-KDE USERS:
- qt4-qtconfig package is advised when using other systems than KDE.
You can also change qBittorrent style to Cleanlooks (GNOME like)
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.

14
README
View File

@@ -3,30 +3,30 @@ qBittorrent - A BitTorrent client in Qt4
Description: Description:
******************************** ********************************
qBittorrent is a bittorrent client programmed in C++ / Qt4 that use qBittorrent is a bittorrent client programmed in C++ / Qt4 that uses
libtorrent (sometimes called rb_libtorrent) by Arvid Norberg. libtorrent (sometimes called rblibtorrent) by Arvid Norberg.
It aims to be a good alternative to all other bittorrent clients It aims to be a good alternative to all other bittorrent clients
out there. qBittorrent is fast, stable and provides unicode out there. qBittorrent is fast, stable and provides unicode
support. support as well as many features.
Installation: Installation:
******************************** ********************************
For installation follow the instructions from INSTALL file, but simple For installation, follow the instructions from INSTALL file, but simple:
./configure ./configure
make && make install make && make install
qbittorrent qbittorrent
will install and execute qBittorrent hopefully without any problems. will install and execute qBittorrent hopefully without any problem.
For more information please visit: For more information please visit:
http://www.qbittorrent.org http://www.qbittorrent.org
Please report any bug (or feature requests) to: Please report any bug (or feature request) to:
http://bugs.qbittorrent.org http://bugs.qbittorrent.org
You can also meet me on IRC: You can also meet me (chris-qBT) on IRC:
#qbittorrent on irc.freenode.net #qbittorrent on irc.freenode.net
------------------------------------------ ------------------------------------------

95
TODO
View File

@@ -1,98 +1,37 @@
// Easy // Easy
- Translations into as many languages as possible - Translations into as many languages as possible
- Improve man page
- Use Launchpad/Rosetta for translations once it supports TS files - Use Launchpad/Rosetta for translations once it supports TS files
// Intermediate // Intermediate
- Port on MacOS, Windows (and create an installer for Windows) - Progressing slowly - Port on MacOS, Windows (and create an installer for Windows) - Slow progress
- Add some transparency (menus,...), improve look - Add some transparency (menus,...), improve look / usabilty
- Skins support? (contact Mateusz)
// Harder // Harder
- Display a progress bar that really displays the pieces we have (like in eMule) - Torrent scheduler ala µtorrent/Bitcomet
// Waiting for libtorrent // Waiting for libtorrent
- File selection in a torrent in compact mode - Allow to prioritize torrents (may code this in qBittorrent?)
- Allow to prioritize torrents
// Unsure // Unsure
- Display the peers we are connected to for each torrent with infos (like flag, dl/up speeds, ...)
- Azureus spoofing to prevent ban from trackers? - Azureus spoofing to prevent ban from trackers?
- Option to shutdown computer when downloads are finished - Option to shutdown computer when downloads are finished
- Add a torrent scheduler - NAT checker/Tester
- Display hard drive space left?
- Make use of dbus on Linux for the single instance instead of socket communication? - Make use of dbus on Linux for the single instance instead of socket communication?
(http://techbase.kde.org/Development/Tutorials/D-Bus/Accessing_Interfaces) (http://techbase.kde.org/Development/Tutorials/D-Bus/Accessing_Interfaces)
- search engines customizing - When favicon can't be downloaded, try to parse the webpage for:
<link rel="icon" href="http://example.com/favicon.ico" type="image/vnd.microsoft.icon">
* Be careful, the link can be relative
- Improve search plugin install (choose in a list taken from plugins.qbittorrent.org)
- support zipped torrents? (useful?)
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
// in v1.2.0 // in v1.2.0
- Allow user to organize the downloads into categories/folders - Allow user to organize the downloads into categories/folders?
// in v1.1.0 // in v1.1.0
- Tabs support in search - Stop calculating ETAs when ETA column is hidden
- Allow to hide columns? -> See https://blueprints.launchpad.net/qbittorrent
- Allow to scan multiple directories? (useful?)
- Web interface (turbogears? php?)
- improve and test tracker authentication code (remember login/pass) (need a tracker to test this)
- support zipped torrents? (useful?)
- Add option for RSS customization (refresh interval, max news per RSS...)
- Allow to disable UPnP/NAT-PMP/LSD in options?
- Allow to automatically delete torrents when they reach a given ratio (in options) : easy
- Allow to limit the number of downloading torrents simultaneously (other are paused until a download finishes)
- Add "Mark all as read" feature for RSS
- Allow to customize lists refreshing interval (in options)
- Use search engines as plugins (split them, load them dynamically) to allow the user to add some
// in v1.0.0 (partial) - WIP
- Check storage st creation + hasher in torrent creation
- Fix all (or almost all) opened bugs in bug tracker
- update sorting when a new torrent is added?
- Keep documention up to date
- Windows port (Chris - Peerkoel)
- write patches libtorrent for file_priority(int index), actual_size() ?
- valgrind --tool=memcheck --leak-check=full src/qbittorrent (Looks ok)
- 128m 29m 16m S 4.8 2.9 0:02.28 qbittorrent
* beta 6
- Translations update (IN PROGRESS)
- Wait for some bug fixes in libtorrent :
- Number of seeds non null for finished torrent (Ticket #122)
LANGUAGES UPDATED:
- French *BETA3*
- English *BETA3*
- Japanese *BETA3*
- Swedish *BETA3*
- Slovak *BETA3*
- Ukrainian *BETA3*
- Chinese (simplified) *BETA4*
- Hungarian *BETA4*
- Italian *BETA5*
- Polish *BETA5*
- Portuguese *BETA5*
- Brazilian *BETA5*
- Spanish *BETA5*
- German *BETA5*
- Russian *BETA5*
- Korean *BETA5*
- Greek *BETA6*
- Dutch *BETA6*
- Romanian *BETA6*
beta5->beta6 changelog:
- FEATURE: Split download tab from GUI class and cleaned up code
- FEATURE: A lot of code optimization (CPU & memory usage)
- FEATURE: Added support for .ico format (useful for RSS favicons)
- FEATURE: Replaced Meganova search engine by TorrentReactor
- I18N: Updated Greek, Dutch and Romanian translation
- I18N: Removed no longer maintained Traditional chinese translation
- BUGFIX: Made torrent deletion from hard-drive safer
- BUGFIX: Fixed a bug when switching from finished to downloading list
- BUGFIX: Showing checking progress for paused torrents too
- BUGFIX: Fixed progress column sorting on startup
- BUGFIX: Prevent downloadFromUrl flooding
- BUGFIX: Fixed pause state toggle for paused and checking torrents
- BUGFIX: Made finished list context menu more similar to the download list one
- BUGFIX: Fixed Pause/Start action in lists context menus
- BUGFIX: Improved ETA calculation
- BUGFIX: ETA was wrong for torrents with filtered files
- BUGFIX: Display the torrent that are being checked as 'checking' in seeding list
- BUGFIX: Fixed file preview and improved previewable files detection
- BUGFIX: Do not store and calculate ETA values for finished/paused torrents
- BUGFIX: Fixed memory leak in GUI

199
configure vendored
View File

@@ -22,11 +22,14 @@ Dependency options:
--with-libtorrent-lib=[path] Path to libtorrent library files --with-libtorrent-lib=[path] Path to libtorrent library files
--with-libtorrent-static-lib=[path] Path to libtorrent .a file --with-libtorrent-static-lib=[path] Path to libtorrent .a file
--with-libboost-inc=[path] Path to libboost include files --with-libboost-inc=[path] Path to libboost include files
--with-libcommoncpp2-inc=[path] Path to libcommoncpp2 include files --with-libcurl-inc=[path] Path to libcurl include files
--with-libcommoncpp2-lib=[path] Path to libcommoncpp2 library files --with-libcurl-lib=[path] Path to libcurl library files
--disable-libmagick Disable use of libmagick --disable-libmagick Disable use of libmagick
--with-libmagick-inc=[path] Path to libmagick++ include files --with-libmagick-inc=[path] Path to libmagick++ include files
--with-libmagick-lib=[path] Path to libmagick++ library files --with-libmagick-lib=[path] Path to libmagick++ library files
--disable-libzzip Disable use of libzzip
--with-libzzip-inc=[path] Path to libzzip++ include files
--with-libzzip-lib=[path] Path to libzzip++ library files
EOT EOT
} }
@@ -163,13 +166,13 @@ while [ $# -gt 0 ]; do
shift shift
;; ;;
--with-libcommoncpp2-inc=*) --with-libcurl-inc=*)
QC_WITH_LIBCOMMONCPP2_INC=$optarg QC_WITH_LIBCURL_INC=$optarg
shift shift
;; ;;
--with-libcommoncpp2-lib=*) --with-libcurl-lib=*)
QC_WITH_LIBCOMMONCPP2_LIB=$optarg QC_WITH_LIBCURL_LIB=$optarg
shift shift
;; ;;
@@ -188,6 +191,21 @@ while [ $# -gt 0 ]; do
shift shift
;; ;;
--disable-libzzip)
QC_DISABLE_libzzip="Y"
shift
;;
--with-libzzip-inc=*)
QC_WITH_LIBZZIP_INC=$optarg
shift
;;
--with-libzzip-lib=*)
QC_WITH_LIBZZIP_LIB=$optarg
shift
;;
--verbose) --verbose)
QC_VERBOSE="Y" QC_VERBOSE="Y"
shift shift
@@ -213,11 +231,14 @@ echo QC_WITH_LIBTORRENT_INC=$QC_WITH_LIBTORRENT_INC
echo QC_WITH_LIBTORRENT_LIB=$QC_WITH_LIBTORRENT_LIB echo QC_WITH_LIBTORRENT_LIB=$QC_WITH_LIBTORRENT_LIB
echo QC_WITH_LIBTORRENT_STATIC_LIB=$QC_WITH_LIBTORRENT_STATIC_LIB echo QC_WITH_LIBTORRENT_STATIC_LIB=$QC_WITH_LIBTORRENT_STATIC_LIB
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
echo QC_WITH_LIBCOMMONCPP2_INC=$QC_WITH_LIBCOMMONCPP2_INC echo QC_WITH_LIBCURL_INC=$QC_WITH_LIBCURL_INC
echo QC_WITH_LIBCOMMONCPP2_LIB=$QC_WITH_LIBCOMMONCPP2_LIB echo QC_WITH_LIBCURL_LIB=$QC_WITH_LIBCURL_LIB
echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick echo QC_DISABLE_libmagick=$QC_DISABLE_libmagick
echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC echo QC_WITH_LIBMAGICK_INC=$QC_WITH_LIBMAGICK_INC
echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB echo QC_WITH_LIBMAGICK_LIB=$QC_WITH_LIBMAGICK_LIB
echo QC_DISABLE_libzzip=$QC_DISABLE_libzzip
echo QC_WITH_LIBZZIP_INC=$QC_WITH_LIBZZIP_INC
echo QC_WITH_LIBZZIP_LIB=$QC_WITH_LIBZZIP_LIB
echo echo
fi fi
@@ -319,21 +340,21 @@ fi
gen_files() { gen_files() {
cat >$1/modules.cpp <<EOT cat >$1/modules.cpp <<EOT
#line 1 "qt42.qcm" #line 1 "qt4.qcm"
/* /*
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: Qt >= 4.2 name: Qt >= 4.3
-----END QCMOD----- -----END QCMOD-----
*/ */
class qc_qt42 : public ConfObj class qc_qt4 : public ConfObj
{ {
public: public:
qc_qt42(Conf *c) : ConfObj(c) {} qc_qt4(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.2"; } QString name() const { return "Qt >= 4.3"; }
QString shortname() const { return "qt42"; } QString shortname() const { return "Qt 4.3"; }
bool exec() bool exec()
{ {
return(QT_VERSION >= 0x040200); return(QT_VERSION >= 0x040300);
} }
}; };
#line 1 "libtorrent.qcm" #line 1 "libtorrent.qcm"
@@ -391,7 +412,9 @@ public:
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(conf->checkLibrary(s, "torrent")){ if(conf->checkLibrary(s, "torrent")){
@@ -402,16 +425,6 @@ public:
if(!found) return false; if(!found) return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
} }
// BUGFIX for Fedora (doesn't support pkg-config?)
QFile issue_file("/etc/issue");
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
QString content = issue_file.readAll();
issue_file.close();
if(content.indexOf("Fedora") != -1){
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
conf->addLib("-lssl -lcrypto -lboost_date_time -lboost_filesystem -lboost_thread -lz -ltorrent");
}
}
return true; return true;
} }
}; };
@@ -472,25 +485,25 @@ public:
return true; return true;
} }
}; };
#line 1 "libcommoncpp2.qcm" #line 1 "libcurl.qcm"
/* /*
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: libcommoncpp2 name: libcommoncpp2
arg: with-libcommoncpp2-inc=[path], Path to libcommoncpp2 include files arg: with-libcurl-inc=[path], Path to libcurl include files
arg: with-libcommoncpp2-lib=[path], Path to libcommoncpp2 library files arg: with-libcurl-lib=[path], Path to libcurl library files
-----END QCMOD----- -----END QCMOD-----
*/ */
class qc_libcommoncpp2 : public ConfObj class qc_libcurl : public ConfObj
{ {
public: public:
qc_libcommoncpp2(Conf *c) : ConfObj(c) {} qc_libcurl(Conf *c) : ConfObj(c) {}
QString name() const { return "GNU Common C++ library (libcommoncpp2)"; } QString name() const { return "libcurl"; }
QString shortname() const { return "libcommoncpp2"; } QString shortname() const { return "libcurl"; }
bool exec(){ bool exec(){
QString s; QString s;
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_INC"); s = conf->getenv("QC_WITH_LIBCURL_INC");
if(!s.isEmpty()) { if(!s.isEmpty()) {
if(!conf->checkHeader(s, "cc++/url.h")) { if(!conf->checkHeader(s, "curl/curl.h")) {
return false; return false;
} }
}else{ }else{
@@ -499,7 +512,7 @@ public:
sl << "/usr/local/include"; sl << "/usr/local/include";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(conf->checkHeader(s, "cc++/url.h")){ if(conf->checkHeader(s, "curl/curl.h")){
found = true; found = true;
break; break;
} }
@@ -510,38 +523,26 @@ public:
} }
conf->addIncludePath(s); conf->addIncludePath(s);
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB"); s = conf->getenv("QC_WITH_LIBCURL_LIB");
if(!s.isEmpty()) { if(!s.isEmpty()) {
if(!QFile::exists(s+QString("libccext2.so"))) if(!QFile::exists(s+QString("/libcurl.so")))
return false;
if(!QFile::exists(s+QString("libccgnu2.so")))
return false; return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(QFile::exists(s+QString("libccext2.so"))){ if(QFile::exists(s+QString("libcurl.so"))){
if(QFile::exists(s+QString("libccgnu2.so"))){
found = true; found = true;
break; break;
} }
} }
}
if(!found) return false; if(!found) return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
}
// BUGFIX for Fedora (doesn't support pkg-config?)
QFile issue_file("/etc/issue");
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
QString content = issue_file.readAll();
issue_file.close();
if(content.indexOf("Fedora") != -1){
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
conf->addLib("-pthread -lccext2 -lz -lccgnu2 -ldl -lrt");
}
} }
return true; return true;
} }
@@ -593,16 +594,18 @@ public:
s = conf->getenv("QC_WITH_LIBMAGICK_LIB"); s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
if(!s.isEmpty()) { if(!s.isEmpty()) {
if(!QFile::exists(s+QString("libMagick++.so"))){ if(!conf->checkLibrary(s, "Magick++")) {
return false; return false;
} }
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(QFile::exists(s+QString("libMagick++.so"))){ if(conf->checkLibrary(s, "Magick++")) {
found = true; found = true;
break; break;
} }
@@ -618,35 +621,88 @@ public:
magickConfig.waitForStarted(); magickConfig.waitForStarted();
magickConfig.waitForFinished(); magickConfig.waitForFinished();
QByteArray result = magickConfig.readAll(); QByteArray result = magickConfig.readAll();
result = result.replace("\n", "");
conf->addLib(result.data()); conf->addLib(result.data());
conf->addDefine("HAVE_MAGICK"); conf->addDefine("HAVE_MAGICK");
return true; return true;
} }
}; };
#line 1 "python.qcm" #line 1 "libzzip.qcm"
/* /*
-----BEGIN QCMOD----- -----BEGIN QCMOD-----
name: python name: libzzip
arg: with-libzzip-inc=[path], Path to libzzip++ include files
arg: with-libzzip-lib=[path], Path to libzzip++ library files
-----END QCMOD----- -----END QCMOD-----
*/ */
class qc_python : public ConfObj #include <QProcess>
class qc_libzzip : public ConfObj
{ {
public: public:
qc_python(Conf *c) : ConfObj(c) {} qc_libzzip(Conf *c) : ConfObj(c) {}
QString name() const { return "python >= 2.3"; } QString name() const { return "Zzip library (libzzip)"; }
QString shortname() const { return "python"; } QString shortname() const { return "libzzip"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec(){ bool exec(){
int r = conf->doCommand("python testpython.py"); if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
if(r == 0)
return true;
else
return false; return false;
QString s;
s = conf->getenv("QC_WITH_LIBZZIP_INC");
if(!s.isEmpty()) {
if(!conf->checkHeader(s, "zzip/zzip.h")) {
return false;
}
}else{
QStringList sl;
sl << "/usr/include";
sl << "/usr/local/include";
bool found = false;
foreach(s, sl){
if(conf->checkHeader(s, "zzip/zzip.h")){
found = true;
break;
}
}
if(!found)
return false;
}
conf->addIncludePath(s);
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
if(!s.isEmpty()) {
if(!QFile::exists(s+QString("/libzzip.so"))){
return false;
}
}else{
QStringList sl;
sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false;
foreach(s, sl){
if(QFile::exists(s+QString("libzzip.so"))){
found = true;
break;
}
}
if(!found)
return false;
}
conf->addLib(QString("-L") + s);
conf->addLib("-lzzip");
conf->addDefine("HAVE_ZZIP");
return true;
} }
}; };
EOT EOT
cat >$1/modules_new.cpp <<EOT cat >$1/modules_new.cpp <<EOT
o = new qc_qt42(conf); o = new qc_qt4(conf);
o->required = true; o->required = true;
o->disabled = false; o->disabled = false;
o = new qc_libtorrent(conf); o = new qc_libtorrent(conf);
@@ -655,14 +711,14 @@ cat >$1/modules_new.cpp <<EOT
o = new qc_libboost(conf); o = new qc_libboost(conf);
o->required = true; o->required = true;
o->disabled = false; o->disabled = false;
o = new qc_libcommoncpp2(conf); o = new qc_libcurl(conf);
o->required = true; o->required = true;
o->disabled = false; o->disabled = false;
o = new qc_libmagick(conf); o = new qc_libmagick(conf);
o->required = false; o->required = false;
o->disabled = false; o->disabled = false;
o = new qc_python(conf); o = new qc_libzzip(conf);
o->required = true; o->required = false;
o->disabled = false; o->disabled = false;
EOT EOT
@@ -1613,11 +1669,14 @@ export QC_WITH_LIBTORRENT_INC
export QC_WITH_LIBTORRENT_LIB export QC_WITH_LIBTORRENT_LIB
export QC_WITH_LIBTORRENT_STATIC_LIB export QC_WITH_LIBTORRENT_STATIC_LIB
export QC_WITH_LIBBOOST_INC export QC_WITH_LIBBOOST_INC
export QC_WITH_LIBCOMMONCPP2_INC export QC_WITH_LIBCURL_INC
export QC_WITH_LIBCOMMONCPP2_LIB export QC_WITH_LIBCURL_LIB
export QC_DISABLE_libmagick export QC_DISABLE_libmagick
export QC_WITH_LIBMAGICK_INC export QC_WITH_LIBMAGICK_INC
export QC_WITH_LIBMAGICK_LIB export QC_WITH_LIBMAGICK_LIB
export QC_DISABLE_libzzip
export QC_WITH_LIBZZIP_INC
export QC_WITH_LIBZZIP_LIB
export QC_VERBOSE export QC_VERBOSE
rm -rf .qconftemp rm -rf .qconftemp
( (

28
doc/qbittorrent.1 Normal file
View File

@@ -0,0 +1,28 @@
.\" 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" "1" "September 1st 2007" "Bittorrent client written in C++ / Qt4" ""
.SH NAME
qBittorrent \- a Bittorrent client written in C++ / Qt4
.SH SYNOPSIS
\fBqbittorrent\fR [FILE | URL] [FILE | URL...]
\fBqbittorrent\fR \-\-help
\fBqbittorrent\fR \-\-version
.PP
.SH "DESCRIPTION"
\fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4,
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.
It also comes with UPnP port forwarding / NAT-PMP, encryption (Azureus compatible), FAST extension (mainline) and PeX support (utorrent compatible).
Please report any problem to http://bugs.qbittorrent.org
.PP
.SH "AUTHOR"
Christophe Dumez <chris@qbittorrent.org>

Binary file not shown.

View File

@@ -94,13 +94,12 @@
<includestyle>4</includestyle> <includestyle>4</includestyle>
<designerintegration>ExternalDesigner</designerintegration> <designerintegration>ExternalDesigner</designerintegration>
<root>/usr/lib/qt4</root> <root>/usr/lib/qt4</root>
<qmake>/usr/bin/qmake-qt4</qmake> <qmake>/usr/bin/qmake</qmake>
<designer>/usr/bin/designer-qt4</designer> <designer>/usr/bin/designer</designer>
<designerpluginpaths/> <designerpluginpaths/>
</qt> </qt>
<references> <references>
<pcs>automatic_%2Fhome%2Fchris%2Fqbittorrent_svn%2Ftrunk</pcs> <pcs>automatic_%2Fhome%2Fishanarora%2Fprojects%2Fqbittorrent</pcs>
<pcs>Qt4</pcs>
</references> </references>
<codecompletion> <codecompletion>
<automaticCodeCompletion>true</automaticCodeCompletion> <automaticCodeCompletion>true</automaticCodeCompletion>
@@ -149,14 +148,14 @@
</kdevfilecreate> </kdevfilecreate>
<kdevtrollproject> <kdevtrollproject>
<general> <general>
<activedir></activedir> <activedir>src</activedir>
</general> </general>
<run> <run>
<directoryradio>executable</directoryradio> <directoryradio>executable</directoryradio>
<mainprogram>/home/chris/qbittorrent_svn/trunk/src/qbittorrent</mainprogram> <mainprogram>/home/ishanarora/projects/qbittorrent/src/qbittorrent</mainprogram>
<programargs/> <programargs/>
<globaldebugarguments/> <globaldebugarguments/>
<globalcwd>/home/chris/qbittorrent_svn/trunk</globalcwd> <globalcwd>/home/ishanarora/projects/qbittorrent</globalcwd>
<useglobalprogram>true</useglobalprogram> <useglobalprogram>true</useglobalprogram>
<terminal>false</terminal> <terminal>false</terminal>
<autocompile>true</autocompile> <autocompile>true</autocompile>
@@ -187,7 +186,7 @@
</cppsupportpart> </cppsupportpart>
<ctagspart> <ctagspart>
<customArguments/> <customArguments/>
<customTagfilePath>/home/chris/qbittorrent_svn/trunk/tags</customTagfilePath> <customTagfilePath>/home/ishanarora/projects/qbittorrent/tags</customTagfilePath>
<activeTagsFiles/> <activeTagsFiles/>
</ctagspart> </ctagspart>
<kdevdocumentation> <kdevdocumentation>

View File

@@ -3,7 +3,7 @@
<profile>qbittorrent.pro</profile> <profile>qbittorrent.pro</profile>
<moddir>qcm</moddir> <moddir>qcm</moddir>
<datadir/> <datadir/>
<dep type='qt42'> <dep type='qt4'>
<required/> <required/>
</dep> </dep>
<dep type='libtorrent'> <dep type='libtorrent'>
@@ -12,11 +12,9 @@
<dep type='libboost'> <dep type='libboost'>
<required/> <required/>
</dep> </dep>
<dep type='libcommoncpp2'> <dep type='libcurl'>
<required/> <required/>
</dep> </dep>
<dep type='libmagick'/> <dep type='libmagick'/>
<dep type='python'> <dep type='libzzip'/>
<required/>
</dep>
</qconf> </qconf>

View File

@@ -37,15 +37,17 @@ public:
s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB"); s = conf->getenv("QC_WITH_LIBCOMMONCPP2_LIB");
if(!s.isEmpty()) { if(!s.isEmpty()) {
if(!QFile::exists(s+QString("libccext2.so"))) if(!QFile::exists(s+QString("/libccext2.so")))
return false; return false;
if(!QFile::exists(s+QString("libccgnu2.so"))) if(!QFile::exists(s+QString("/libccgnu2.so")))
return false; return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(QFile::exists(s+QString("libccext2.so"))){ if(QFile::exists(s+QString("libccext2.so"))){
@@ -57,16 +59,6 @@ public:
} }
if(!found) return false; if(!found) return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
}
// BUGFIX for Fedora (doesn't support pkg-config?)
QFile issue_file("/etc/issue");
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
QString content = issue_file.readAll();
issue_file.close();
if(content.indexOf("Fedora") != -1){
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
conf->addLib("-pthread -lccext2 -lz -lccgnu2 -ldl -lrt");
}
} }
return true; return true;
} }

61
qcm/libcurl.qcm Normal file
View File

@@ -0,0 +1,61 @@
/*
-----BEGIN QCMOD-----
name: libcommoncpp2
arg: with-libcurl-inc=[path], Path to libcurl include files
arg: with-libcurl-lib=[path], Path to libcurl library files
-----END QCMOD-----
*/
class qc_libcurl : public ConfObj
{
public:
qc_libcurl(Conf *c) : ConfObj(c) {}
QString name() const { return "libcurl"; }
QString shortname() const { return "libcurl"; }
bool exec(){
QString s;
s = conf->getenv("QC_WITH_LIBCURL_INC");
if(!s.isEmpty()) {
if(!conf->checkHeader(s, "curl/curl.h")) {
return false;
}
}else{
QStringList sl;
sl << "/usr/include";
sl << "/usr/local/include";
bool found = false;
foreach(s, sl){
if(conf->checkHeader(s, "curl/curl.h")){
found = true;
break;
}
}
if(!found) {
return false;
}
}
conf->addIncludePath(s);
s = conf->getenv("QC_WITH_LIBCURL_LIB");
if(!s.isEmpty()) {
if(!QFile::exists(s+QString("/libcurl.so")))
return false;
conf->addLib(QString("-L") + s);
}else{
QStringList sl;
sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false;
foreach(s, sl){
if(QFile::exists(s+QString("libcurl.so"))){
found = true;
break;
}
}
if(!found) return false;
conf->addLib(QString("-L") + s);
}
return true;
}
};

View File

@@ -44,16 +44,18 @@ public:
s = conf->getenv("QC_WITH_LIBMAGICK_LIB"); s = conf->getenv("QC_WITH_LIBMAGICK_LIB");
if(!s.isEmpty()) { if(!s.isEmpty()) {
if(!QFile::exists(s+QString("libMagick++.so"))){ if(!conf->checkLibrary(s, "Magick++")) {
return false; return false;
} }
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(QFile::exists(s+QString("libMagick++.so"))){ if(conf->checkLibrary(s, "Magick++")) {
found = true; found = true;
break; break;
} }
@@ -69,6 +71,7 @@ public:
magickConfig.waitForStarted(); magickConfig.waitForStarted();
magickConfig.waitForFinished(); magickConfig.waitForFinished();
QByteArray result = magickConfig.readAll(); QByteArray result = magickConfig.readAll();
result = result.replace("\n", "");
conf->addLib(result.data()); conf->addLib(result.data());
conf->addDefine("HAVE_MAGICK"); conf->addDefine("HAVE_MAGICK");
return true; return true;

View File

@@ -52,7 +52,9 @@ public:
}else{ }else{
QStringList sl; QStringList sl;
sl << "/usr/lib/"; sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/"; sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false; bool found = false;
foreach(s, sl){ foreach(s, sl){
if(conf->checkLibrary(s, "torrent")){ if(conf->checkLibrary(s, "torrent")){
@@ -63,16 +65,6 @@ public:
if(!found) return false; if(!found) return false;
conf->addLib(QString("-L") + s); conf->addLib(QString("-L") + s);
} }
// BUGFIX for Fedora (doesn't support pkg-config?)
QFile issue_file("/etc/issue");
if(issue_file.open(QIODevice::ReadOnly | QIODevice::Text)){
QString content = issue_file.readAll();
issue_file.close();
if(content.indexOf("Fedora") != -1){
qWarning("Fedora detected. WORKAROUND for Fedora pkg-config problem enabled");
conf->addLib("-lssl -lcrypto -lboost_date_time -lboost_filesystem -lboost_thread -lz -ltorrent");
}
}
return true; return true;
} }
}; };

71
qcm/libzzip.qcm Normal file
View File

@@ -0,0 +1,71 @@
/*
-----BEGIN QCMOD-----
name: libzzip
arg: with-libzzip-inc=[path], Path to libzzip++ include files
arg: with-libzzip-lib=[path], Path to libzzip++ library files
-----END QCMOD-----
*/
#include <QProcess>
class qc_libzzip : public ConfObj
{
public:
qc_libzzip(Conf *c) : ConfObj(c) {}
QString name() const { return "Zzip library (libzzip)"; }
QString shortname() const { return "libzzip"; }
QString checkString() const {
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
return "";
return ConfObj::checkString();
}
bool exec(){
if(!conf->getenv("QC_DISABLE_LIBZZIP").isEmpty())
return false;
QString s;
s = conf->getenv("QC_WITH_LIBZZIP_INC");
if(!s.isEmpty()) {
if(!conf->checkHeader(s, "zzip/zzip.h")) {
return false;
}
}else{
QStringList sl;
sl << "/usr/include";
sl << "/usr/local/include";
bool found = false;
foreach(s, sl){
if(conf->checkHeader(s, "zzip/zzip.h")){
found = true;
break;
}
}
if(!found)
return false;
}
conf->addIncludePath(s);
s = conf->getenv("QC_WITH_LIBZZIP_LIB");
if(!s.isEmpty()) {
if(!QFile::exists(s+QString("/libzzip.so"))){
return false;
}
}else{
QStringList sl;
sl << "/usr/lib/";
sl << "/usr/lib64/";
sl << "/usr/local/lib/";
sl << "/usr/local/lib64/";
bool found = false;
foreach(s, sl){
if(QFile::exists(s+QString("libzzip.so"))){
found = true;
break;
}
}
if(!found)
return false;
}
conf->addLib(QString("-L") + s);
conf->addLib("-lzzip");
conf->addDefine("HAVE_ZZIP");
return true;
}
};

View File

@@ -1,19 +0,0 @@
/*
-----BEGIN QCMOD-----
name: python
-----END QCMOD-----
*/
class qc_python : public ConfObj
{
public:
qc_python(Conf *c) : ConfObj(c) {}
QString name() const { return "python >= 2.3"; }
QString shortname() const { return "python"; }
bool exec(){
int r = conf->doCommand("python testpython.py");
if(r == 0)
return true;
else
return false;
}
};

16
qcm/qt4.qcm Normal file
View File

@@ -0,0 +1,16 @@
/*
-----BEGIN QCMOD-----
name: Qt >= 4.3
-----END QCMOD-----
*/
class qc_qt4 : public ConfObj
{
public:
qc_qt4(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.3"; }
QString shortname() const { return "Qt 4.3"; }
bool exec()
{
return(QT_VERSION >= 0x040300);
}
};

View File

@@ -1,16 +0,0 @@
/*
-----BEGIN QCMOD-----
name: Qt >= 4.2
-----END QCMOD-----
*/
class qc_qt42 : public ConfObj
{
public:
qc_qt42(Conf *c) : ConfObj(c) {}
QString name() const { return "Qt >= 4.2"; }
QString shortname() const { return "qt42"; }
bool exec()
{
return(QT_VERSION >= 0x040200);
}
};

View File

@@ -83,11 +83,9 @@ class DLListDelegate: public QItemDelegate {
newopt.maximum = 100; newopt.maximum = 100;
newopt.minimum = 0; newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled; newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false; newopt.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
painter); painter);
painter->setPen(QColor("Black"));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break; break;
} }
default: default:

View File

@@ -34,11 +34,10 @@
// Defines for download list list columns // Defines for download list list columns
#define F_NAME 0 #define F_NAME 0
#define F_SIZE 1 #define F_SIZE 1
#define F_PROGRESS 2 #define F_UPSPEED 2
#define F_UPSPEED 3 #define F_LEECH 3
#define F_SEEDSLEECH 4 #define F_RATIO 4
#define F_RATIO 5 #define F_HASH 5
#define F_HASH 6
class FinishedListDelegate: public QItemDelegate { class FinishedListDelegate: public QItemDelegate {
Q_OBJECT Q_OBJECT
@@ -67,22 +66,6 @@ class FinishedListDelegate: public QItemDelegate {
QItemDelegate::drawDisplay(painter, opt, opt.rect, QString(QByteArray::number(ratio, 'f', 1))); QItemDelegate::drawDisplay(painter, opt, opt.rect, QString(QByteArray::number(ratio, 'f', 1)));
break; break;
} }
case F_PROGRESS:{
QStyleOptionProgressBarV2 newopt;
double progress = index.data().toDouble()*100.;
newopt.rect = opt.rect;
newopt.text = QString(QByteArray::number(progress, 'f', 1))+QString::fromUtf8("%");
newopt.progress = (int)progress;
newopt.maximum = 100;
newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt,
painter);
painter->setPen(QColor("Black"));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break;
}
default: default:
QItemDelegate::paint(painter, option, index); QItemDelegate::paint(painter, option, index);
} }

View File

@@ -37,15 +37,15 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
actionStart->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/play.png"))); actionStart->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/play.png")));
actionPause->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/pause.png"))); actionPause->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/pause.png")));
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool))); connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
finishedListModel = new QStandardItemModel(0,7); finishedListModel = new QStandardItemModel(0,6);
finishedListModel->setHeaderData(F_NAME, Qt::Horizontal, tr("Name", "i.e: file name")); finishedListModel->setHeaderData(F_NAME, Qt::Horizontal, tr("Name", "i.e: file name"));
finishedListModel->setHeaderData(F_SIZE, Qt::Horizontal, tr("Size", "i.e: file size")); finishedListModel->setHeaderData(F_SIZE, Qt::Horizontal, tr("Size", "i.e: file size"));
finishedListModel->setHeaderData(F_PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
finishedListModel->setHeaderData(F_UPSPEED, Qt::Horizontal, tr("UP Speed", "i.e: Upload speed")); finishedListModel->setHeaderData(F_UPSPEED, Qt::Horizontal, tr("UP Speed", "i.e: Upload speed"));
finishedListModel->setHeaderData(F_SEEDSLEECH, Qt::Horizontal, tr("Seeds/Leechs", "i.e: full/partial sources")); finishedListModel->setHeaderData(F_LEECH, Qt::Horizontal, tr("Leechers", "i.e: full/partial sources"));
finishedListModel->setHeaderData(F_RATIO, Qt::Horizontal, tr("Ratio")); finishedListModel->setHeaderData(F_RATIO, Qt::Horizontal, tr("Ratio"));
finishedList->setModel(finishedListModel); finishedList->setModel(finishedListModel);
// Hide ETA & hash column loadHiddenColumns();
// Hide hash column
finishedList->hideColumn(F_HASH); finishedList->hideColumn(F_HASH);
// Load last columns width for download list // Load last columns width for download list
if(!loadColWidthFinishedList()){ if(!loadColWidthFinishedList()){
@@ -58,6 +58,9 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
finishedListDelegate = new FinishedListDelegate(finishedList); finishedListDelegate = new FinishedListDelegate(finishedList);
finishedList->setItemDelegate(finishedListDelegate); finishedList->setItemDelegate(finishedListDelegate);
connect(finishedList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedListMenu(const QPoint&))); connect(finishedList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedListMenu(const QPoint&)));
finishedList->header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(finishedList->header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayFinishedHoSMenu(const QPoint&)));
connect(finishedList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&))); connect(finishedList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
actionDelete->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/delete.png"))); actionDelete->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/delete.png")));
actionPreview_file->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/preview.png"))); actionPreview_file->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/preview.png")));
@@ -69,11 +72,20 @@ FinishedTorrents::FinishedTorrents(QObject *parent, bittorrent *BTSession) : par
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered())); connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered())); connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered())); connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
connect(actionOpen_destination_folder, SIGNAL(triggered()), (GUI*)parent, SLOT(openDestinationFolder()));
connect(actionBuy_it, SIGNAL(triggered()), (GUI*)parent, SLOT(goBuyPage()));
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection())); connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
connect(actionHOSColName, SIGNAL(triggered()), this, SLOT(hideOrShowColumnName()));
connect(actionHOSColSize, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSize()));
connect(actionHOSColUpSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnUpSpeed()));
connect(actionHOSColLeechers, SIGNAL(triggered()), this, SLOT(hideOrShowColumnLeechers()));
connect(actionHOSColRatio, SIGNAL(triggered()), this, SLOT(hideOrShowColumnRatio()));
} }
FinishedTorrents::~FinishedTorrents(){ FinishedTorrents::~FinishedTorrents(){
saveColWidthFinishedList(); saveColWidthFinishedList();
saveHiddenColumns();
delete finishedListDelegate; delete finishedListDelegate;
delete finishedListModel; delete finishedListModel;
} }
@@ -81,7 +93,7 @@ FinishedTorrents::~FinishedTorrents(){
void FinishedTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) { void FinishedTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
unsigned int row = index.row(); unsigned int row = index.row();
QString hash = getHashFromRow(row); QString hash = getHashFromRow(row);
emit torrentDoubleClicked(hash); emit torrentDoubleClicked(hash, true);
} }
void FinishedTorrents::addTorrent(QString hash){ void FinishedTorrents::addTorrent(QString hash){
@@ -97,10 +109,9 @@ void FinishedTorrents::addTorrent(QString hash){
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(h.name())); finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(h.name()));
finishedListModel->setData(finishedListModel->index(row, F_SIZE), QVariant((qlonglong)h.actual_size())); finishedListModel->setData(finishedListModel->index(row, F_SIZE), QVariant((qlonglong)h.actual_size()));
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.)); finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.));
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant("0/0")); finishedListModel->setData(finishedListModel->index(row, F_LEECH), QVariant("0"));
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(QString::fromUtf8(misc::toString(BTSession->getRealRatio(hash)).c_str()))); finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(QString::fromUtf8(misc::toString(BTSession->getRealRatio(hash)).c_str())));
finishedListModel->setData(finishedListModel->index(row, F_HASH), QVariant(hash)); finishedListModel->setData(finishedListModel->index(row, F_HASH), QVariant(hash));
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)1.));
if(h.is_paused()) { if(h.is_paused()) {
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(":/Icons/skin/paused.png"), Qt::DecorationRole); finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(":/Icons/skin/paused.png"), Qt::DecorationRole);
setRowColor(row, "red"); setRowColor(row, "red");
@@ -122,7 +133,8 @@ void FinishedTorrents::torrentAdded(QString, QTorrentHandle& h, bool) {
// Set the color of a row in data model // Set the color of a row in data model
void FinishedTorrents::setRowColor(int row, QString color){ void FinishedTorrents::setRowColor(int row, QString color){
for(int i=0; i<finishedListModel->columnCount(); ++i){ unsigned int nbColumns = finishedListModel->columnCount()-1;
for(unsigned int i=0; i<nbColumns; ++i){
finishedListModel->setData(finishedListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole); finishedListModel->setData(finishedListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole);
} }
} }
@@ -155,7 +167,7 @@ bool FinishedTorrents::loadColWidthFinishedList(){
if(line.isEmpty()) if(line.isEmpty())
return false; return false;
QStringList width_list = line.split(' '); QStringList width_list = line.split(' ');
if(width_list.size() != finishedListModel->columnCount()-1) if(width_list.size() < finishedListModel->columnCount()-1)
return false; return false;
unsigned int listSize = width_list.size(); unsigned int listSize = width_list.size();
for(unsigned int i=0; i<listSize; ++i){ for(unsigned int i=0; i<listSize; ++i){
@@ -171,11 +183,27 @@ void FinishedTorrents::saveColWidthFinishedList() const{
qDebug("Saving columns width in finished list"); qDebug("Saving columns width in finished list");
QSettings settings("qBittorrent", "qBittorrent"); QSettings settings("qBittorrent", "qBittorrent");
QStringList width_list; QStringList width_list;
unsigned int nbColumns = finishedListModel->columnCount()-1; QStringList new_width_list;
for(unsigned int i=0; i<nbColumns; ++i){ short nbColumns = finishedListModel->columnCount()-1;
width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
QString line = settings.value("FinishedListColsWidth", QString()).toString();
if(!line.isEmpty()) {
width_list = line.split(' ');
} }
settings.setValue("FinishedListColsWidth", width_list.join(" ")); for(short i=0; i<nbColumns; ++i){
if(finishedList->columnWidth(i)<1 && width_list.size() == finishedListModel->columnCount()-1 && width_list.at(i).toInt()>=1) {
// load the former width
new_width_list << width_list.at(i);
} else if(finishedList->columnWidth(i)>=1) {
// usual case, save the current width
new_width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
} else {
// default width
finishedList->resizeColumnToContents(i);
new_width_list << QString::fromUtf8(misc::toString(finishedList->columnWidth(i)).c_str());
}
}
settings.setValue("FinishedListColsWidth", new_width_list.join(" "));
qDebug("Finished list columns width saved"); qDebug("Finished list columns width saved");
} }
@@ -209,6 +237,9 @@ void FinishedTorrents::updateFinishedList(){
} }
Q_ASSERT(row != -1); Q_ASSERT(row != -1);
if(h.is_paused()) continue; if(h.is_paused()) continue;
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
continue;
}
if(h.state() == torrent_status::downloading || (h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking && h.progress() < 1.)) { if(h.state() == torrent_status::downloading || (h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking && h.progress() < 1.)) {
// What are you doing here? go back to download tab! // What are you doing here? go back to download tab!
qDebug("Info: a torrent was moved from finished to download tab"); qDebug("Info: a torrent was moved from finished to download tab");
@@ -218,19 +249,21 @@ void FinishedTorrents::updateFinishedList(){
continue; continue;
} }
if(h.state() == torrent_status::checking_files){ if(h.state() == torrent_status::checking_files){
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole); finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
setRowColor(row, QString::fromUtf8("grey")); setRowColor(row, QString::fromUtf8("grey"));
}
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)h.progress()));
continue; continue;
} }
setRowColor(row, QString::fromUtf8("orange")); setRowColor(row, QString::fromUtf8("orange"));
finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))), Qt::DecorationRole); finishedListModel->setData(finishedListModel->index(row, F_NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/seeding.png"))), Qt::DecorationRole);
if(!finishedList->isColumnHidden(F_UPSPEED)) {
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)h.upload_payload_rate())); finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)h.upload_payload_rate()));
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+"/"+misc::toQString(h.num_peers() - h.num_seeds(), true))); }
if(!finishedList->isColumnHidden(F_LEECH)) {
finishedListModel->setData(finishedListModel->index(row, F_LEECH), misc::toQString(h.num_peers() - h.num_seeds(), true));
}
if(!finishedList->isColumnHidden(F_RATIO)) {
finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash)))); finishedListModel->setData(finishedListModel->index(row, F_RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
finishedListModel->setData(finishedListModel->index(row, F_PROGRESS), QVariant((double)1.)); }
} }
} }
@@ -247,10 +280,11 @@ int FinishedTorrents::getRowFromHash(QString hash) const{
// Note: does not actually pause the torrent in BT Session // Note: does not actually pause the torrent in BT Session
void FinishedTorrents::pauseTorrent(QString hash) { void FinishedTorrents::pauseTorrent(QString hash) {
int row = getRowFromHash(hash); int row = getRowFromHash(hash);
Q_ASSERT(row != -1); if(row == -1)
return;
finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.0)); finishedListModel->setData(finishedListModel->index(row, F_UPSPEED), QVariant((double)0.0));
finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(QString::fromUtf8(":/Icons/skin/paused.png")), Qt::DecorationRole); finishedListModel->setData(finishedListModel->index(row, F_NAME), QIcon(QString::fromUtf8(":/Icons/skin/paused.png")), Qt::DecorationRole);
finishedListModel->setData(finishedListModel->index(row, F_SEEDSLEECH), QVariant(QString::fromUtf8("0/0"))); finishedListModel->setData(finishedListModel->index(row, F_LEECH), QVariant(QString::fromUtf8("0")));
setRowColor(row, QString::fromUtf8("red")); setRowColor(row, QString::fromUtf8("red"));
} }
@@ -280,11 +314,14 @@ void FinishedTorrents::deleteTorrent(QString hash){
// Show torrent properties dialog // Show torrent properties dialog
void FinishedTorrents::showProperties(const QModelIndex &index){ void FinishedTorrents::showProperties(const QModelIndex &index){
int row = index.row(); showPropertiesFromHash(finishedListModel->data(finishedListModel->index(index.row(), F_HASH)).toString());
QString hash = finishedListModel->data(finishedListModel->index(row, F_HASH)).toString(); }
void FinishedTorrents::showPropertiesFromHash(QString hash){
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);
properties *prop = new properties(this, BTSession, h); properties *prop = new properties(this, BTSession, h);
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSize(QString))); connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSize(QString)));
connect(prop, SIGNAL(trackersChanged(QString)), BTSession, SLOT(saveTrackerFile(QString)));
prop->show(); prop->show();
} }
@@ -310,8 +347,6 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
QModelIndex index; QModelIndex index;
// Enable/disable pause/start action given the DL state // Enable/disable pause/start action given the DL state
QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes = finishedList->selectionModel()->selectedIndexes();
QSettings settings("qBittorrent", "qBittorrent");
QString previewProgram = settings.value("Options/Misc/PreviewProgram", QString()).toString();
bool has_pause = false, has_start = false, has_preview = false; bool has_pause = false, has_start = false, has_preview = false;
foreach(index, selectedIndexes) { foreach(index, selectedIndexes) {
if(index.column() == F_NAME) { if(index.column() == F_NAME) {
@@ -331,7 +366,7 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
has_pause = true; has_pause = true;
} }
} }
if(!previewProgram.isEmpty() && BTSession->isFilePreviewPossible(hash) && !has_preview) { if(BTSession->isFilePreviewPossible(hash) && !has_preview) {
myFinishedListMenu.addAction(actionPreview_file); myFinishedListMenu.addAction(actionPreview_file);
has_preview = true; has_preview = true;
} }
@@ -344,12 +379,150 @@ void FinishedTorrents::displayFinishedListMenu(const QPoint& pos){
myFinishedListMenu.addSeparator(); myFinishedListMenu.addSeparator();
myFinishedListMenu.addAction(actionSet_upload_limit); myFinishedListMenu.addAction(actionSet_upload_limit);
myFinishedListMenu.addSeparator(); myFinishedListMenu.addSeparator();
myFinishedListMenu.addAction(actionOpen_destination_folder);
myFinishedListMenu.addAction(actionTorrent_Properties); myFinishedListMenu.addAction(actionTorrent_Properties);
myFinishedListMenu.addSeparator();
myFinishedListMenu.addAction(actionBuy_it);
// Call menu // Call menu
// XXX: why mapToGlobal() is not enough? // XXX: why mapToGlobal() is not enough?
myFinishedListMenu.exec(mapToGlobal(pos)+QPoint(10,55)); myFinishedListMenu.exec(mapToGlobal(pos)+QPoint(10,55));
} }
/*
* Hiding Columns functions
*/
// hide/show columns menu
void FinishedTorrents::displayFinishedHoSMenu(const QPoint& pos){
QMenu hideshowColumn(this);
hideshowColumn.setTitle(tr("Hide or Show Column"));
for(int i=0; i<=F_RATIO; i++) {
hideshowColumn.addAction(getActionHoSCol(i));
}
// Call menu
hideshowColumn.exec(mapToGlobal(pos)+QPoint(10,55));
}
// toggle hide/show a column
void FinishedTorrents::hideOrShowColumn(int index) {
unsigned int nbVisibleColumns = 0;
unsigned int nbCols = finishedListModel->columnCount();
// Count visible columns
for(unsigned int i=0; i<nbCols; ++i) {
if(!finishedList->isColumnHidden(i))
++nbVisibleColumns;
}
if(!finishedList->isColumnHidden(index)) {
// User wants to hide the column
// Is there at least one other visible column?
if(nbVisibleColumns <= 1) return;
// User can hide the column, do it.
finishedList->setColumnHidden(index, true);
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
--nbVisibleColumns;
} else {
// User want to display the column
finishedList->setColumnHidden(index, false);
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
++nbVisibleColumns;
}
//resize all others non-hidden columns
for(unsigned int i=0; i<nbCols; ++i) {
if(!finishedList->isColumnHidden(i)) {
finishedList->setColumnWidth(i, (int)ceil(finishedList->columnWidth(i)+(finishedList->columnWidth(index)/nbVisibleColumns)));
}
}
}
void FinishedTorrents::hideOrShowColumnName() {
hideOrShowColumn(F_NAME);
}
void FinishedTorrents::hideOrShowColumnSize() {
hideOrShowColumn(F_SIZE);
}
void FinishedTorrents::hideOrShowColumnUpSpeed() {
hideOrShowColumn(F_UPSPEED);
}
void FinishedTorrents::hideOrShowColumnLeechers() {
hideOrShowColumn(F_LEECH);
}
void FinishedTorrents::hideOrShowColumnRatio() {
hideOrShowColumn(F_RATIO);
}
// load the previous settings, and hide the columns
bool FinishedTorrents::loadHiddenColumns() {
bool loaded = false;
QSettings settings("qBittorrent", "qBittorrent");
QString line = settings.value("FinishedListColsHoS", QString()).toString();
QStringList ishidden_list;
if(!line.isEmpty()) {
ishidden_list = line.split(' ');
if(ishidden_list.size() == finishedListModel->columnCount()-1) {
unsigned int listSize = ishidden_list.size();
for(unsigned int i=0; i<listSize; ++i){
finishedList->header()->resizeSection(i, ishidden_list.at(i).toInt());
}
loaded = true;
}
}
for(int i=0; i<finishedListModel->columnCount()-1; i++) {
if(loaded && ishidden_list.at(i) == "0") {
finishedList->setColumnHidden(i, true);
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
} else {
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
}
}
return loaded;
}
// save the hidden columns in settings
void FinishedTorrents::saveHiddenColumns() {
QSettings settings("qBittorrent", "qBittorrent");
QStringList ishidden_list;
short nbColumns = finishedListModel->columnCount()-1;
for(short i=0; i<nbColumns; ++i){
if(finishedList->isColumnHidden(i)) {
ishidden_list << QString::fromUtf8(misc::toString(0).c_str());
} else {
ishidden_list << QString::fromUtf8(misc::toString(1).c_str());
}
}
settings.setValue("FinishedListColsHoS", ishidden_list.join(" "));
}
// getter, return the action hide or show whose id is index
QAction* FinishedTorrents::getActionHoSCol(int index) {
switch(index) {
case F_NAME :
return actionHOSColName;
break;
case F_SIZE :
return actionHOSColSize;
break;
case F_UPSPEED :
return actionHOSColUpSpeed;
break;
case F_LEECH :
return actionHOSColLeechers;
break;
case F_RATIO :
return actionHOSColRatio;
break;
default :
return NULL;
}
}
/* /*
* Sorting functions * Sorting functions
*/ */
@@ -367,7 +540,6 @@ void FinishedTorrents::sortFinishedList(int index){
switch(index){ switch(index){
case F_SIZE: case F_SIZE:
case F_UPSPEED: case F_UPSPEED:
case F_PROGRESS:
sortFinishedListFloat(index, sortOrder); sortFinishedListFloat(index, sortOrder);
break; break;
default: default:

View File

@@ -39,6 +39,10 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
FinishedListDelegate *finishedListDelegate; FinishedListDelegate *finishedListDelegate;
QStandardItemModel *finishedListModel; QStandardItemModel *finishedListModel;
unsigned int nbFinished; unsigned int nbFinished;
void hideOrShowColumn(int index);
bool loadHiddenColumns();
void saveHiddenColumns();
QAction* getActionHoSCol(int index);
public: public:
FinishedTorrents(QObject *parent, bittorrent *BTSession); FinishedTorrents(QObject *parent, bittorrent *BTSession);
@@ -53,6 +57,7 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
protected slots: protected slots:
void showProperties(const QModelIndex &index); void showProperties(const QModelIndex &index);
void displayFinishedListMenu(const QPoint&); void displayFinishedListMenu(const QPoint&);
void displayFinishedHoSMenu(const QPoint&);
void setRowColor(int row, QString color); void setRowColor(int row, QString color);
void saveColWidthFinishedList() const; void saveColWidthFinishedList() const;
void sortFinishedList(int index); void sortFinishedList(int index);
@@ -62,6 +67,11 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
void torrentAdded(QString path, QTorrentHandle& h, bool fastResume); void torrentAdded(QString path, QTorrentHandle& h, bool fastResume);
void on_actionSet_upload_limit_triggered(); void on_actionSet_upload_limit_triggered();
void notifyTorrentDoubleClicked(const QModelIndex& index); void notifyTorrentDoubleClicked(const QModelIndex& index);
void hideOrShowColumnName();
void hideOrShowColumnSize();
void hideOrShowColumnUpSpeed();
void hideOrShowColumnLeechers();
void hideOrShowColumnRatio();
public slots: public slots:
void addTorrent(QString hash); void addTorrent(QString hash);
@@ -70,10 +80,11 @@ class FinishedTorrents : public QWidget, public Ui::seeding {
void resumeTorrent(QString hash); void resumeTorrent(QString hash);
void propertiesSelection(); void propertiesSelection();
void deleteTorrent(QString hash); void deleteTorrent(QString hash);
void showPropertiesFromHash(QString hash);
signals: signals:
void torrentMovedFromFinishedList(QString); void torrentMovedFromFinishedList(QString);
void torrentDoubleClicked(QString hash); void torrentDoubleClicked(QString hash, bool finished);
void finishedTorrentsNumberChanged(unsigned int); void finishedTorrentsNumberChanged(unsigned int);
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,7 @@ class options_imp;
class QTabWidget; class QTabWidget;
class QLabel; class QLabel;
class QModelIndex; class QModelIndex;
class HttpServer;
class GUI : public QMainWindow, private Ui::MainWindow{ class GUI : public QMainWindow, private Ui::MainWindow{
Q_OBJECT Q_OBJECT
@@ -59,12 +60,15 @@ class GUI : public QMainWindow, private Ui::MainWindow{
QTabWidget *tabs; QTabWidget *tabs;
options_imp *options; options_imp *options;
QSystemTrayIcon *myTrayIcon; QSystemTrayIcon *myTrayIcon;
QTimer *systrayCreator;
QMenu *myTrayIconMenu; QMenu *myTrayIconMenu;
DownloadingTorrents *downloadingTorrentTab; DownloadingTorrents *downloadingTorrentTab;
FinishedTorrents *finishedTorrentTab; FinishedTorrents *finishedTorrentTab;
QLabel *connecStatusLblIcon; QLabel *connecStatusLblIcon;
bool systrayIntegration; bool systrayIntegration;
bool displaySpeedInTitle;
bool force_exit; bool force_exit;
unsigned int refreshInterval;
QTimer *refresher; QTimer *refresher;
// Keyboard shortcuts // Keyboard shortcuts
QShortcut *switchSearchShortcut; QShortcut *switchSearchShortcut;
@@ -72,12 +76,12 @@ class GUI : public QMainWindow, private Ui::MainWindow{
QShortcut *switchDownShortcut; QShortcut *switchDownShortcut;
QShortcut *switchUpShortcut; QShortcut *switchUpShortcut;
QShortcut *switchRSSShortcut; QShortcut *switchRSSShortcut;
// Preview
QProcess *previewProcess;
// Search // Search
SearchEngine *searchEngine; SearchEngine *searchEngine;
// RSS // RSS
RSSImp *rssWidget; RSSImp *rssWidget;
// Web UI
HttpServer *httpServer;
// Misc // Misc
QTcpServer *tcpServer; QTcpServer *tcpServer;
QTcpSocket *clientConnection; QTcpSocket *clientConnection;
@@ -94,9 +98,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
void readParamsOnSocket(); void readParamsOnSocket();
void acceptConnection(); void acceptConnection();
void togglePausedState(QString hash); void togglePausedState(QString hash);
void torrentDoubleClicked(QString hash, bool finished);
void on_actionPreview_file_triggered(); void on_actionPreview_file_triggered();
void previewFile(QString filePath); void previewFile(QString filePath);
void cleanTempPreviewFile(int, QProcess::ExitStatus) const;
void balloonClicked(); void balloonClicked();
void writeSettings(); void writeSettings();
void readSettings(); void readSettings();
@@ -106,6 +110,7 @@ class GUI : public QMainWindow, private Ui::MainWindow{
void updateFinishedTorrentNumber(unsigned int nb); void updateFinishedTorrentNumber(unsigned int nb);
void fullDiskError(QTorrentHandle& h) const; void fullDiskError(QTorrentHandle& h) const;
void handleDownloadFromUrlFailure(QString, QString) const; void handleDownloadFromUrlFailure(QString, QString) const;
void createSystrayDelayed();
// Keyboard shortcuts // Keyboard shortcuts
void createKeyboardShortcuts(); void createKeyboardShortcuts();
void displayDownTab() const; void displayDownTab() const;
@@ -127,13 +132,18 @@ class GUI : public QMainWindow, private Ui::MainWindow{
void checkConnectionStatus(); void checkConnectionStatus();
void configureSession(bool deleteOptions); void configureSession(bool deleteOptions);
void processParams(const QStringList& params); void processParams(const QStringList& params);
void addTorrent(QString path);
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker); void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
void processScannedFiles(const QStringList& params); void processScannedFiles(const QStringList& params);
void processDownloadedFiles(QString path, QString url); void processDownloadedFiles(QString path, QString url);
void downloadFromURLList(const QStringList& urls); void downloadFromURLList(const QStringList& urls);
void deleteTorrent(QString hash);
void deleteRatioTorrent(QString fileName);
void finishedTorrent(QTorrentHandle& h) const; void finishedTorrent(QTorrentHandle& h) const;
void torrentChecked(QString hash) const; void torrentChecked(QString hash) const;
void updateLists(); void updateLists();
bool initWebUi(QString username, QString password, int port);
void pauseTorrent(QString hash);
// Options slots // Options slots
void on_actionOptions_triggered(); void on_actionOptions_triggered();
void OptionsSaved(QString info, bool deleteOptions); void OptionsSaved(QString info, bool deleteOptions);
@@ -144,6 +154,9 @@ class GUI : public QMainWindow, private Ui::MainWindow{
public slots: public slots:
void trackerAuthenticationRequired(QTorrentHandle& h); void trackerAuthenticationRequired(QTorrentHandle& h);
void setTabText(int index, QString text) const; void setTabText(int index, QString text) const;
void openDestinationFolder() const;
void goBuyPage() const;
void updateRatio();
protected: protected:
void closeEvent(QCloseEvent *); void closeEvent(QCloseEvent *);
@@ -154,7 +167,7 @@ class GUI : public QMainWindow, private Ui::MainWindow{
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList()); GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
~GUI(); ~GUI();
// Methods // Methods
unsigned int getCurrentTabIndex() const; int getCurrentTabIndex() const;
QPoint screenCenter() const; QPoint screenCenter() const;
}; };

BIN
src/Icons/bt_settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/Icons/configure.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 820 B

BIN
src/Icons/download.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
src/Icons/edit_clear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/Icons/file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

BIN
src/Icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

BIN
src/Icons/gear.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/Icons/money.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -1,22 +1,23 @@
[Desktop Entry] [Desktop Entry]
Categories=Qt;Application;Network;P2P Categories=Qt;Application;Network;P2P
Comment=V1.0.0 Comment=V1.1.0
Encoding=UTF-8
Exec=qbittorrent Exec=qbittorrent
GenericName=Bittorrent client GenericName=Bittorrent client
GenericName[fr]=Client Bittorrent GenericName[bg]=Торент клиент
GenericName[nl]=Bittorrent client GenericName[de]=Bittorren Client
GenericName[el]=Τορεντ πελάτης
GenericName[es]=Cliente Bittorrent GenericName[es]=Cliente Bittorrent
GenericName[fr]=Client Bittorrent
GenericName[it]=Client Bittorrent
GenericName[ja]=Bittorrent クライアント
GenericName[ko]=비토렌트 클라이언트
GenericName[nl]=Bittorrent client
GenericName[pl]=Klient Bittorrent
GenericName[ru]=клиент Bittorrent
GenericName[sv]=Bittorrent-klient GenericName[sv]=Bittorrent-klient
GenericName[tr]=Bittorrent istemcisi GenericName[tr]=Bittorrent istemcisi
GenericName[de]=Bittorren Client
GenericName[pl]=Klient Bittorrent
GenericName[zh]=Bittorrent之用户
GenericName[ko]=비토렌트 클라이언트
GenericName[el]=Τορεντ πελάτης
GenericName[bg]=Торент клиент
GenericName[uk]=Bittorrent-клієнт GenericName[uk]=Bittorrent-клієнт
GenericName[ru]=клиент Bittorrent GenericName[zh]=Bittorrent之用户
Icon=qbittorrent Icon=qbittorrent
MimeType=application/x-bittorrent MimeType=application/x-bittorrent
Name=qBittorrent Name=qBittorrent

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 948 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

BIN
src/Icons/rss16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

BIN
src/Icons/rss32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 75 KiB

BIN
src/Icons/star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/Icons/subscribe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/Icons/subscribe16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

After

Width:  |  Height:  |  Size: 856 B

BIN
src/Icons/unsubscribe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
src/Icons/unsubscribe16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

View File

@@ -16,13 +16,194 @@
<string/> <string/>
</property> </property>
<widget class="QWidget" name="centralwidget" > <widget class="QWidget" name="centralwidget" >
<layout class="QVBoxLayout" > <property name="geometry" >
<rect>
<x>0</x>
<y>58</y>
<width>849</width>
<height>505</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<layout class="QHBoxLayout" name="_3" >
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="margin" > <property name="margin" >
<number>9</number> <number>0</number>
</property> </property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lbl_DLSpeed_2" >
<property name="text" >
<string>Total DL Speed:</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_DownSpeed" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
</property>
<property name="smallDecimalPoint" >
<bool>false</bool>
</property>
<property name="numDigits" >
<number>6</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>0.000000000000000</double>
</property>
<property name="intValue" stdset="0" >
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="unitDL_2" >
<property name="text" >
<string>KiB/s</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Session ratio: </string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_Ratio" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="numDigits" >
<number>4</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbl_ratio_icon" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lbl_UPSpeed_2" >
<property name="text" >
<string>Total UP Speed:</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_UpSpeed" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="smallDecimalPoint" >
<bool>false</bool>
</property>
<property name="numDigits" >
<number>6</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="unitUP_2" >
<property name="text" >
<string>KiB/s</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QMenuBar" name="menubar" > <widget class="QMenuBar" name="menubar" >
@@ -31,7 +212,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>849</width> <width>849</width>
<height>29</height> <height>26</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menu_Edit" > <widget class="QMenu" name="menu_Edit" >
@@ -82,6 +263,14 @@
<property name="enabled" > <property name="enabled" >
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="geometry" >
<rect>
<x>0</x>
<y>26</y>
<width>849</width>
<height>32</height>
</rect>
</property>
<property name="contextMenuPolicy" > <property name="contextMenuPolicy" >
<enum>Qt::NoContextMenu</enum> <enum>Qt::NoContextMenu</enum>
</property> </property>
@@ -98,7 +287,7 @@
</size> </size>
</property> </property>
<attribute name="toolBarArea" > <attribute name="toolBarArea" >
<number>4</number> <enum>TopToolBarArea</enum>
</attribute> </attribute>
<attribute name="toolBarBreak" > <attribute name="toolBarBreak" >
<bool>false</bool> <bool>false</bool>

View File

@@ -61,10 +61,8 @@ class PreviewListDelegate: public QItemDelegate {
newopt.maximum = 100; newopt.maximum = 100;
newopt.minimum = 0; newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled; newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false; newopt.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter); QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
painter->setPen(QColor("Black"));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break; break;
} }
default: default:

View File

@@ -38,6 +38,7 @@
#define SIZE 1 #define SIZE 1
#define PROGRESS 2 #define PROGRESS 2
#define PRIORITY 3 #define PRIORITY 3
#define INDEX 4
#define IGNORED 0 #define IGNORED 0
#define NORMAL 1 #define NORMAL 1
@@ -74,10 +75,8 @@ class PropListDelegate: public QItemDelegate {
newopt.maximum = 100; newopt.maximum = 100;
newopt.minimum = 0; newopt.minimum = 0;
newopt.state |= QStyle::State_Enabled; newopt.state |= QStyle::State_Enabled;
newopt.textVisible = false; newopt.textVisible = true;
QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter); QApplication::style()->drawControl(QStyle::CE_ProgressBar, &newopt, painter);
painter->setPen(QColor("Black"));
painter->drawText(opt.rect, Qt::AlignCenter, newopt.text);
break; break;
} }
case PRIORITY:{ case PRIORITY:{
@@ -115,7 +114,6 @@ class PropListDelegate: public QItemDelegate {
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const { QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex & index) const {
if(index.column() != PRIORITY) return 0; if(index.column() != PRIORITY) return 0;
if(onlyOneItem(index)) return 0;
QComboBox* editor = new QComboBox(parent); QComboBox* editor = new QComboBox(parent);
editor->setFocusPolicy(Qt::StrongFocus); editor->setFocusPolicy(Qt::StrongFocus);
editor->addItem(tr("Ignored")); editor->addItem(tr("Ignored"));
@@ -128,6 +126,7 @@ class PropListDelegate: public QItemDelegate {
void setEditorData(QWidget *editor, const QModelIndex &index) const { void setEditorData(QWidget *editor, const QModelIndex &index) const {
unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt(); unsigned short val = index.model()->data(index, Qt::DisplayRole).toInt();
QComboBox *combobox = static_cast<QComboBox*>(editor); QComboBox *combobox = static_cast<QComboBox*>(editor);
qDebug("Set Editor data: Prio is %d", val);
switch(val){ switch(val){
case IGNORED: case IGNORED:
combobox->setCurrentIndex(0); combobox->setCurrentIndex(0);
@@ -156,28 +155,11 @@ class PropListDelegate: public QItemDelegate {
return textRect.size(); return textRect.size();
} }
bool onlyOneItem(const QModelIndex& index) const {
const QAbstractItemModel *model = index.model();
unsigned int nbRows = model->rowCount();
if(nbRows == 1) return true;
for(unsigned int i=0; i<nbRows; ++i){
if((unsigned int)index.row() == i) continue;
if(model->data(model->index(i, PRIORITY)).toInt()) return false;
}
return true;
}
public slots: public slots:
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const {
QComboBox *combobox = static_cast<QComboBox*>(editor); QComboBox *combobox = static_cast<QComboBox*>(editor);
int value = combobox->currentIndex(); int value = combobox->currentIndex();
qDebug("Setting combobox value in index: %d", value); qDebug("Setting combobox value in index: %d", value);
QString color;
if(value) {
color = QString::fromUtf8("green");
} else {
color = QString::fromUtf8("red");
}
unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt(); unsigned short old_val = index.model()->data(index, Qt::DisplayRole).toInt();
switch(value){ switch(value){
case 0: case 0:
@@ -185,20 +167,32 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(IGNORED)); model->setData(index, QVariant(IGNORED));
if(filteredFilesChanged != 0) if(filteredFilesChanged != 0)
*filteredFilesChanged = true; *filteredFilesChanged = true;
} else {
// XXX: hack to force the model to send the itemChanged() signal
model->setData(index, QVariant(NORMAL));
model->setData(index, QVariant(IGNORED));
} }
break; break;
case 1: case 1:
if(old_val != NORMAL){ // if(old_val != NORMAL){
// model->setData(index, QVariant(NORMAL));
// if(filteredFilesChanged != 0)
// *filteredFilesChanged = true;
// } else {
model->setData(index, QVariant(HIGH));
model->setData(index, QVariant(NORMAL)); model->setData(index, QVariant(NORMAL));
if(filteredFilesChanged != 0) if(filteredFilesChanged != 0)
*filteredFilesChanged = true; *filteredFilesChanged = true;
} // }
break; break;
case 2: case 2:
if(old_val != HIGH){ if(old_val != HIGH){
model->setData(index, QVariant(HIGH)); model->setData(index, QVariant(HIGH));
if(filteredFilesChanged != 0) if(filteredFilesChanged != 0)
*filteredFilesChanged = true; *filteredFilesChanged = true;
} else {
model->setData(index, QVariant(NORMAL));
model->setData(index, QVariant(HIGH));
} }
break; break;
case 3: case 3:
@@ -206,6 +200,9 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(MAXIMUM)); model->setData(index, QVariant(MAXIMUM));
if(filteredFilesChanged != 0) if(filteredFilesChanged != 0)
*filteredFilesChanged = true; *filteredFilesChanged = true;
} else {
model->setData(index, QVariant(HIGH));
model->setData(index, QVariant(MAXIMUM));
} }
break; break;
default: default:
@@ -213,16 +210,17 @@ class PropListDelegate: public QItemDelegate {
model->setData(index, QVariant(NORMAL)); model->setData(index, QVariant(NORMAL));
if(filteredFilesChanged != 0) if(filteredFilesChanged != 0)
*filteredFilesChanged = true; *filteredFilesChanged = true;
} else {
model->setData(index, QVariant(HIGH));
model->setData(index, QVariant(NORMAL));
} }
} }
for(int i=0; i<model->columnCount(); ++i){
model->setData(model->index(index.row(), i), QVariant(QColor(color)), Qt::ForegroundRole);
}
} }
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const {
editor->setGeometry(option.rect); editor->setGeometry(option.rect);
} }
}; };
#endif #endif

53
src/TrackersAdditionDlg.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef TRACKERSADDITION_H
#define TRACKERSADDITION_H
#include <QDialog>
#include <QStringList>
#include "ui_trackersAdd.h"
class TrackersAddDlg : public QDialog, private Ui::TrackersAdditionDlg{
Q_OBJECT
public:
TrackersAddDlg(QWidget *parent): QDialog(parent){
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
show();
}
~TrackersAddDlg(){}
signals:
void TrackersToAdd(QStringList trackers);
public slots:
void on_buttonBox_accepted() {
QStringList trackers = trackers_list->toPlainText().trimmed().split("\n");
if(trackers.size()) {
emit TrackersToAdd(trackers);
}
}
};
#endif

View File

@@ -44,9 +44,9 @@ class about : public QDialog, private Ui::AboutDlg{
// Thanks // Thanks
te_thanks->append(QString::fromUtf8("<a name='top'></a>")); te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
te_thanks->append(QString::fromUtf8("<ul><li>I would like to thank sourceforge.net for hosting qBittorrent project.</li>")); te_thanks->append(QString::fromUtf8("<ul><li>I would like to thank sourceforge.net for hosting qBittorrent project.</li>"));
te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière joined the project as a programmer. His help is greatly appreciated</li>")); te_thanks->append(QString::fromUtf8("<li>I am happy that Arnaud Demaizière is contributing to the project as a developer. His help is greatly appreciated</li>"));
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, webdevelopper and RPM packager, for his help.</li>")); te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, RPM packager, for his help and support.</li>"));
te_thanks->append(QString::fromUtf8("<li>I am gratefull to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>")); te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) who is helping port qBittorrent to Windows.</li>"));
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->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->scrollToAnchor(QString::fromUtf8("top")); te_thanks->scrollToAnchor(QString::fromUtf8("top"));
// Translation // Translation

View File

@@ -5,20 +5,29 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>440</width> <width>511</width>
<height>389</height> <height>441</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
<string>Torrent addition dialog</string> <string>Torrent addition dialog</string>
</property> </property>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>9</number>
</property>
<property name="topMargin" >
<number>9</number>
</property>
<property name="rightMargin" >
<number>9</number>
</property>
<property name="bottomMargin" >
<number>9</number>
</property>
<item> <item>
<widget class="QLabel" name="fileNameLbl" > <widget class="QLabel" name="fileNameLbl" >
<property name="text" > <property name="text" >
@@ -48,12 +57,21 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item> <item>
<widget class="QLineEdit" name="savePathTxt" /> <widget class="QLineEdit" name="savePathTxt" />
</item> </item>
@@ -108,12 +126,21 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item> <item>
<spacer> <spacer>
<property name="orientation" > <property name="orientation" >

View File

@@ -157,13 +157,13 @@ class BandwidthAllocationDialog : public QDialog, private Ui_bandwidth_dlg {
s->set_upload_rate_limit(-1); s->set_upload_rate_limit(-1);
else else
s->set_upload_rate_limit(val*1024); s->set_upload_rate_limit(val*1024);
settings.setValue(QString::fromUtf8("Options/Main/UPLimit"), val); settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalUPLimit"), val);
}else{ }else{
if(!val) if(!val)
s->set_download_rate_limit(-1); s->set_download_rate_limit(-1);
else else
s->set_download_rate_limit(val*1024); s->set_download_rate_limit(val*1024);
settings.setValue(QString::fromUtf8("Options/Main/DLLimit"), val); settings.setValue(QString::fromUtf8("Preferences/Connection/GlobalDLLimit"), val);
} }
} }
close(); close();

264
src/arborescence.h Normal file
View File

@@ -0,0 +1,264 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef ARBORESCENCE_H
#define ARBORESCENCE_H
#include <QFileInfo>
#include <QStringList>
#include <QDir>
#include "misc.h"
class torrent_file {
private:
torrent_file *parent;
bool is_dir;
QString rel_path;
QList<torrent_file*> children;
size_type size;
float progress;
int priority;
int index; // Index in torrent_info
public:
torrent_file(torrent_file *parent, QString path, bool dir, size_type size=0, int index=-1, float progress=0., int priority=1): parent(parent), is_dir(dir), size(size), progress(progress), priority(priority), index(index){
qDebug("created a file with index %d", index);
rel_path = QDir::cleanPath(path);
if(parent) {
parent->updateProgress();
parent->updatePriority(priority);
}
}
~torrent_file() {
qDeleteAll(children);
}
QString path() const {
return rel_path;
}
QString name() const {
return rel_path.split(QDir::separator()).last();
}
void updateProgress() {
Q_ASSERT(is_dir);
if(children.isEmpty()) {
progress = 0.;
return;
}
float wanted = 0.;
float done = 0.;
torrent_file *child;
foreach(child, children) {
wanted += child->getSize();
done += child->getSize()*child->getProgress();
}
progress = done / wanted;
Q_ASSERT(progress >= 0.);
Q_ASSERT(progress <= 1.);
}
void updatePriority(int prio) {
Q_ASSERT(is_dir);
torrent_file *child;
foreach(child, children) {
if(child->getPriority() != prio) return;
}
priority = prio;
}
int getPriority() const {
return priority;
}
size_type getSize() const {
return size;
}
float getProgress() const {
return progress;
}
int getIndex() const {
return index;
}
bool isDir() const {
return is_dir;
}
bool hasChildren() const {
return (!children.isEmpty());
}
QList<torrent_file*> getChildren() const {
return children;
}
torrent_file* getChild(QString fileName) const {
Q_ASSERT(is_dir);
torrent_file* f;
foreach(f, children) {
if(f->name() == fileName) return f;
}
return 0;
}
void addBytes(size_type b) {
size += b;
if(parent)
parent->addBytes(b);
}
torrent_file* addChild(QString fileName, bool dir, size_type size=0, int index = -1, float progress=0., int priority=1) {
Q_ASSERT(is_dir);
qDebug("Adding a new child of size: %ld", (long)size);
torrent_file *f = new torrent_file(this, QDir::cleanPath(rel_path+QDir::separator()+fileName), dir, size, index, progress, priority);
children << f;
if(size) {
addBytes(size);
}
return f;
}
bool removeFromFS(QString saveDir) {
QString full_path = saveDir + QDir::separator() + rel_path;
if(!QFile::exists(full_path)) {
qDebug("%s does not exist, no need to remove it", full_path.toUtf8().data());
return true;
}
bool success = true;
torrent_file *f;
qDebug("We have %d children", children.size());
foreach(f, children) {
bool s = f->removeFromFS(saveDir);
success = s && success;
}
if(is_dir) {
qDebug("trying to remove directory: %s", full_path.toUtf8().data());
QDir dir(full_path);
dir.rmdir(full_path);
} else {
qDebug("trying to remove file: %s", full_path.toUtf8().data());
bool s = QFile::remove(full_path);
success = s && success;
}
return success;
}
};
class arborescence {
private:
torrent_file *root;
public:
arborescence(torrent_info t) {
torrent_info::file_iterator fi = t.begin_files();
if(t.num_files() > 1) {
root = new torrent_file(0, misc::toQString(t.name()), true);
} else {
// XXX: Will crash if there is no file in torrent
root = new torrent_file(0, misc::toQString(t.name()), false, fi->size, 0);
return;
}
int i = 0;
while(fi != t.end_files()) {
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
addFile(path, fi->size, i);
fi++;
++i;
}
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
Q_ASSERT(root->getSize() == t.total_size());
}
arborescence(torrent_info t, std::vector<float> fp, int *prioritiesTab) {
torrent_info::file_iterator fi = t.begin_files();
if(t.num_files() > 1) {
qDebug("More than one file in the torrent, setting a folder as root");
root = new torrent_file(0, misc::toQString(t.name()), true);
} else {
// XXX: Will crash if there is no file in torrent
qDebug("one file in the torrent, setting it as root with index 0");
root = new torrent_file(0, misc::toQString(t.name()), false, fi->size, 0, fp[0], prioritiesTab[0]);
return;
}
int i = 0;
while(fi != t.end_files()) {
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
addFile(path, fi->size, i, fp[i], prioritiesTab[i]);
fi++;
++i;
}
qDebug("real size: %ld, tree size: %ld", (long)t.total_size(), (long)root->getSize());
Q_ASSERT(root->getSize() == t.total_size());
}
~arborescence() {
delete root;
}
torrent_file* getRoot() const {
return root;
}
bool removeFromFS(QString saveDir) {
if(!QFile::exists(saveDir+QDir::separator()+root->path())) return true;
bool success = root->removeFromFS(saveDir);
QDir root_dir(root->path());
root_dir.rmdir(saveDir+QDir::separator()+root->path());
return success;
}
protected:
void addFile(QString path, size_type file_size, int index, float progress=0., int priority=1) {
Q_ASSERT(root->isDir());
path = QDir::cleanPath(path);
Q_ASSERT(path.startsWith(root->path()));
QString relative_path = path.remove(0, root->path().size());
if(relative_path.at(0) ==QDir::separator())
relative_path.remove(0, 1);
QStringList fileNames = relative_path.split(QDir::separator());
QString fileName;
torrent_file *dad = root;
unsigned int nb_i = 0;
unsigned int size = fileNames.size();
foreach(fileName, fileNames) {
++nb_i;
if(fileName == ".") continue;
torrent_file* child = dad->getChild(fileName);
if(!child) {
if(nb_i != size) {
// Folder
child = dad->addChild(fileName, true);
} else {
// File
child = dad->addChild(fileName, false, file_size, index, progress, priority);
}
}
dad = child;
}
}
};
#endif

View File

@@ -23,6 +23,13 @@
#include <QTime> #include <QTime>
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
#include <QSettings>
#include "bittorrent.h"
#include "misc.h"
#include "downloadThread.h"
#include "deleteThread.h"
#include "filterParserThread.h"
#include <libtorrent/extensions/metadata_transfer.hpp> #include <libtorrent/extensions/metadata_transfer.hpp>
#include <libtorrent/extensions/ut_pex.hpp> #include <libtorrent/extensions/ut_pex.hpp>
@@ -30,59 +37,107 @@
#include <libtorrent/bencode.hpp> #include <libtorrent/bencode.hpp>
#include <libtorrent/identify_client.hpp> #include <libtorrent/identify_client.hpp>
#include <libtorrent/alert_types.hpp> #include <libtorrent/alert_types.hpp>
#include <libtorrent/ip_filter.hpp>
#include <libtorrent/torrent_info.hpp> #include <libtorrent/torrent_info.hpp>
#include <boost/filesystem/exception.hpp> #include <boost/filesystem/exception.hpp>
#include "bittorrent.h"
#include "misc.h"
#include "downloadThread.h"
#include "deleteThread.h"
#define ETAS_MAX_VALUES 3
#define ETA_REFRESH_INTERVAL 10000
#define MAX_TRACKER_ERRORS 2 #define MAX_TRACKER_ERRORS 2
// Main constructor // Main constructor
bittorrent::bittorrent() : timerScan(0), DHTEnabled(false){ bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false), addInPause(false), maxConnecsPerTorrent(500), maxUploadsPerTorrent(4), max_ratio(-1), UPnPEnabled(false), NATPMPEnabled(false), LSDEnabled(false), folderScanInterval(5) {
// To avoid some exceptions // To avoid some exceptions
fs::path::default_name_check(fs::no_check); fs::path::default_name_check(fs::no_check);
// Creating bittorrent session // Creating bittorrent session
// Check if we should spoof azureus
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
if(settings.value(QString::fromUtf8("AzureusSpoof"), false).toBool()) {
s = new session(fingerprint("AZ", 3, 0, 5, 2));
} else {
s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0)); s = new session(fingerprint("qB", VERSION_MAJOR, VERSION_MINOR, VERSION_BUGFIX, 0));
}
// Set severity level of libtorrent session // Set severity level of libtorrent session
s->set_severity_level(alert::info); s->set_severity_level(alert::info);
// Enable LSD/UPnP/NAT-PMP
s->start_lsd();
s->start_natpmp();
s->start_upnp();
// Enabling metadata plugin // Enabling metadata plugin
s->add_extension(&create_metadata_plugin); s->add_extension(&create_metadata_plugin);
timerAlerts = new QTimer(); timerAlerts = new QTimer();
connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts())); connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts()));
timerAlerts->start(3000); timerAlerts->start(3000);
ETARefresher = new QTimer(); fastResumeSaver = new QTimer();
connect(ETARefresher, SIGNAL(timeout()), this, SLOT(updateETAs())); connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioData()));
ETARefresher->start(ETA_REFRESH_INTERVAL); fastResumeSaver->start(60000);
// To download from urls // To download from urls
downloader = new downloadThread(this); downloader = new downloadThread(this);
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString))); connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
// File deleter (thread) // File deleter (thread)
deleter = new deleteThread(this); deleter = new deleteThread(this);
BigRatioTimer = 0;
filterParser = 0;
qDebug("* BTSession constructed"); qDebug("* BTSession constructed");
} }
// Main destructor // Main destructor
bittorrent::~bittorrent() { bittorrent::~bittorrent() {
qDebug("BTSession deletion");
// Set Session settings
session_settings ss;
ss.tracker_receive_timeout = 1;
ss.stop_tracker_timeout = 1;
ss.tracker_completion_timeout = 1;
ss.piece_timeout = 1;
ss.peer_timeout = 1;
ss.urlseed_timeout = 1;
s->set_settings(ss);
// Disable directory scanning // Disable directory scanning
disableDirectoryScanning(); disableDirectoryScanning();
// Delete our objects // Delete our objects
delete deleter; delete deleter;
delete fastResumeSaver;
delete timerAlerts; delete timerAlerts;
delete ETARefresher; if(BigRatioTimer != 0)
delete BigRatioTimer;
if(filterParser != 0)
delete filterParser;
delete downloader; delete downloader;
// Delete BT session // Delete BT session
qDebug("Deleting session");
delete s; delete s;
qDebug("Session deleted");
}
void bittorrent::preAllocateAllFiles(bool b) {
bool change = (preAllocateAll != b);
if(change) {
qDebug("PreAllocateAll changed, reloading all torrents!");
preAllocateAll = b;
// Reload All unfinished torrents
QString hash;
foreach(hash, unfinishedTorrents) {
QTorrentHandle h = getTorrentHandle(hash);
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
reloadTorrent(h, b);
}
}
}
void bittorrent::deleteBigRatios() {
if(max_ratio == -1) return;
QString hash;
foreach(hash, finishedTorrents) {
QTorrentHandle h = getTorrentHandle(hash);
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
QString hash = h.hash();
if(getRealRatio(hash) > max_ratio) {
QString fileName = h.name();
deleteTorrent(hash);
emit torrent_ratio_deleted(fileName);
}
}
} }
void bittorrent::setDownloadLimit(QString hash, long val) { void bittorrent::setDownloadLimit(QString hash, long val) {
@@ -104,36 +159,38 @@ void bittorrent::handleDownloadFailure(QString url, QString reason) {
emit downloadFromUrlFailure(url, reason); emit downloadFromUrlFailure(url, reason);
} }
void bittorrent::updateETAs() { void bittorrent::startTorrentsInPause(bool b) {
QString hash; addInPause = b;
foreach(hash, unfinishedTorrents) {
QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid()) {
if(h.is_paused()) continue;
QString hash = h.hash();
QList<qlonglong> listEtas = ETAstats.value(hash, QList<qlonglong>());
if(listEtas.size() == ETAS_MAX_VALUES) {
listEtas.removeFirst();
}
if(h.download_payload_rate()) {
listEtas << (qlonglong)((h.actual_size()-h.total_done())/(double)h.download_payload_rate());
ETAstats[hash] = listEtas;
long moy = 0;
long val;
unsigned int nbETAs = listEtas.size();
Q_ASSERT(nbETAs);
foreach(val, listEtas) {
moy += (qlonglong)((double)val/(double)nbETAs);
Q_ASSERT(moy >= 0);
}
ETAs[hash] = moy;
}
}
}
} }
long bittorrent::getETA(QString hash) const{ // Calculate the ETA using GASA
return ETAs.value(hash, -1); // GASA: global Average Speed Algorithm
qlonglong bittorrent::getETA(QString hash) const {
QTorrentHandle h = getTorrentHandle(hash);
if(!h.is_valid()) return -1;
switch(h.state()) {
case torrent_status::downloading:
case torrent_status::connecting_to_tracker: {
if(!TorrentsStartTime.contains(hash)) return -1;
int timeElapsed = TorrentsStartTime.value(hash).secsTo(QDateTime::currentDateTime());
double avg_speed;
if(timeElapsed) {
size_type data_origin = TorrentsStartData.value(hash, 0);
avg_speed = (double)(h.total_payload_download()-data_origin) / (double)timeElapsed;
} else {
return -1;
}
if(avg_speed) {
return (qlonglong) floor((double) (h.actual_size() - h.total_wanted_done()) / avg_speed);
} else {
return -1;
}
}
default:
return -1;
}
} }
// Return the torrent handle, given its hash // Return the torrent handle, given its hash
@@ -154,6 +211,26 @@ bool bittorrent::isPaused(QString hash) const{
return h.is_paused(); return h.is_paused();
} }
unsigned int bittorrent::getFinishedPausedTorrentsNb() const {
unsigned int nbPaused = 0;
foreach(QString hash, finishedTorrents) {
if(isPaused(hash)) {
++nbPaused;
}
}
return nbPaused;
}
unsigned int bittorrent::getUnfinishedPausedTorrentsNb() const {
unsigned int nbPaused = 0;
foreach(QString hash, unfinishedTorrents) {
if(isPaused(hash)) {
++nbPaused;
}
}
return nbPaused;
}
// Delete a torrent from the session, given its hash // Delete a torrent from the session, given its hash
// permanent = true means that the torrent will be removed from the hard-drive too // permanent = true means that the torrent will be removed from the hard-drive too
void bittorrent::deleteTorrent(QString hash, bool permanent) { void bittorrent::deleteTorrent(QString hash, bool permanent) {
@@ -165,9 +242,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
} }
QString savePath = h.save_path(); QString savePath = h.save_path();
QString fileName = h.name(); QString fileName = h.name();
QStringList files_path; arborescence *files_arb = 0;
if(permanent){ if(permanent){
files_path = h.files_path(); files_arb = new arborescence(h.get_torrent_info());
} }
// Remove it from session // Remove it from session
s->remove_torrent(h.get_torrent_handle()); s->remove_torrent(h.get_torrent_handle());
@@ -180,9 +257,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
foreach(file, files) { foreach(file, files) {
torrentBackup.remove(file); torrentBackup.remove(file);
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
// Remove tracker errors // Remove tracker errors
trackersErrors.remove(hash); trackersErrors.remove(hash);
// Remove it from ratio table // Remove it from ratio table
@@ -198,12 +275,13 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n"; std::cerr << "Error: Torrent " << hash.toStdString() << " is neither in finished or unfinished list\n";
} }
} }
if(permanent) { if(permanent && files_arb != 0) {
// Remove from Hard drive // Remove from Hard drive
qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName)); qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName));
// Deleting in a thread to avoid GUI freeze // Deleting in a thread to avoid GUI freeze
deleter->deleteTorrent(savePath, files_path); deleter->deleteTorrent(savePath, files_arb);
} }
emit deletedTorrent(hash);
} }
// Return a list of hashes for the finished torrents // Return a list of hashes for the finished torrents
@@ -230,6 +308,9 @@ void bittorrent::setUnfinishedTorrent(QString hash) {
} }
if(!unfinishedTorrents.contains(hash)) { if(!unfinishedTorrents.contains(hash)) {
unfinishedTorrents << hash; unfinishedTorrents << hash;
QTorrentHandle h = getTorrentHandle(hash);
TorrentsStartData[hash] = h.total_payload_download();
TorrentsStartTime[hash] = QDateTime::currentDateTime();
} }
} }
@@ -247,9 +328,11 @@ void bittorrent::setFinishedTorrent(QString hash) {
if(index != -1) { if(index != -1) {
unfinishedTorrents.removeAt(index); unfinishedTorrents.removeAt(index);
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
// Save fast resume data
saveFastResumeAndRatioData(hash);
} }
// Pause a running torrent // Pause a running torrent
@@ -259,7 +342,10 @@ bool bittorrent::pauseTorrent(QString hash) {
if(h.is_valid() && !h.is_paused()) { if(h.is_valid() && !h.is_paused()) {
h.pause(); h.pause();
change = true; change = true;
// Save fast resume data
saveFastResumeAndRatioData(hash);
qDebug("Torrent paused successfully"); qDebug("Torrent paused successfully");
emit pausedTorrent(hash);
}else{ }else{
if(!h.is_valid()) { if(!h.is_valid()) {
qDebug("Could not pause torrent %s, reason: invalid", hash.toUtf8().data()); qDebug("Could not pause torrent %s, reason: invalid", hash.toUtf8().data());
@@ -273,9 +359,9 @@ bool bittorrent::pauseTorrent(QString hash) {
paused_file.open(QIODevice::WriteOnly | QIODevice::Text); paused_file.open(QIODevice::WriteOnly | QIODevice::Text);
paused_file.close(); paused_file.close();
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
return change; return change;
} }
@@ -284,8 +370,12 @@ bool bittorrent::resumeTorrent(QString hash) {
bool success = false; bool success = false;
QTorrentHandle h = getTorrentHandle(hash); QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid() && h.is_paused()) { if(h.is_valid() && h.is_paused()) {
// Save Addition DateTime
TorrentsStartData[hash] = h.total_payload_download();
TorrentsStartTime[hash] = QDateTime::currentDateTime();
h.resume(); h.resume();
success = true; success = true;
emit resumedTorrent(hash);
} }
// Delete .paused file // Delete .paused file
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused"))
@@ -298,6 +388,18 @@ bool bittorrent::resumeTorrent(QString hash) {
return success; return success;
} }
void bittorrent::pauseAllTorrents() {
QStringList list = getUnfinishedTorrents() + getFinishedTorrents();
foreach(QString hash, list)
pauseTorrent(hash);
}
void bittorrent::resumeAllTorrents() {
QStringList list = getUnfinishedTorrents() + getFinishedTorrents();
foreach(QString hash, list)
resumeTorrent(hash);
}
void bittorrent::loadWebSeeds(QString hash) { void bittorrent::loadWebSeeds(QString hash) {
QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".urlseeds"); QFile urlseeds_file(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".urlseeds");
if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return; if(!urlseeds_file.open(QIODevice::ReadOnly | QIODevice::Text)) return;
@@ -330,7 +432,7 @@ void bittorrent::loadWebSeeds(QString hash) {
} }
// Add a torrent to the bittorrent session // Add a torrent to the bittorrent session
void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) { void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url, bool resumed) {
QTorrentHandle h; QTorrentHandle h;
entry resume_data; entry resume_data;
bool fastResume=false; bool fastResume=false;
@@ -346,11 +448,11 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
} }
} }
// Processing torrents // Processing torrents
file = path.trimmed().replace("file://", ""); file = path.trimmed().replace("file://", "", Qt::CaseInsensitive);
if(file.isEmpty()) { if(file.isEmpty()) {
return; return;
} }
Q_ASSERT(!file.startsWith("http://") && !file.startsWith("https://") && !file.startsWith("ftp://")); Q_ASSERT(!file.startsWith("http://", Qt::CaseInsensitive) && !file.startsWith("https://", Qt::CaseInsensitive) && !file.startsWith("ftp://", Qt::CaseInsensitive));
qDebug("Adding %s to download list", file.toUtf8().data()); qDebug("Adding %s to download list", file.toUtf8().data());
std::ifstream in(file.toUtf8().data(), std::ios_base::binary); std::ifstream in(file.toUtf8().data(), std::ios_base::binary);
in.unsetf(std::ios_base::skipws); in.unsetf(std::ios_base::skipws);
@@ -358,27 +460,18 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
// Decode torrent file // Decode torrent file
entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>()); entry e = bdecode(std::istream_iterator<char>(in), std::istream_iterator<char>());
// Getting torrent file informations // Getting torrent file informations
torrent_info t(e); boost::intrusive_ptr<torrent_info> t(new torrent_info(e));
qDebug(" -> Hash: %s", misc::toString(t.info_hash()).c_str()); qDebug(" -> Hash: %s", misc::toString(t->info_hash()).c_str());
qDebug(" -> Name: %s", t.name().c_str()); qDebug(" -> Name: %s", t->name().c_str());
QString hash = misc::toQString(t.info_hash()); QString hash = misc::toQString(t->info_hash());
if(file.startsWith(torrentBackup.path())) { if(file.startsWith(torrentBackup.path())) {
QFileInfo fi(file); QFileInfo fi(file);
QString old_hash = fi.baseName(); QString old_hash = fi.baseName();
if(old_hash != hash){ if(old_hash != hash){
qDebug("* ERROR: Strange, hash changed from %s to %s", old_hash.toUtf8().data(), hash.toUtf8().data()); qDebug("* ERROR: Strange, hash changed from %s to %s", old_hash.toUtf8().data(), hash.toUtf8().data());
// QStringList filters;
// filters << old_hash+".*";
// QStringList files = torrentBackup.entryList(filters, QDir::Files, QDir::Unsorted);
// QString my_f;
// foreach(my_f, files) {
// qDebug("* deleting %s", my_f.toUtf8().data());
// torrentBackup.remove(my_f);
// }
// return;
} }
} }
if(s->find_torrent(t.info_hash()).is_valid()) { if(s->find_torrent(t->info_hash()).is_valid()) {
qDebug("/!\\ Torrent is already in download list"); qDebug("/!\\ Torrent is already in download list");
// Update info Bar // Update info Bar
if(!fromScanDir) { if(!fromScanDir) {
@@ -409,12 +502,12 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
} }
QString savePath = getSavePath(hash); QString savePath = getSavePath(hash);
// Adding files to bittorrent session // Adding files to bittorrent session
if(has_filtered_files(hash)) { if(preAllocateAll) {
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, false, true); h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, storage_mode_allocate, true);
qDebug(" -> Full allocation mode"); qDebug(" -> Full allocation mode");
}else{ }else{
h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, true, true); h = s->add_torrent(t, fs::path(savePath.toUtf8().data()), resume_data, storage_mode_sparse, true);
qDebug(" -> Compact allocation mode"); qDebug(" -> Sparse allocation mode");
} }
if(!h.is_valid()) { if(!h.is_valid()) {
// No need to keep on, it failed. // No need to keep on, it failed.
@@ -423,9 +516,10 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
if(!from_url.isNull()) QFile::remove(file); if(!from_url.isNull()) QFile::remove(file);
return; return;
} }
// Is this really useful and appropriate ? // Connections limit per torrent
//h.set_max_connections(60); h.set_max_connections(maxConnecsPerTorrent);
h.set_max_uploads(-1); // Uploads limit per torrent
h.set_max_uploads(maxUploadsPerTorrent);
// Load filtered files // Load filtered files
loadFilesPriorities(h); loadFilesPriorities(h);
// Load custom url seeds // Load custom url seeds
@@ -450,13 +544,13 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
QFile::copy(file, newFile); QFile::copy(file, newFile);
} }
// Pause torrent if it was paused last time // Pause torrent if it was paused last time
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) { if((!resumed && addInPause) || QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".paused")) {
torrentsToPauseAfterChecking << hash; torrentsToPauseAfterChecking << hash;
qDebug("Adding a torrent to the torrentsToPauseAfterChecking list"); qDebug("Adding a torrent to the torrentsToPauseAfterChecking list");
} }
// Incremental download // Incremental download
if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".incremental")) { if(QFile::exists(misc::qBittorrentPath()+"BT_backup"+QDir::separator()+hash+".incremental")) {
qDebug("Incremental download enabled for %s", t.name().c_str()); qDebug("Incremental download enabled for %s", t->name().c_str());
h.set_sequenced_download_threshold(1); h.set_sequenced_download_threshold(1);
} }
// Start torrent because it was added in paused state // Start torrent because it was added in paused state
@@ -483,6 +577,7 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
// Display warning to tell user we can't decode the torrent file // Display warning to tell user we can't decode the torrent file
if(!from_url.isNull()) { if(!from_url.isNull()) {
emit invalidTorrent(from_url); emit invalidTorrent(from_url);
QFile::remove(file);
}else{ }else{
emit invalidTorrent(file); emit invalidTorrent(file);
} }
@@ -497,6 +592,24 @@ void bittorrent::addTorrent(QString path, bool fromScanDir, QString from_url) {
// Display warning to tell user we can't decode the torrent file // Display warning to tell user we can't decode the torrent file
if(!from_url.isNull()) { if(!from_url.isNull()) {
emit invalidTorrent(from_url); emit invalidTorrent(from_url);
qDebug("File path is: %s", file.toUtf8().data());
QFile::remove(file);
}else{
emit invalidTorrent(file);
}
if(fromScanDir) {
// Remove .corrupt file in case it already exists
QFile::remove(file+".corrupt");
//Rename file extension so that it won't display error message more than once
QFile::rename(file,file+".corrupt");
}
}
catch (std::exception& e) {
std::cerr << "Could not decode file, reason: " << e.what() << '\n';
// Display warning to tell user we can't decode the torrent file
if(!from_url.isNull()) {
emit invalidTorrent(from_url);
QFile::remove(file);
}else{ }else{
emit invalidTorrent(file); emit invalidTorrent(file);
} }
@@ -538,13 +651,92 @@ void bittorrent::setMaxConnections(int maxConnec) {
s->set_max_connections(maxConnec); s->set_max_connections(maxConnec);
} }
void bittorrent::setMaxConnectionsPerTorrent(int max) {
maxConnecsPerTorrent = max;
// Apply this to all session torrents
std::vector<torrent_handle> handles = s->get_torrents();
unsigned int nbHandles = handles.size();
for(unsigned int i=0; i<nbHandles; ++i) {
QTorrentHandle h = handles[i];
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
h.set_max_connections(max);
}
}
void bittorrent::setMaxUploadsPerTorrent(int max) {
maxUploadsPerTorrent = max;
// Apply this to all session torrents
std::vector<torrent_handle> handles = s->get_torrents();
unsigned int nbHandles = handles.size();
for(unsigned int i=0; i<nbHandles; ++i) {
QTorrentHandle h = handles[i];
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
h.set_max_uploads(max);
}
}
// Return DHT state // Return DHT state
bool bittorrent::isDHTEnabled() const{ bool bittorrent::isDHTEnabled() const{
return DHTEnabled; return DHTEnabled;
} }
void bittorrent::enableUPnP(bool b) {
if(b) {
if(!UPnPEnabled) {
qDebug("Enabling UPnP");
s->start_upnp();
UPnPEnabled = true;
}
} else {
if(UPnPEnabled) {
qDebug("Disabling UPnP");
s->stop_upnp();
UPnPEnabled = false;
}
}
}
void bittorrent::enableNATPMP(bool b) {
if(b) {
if(!NATPMPEnabled) {
qDebug("Enabling NAT-PMP");
s->start_natpmp();
NATPMPEnabled = true;
}
} else {
if(NATPMPEnabled) {
qDebug("Disabling NAT-PMP");
s->stop_natpmp();
NATPMPEnabled = false;
}
}
}
void bittorrent::enableLSD(bool b) {
if(b) {
if(!LSDEnabled) {
qDebug("Enabling LSD");
s->start_lsd();
LSDEnabled = true;
}
} else {
if(LSDEnabled) {
qDebug("Disabling LSD");
s->stop_lsd();
LSDEnabled = false;
}
}
}
// Enable DHT // Enable DHT
void bittorrent::enableDHT() { bool bittorrent::enableDHT(bool b) {
if(b) {
if(!DHTEnabled) { if(!DHTEnabled) {
boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary); boost::filesystem::ifstream dht_state_file((misc::qBittorrentPath()+QString::fromUtf8("dht_state")).toUtf8().data(), std::ios_base::binary);
dht_state_file.unsetf(std::ios_base::skipws); dht_state_file.unsetf(std::ios_base::skipws);
@@ -552,22 +744,26 @@ void bittorrent::enableDHT() {
try{ try{
dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>()); dht_state = bdecode(std::istream_iterator<char>(dht_state_file), std::istream_iterator<char>());
}catch (std::exception&) {} }catch (std::exception&) {}
try {
s->start_dht(dht_state); s->start_dht(dht_state);
s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881)); s->add_dht_router(std::make_pair(std::string("router.bittorrent.com"), 6881));
s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881)); s->add_dht_router(std::make_pair(std::string("router.utorrent.com"), 6881));
s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881)); s->add_dht_router(std::make_pair(std::string("router.bitcomet.com"), 6881));
DHTEnabled = true; DHTEnabled = true;
qDebug("DHT enabled"); qDebug("DHT enabled");
}catch(std::exception e) {
qDebug("Could not enable DHT, reason: %s", e.what());
return false;
} }
} }
} else {
// Disable DHT
void bittorrent::disableDHT() {
if(DHTEnabled) { if(DHTEnabled) {
DHTEnabled = false; DHTEnabled = false;
s->stop_dht(); s->stop_dht();
qDebug("DHT disabled"); qDebug("DHT disabled");
} }
}
return true;
} }
void bittorrent::saveTorrentSpeedLimits(QString hash) { void bittorrent::saveTorrentSpeedLimits(QString hash) {
@@ -605,6 +801,7 @@ void bittorrent::loadTorrentSpeedLimits(QString hash) {
// Read pieces priorities from .priorities file // Read pieces priorities from .priorities file
// and ask QTorrentHandle to consider them // and ask QTorrentHandle to consider them
void bittorrent::loadFilesPriorities(QTorrentHandle &h) { void bittorrent::loadFilesPriorities(QTorrentHandle &h) {
qDebug("Applying pieces priorities");
if(!h.is_valid()) { if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle"); qDebug("/!\\ Error: Invalid handle");
return; return;
@@ -633,7 +830,7 @@ void bittorrent::loadFilesPriorities(QTorrentHandle &h) {
if( priority < 0 || priority > 7) { if( priority < 0 || priority > 7) {
priority = 1; priority = 1;
} }
//qDebug("Setting piece piority to %d", priority); qDebug("Setting piece piority to %d", priority);
v.push_back(priority); v.push_back(priority);
} }
h.prioritize_files(v); h.prioritize_files(v);
@@ -660,7 +857,11 @@ void bittorrent::loadDownloadUploadForTorrent(QString hash) {
QPair<size_type,size_type> downUp; QPair<size_type,size_type> downUp;
downUp.first = (size_type)data_list.at(0).toLongLong(); downUp.first = (size_type)data_list.at(0).toLongLong();
downUp.second = (size_type)data_list.at(1).toLongLong(); downUp.second = (size_type)data_list.at(1).toLongLong();
Q_ASSERT(downUp.first >= 0 && downUp.second >= 0); if(downUp.first < 0 || downUp.second < 0) {
qDebug("** Overflow in ratio!!! fixing...");
downUp.first = 0;
downUp.second = 0;
}
ratioData[hash] = downUp; ratioData[hash] = downUp;
} }
@@ -715,10 +916,26 @@ void bittorrent::saveDownloadUploadForTorrent(QString hash) {
ratio_file.close(); ratio_file.close();
} }
// Save fastresume data for all torrents // Only save fast resume data for unfinished and unpaused torrents (Optimization)
// and remove them from the session // Called periodically and on exit
void bittorrent::saveFastResumeAndRatioData() { void bittorrent::saveFastResumeAndRatioData() {
qDebug("Saving fast resume and ratio data"); QString hash;
QStringList hashes = getUnfinishedTorrents();
foreach(hash, hashes) {
QTorrentHandle h = getTorrentHandle(hash);
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
if(h.is_paused()) {
// Do not need to save fast resume data for paused torrents
continue;
}
saveFastResumeAndRatioData(hash);
}
}
void bittorrent::saveFastResumeAndRatioData(QString hash) {
QString file; QString file;
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup"); QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
// Checking if torrentBackup Dir exists // Checking if torrentBackup Dir exists
@@ -726,35 +943,13 @@ void bittorrent::saveFastResumeAndRatioData() {
if(! torrentBackup.exists()) { if(! torrentBackup.exists()) {
torrentBackup.mkpath(torrentBackup.path()); torrentBackup.mkpath(torrentBackup.path());
} }
// Pause torrents
std::vector<torrent_handle> handles = s->get_torrents();
for(unsigned int i=0; i<handles.size(); ++i) {
QTorrentHandle h = handles[i];
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
// Pause download (needed before fast resume writing)
if(!h.is_paused()){
waitingForPause << h.hash();
h.pause();
}
}
// Write fast resume data
for(unsigned int i=0; i<handles.size(); ++i) {
QTorrentHandle h = handles[i];
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
continue;
}
QString hash = h.hash();
while(waitingForPause.contains(hash)) {
//qDebug("Sleeping while waiting that %s is paused", misc::toString(h.info_hash()).c_str());
SleeperThread::msleep(300);
readAlerts();
}
// Extracting resume data // Extracting resume data
if (h.has_metadata()) { QTorrentHandle h = getTorrentHandle(hash);
if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle");
return;
}
if (h.has_metadata() && h.state() != torrent_status::checking_files && h.state() != torrent_status::queued_for_checking) {
if(QFile::exists(torrentBackup.path()+QDir::separator()+hash+".torrent")) { if(QFile::exists(torrentBackup.path()+QDir::separator()+hash+".torrent")) {
// Remove old .fastresume data in case it exists // Remove old .fastresume data in case it exists
QFile::remove(torrentBackup.path()+QDir::separator()+hash + ".fastresume"); QFile::remove(torrentBackup.path()+QDir::separator()+hash + ".fastresume");
@@ -767,13 +962,7 @@ void bittorrent::saveFastResumeAndRatioData() {
} }
// Save ratio data // Save ratio data
saveDownloadUploadForTorrent(hash); saveDownloadUploadForTorrent(hash);
// Save trackers
saveTrackerFile(hash);
} }
// Remove torrent
s->remove_torrent(h.get_torrent_handle());
}
qDebug("Fast resume and ratio data saved");
} }
bool bittorrent::isFilePreviewPossible(QString hash) const{ bool bittorrent::isFilePreviewPossible(QString hash) const{
@@ -816,13 +1005,22 @@ void bittorrent::setDefaultSavePath(QString savepath) {
defaultSavePath = savepath; defaultSavePath = savepath;
} }
void bittorrent::setTimerScanInterval(int secs) {
if(folderScanInterval != secs) {
folderScanInterval = secs;
if(!scan_dir.isNull()) {
timerScan->start(folderScanInterval*1000);
}
}
}
// Enable directory scanning // Enable directory scanning
void bittorrent::enableDirectoryScanning(QString _scan_dir) { void bittorrent::enableDirectoryScanning(QString _scan_dir) {
if(!_scan_dir.isEmpty()) { if(!_scan_dir.isEmpty()) {
scan_dir = _scan_dir; scan_dir = _scan_dir;
timerScan = new QTimer(this); timerScan = new QTimer(this);
connect(timerScan, SIGNAL(timeout()), this, SLOT(scanDirectory())); connect(timerScan, SIGNAL(timeout()), this, SLOT(scanDirectory()));
timerScan->start(5000); timerScan->start(folderScanInterval*1000);
} }
} }
@@ -865,6 +1063,11 @@ void bittorrent::setUploadRateLimit(long rate) {
// libtorrent allow to adjust ratio for each torrent // libtorrent allow to adjust ratio for each torrent
// This function will apply to same ratio to all torrents // This function will apply to same ratio to all torrents
void bittorrent::setGlobalRatio(float ratio) { void bittorrent::setGlobalRatio(float ratio) {
if(ratio != -1 && ratio < 1.) ratio = 1.;
if(ratio == -1) {
// 0 means unlimited for libtorrent
ratio = 0;
}
std::vector<torrent_handle> handles = s->get_torrents(); std::vector<torrent_handle> handles = s->get_torrents();
unsigned int nbHandles = handles.size(); unsigned int nbHandles = handles.size();
for(unsigned int i=0; i<nbHandles; ++i) { for(unsigned int i=0; i<nbHandles; ++i) {
@@ -877,6 +1080,28 @@ void bittorrent::setGlobalRatio(float ratio) {
} }
} }
// Torrents will a ratio superior to the given value will
// be automatically deleted
void bittorrent::setDeleteRatio(float ratio) {
if(ratio != -1 && ratio < 1.) ratio = 1.;
if(max_ratio == -1 && ratio != -1) {
Q_ASSERT(!BigRatioTimer);
BigRatioTimer = new QTimer(this);
connect(BigRatioTimer, SIGNAL(timeout()), this, SLOT(deleteBigRatios()));
BigRatioTimer->start(5000);
} else {
if(max_ratio != -1 && ratio == -1) {
delete BigRatioTimer;
BigRatioTimer = 0;
}
}
if(max_ratio != ratio) {
max_ratio = ratio;
qDebug("* Set deleteRatio to %.1f", max_ratio);
deleteBigRatios();
}
}
bool bittorrent::loadTrackerFile(QString hash) { bool bittorrent::loadTrackerFile(QString hash) {
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup"); QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers"); QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
@@ -895,6 +1120,7 @@ bool bittorrent::loadTrackerFile(QString hash) {
if(trackers.size() != 0) { if(trackers.size() != 0) {
QTorrentHandle h = getTorrentHandle(hash); QTorrentHandle h = getTorrentHandle(hash);
h.replace_trackers(trackers); h.replace_trackers(trackers);
h.force_reannounce();
return true; return true;
}else{ }else{
return false; return false;
@@ -902,6 +1128,7 @@ bool bittorrent::loadTrackerFile(QString hash) {
} }
void bittorrent::saveTrackerFile(QString hash) { void bittorrent::saveTrackerFile(QString hash) {
qDebug("Saving tracker file for %s", hash.toUtf8().data());
QDir torrentBackup(misc::qBittorrentPath() + "BT_backup"); QDir torrentBackup(misc::qBittorrentPath() + "BT_backup");
QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers"); QFile tracker_file(torrentBackup.path()+QDir::separator()+ hash + ".trackers");
if(tracker_file.exists()) { if(tracker_file.exists()) {
@@ -933,15 +1160,25 @@ void bittorrent::setDHTPort(int dht_port) {
} }
// Enable IP Filtering // Enable IP Filtering
void bittorrent::enableIPFilter(ip_filter filter) { void bittorrent::enableIPFilter(QString filter) {
qDebug("Enabling IPFiler"); qDebug("Enabling IPFiler");
s->set_ip_filter(filter); if(!filterParser) {
filterParser = new FilterParserThread(this, s);
}
if(filterPath.isEmpty() || filterPath != filter) {
filterPath = filter;
filterParser->processFilterFile(filter);
}
} }
// Disable IP Filtering // Disable IP Filtering
void bittorrent::disableIPFilter() { void bittorrent::disableIPFilter() {
qDebug("Disabling IPFilter"); qDebug("Disabling IPFilter");
s->set_ip_filter(ip_filter()); s->set_ip_filter(ip_filter());
if(filterParser) {
delete filterParser;
}
filterPath = "";
} }
// Set BT session settings (user_agent) // Set BT session settings (user_agent)
@@ -980,6 +1217,7 @@ void bittorrent::readAlerts() {
} }
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) { else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
qDebug("File Error: %s", p->msg().c_str());
if(h.is_valid()) if(h.is_valid())
emit fullDiskError(h); emit fullDiskError(h);
} }
@@ -991,33 +1229,36 @@ void bittorrent::readAlerts() {
// Level: fatal // Level: fatal
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if(h.is_valid()){ if(h.is_valid()){
QString hash = h.hash();
QList<QPair<QString, QString> > errors = trackersErrors.value(hash, QList<QPair<QString, QString> >());
if(errors.size() > MAX_TRACKER_ERRORS)
errors.removeAt(0);
errors << QPair<QString,QString>(QTime::currentTime().toString("hh:mm:ss"), QString::fromUtf8(a->msg().c_str()));
trackersErrors[hash] = errors;
// Authentication // Authentication
if(p->status_code == 401) { if(p->status_code != 401) {
QString hash = h.hash();
qDebug("Received a tracker error for %s", p->url.c_str());
QHash<QString, QString> errors = trackersErrors.value(hash, QHash<QString, QString>());
// p->url requires at least libtorrent v0.13.1
errors[misc::toQString(p->url)] = QString::fromUtf8(a->msg().c_str());
trackersErrors[hash] = errors;
} else {
emit trackerAuthenticationRequired(h); emit trackerAuthenticationRequired(h);
} }
} }
} }
else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a.get())) { else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a.get())) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if(h.is_valid()){ if(h.is_valid()){
qDebug("Received a tracker reply from %s", (const char*)h.current_tracker().toUtf8());
QString hash = h.hash(); QString hash = h.hash();
qDebug("Received torrent_paused_alert for %s", hash.toUtf8().data()); QHash<QString, QString> errors = trackersErrors.value(hash, QHash<QString, QString>());
int index = waitingForPause.indexOf(hash); // p->url requires at least libtorrent v0.13.1
if(index != -1){ errors.remove(h.current_tracker());
waitingForPause.removeAt(index); trackersErrors[hash] = errors;
}
index = reloadingTorrents.indexOf(hash);
if(index != -1) {
reloadingTorrents.removeAt(index);
reloadTorrent(h);
} }
} }
else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a.get())) {
emit UPnPError(QString(p->msg().c_str()));
}
else if (portmap_alert* p = dynamic_cast<portmap_alert*>(a.get())) {
qDebug("UPnP Success, msg: %s", p->msg().c_str());
emit UPnPSuccess(QString(p->msg().c_str()));
} }
else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) { else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) {
emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str())); emit peerBlocked(QString::fromUtf8(p->ip.to_string().c_str()));
@@ -1043,6 +1284,10 @@ void bittorrent::readAlerts() {
// Pause torrent // Pause torrent
pauseTorrent(hash); pauseTorrent(hash);
qDebug("%s was paused after checking", hash.toUtf8().data()); qDebug("%s was paused after checking", hash.toUtf8().data());
} else {
// Save Addition DateTime
TorrentsStartTime[hash] = QDateTime::currentDateTime();
TorrentsStartData[hash] = h.total_payload_download();
} }
emit torrentFinishedChecking(hash); emit torrentFinishedChecking(hash);
} }
@@ -1051,32 +1296,16 @@ void bittorrent::readAlerts() {
} }
} }
QList<QPair<QString, QString> > bittorrent::getTrackersErrors(QString hash) const{ QHash<QString, QString> bittorrent::getTrackersErrors(QString hash) const{
return trackersErrors.value(hash, QList<QPair<QString, QString> >()); return trackersErrors.value(hash, QHash<QString, QString>());
} }
QStringList bittorrent::getTorrentsToPauseAfterChecking() const{ QStringList bittorrent::getTorrentsToPauseAfterChecking() const{
return torrentsToPauseAfterChecking; return torrentsToPauseAfterChecking;
} }
// Function to reload the torrent async after the torrent is actually
// paused so that we can get fastresume data
void bittorrent::pauseAndReloadTorrent(QTorrentHandle h) {
if(!h.is_valid()) {
std::cerr << "/!\\ Error: Invalid handle\n";
return;
}
// ask to pause the torrent (async)
h.pause();
QString hash = h.hash();
// Add it to reloadingTorrents list so that we now we
// we should reload the torrent once we receive the
// torrent_paused_alert. pause() is async now...
reloadingTorrents << hash;
}
// Reload a torrent with full allocation mode // Reload a torrent with full allocation mode
void bittorrent::reloadTorrent(const QTorrentHandle &h) { void bittorrent::reloadTorrent(const QTorrentHandle &h, bool full_alloc) {
qDebug("** Reloading a torrent"); qDebug("** Reloading a torrent");
if(!h.is_valid()) { if(!h.is_valid()) {
qDebug("/!\\ Error: Invalid handle"); qDebug("/!\\ Error: Invalid handle");
@@ -1086,7 +1315,7 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
fs::path saveDir = h.save_path_boost(); fs::path saveDir = h.save_path_boost();
QString fileName = h.name(); QString fileName = h.name();
QString hash = h.hash(); QString hash = h.hash();
torrent_info t = h.get_torrent_info(); boost::intrusive_ptr<torrent_info> t(new torrent_info(h.get_torrent_info()));
qDebug("Reloading torrent: %s", fileName.toUtf8().data()); qDebug("Reloading torrent: %s", fileName.toUtf8().data());
entry resumeData; entry resumeData;
// Checking if torrentBackup Dir exists // Checking if torrentBackup Dir exists
@@ -1104,13 +1333,22 @@ void bittorrent::reloadTorrent(const QTorrentHandle &h) {
// Add torrent again to session // Add torrent again to session
unsigned int timeout = 0; unsigned int timeout = 0;
while(h.is_valid() && timeout < 6) { while(h.is_valid() && timeout < 6) {
qDebug("Waiting for the torrent to be removed...");
SleeperThread::msleep(1000); SleeperThread::msleep(1000);
++timeout; ++timeout;
} }
QTorrentHandle new_h = s->add_torrent(t, saveDir, resumeData, false); QTorrentHandle new_h;
if(full_alloc) {
new_h = s->add_torrent(t, saveDir, resumeData, storage_mode_allocate);
qDebug("Using full allocation mode"); qDebug("Using full allocation mode");
} else {
new_h.set_max_uploads(-1); new_h = s->add_torrent(t, saveDir, resumeData, storage_mode_sparse);
qDebug("Using sparse mode");
}
// Connections limit per torrent
new_h.set_max_connections(maxConnecsPerTorrent);
// Uploads limit per torrent
new_h.set_max_uploads(maxUploadsPerTorrent);
// Load filtered Files // Load filtered Files
loadFilesPriorities(new_h); loadFilesPriorities(new_h);
// Load speed limit from hard drive // Load speed limit from hard drive
@@ -1151,6 +1389,7 @@ QString bittorrent::getSavePath(QString hash) {
savePath = QString::fromUtf8(line.data()); savePath = QString::fromUtf8(line.data());
}else{ }else{
// use default save path // use default save path
qDebug("Using default save path because none was set");
savePath = defaultSavePath; savePath = defaultSavePath;
} }
// Checking if savePath Dir exists // Checking if savePath Dir exists
@@ -1226,7 +1465,7 @@ void bittorrent::applyEncryptionSettings(pe_settings se) {
s->set_pe_settings(se); s->set_pe_settings(se);
} }
// Will fast resume unfinished torrents in // Will fast resume torrents in
// backup directory // backup directory
void bittorrent::resumeUnfinishedTorrents() { void bittorrent::resumeUnfinishedTorrents() {
qDebug("Resuming unfinished torrents"); qDebug("Resuming unfinished torrents");
@@ -1242,7 +1481,7 @@ void bittorrent::resumeUnfinishedTorrents() {
} }
// Resume downloads // Resume downloads
foreach(fileName, filePaths) { foreach(fileName, filePaths) {
addTorrent(fileName, false); addTorrent(fileName, false, QString(), true);
} }
qDebug("Unfinished torrents resumed"); qDebug("Unfinished torrents resumed");
} }

View File

@@ -25,8 +25,10 @@
#include <QList> #include <QList>
#include <QPair> #include <QPair>
#include <QStringList> #include <QStringList>
#include <QDateTime>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp>
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
using namespace libtorrent; using namespace libtorrent;
@@ -34,6 +36,7 @@ using namespace libtorrent;
class downloadThread; class downloadThread;
class deleteThread; class deleteThread;
class QTimer; class QTimer;
class FilterParserThread;
class bittorrent : public QObject{ class bittorrent : public QObject{
Q_OBJECT Q_OBJECT
@@ -43,20 +46,30 @@ class bittorrent : public QObject{
QString scan_dir; QString scan_dir;
QTimer *timerScan; QTimer *timerScan;
QTimer *timerAlerts; QTimer *timerAlerts;
QTimer *fastResumeSaver;
QTimer *BigRatioTimer;
bool DHTEnabled; bool DHTEnabled;
downloadThread *downloader; downloadThread *downloader;
QString defaultSavePath; QString defaultSavePath;
QStringList torrentsToPauseAfterChecking; QStringList torrentsToPauseAfterChecking;
QStringList reloadingTorrents; QHash<QString, QDateTime> TorrentsStartTime;
QHash<QString, QList<qlonglong> > ETAstats; QHash<QString, size_type> TorrentsStartData;
QHash<QString, qlonglong> ETAs;
QHash<QString, QPair<size_type,size_type> > ratioData; QHash<QString, QPair<size_type,size_type> > ratioData;
QTimer *ETARefresher; QHash<QString, QHash<QString, QString> > trackersErrors;
QHash<QString, QList<QPair<QString, QString> > > trackersErrors;
deleteThread *deleter; deleteThread *deleter;
QStringList waitingForPause;
QStringList finishedTorrents; QStringList finishedTorrents;
QStringList unfinishedTorrents; QStringList unfinishedTorrents;
bool preAllocateAll;
bool addInPause;
int maxConnecsPerTorrent;
int maxUploadsPerTorrent;
float max_ratio;
bool UPnPEnabled;
bool NATPMPEnabled;
bool LSDEnabled;
FilterParserThread *filterParser;
QString filterPath;
int folderScanInterval; // in seconds
protected: protected:
QString getSavePath(QString hash); QString getSavePath(QString hash);
@@ -74,34 +87,36 @@ class bittorrent : public QObject{
session_status getSessionStatus() const; session_status getSessionStatus() const;
int getListenPort() const; int getListenPort() const;
QStringList getTorrentsToPauseAfterChecking() const; QStringList getTorrentsToPauseAfterChecking() const;
long getETA(QString hash) const; qlonglong getETA(QString hash) const;
float getRealRatio(QString hash) const; float getRealRatio(QString hash) const;
session* getSession() const; session* getSession() const;
QList<QPair<QString, QString> > getTrackersErrors(QString hash) const; QHash<QString, QString> getTrackersErrors(QString hash) const;
QStringList getFinishedTorrents() const; QStringList getFinishedTorrents() const;
QStringList getUnfinishedTorrents() const; QStringList getUnfinishedTorrents() const;
bool isFinished(QString hash) const; bool isFinished(QString hash) const;
bool has_filtered_files(QString hash) const; bool has_filtered_files(QString hash) const;
unsigned int getFinishedPausedTorrentsNb() const;
unsigned int getUnfinishedPausedTorrentsNb() const;
public slots: public slots:
void addTorrent(QString path, bool fromScanDir = false, QString from_url = QString()); void addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
void downloadFromUrl(QString url); void downloadFromUrl(QString url);
void downloadFromURLList(const QStringList& url_list); void downloadFromURLList(const QStringList& url_list);
void deleteTorrent(QString hash, bool permanent = false); void deleteTorrent(QString hash, bool permanent = false);
bool pauseTorrent(QString hash); bool pauseTorrent(QString hash);
bool resumeTorrent(QString hash); bool resumeTorrent(QString hash);
void enableDHT(); void pauseAllTorrents();
void disableDHT(); void resumeAllTorrents();
void saveDHTEntry(); void saveDHTEntry();
void preAllocateAllFiles(bool b);
void saveFastResumeAndRatioData(); void saveFastResumeAndRatioData();
void saveFastResumeAndRatioData(QString hash);
void enableDirectoryScanning(QString scan_dir); void enableDirectoryScanning(QString scan_dir);
void disableDirectoryScanning(); void disableDirectoryScanning();
void enablePeerExchange(); void enablePeerExchange();
void enableIPFilter(ip_filter filter); void enableIPFilter(QString filter);
void disableIPFilter(); void disableIPFilter();
void pauseAndReloadTorrent(QTorrentHandle h);
void resumeUnfinishedTorrents(); void resumeUnfinishedTorrents();
void updateETAs();
void saveTorrentSpeedLimits(QString hash); void saveTorrentSpeedLimits(QString hash);
void loadTorrentSpeedLimits(QString hash); void loadTorrentSpeedLimits(QString hash);
void saveDownloadUploadForTorrent(QString hash); void saveDownloadUploadForTorrent(QString hash);
@@ -111,12 +126,16 @@ class bittorrent : public QObject{
// Session configuration - Setters // Session configuration - Setters
void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports); void setListeningPortsRange(std::pair<unsigned short, unsigned short> ports);
void setMaxConnections(int maxConnec); void setMaxConnections(int maxConnec);
void setMaxConnectionsPerTorrent(int max);
void setMaxUploadsPerTorrent(int max);
void setDownloadRateLimit(long rate); void setDownloadRateLimit(long rate);
void setUploadRateLimit(long rate); void setUploadRateLimit(long rate);
void setGlobalRatio(float ratio); void setGlobalRatio(float ratio);
void setDeleteRatio(float ratio);
void setDHTPort(int dht_port); void setDHTPort(int dht_port);
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true); void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
void setSessionSettings(session_settings sessionSettings); void setSessionSettings(session_settings sessionSettings);
void startTorrentsInPause(bool b);
void setDefaultSavePath(QString savepath); void setDefaultSavePath(QString savepath);
void applyEncryptionSettings(pe_settings se); void applyEncryptionSettings(pe_settings se);
void loadFilesPriorities(QTorrentHandle& h); void loadFilesPriorities(QTorrentHandle& h);
@@ -124,6 +143,12 @@ class bittorrent : public QObject{
void setUploadLimit(QString hash, long val); void setUploadLimit(QString hash, long val);
void setUnfinishedTorrent(QString hash); void setUnfinishedTorrent(QString hash);
void setFinishedTorrent(QString hash); void setFinishedTorrent(QString hash);
void enableUPnP(bool b);
void enableNATPMP(bool b);
void enableLSD(bool b);
bool enableDHT(bool b);
void reloadTorrent(const QTorrentHandle &h, bool full_alloc);
void setTimerScanInterval(int secs);
protected slots: protected slots:
void scanDirectory(); void scanDirectory();
@@ -131,12 +156,15 @@ class bittorrent : public QObject{
void processDownloadedFile(QString, QString); void processDownloadedFile(QString, QString);
bool loadTrackerFile(QString hash); bool loadTrackerFile(QString hash);
void saveTrackerFile(QString hash); void saveTrackerFile(QString hash);
void reloadTorrent(const QTorrentHandle &h); // This is protected now, call pauseAndReloadTorrent() instead void deleteBigRatios();
signals: signals:
void invalidTorrent(QString path); void invalidTorrent(QString path);
void duplicateTorrent(QString path); void duplicateTorrent(QString path);
void addedTorrent(QString path, QTorrentHandle& h, bool fastResume); void addedTorrent(QString path, QTorrentHandle& h, bool fastResume);
void deletedTorrent(QString hash);
void pausedTorrent(QString hash);
void resumedTorrent(QString hash);
void finishedTorrent(QTorrentHandle& h); void finishedTorrent(QTorrentHandle& h);
void fullDiskError(QTorrentHandle& h); void fullDiskError(QTorrentHandle& h);
void trackerError(QString hash, QString time, QString msg); void trackerError(QString hash, QString time, QString msg);
@@ -151,7 +179,9 @@ class bittorrent : public QObject{
void fastResumeDataRejected(QString name); void fastResumeDataRejected(QString name);
void urlSeedProblem(QString url, QString msg); void urlSeedProblem(QString url, QString msg);
void torrentFinishedChecking(QString hash); void torrentFinishedChecking(QString hash);
void torrent_ratio_deleted(QString fileName);
void UPnPError(QString msg);
void UPnPSuccess(QString msg);
}; };
#endif #endif

View File

@@ -5,22 +5,16 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>605</width> <width>592</width>
<height>588</height> <height>655</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
<string>Torrent Creation Tool</string> <string>Torrent Creation Tool</string>
</property> </property>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="createTorrent_title" > <widget class="QLabel" name="createTorrent_title" >
<property name="geometry" >
<rect>
<x>9</x>
<y>9</y>
<width>587</width>
<height>27</height>
</rect>
</property>
<property name="minimumSize" > <property name="minimumSize" >
<size> <size>
<width>0</width> <width>0</width>
@@ -51,49 +45,45 @@
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
<widget class="QWidget" name="layoutWidget" > </item>
<property name="geometry" >
<rect>
<x>9</x>
<y>42</y>
<width>597</width>
<height>438</height>
</rect>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item> <item>
<widget class="QLabel" name="lbl_input" > <widget class="QLabel" name="lbl_input" >
<property name="minimumSize" >
<size>
<width>0</width>
<height>101</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>26</height>
</size>
</property>
<property name="text" > <property name="text" >
<string>Input files or directories:</string> <string>File or folder to add to the torrent:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QLineEdit" name="textInputPath" />
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="addFile_button" >
<property name="text" >
<string>Add a file</string>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/add_file.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addFolder_button" >
<property name="text" >
<string>Add a folder</string>
</property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/add_folder.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<layout class="QVBoxLayout" >
<item> <item>
<widget class="QLabel" name="lbl_announce_url" > <widget class="QLabel" name="lbl_announce_url" >
<property name="minimumSize" > <property name="minimumSize" >
@@ -155,129 +145,22 @@
</item> </item>
<item> <item>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<item> <property name="leftMargin" >
<widget class="QListWidget" name="input_list" >
<property name="selectionMode" >
<enum>QAbstractItemView::MultiSelection</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number> <number>0</number>
</property> </property>
<property name="spacing" > <property name="topMargin" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="addFolder_button" >
<property name="minimumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeFolder_button" >
<property name="minimumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addFile_button" >
<property name="minimumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number> <number>0</number>
</property> </property>
<property name="spacing" > <property name="rightMargin" >
<number>6</number> <number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property> </property>
<item> <item>
<widget class="QListWidget" name="trackers_list" > <widget class="QListWidget" name="trackers_list" >
@@ -288,12 +171,21 @@
</item> </item>
<item> <item>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item> <item>
<spacer> <spacer>
<property name="orientation" > <property name="orientation" >
@@ -324,6 +216,9 @@
<property name="text" > <property name="text" >
<string/> <string/>
</property> </property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -343,6 +238,9 @@
<property name="text" > <property name="text" >
<string/> <string/>
</property> </property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -364,12 +262,21 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item> <item>
<widget class="QListWidget" name="URLSeeds_list" > <widget class="QListWidget" name="URLSeeds_list" >
<property name="selectionMode" > <property name="selectionMode" >
@@ -379,12 +286,21 @@
</item> </item>
<item> <item>
<layout class="QVBoxLayout" > <layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item> <item>
<spacer> <spacer>
<property name="orientation" > <property name="orientation" >
@@ -415,6 +331,9 @@
<property name="text" > <property name="text" >
<string/> <string/>
</property> </property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/add.png</iconset>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -434,6 +353,9 @@
<property name="text" > <property name="text" >
<string/> <string/>
</property> </property>
<property name="icon" >
<iconset resource="icons.qrc" >:/Icons/skin/remove.png</iconset>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@@ -455,12 +377,6 @@
</item> </item>
<item> <item>
<widget class="QTextEdit" name="txt_comment" > <widget class="QTextEdit" name="txt_comment" >
<property name="minimumSize" >
<size>
<width>421</width>
<height>102</height>
</size>
</property>
<property name="maximumSize" > <property name="maximumSize" >
<size> <size>
<width>421</width> <width>421</width>
@@ -475,89 +391,122 @@
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </item>
<widget class="QCheckBox" name="check_private" > <item>
<property name="geometry" >
<rect>
<x>9</x>
<y>482</y>
<width>587</width>
<height>22</height>
</rect>
</property>
<property name="text" >
<string>Private (won't be distributed on trackerless network / DHT if enabled)</string>
</property>
</widget>
<widget class="QWidget" name="layoutWidget2" >
<property name="geometry" >
<rect>
<x>9</x>
<y>510</y>
<width>587</width>
<height>30</height>
</rect>
</property>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" > <item>
<number>0</number> <widget class="QLabel" name="txtPieceSize" >
<property name="text" >
<string>Piece size:</string>
</property> </property>
<property name="spacing" > </widget>
<number>6</number> </item>
<item>
<widget class="QComboBox" name="comboPieceSize" >
<property name="currentIndex" >
<number>3</number>
</property> </property>
<item> <item>
<widget class="QLabel" name="lbl_destination" > <property name="text" >
<property name="maximumSize" > <string>32 KiB</string>
</property>
</item>
<item>
<property name="text" >
<string>64 KiB</string>
</property>
</item>
<item>
<property name="text" >
<string>128 KiB</string>
</property>
</item>
<item>
<property name="text" >
<string>256 KiB</string>
</property>
</item>
<item>
<property name="text" >
<string>512 KiB</string>
</property>
</item>
<item>
<property name="text" >
<string>1 MiB</string>
</property>
</item>
<item>
<property name="text" >
<string>2 MiB</string>
</property>
</item>
<item>
<property name="text" >
<string>4 MiB</string>
</property>
</item>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size> <size>
<width>16777215</width> <width>40</width>
<height>26</height> <height>20</height>
</size> </size>
</property> </property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="check_private" >
<property name="text" > <property name="text" >
<string>Destination torrent file:</string> <string>Private (won't be distributed on DHT network if enabled)</string>
</property> </property>
<property name="buddy" > </widget>
<cstring>txt_destination</cstring> </item>
<item>
<widget class="QCheckBox" name="checkStartSeeding" >
<property name="text" >
<string>Start seeding after creation</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="progressLbl" >
<property name="text" >
<string>Progress:</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar" >
<property name="value" >
<number>0</number>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" > <layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" > <property name="spacing" >
<number>6</number> <number>6</number>
</property> </property>
<item> <property name="leftMargin" >
<widget class="QLineEdit" name="txt_destination" />
</item>
<item>
<widget class="QToolButton" name="browse_destination" >
<property name="text" >
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="layoutWidget3" >
<property name="geometry" >
<rect>
<x>9</x>
<y>546</y>
<width>587</width>
<height>33</height>
</rect>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number> <number>0</number>
</property> </property>
<property name="spacing" > <property name="topMargin" >
<number>6</number> <number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property> </property>
<item> <item>
<spacer> <spacer>
@@ -575,7 +524,7 @@
<item> <item>
<widget class="QPushButton" name="createButton" > <widget class="QPushButton" name="createButton" >
<property name="text" > <property name="text" >
<string>Create</string> <string>Create and save...</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -600,9 +549,12 @@
</spacer> </spacer>
</item> </item>
</layout> </layout>
</item>
</layout>
</widget> </widget>
</widget> <resources>
<resources/> <include location="icons.qrc" />
</resources>
<connections> <connections>
<connection> <connection>
<sender>cancelButton</sender> <sender>cancelButton</sender>

View File

@@ -36,57 +36,38 @@
#include <libtorrent/file_pool.hpp> #include <libtorrent/file_pool.hpp>
#include "createtorrent_imp.h" #include "createtorrent_imp.h"
#include "misc.h"
using namespace libtorrent; using namespace libtorrent;
using namespace boost::filesystem; using namespace boost::filesystem;
createtorrent::createtorrent(QWidget *parent): QDialog(parent){ createtorrent::createtorrent(QWidget *parent): QDialog(parent){
setupUi(this); setupUi(this);
addTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
removeTracker_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
addURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/add.png")));
removeURLSeed_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
removeFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
addFolder_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_folder.png")));
addFile_button->setIcon(QIcon(QString::fromUtf8(":/Icons/add_file.png")));
setAttribute(Qt::WA_DeleteOnClose); setAttribute(Qt::WA_DeleteOnClose);
creatorThread = new torrentCreatorThread();
connect(creatorThread, SIGNAL(creationSuccess(QString)), this, SLOT(handleCreationSucess(QString)));
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
show(); show();
} }
void createtorrent::on_browse_destination_clicked(){ createtorrent::~createtorrent() {
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)")); delete creatorThread;
if(!destination.isEmpty()){
if(!destination.endsWith(QString::fromUtf8(".torrent")))
destination += QString::fromUtf8(".torrent");
txt_destination->setText(destination);
}
} }
void createtorrent::on_addFolder_button_clicked(){ void createtorrent::on_addFolder_button_clicked(){
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly); QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
if(!dir.isEmpty() && input_list->findItems(dir, Qt::MatchCaseSensitive).size() == 0) if(!dir.isEmpty())
input_list->addItem(dir); textInputPath->setText(dir);
} }
void createtorrent::on_addFile_button_clicked(){ void createtorrent::on_addFile_button_clicked(){
QStringList files = QFileDialog::getOpenFileNames(this, tr("Select files to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly); QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
QString file; if(!file.isEmpty())
foreach(file, files){ textInputPath->setText(file);
if(input_list->findItems(file, Qt::MatchCaseSensitive).size() == 0){
input_list->addItem(file);
}
}
} }
void createtorrent::on_removeFolder_button_clicked(){ void createtorrent::on_removeTracker_button_clicked() {
QModelIndexList selectedIndexes = input_list->selectionModel()->selectedIndexes();
for(int i=selectedIndexes.size()-1; i>=0; --i){
QListWidgetItem *item = input_list->takeItem(selectedIndexes.at(i).row());
delete item;
}
}
void createtorrent::on_removeTracker_button_clicked(){
QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes = trackers_list->selectionModel()->selectedIndexes();
for(int i=selectedIndexes.size()-1; i>=0; --i){ for(int i=selectedIndexes.size()-1; i>=0; --i){
QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row()); QListWidgetItem *item = trackers_list->takeItem(selectedIndexes.at(i).row());
@@ -94,7 +75,28 @@ void createtorrent::on_removeTracker_button_clicked(){
} }
} }
void createtorrent::on_addTracker_button_clicked(){ int createtorrent::getPieceSize() const {
switch(comboPieceSize->currentIndex()) {
case 0:
return 32*1024;
case 1:
return 64*1024;
case 2:
return 128*1024;
case 3:
return 256*1024;
case 4:
return 512*1024;
case 5:
return 1024*1024;
case 6:
return 2048*1024;
default:
return 256*1024;
}
}
void createtorrent::on_addTracker_button_clicked() {
bool ok; bool ok;
QString URL = QInputDialog::getText(this, tr("Please type an announce URL"), QString URL = QInputDialog::getText(this, tr("Please type an announce URL"),
tr("Announce URL:", "Tracker URL"), QLineEdit::Normal, tr("Announce URL:", "Tracker URL"), QLineEdit::Normal,
@@ -127,11 +129,13 @@ void createtorrent::on_addURLSeed_button_clicked(){
// Subfunction to add files to a torrent_info structure // Subfunction to add files to a torrent_info structure
// Written by Arvid Norberg (libtorrent Author) // Written by Arvid Norberg (libtorrent Author)
void add_files(torrent_info& t, path const& p, path const& l){ void add_files(torrent_info& t, path const& p, path const& l){
qDebug("p: %s, l: %s, l.leaf(): %s", p.string().c_str(), l.string().c_str(), l.leaf().c_str());
path f(p / l); path f(p / l);
if (is_directory(f)){ if (is_directory(f)){
for (directory_iterator i(f), end; i != end; ++i) for (directory_iterator i(f), end; i != end; ++i)
add_files(t, p, l / i->leaf()); add_files(t, p, l / i->leaf());
}else{ }else{
qDebug("Adding %s", l.string().c_str());
t.add_file(l, file_size(f)); t.add_file(l, file_size(f));
} }
} }
@@ -147,67 +151,118 @@ QStringList createtorrent::allItems(QListWidget *list){
// Main function that create a .torrent file // Main function that create a .torrent file
void createtorrent::on_createButton_clicked(){ void createtorrent::on_createButton_clicked(){
QString destination = txt_destination->text(); QString input = textInputPath->text().trimmed();
if(destination.isEmpty()){ if (input.endsWith(QDir::separator()))
QMessageBox::critical(0, tr("No destination path set"), tr("Please type a destination path first")); input.chop(1);
return; if(input.isEmpty()){
}
QStringList input = allItems(input_list);
if(input.size() == 0){
QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first")); QMessageBox::critical(0, tr("No input path set"), tr("Please type an input path first"));
return; return;
} }
QStringList trackers = allItems(trackers_list);
if(!trackers.size()){
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
return;
}
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
if(!destination.isEmpty()) {
if(!destination.endsWith(QString::fromUtf8(".torrent")))
destination += QString::fromUtf8(".torrent");
} else {
return;
}
QStringList url_seeds = allItems(URLSeeds_list);
QString comment = txt_comment->toPlainText();
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
}
void createtorrent::handleCreationFailure(QString msg) {
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
hide();
}
void createtorrent::handleCreationSuccess(QString path, const char* branch_path, QString hash) {
if(checkStartSeeding->isChecked()) {
// Create save path file
QFile savepath_file(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".savepath"));
savepath_file.open(QIODevice::WriteOnly | QIODevice::Text);
savepath_file.write(branch_path);
savepath_file.close();
emit torrent_to_seed(path);
}
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+path);
hide();
}
void createtorrent::updateProgressBar(int progress) {
progressBar->setValue(progress);
}
//
// Torrent Creator Thread
//
void torrentCreatorThread::create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size) {
input_path = _input_path;
save_path = _save_path;
trackers = _trackers;
url_seeds = _url_seeds;
comment = _comment;
is_private = _is_private;
piece_size = _piece_size;
abort = false;
start();
}
void torrentCreatorThread::run() {
emit updateProgress(0);
char const* creator_str = "qBittorrent "VERSION; char const* creator_str = "qBittorrent "VERSION;
try { try {
torrent_info t; boost::intrusive_ptr<torrent_info> t(new torrent_info);
QString input_path; ofstream out(complete(path((const char*)save_path.toUtf8())), std::ios_base::binary);
path full_path; // Adding files to the torrent
ofstream out(complete(path((const char*)destination.toUtf8())), std::ios_base::binary); path full_path = complete(path(input_path.toUtf8().data()));
foreach(input_path, input){ add_files(*t, full_path.branch_path(), full_path.leaf());
full_path = complete(path((const char*)input_path.toUtf8())); if(abort) return;
add_files(t, full_path.branch_path(), full_path.leaf()); // Set piece size
} t->set_piece_size(piece_size);
int piece_size = 256 * 1024;
t.set_piece_size(piece_size);
// Add url seeds // Add url seeds
QStringList urlSeeds = allItems(URLSeeds_list);
QString seed; QString seed;
foreach(seed, urlSeeds){ foreach(seed, url_seeds){
t.add_url_seed((const char*)seed.toUtf8()); t->add_url_seed(seed.toUtf8().data());
} }
QStringList trackers = allItems(trackers_list);
for(int i=0; i<trackers.size(); ++i){ for(int i=0; i<trackers.size(); ++i){
t.add_tracker((const char*)trackers.at(i).toUtf8()); t->add_tracker(trackers.at(i).toUtf8().data());
} }
if(abort) return;
// calculate the hash for all pieces // calculate the hash for all pieces
file_pool fp; file_pool fp;
boost::scoped_ptr<storage_interface> st(default_storage_constructor(t, full_path.branch_path(), fp)); boost::scoped_ptr<storage_interface> st(default_storage_constructor(t, full_path.branch_path(), fp));
int num = t.num_pieces(); int num = t->num_pieces();
std::vector<char> buf(piece_size); std::vector<char> buf(piece_size);
for (int i = 0; i < num; ++i) { for (int i = 0; i < num; ++i) {
st->read(&buf[0], i, 0, t.piece_size(i)); st->read(&buf[0], i, 0, t->piece_size(i));
hasher h(&buf[0], t.piece_size(i)); hasher h(&buf[0], t->piece_size(i));
t.set_hash(i, h.final()); t->set_hash(i, h.final());
emit updateProgress((int)(i*100./(float)num));
if(abort) return;
} }
// 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*)txt_comment->toPlainText().toUtf8()); t->set_comment((const char*)comment.toUtf8());
// Is private ? // Is private ?
if(check_private->isChecked()){ if(is_private){
t.set_priv(true); t->set_priv(true);
} }
if(abort) return;
// create the torrent and print it to out // create the torrent and print it to out
entry e = t.create_torrent(); entry e = t->create_torrent();
libtorrent::bencode(std::ostream_iterator<char>(out), e); libtorrent::bencode(std::ostream_iterator<char>(out), e);
out.flush();
emit updateProgress(100);
emit creationSuccess(save_path, full_path.branch_path().string().c_str(), misc::toQString(t->info_hash()));
} }
catch (std::exception& e){ catch (std::exception& e){
std::cerr << e.what() << "\n"; emit creationFailure(QString::fromUtf8(e.what()));
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(QString::fromUtf8(e.what())));
hide();
return;
} }
hide();
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+destination);
} }

View File

@@ -22,25 +22,65 @@
#ifndef CREATE_TORRENT_IMP_H #ifndef CREATE_TORRENT_IMP_H
#define CREATE_TORRENT_IMP_H #define CREATE_TORRENT_IMP_H
#include <QThread>
#include "ui_createtorrent.h" #include "ui_createtorrent.h"
class torrentCreatorThread : public QThread {
Q_OBJECT
QString input_path;
QString save_path;
QStringList trackers;
QStringList url_seeds;
QString comment;
bool is_private;
int piece_size;
bool abort;
public:
torrentCreatorThread() {}
~torrentCreatorThread() {
abort = true;
wait();
}
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
protected:
void run();
signals:
void creationFailure(QString msg);
void creationSuccess(QString path, const char* branch_path, QString hash);
void updateProgress(int progress);
};
class createtorrent : public QDialog, private Ui::createTorrentDialog{ class createtorrent : public QDialog, private Ui::createTorrentDialog{
Q_OBJECT Q_OBJECT
private:
torrentCreatorThread *creatorThread;
public: public:
createtorrent(QWidget *parent = 0); createtorrent(QWidget *parent = 0);
~createtorrent();
QStringList allItems(QListWidget *list); QStringList allItems(QListWidget *list);
int getPieceSize() const;
signals:
void torrent_to_seed(QString path);
protected slots: protected slots:
void on_browse_destination_clicked();
void on_createButton_clicked(); void on_createButton_clicked();
void on_addFile_button_clicked(); void on_addFile_button_clicked();
void on_addFolder_button_clicked(); void on_addFolder_button_clicked();
void on_removeFolder_button_clicked();
void on_addTracker_button_clicked(); void on_addTracker_button_clicked();
void on_removeTracker_button_clicked(); void on_removeTracker_button_clicked();
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 handleCreationSuccess(QString path, const char* branch_path, QString hash);
void updateProgressBar(int progress);
}; };
#endif #endif

View File

@@ -28,17 +28,17 @@
#include <QMutexLocker> #include <QMutexLocker>
#include <QPair> #include <QPair>
#include "misc.h" #include "arborescence.h"
class subDeleteThread : public QThread { class subDeleteThread : public QThread {
Q_OBJECT Q_OBJECT
private: private:
QString save_path; QString save_path;
QStringList files_path; arborescence *arb;
bool abort; bool abort;
public: public:
subDeleteThread(QObject *parent, QString save_path, QStringList files_path) : QThread(parent), save_path(save_path), files_path(files_path), abort(false){} subDeleteThread(QObject *parent, QString saveDir, arborescence *arb) : QThread(parent), save_path(saveDir), arb(arb), abort(false){}
~subDeleteThread(){ ~subDeleteThread(){
abort = true; abort = true;
@@ -52,10 +52,11 @@ class subDeleteThread : public QThread {
protected: protected:
void run(){ void run(){
if(misc::removeTorrentSavePath(save_path, files_path)) if(arb->removeFromFS(save_path))
emit deletionSuccessST(this); emit deletionSuccessST(this);
else else
emit deletionFailureST(this); emit deletionFailureST(this);
delete arb;
} }
}; };
@@ -63,7 +64,7 @@ class deleteThread : public QThread {
Q_OBJECT Q_OBJECT
private: private:
QList<QPair<QString, QStringList> > torrents_list; QList<QPair<QString, arborescence*> > torrents_list;
QMutex mutex; QMutex mutex;
QWaitCondition condition; QWaitCondition condition;
bool abort; bool abort;
@@ -81,9 +82,10 @@ class deleteThread : public QThread {
wait(); wait();
} }
void deleteTorrent(QString save_path, QStringList files_path){ void deleteTorrent(QString saveDir, arborescence *arb){
qDebug("deleteThread called");
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
torrents_list << QPair<QString, QStringList>(save_path, files_path); torrents_list << QPair<QString, arborescence*>(saveDir, arb);
if(!isRunning()){ if(!isRunning()){
start(); start();
}else{ }else{
@@ -98,7 +100,7 @@ class deleteThread : public QThread {
return; return;
mutex.lock(); mutex.lock();
if(torrents_list.size() != 0){ if(torrents_list.size() != 0){
QPair<QString, QStringList> torrent = torrents_list.takeFirst(); QPair<QString, arborescence *> torrent = torrents_list.takeFirst();
mutex.unlock(); mutex.unlock();
subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second); subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second);
subThreads << st; subThreads << st;

View File

@@ -21,185 +21,6 @@
<property name="margin" > <property name="margin" >
<number>0</number> <number>0</number>
</property> </property>
<item>
<layout class="QHBoxLayout" >
<property name="spacing" >
<number>6</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lbl_DLSpeed" >
<property name="text" >
<string>Total DL Speed:</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_DownSpeed" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
</property>
<property name="smallDecimalPoint" >
<bool>false</bool>
</property>
<property name="numDigits" >
<number>6</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>0.000000000000000</double>
</property>
<property name="intValue" stdset="0" >
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="unitDL" >
<property name="text" >
<string>KiB/s</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>Session ratio: </string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_Ratio" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="numDigits" >
<number>4</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lbl_ratio_icon" >
<property name="text" >
<string/>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lbl_UPSpeed" >
<property name="text" >
<string>Total UP Speed:</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<widget class="QLCDNumber" name="LCD_UpSpeed" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
<property name="smallDecimalPoint" >
<bool>false</bool>
</property>
<property name="numDigits" >
<number>6</number>
</property>
<property name="segmentStyle" >
<enum>QLCDNumber::Flat</enum>
</property>
<property name="value" stdset="0" >
<double>0.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="unitUP" >
<property name="text" >
<string>KiB/s</string>
</property>
<property name="alignment" >
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<widget class="QTreeView" name="downloadList" > <widget class="QTreeView" name="downloadList" >
<property name="minimumSize" > <property name="minimumSize" >
@@ -242,6 +63,14 @@
<number>0</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="log_tab" > <widget class="QWidget" name="log_tab" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>765</width>
<height>138</height>
</rect>
</property>
<attribute name="title" > <attribute name="title" >
<string>Log</string> <string>Log</string>
</attribute> </attribute>
@@ -255,9 +84,7 @@
<item> <item>
<widget class="QTextBrowser" name="infoBar" > <widget class="QTextBrowser" name="infoBar" >
<property name="sizePolicy" > <property name="sizePolicy" >
<sizepolicy> <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<hsizetype>7</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@@ -276,6 +103,14 @@
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="filter_tab" > <widget class="QWidget" name="filter_tab" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>765</width>
<height>138</height>
</rect>
</property>
<attribute name="title" > <attribute name="title" >
<string>IP filter</string> <string>IP filter</string>
</attribute> </attribute>
@@ -346,7 +181,69 @@
<string>Torrent Properties</string> <string>Torrent Properties</string>
</property> </property>
</action> </action>
<action name="actionOpen_destination_folder" >
<property name="icon" >
<iconset resource="icons.qrc" >
<normaloff>:/Icons/folder.png</normaloff>:/Icons/folder.png</iconset>
</property>
<property name="text" >
<string>Open destination folder</string>
</property>
</action>
<action name="actionHOSColName" >
<property name="text" >
<string>Name</string>
</property>
</action>
<action name="actionHOSColSize" >
<property name="text" >
<string>Size</string>
</property>
</action>
<action name="actionHOSColProgress" >
<property name="text" >
<string>Progress</string>
</property>
</action>
<action name="actionHOSColDownSpeed" >
<property name="text" >
<string>DLSpeed</string>
</property>
</action>
<action name="actionHOSColUpSpeed" >
<property name="text" >
<string>UpSpeed</string>
</property>
</action>
<action name="actionHOSColSeedersLeechers" >
<property name="text" >
<string>Seeds/Leechs</string>
</property>
</action>
<action name="actionHOSColRatio" >
<property name="text" >
<string>Ratio</string>
</property>
</action>
<action name="actionHOSColEta" >
<property name="text" >
<string>ETA</string>
</property>
</action>
<action name="actionBuy_it" >
<property name="icon" >
<iconset resource="icons.qrc" >
<normaloff>:/Icons/money.png</normaloff>:/Icons/money.png</iconset>
</property>
<property name="text" >
<string>Buy it</string>
</property>
</action>
<zorder>tabBottom</zorder>
<zorder></zorder>
</widget> </widget>
<resources/> <resources>
<include location="icons.qrc" />
</resources>
<connections/> <connections/>
</ui> </ui>

207
src/downloadThread.cpp Normal file
View File

@@ -0,0 +1,207 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include "downloadThread.h"
#include <iostream>
#include <QSettings>
#include <stdio.h>
// http://curl.rtin.bz/libcurl/c/libcurl-errors.html
QString subDownloadThread::errorCodeToString(CURLcode status) {
switch(status){
case CURLE_FTP_CANT_GET_HOST:
case CURLE_COULDNT_RESOLVE_HOST:
return tr("Host is unreachable");
case CURLE_READ_ERROR:
case CURLE_FILE_COULDNT_READ_FILE:
return tr("File was not found (404)");
case CURLE_FTP_ACCESS_DENIED:
case CURLE_LOGIN_DENIED:
case CURLE_FTP_USER_PASSWORD_INCORRECT:
return tr("Connection was denied");
case CURLE_URL_MALFORMAT:
return tr("Url is invalid");
case CURLE_COULDNT_RESOLVE_PROXY:
return tr("Could not resolve proxy");
//case 5:
// return tr("Connection forbidden (403)");
//case 6:
// return tr("Connection was not authorized (401)");
//case 7:
// return tr("Content has moved (301)");
case CURLE_COULDNT_CONNECT:
return tr("Connection failure");
case CURLE_OPERATION_TIMEOUTED:
return tr("Connection was timed out");
case CURLE_INTERFACE_FAILED:
return tr("Incorrect network interface");
default:
return tr("Unknown error");
}
}
subDownloadThread::subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){}
subDownloadThread::~subDownloadThread(){
abort = true;
wait();
}
void subDownloadThread::run(){
// XXX: Trick to get a unique filename
QString filePath;
QTemporaryFile *tmpfile = new QTemporaryFile();
if (tmpfile->open()) {
filePath = tmpfile->fileName();
}
delete tmpfile;
FILE *f = fopen(filePath.toUtf8().data(), "wb");
if(!f) {
std::cerr << "couldn't open destination file" << "\n";
return;
}
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if(curl) {
std::string c_url = url.toUtf8().data();
curl_easy_setopt(curl, CURLOPT_URL, c_url.c_str());
// SSL support
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
// PROXY SUPPORT
QSettings settings("qBittorrent", "qBittorrent");
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/ProxyType"), 0).toInt();
if(intValue > 0) {
// Proxy enabled
QString IP = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/IP"), "0.0.0.0").toString();
QString port = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Port"), 8080).toString();
qDebug("Using proxy: %s", (IP+QString(":")+port).toUtf8().data());
curl_easy_setopt(curl, CURLOPT_PROXYPORT, (IP+QString(":")+port).toUtf8().data());
// Default proxy type is HTTP, we must change if it is SOCKS5
if(intValue%2==0) {
qDebug("Proxy is SOCKS5, not HTTP");
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
}
// Authentication?
if(intValue > 2) {
qDebug("Proxy requires authentication, authenticating");
QString username = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Username"), QString()).toString();
QString password = settings.value(QString::fromUtf8("Preferences/Connection/Proxy/Password"), QString()).toString();
curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, (username+QString(":")+password).toUtf8().data());
}
}
// We have to define CURLOPT_WRITEFUNCTION or it will crash on windows
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
// Verbose
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
// No progress info (we don't use it)
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
// Redirections
curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, -1);
qDebug("Downloading %s", url.toUtf8().data());
res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
fclose(f);
if(res) {
emit downloadFailureST(this, url, errorCodeToString(res));
} else {
emit downloadFinishedST(this, url, filePath);
}
} else {
std::cerr << "Could not initialize CURL" << "\n";
}
}
/** Download Thread **/
downloadThread::downloadThread(QObject* parent) : QThread(parent), abort(false){}
downloadThread::~downloadThread(){
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();
qDeleteAll(subThreads);
wait();
}
void downloadThread::downloadUrl(QString url){
QMutexLocker locker(&mutex);
if(downloading_list.contains(url)) return;
url_list << url;
downloading_list << url;
if(!isRunning()){
start();
}else{
condition.wakeOne();
}
}
void downloadThread::run(){
forever{
if(abort)
return;
mutex.lock();
if(url_list.size() != 0){
QString url = url_list.takeFirst();
mutex.unlock();
subDownloadThread *st = new subDownloadThread(0, url);
subThreads << st;
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
st->start();
}else{
condition.wait(&mutex);
mutex.unlock();
}
}
}
void downloadThread::propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
int index = subThreads.indexOf(st);
Q_ASSERT(index != -1);
subThreads.removeAt(index);
delete st;
emit downloadFinished(url, path);
mutex.lock();
index = downloading_list.indexOf(url);
Q_ASSERT(index != -1);
downloading_list.removeAt(index);
mutex.unlock();
}
void downloadThread::propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){
int index = subThreads.indexOf(st);
Q_ASSERT(index != -1);
subThreads.removeAt(index);
delete st;
emit downloadFailure(url, reason);
mutex.lock();
index = downloading_list.indexOf(url);
Q_ASSERT(index != -1);
downloading_list.removeAt(index);
mutex.unlock();
}

View File

@@ -29,55 +29,18 @@
#include <QMutexLocker> #include <QMutexLocker>
#include <QWaitCondition> #include <QWaitCondition>
#include <QStringList> #include <QStringList>
#include <iostream> #include <curl/curl.h>
#include <cc++/common.h>
#ifdef CCXX_NAMESPACES
using namespace std;
using namespace ost;
#endif
class subDownloadThread : public QThread { class subDownloadThread : public QThread {
Q_OBJECT Q_OBJECT
private: private:
QString url; QString url;
URLStream url_stream;
bool abort; bool abort;
public: public:
subDownloadThread(QObject *parent, QString url) : QThread(parent), url(url), abort(false){} subDownloadThread(QObject *parent, QString url);
~subDownloadThread();
~subDownloadThread(){ QString errorCodeToString(CURLcode status);
abort = true;
wait();
}
static QString errorCodeToString(URLStream::Error status){
switch(status){
case URLStream::errUnreachable:
return tr("Host is unreachable");
case URLStream::errMissing:
return tr("File was not found (404)");
case URLStream::errDenied:
return tr("Connection was denied");
case URLStream::errInvalid:
return tr("Url is invalid");
case URLStream::errForbidden:
return tr("Connection forbidden (403)");
case URLStream::errUnauthorized:
return tr("Connection was not authorized (401)");
case URLStream::errRelocated:
return tr("Content has moved (301)");
case URLStream::errFailure:
return tr("Connection failure");
case URLStream::errTimeout:
return tr("Connection was timed out");
case URLStream::errInterface:
return tr("Incorrect network interface");
default:
return tr("Unknown error");
}
}
signals: signals:
// For subthreads // For subthreads
@@ -85,47 +48,7 @@ class subDownloadThread : public QThread {
void downloadFailureST(subDownloadThread* st, QString url, QString reason); void downloadFailureST(subDownloadThread* st, QString url, QString reason);
protected: protected:
void run(){ void run();
// XXX: Trick to get a unique filename
QString filePath;
QTemporaryFile *tmpfile = new QTemporaryFile();
if (tmpfile->open()) {
filePath = tmpfile->fileName();
}
delete tmpfile;
QFile dest_file(filePath);
if(!dest_file.open(QIODevice::WriteOnly | QIODevice::Text)){
std::cerr << "Error: could't create temporary file: " << (const char*)filePath.toUtf8() << '\n';
return;
}
URLStream::Error status = url_stream.get((const char*)url.toUtf8());
if(status){
// Failure
QString error_msg = errorCodeToString(status);
qDebug("Download failed for %s, reason: %s", (const char*)url.toUtf8(), (const char*)error_msg.toUtf8());
url_stream.close();
emit downloadFailureST(this, url, error_msg);
return;
}
qDebug("Downloading %s...", (const char*)url.toUtf8());
char cbuf[1024];
int len;
while(!url_stream.eof()) {
url_stream.read(cbuf, sizeof(cbuf));
len = url_stream.gcount();
if(len > 0)
dest_file.write(cbuf, len);
if(abort){
dest_file.close();
url_stream.close();
return;
}
}
dest_file.close();
url_stream.close();
emit downloadFinishedST(this, url, filePath);
qDebug("download completed here: %s", (const char*)filePath.toUtf8());
}
}; };
class downloadThread : public QThread { class downloadThread : public QThread {
@@ -144,75 +67,19 @@ class downloadThread : public QThread {
void downloadFailure(QString url, QString reason); void downloadFailure(QString url, QString reason);
public: public:
downloadThread(QObject* parent) : QThread(parent), abort(false){} downloadThread(QObject* parent);
~downloadThread(){ ~downloadThread();
mutex.lock();
abort = true;
condition.wakeOne();
mutex.unlock();
qDeleteAll(subThreads);
wait();
}
void downloadUrl(QString url){ void downloadUrl(QString url);
QMutexLocker locker(&mutex); void setProxy(QString IP, int port, QString username, QString password);
if(downloading_list.contains(url)) return;
url_list << url;
downloading_list << url;
if(!isRunning()){
start();
}else{
condition.wakeOne();
}
}
protected: protected:
void run(){ void run();
forever{
if(abort)
return;
mutex.lock();
if(url_list.size() != 0){
QString url = url_list.takeFirst();
mutex.unlock();
subDownloadThread *st = new subDownloadThread(0, url);
subThreads << st;
connect(st, SIGNAL(downloadFinishedST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadedFile(subDownloadThread*, QString, QString)));
connect(st, SIGNAL(downloadFailureST(subDownloadThread*, QString, QString)), this, SLOT(propagateDownloadFailure(subDownloadThread*, QString, QString)));
st->start();
}else{
condition.wait(&mutex);
mutex.unlock();
}
}
}
protected slots:
void propagateDownloadedFile(subDownloadThread* st, QString url, QString path){
int index = subThreads.indexOf(st);
Q_ASSERT(index != -1);
subThreads.removeAt(index);
delete st;
emit downloadFinished(url, path);
mutex.lock();
index = downloading_list.indexOf(url);
Q_ASSERT(index != -1);
downloading_list.removeAt(index);
mutex.unlock();
}
void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason){ protected slots:
int index = subThreads.indexOf(st); void propagateDownloadedFile(subDownloadThread* st, QString url, QString path);
Q_ASSERT(index != -1); void propagateDownloadFailure(subDownloadThread* st, QString url, QString reason);
subThreads.removeAt(index);
delete st;
emit downloadFailure(url, reason);
mutex.lock();
index = downloading_list.indexOf(url);
Q_ASSERT(index != -1);
downloading_list.removeAt(index);
mutex.unlock();
}
}; };
#endif #endif

View File

@@ -47,8 +47,6 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
actionTorrent_Properties->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/properties.png"))); actionTorrent_Properties->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/properties.png")));
// tabBottom->setTabIcon(0, QIcon(QString::fromUtf8(":/Icons/log.png"))); // tabBottom->setTabIcon(0, QIcon(QString::fromUtf8(":/Icons/log.png")));
// tabBottom->setTabIcon(1, QIcon(QString::fromUtf8(":/Icons/filter.png"))); // tabBottom->setTabIcon(1, QIcon(QString::fromUtf8(":/Icons/filter.png")));
// Set default ratio
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/stare.png")));
// Set Download list model // Set Download list model
DLListModel = new QStandardItemModel(0,9); DLListModel = new QStandardItemModel(0,9);
@@ -65,6 +63,7 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
downloadList->setItemDelegate(DLDelegate); downloadList->setItemDelegate(DLDelegate);
// Hide hash column // Hide hash column
downloadList->hideColumn(HASH); downloadList->hideColumn(HASH);
loadHiddenColumns();
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool))); connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), this, SLOT(torrentAdded(QString, QTorrentHandle&, bool)));
connect(BTSession, SIGNAL(duplicateTorrent(QString)), this, SLOT(torrentDuplicate(QString))); connect(BTSession, SIGNAL(duplicateTorrent(QString)), this, SLOT(torrentDuplicate(QString)));
@@ -74,6 +73,8 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
connect(BTSession, SIGNAL(fastResumeDataRejected(QString)), this, SLOT(addFastResumeRejectedAlert(QString))); connect(BTSession, SIGNAL(fastResumeDataRejected(QString)), this, SLOT(addFastResumeRejectedAlert(QString)));
connect(BTSession, SIGNAL(aboutToDownloadFromUrl(QString)), this, SLOT(displayDownloadingUrlInfos(QString))); connect(BTSession, SIGNAL(aboutToDownloadFromUrl(QString)), this, SLOT(displayDownloadingUrlInfos(QString)));
connect(BTSession, SIGNAL(urlSeedProblem(QString, QString)), this, SLOT(addUrlSeedError(QString, QString))); connect(BTSession, SIGNAL(urlSeedProblem(QString, QString)), this, SLOT(addUrlSeedError(QString, QString)));
connect(BTSession, SIGNAL(UPnPError(QString)), this, SLOT(displayUPnPError(QString)));
connect(BTSession, SIGNAL(UPnPSuccess(QString)), this, SLOT(displayUPnPSuccess(QString)));
// Load last columns width for download list // Load last columns width for download list
if(!loadColWidthDLList()) { if(!loadColWidthDLList()) {
@@ -86,6 +87,8 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
connect(downloadList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&))); connect(downloadList, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(notifyTorrentDoubleClicked(const QModelIndex&)));
connect(downloadList->header(), SIGNAL(sectionPressed(int)), this, SLOT(sortDownloadList(int))); connect(downloadList->header(), SIGNAL(sectionPressed(int)), this, SLOT(sortDownloadList(int)));
connect(downloadList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLListMenu(const QPoint&))); connect(downloadList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLListMenu(const QPoint&)));
downloadList->header()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(downloadList->header(), SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayDLHoSMenu(const QPoint&)));
connect(infoBar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayInfoBarMenu(const QPoint&))); connect(infoBar, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayInfoBarMenu(const QPoint&)));
// Actions // Actions
connect(actionPause, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPause_triggered())); connect(actionPause, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPause_triggered()));
@@ -93,15 +96,27 @@ DownloadingTorrents::DownloadingTorrents(QObject *parent, bittorrent *BTSession)
connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered())); connect(actionDelete, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_triggered()));
connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered())); connect(actionPreview_file, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionPreview_file_triggered()));
connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered())); connect(actionDelete_Permanently, SIGNAL(triggered()), (GUI*)parent, SLOT(on_actionDelete_Permanently_triggered()));
connect(actionOpen_destination_folder, SIGNAL(triggered()), (GUI*)parent, SLOT(openDestinationFolder()));
connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection())); connect(actionTorrent_Properties, SIGNAL(triggered()), this, SLOT(propertiesSelection()));
connect(actionBuy_it, SIGNAL(triggered()), (GUI*)parent, SLOT(goBuyPage()));
connect(actionHOSColName, SIGNAL(triggered()), this, SLOT(hideOrShowColumnName()));
connect(actionHOSColSize, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSize()));
connect(actionHOSColProgress, SIGNAL(triggered()), this, SLOT(hideOrShowColumnProgress()));
connect(actionHOSColDownSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnDownSpeed()));
connect(actionHOSColUpSpeed, SIGNAL(triggered()), this, SLOT(hideOrShowColumnUpSpeed()));
connect(actionHOSColSeedersLeechers, SIGNAL(triggered()), this, SLOT(hideOrShowColumnSeedersLeechers()));
connect(actionHOSColRatio, SIGNAL(triggered()), this, SLOT(hideOrShowColumnRatio()));
connect(actionHOSColEta, SIGNAL(triggered()), this, SLOT(hideOrShowColumnEta()));
// Set info Bar infos // Set info Bar infos
setInfoBar(tr("qBittorrent %1 started.", "e.g: qBittorrent v0.x started.").arg(QString::fromUtf8(""VERSION))); setInfoBar(tr("qBittorrent %1 started.", "e.g: qBittorrent v0.x started.").arg(QString::fromUtf8(""VERSION)));
setInfoBar(tr("Be careful, sharing copyrighted material without permission is against the law."), QString::fromUtf8("red"));
qDebug("Download tab built"); qDebug("Download tab built");
} }
DownloadingTorrents::~DownloadingTorrents() { DownloadingTorrents::~DownloadingTorrents() {
saveColWidthDLList(); saveColWidthDLList();
saveHiddenColumns();
delete DLDelegate; delete DLDelegate;
delete DLListModel; delete DLListModel;
} }
@@ -110,7 +125,7 @@ DownloadingTorrents::~DownloadingTorrents() {
void DownloadingTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) { void DownloadingTorrents::notifyTorrentDoubleClicked(const QModelIndex& index) {
unsigned int row = index.row(); unsigned int row = index.row();
QString hash = getHashFromRow(row); QString hash = getHashFromRow(row);
emit torrentDoubleClicked(hash); emit torrentDoubleClicked(hash, false);
} }
void DownloadingTorrents::addLogPeerBlocked(QString ip) { void DownloadingTorrents::addLogPeerBlocked(QString ip) {
@@ -130,7 +145,8 @@ unsigned int DownloadingTorrents::getNbTorrentsInList() const {
// Note: do not actually pause the torrent in BT session // Note: do not actually pause the torrent in BT session
void DownloadingTorrents::pauseTorrent(QString hash) { void DownloadingTorrents::pauseTorrent(QString hash) {
int row = getRowFromHash(hash); int row = getRowFromHash(hash);
Q_ASSERT(row != -1); if(row == -1)
return;
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)0.0)); DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)0.0));
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)0.0)); DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)0.0));
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1)); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
@@ -154,11 +170,14 @@ void DownloadingTorrents::setBottomTabEnabled(unsigned int index, bool b){
// Show torrent properties dialog // Show torrent properties dialog
void DownloadingTorrents::showProperties(const QModelIndex &index) { void DownloadingTorrents::showProperties(const QModelIndex &index) {
int row = index.row(); showPropertiesFromHash(DLListModel->data(DLListModel->index(index.row(), HASH)).toString());
QString hash = DLListModel->data(DLListModel->index(row, HASH)).toString(); }
void DownloadingTorrents::showPropertiesFromHash(QString hash) {
QTorrentHandle h = BTSession->getTorrentHandle(hash); QTorrentHandle h = BTSession->getTorrentHandle(hash);
properties *prop = new properties(this, BTSession, h); properties *prop = new properties(this, BTSession, h);
connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSizeAndProgress(QString))); connect(prop, SIGNAL(filteredFilesChanged(QString)), this, SLOT(updateFileSizeAndProgress(QString)));
connect(prop, SIGNAL(trackersChanged(QString)), BTSession, SLOT(saveTrackerFile(QString)));
prop->show(); prop->show();
} }
@@ -181,8 +200,16 @@ void DownloadingTorrents::deleteTorrent(QString hash) {
emit unfinishedTorrentsNumberChanged(nbTorrents); emit unfinishedTorrentsNumberChanged(nbTorrents);
} }
void DownloadingTorrents::displayUPnPError(QString msg) {
setInfoBar(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(msg), QColor("red"));
}
void DownloadingTorrents::displayUPnPSuccess(QString msg) {
DownloadingTorrents::setInfoBar(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(msg), QColor("blue"));
}
// Update Info Bar information // Update Info Bar information
void DownloadingTorrents::setInfoBar(QString info, QString color) { void DownloadingTorrents::setInfoBar(QString info, QColor color) {
static unsigned int nbLines = 0; static unsigned int nbLines = 0;
++nbLines; ++nbLines;
// Check log size, clear it if too big // Check log size, clear it if too big
@@ -190,7 +217,7 @@ void DownloadingTorrents::setInfoBar(QString info, QString color) {
infoBar->clear(); infoBar->clear();
nbLines = 1; nbLines = 1;
} }
infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>")); infoBar->append(QString::fromUtf8("<font color='grey'>")+ QTime::currentTime().toString(QString::fromUtf8("hh:mm:ss")) + QString::fromUtf8("</font> - <font color='") + color.name() +QString::fromUtf8("'><i>") + info + QString::fromUtf8("</i></font>"));
} }
void DownloadingTorrents::addFastResumeRejectedAlert(QString name) { void DownloadingTorrents::addFastResumeRejectedAlert(QString name) {
@@ -245,8 +272,6 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
QModelIndex index; QModelIndex index;
// Enable/disable pause/start action given the DL state // Enable/disable pause/start action given the DL state
QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes = downloadList->selectionModel()->selectedIndexes();
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
QString previewProgram = settings.value(QString::fromUtf8("Options/Misc/PreviewProgram"), QString()).toString();
bool has_pause = false, has_start = false, has_preview = false; bool has_pause = false, has_start = false, has_preview = false;
foreach(index, selectedIndexes) { foreach(index, selectedIndexes) {
if(index.column() == NAME) { if(index.column() == NAME) {
@@ -266,7 +291,7 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
has_pause = true; has_pause = true;
} }
} }
if(!previewProgram.isEmpty() && BTSession->isFilePreviewPossible(hash) && !has_preview) { if(BTSession->isFilePreviewPossible(hash) && !has_preview) {
myDLLlistMenu.addAction(actionPreview_file); myDLLlistMenu.addAction(actionPreview_file);
has_preview = true; has_preview = true;
} }
@@ -280,16 +305,174 @@ void DownloadingTorrents::displayDLListMenu(const QPoint& pos) {
myDLLlistMenu.addAction(actionSet_download_limit); myDLLlistMenu.addAction(actionSet_download_limit);
myDLLlistMenu.addAction(actionSet_upload_limit); myDLLlistMenu.addAction(actionSet_upload_limit);
myDLLlistMenu.addSeparator(); myDLLlistMenu.addSeparator();
myDLLlistMenu.addAction(actionOpen_destination_folder);
myDLLlistMenu.addAction(actionTorrent_Properties); myDLLlistMenu.addAction(actionTorrent_Properties);
myDLLlistMenu.addSeparator();
myDLLlistMenu.addAction(actionBuy_it);
// Call menu // Call menu
// XXX: why mapToGlobal() is not enough? // XXX: why mapToGlobal() is not enough?
myDLLlistMenu.exec(mapToGlobal(pos)+QPoint(10,60)); myDLLlistMenu.exec(mapToGlobal(pos)+QPoint(10,60));
} }
/*
* Hiding Columns functions
*/
// hide/show columns menu
void DownloadingTorrents::displayDLHoSMenu(const QPoint& pos){
QMenu hideshowColumn(this);
hideshowColumn.setTitle(tr("Hide or Show Column"));
for(int i=0; i<=ETA; i++) {
hideshowColumn.addAction(getActionHoSCol(i));
}
// Call menu
hideshowColumn.exec(mapToGlobal(pos)+QPoint(10,55));
}
// toggle hide/show a column
void DownloadingTorrents::hideOrShowColumn(int index) {
unsigned int nbVisibleColumns = 0;
unsigned int nbCols = DLListModel->columnCount();
// Count visible columns
for(unsigned int i=0; i<nbCols; ++i) {
if(!downloadList->isColumnHidden(i))
++nbVisibleColumns;
}
if(!downloadList->isColumnHidden(index)) {
// User wants to hide the column
// Is there at least one other visible column?
if(nbVisibleColumns <= 1) return;
// User can hide the column, do it.
downloadList->setColumnHidden(index, true);
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
--nbVisibleColumns;
} else {
// User want to display the column
downloadList->setColumnHidden(index, false);
getActionHoSCol(index)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
++nbVisibleColumns;
}
//resize all others non-hidden columns
for(unsigned int i=0; i<nbCols; ++i) {
if(!downloadList->isColumnHidden(i)) {
downloadList->setColumnWidth(i, (int)ceil(downloadList->columnWidth(i)+(downloadList->columnWidth(index)/nbVisibleColumns)));
}
}
}
// save the hidden columns in settings
void DownloadingTorrents::saveHiddenColumns() {
QSettings settings("qBittorrent", "qBittorrent");
QStringList ishidden_list;
short nbColumns = DLListModel->columnCount()-1;
for(short i=0; i<nbColumns; ++i){
if(downloadList->isColumnHidden(i)) {
ishidden_list << QString::fromUtf8(misc::toString(0).c_str());
} else {
ishidden_list << QString::fromUtf8(misc::toString(1).c_str());
}
}
settings.setValue("DownloadListColsHoS", ishidden_list.join(" "));
}
// load the previous settings, and hide the columns
bool DownloadingTorrents::loadHiddenColumns() {
bool loaded = false;
QSettings settings("qBittorrent", "qBittorrent");
QString line = settings.value("DownloadListColsHoS", QString()).toString();
QStringList ishidden_list;
if(!line.isEmpty()) {
ishidden_list = line.split(' ');
if(ishidden_list.size() == DLListModel->columnCount()-1) {
unsigned int listSize = ishidden_list.size();
for(unsigned int i=0; i<listSize; ++i){
downloadList->header()->resizeSection(i, ishidden_list.at(i).toInt());
}
loaded = true;
}
}
for(int i=0; i<DLListModel->columnCount()-1; i++) {
if(loaded && ishidden_list.at(i) == "0") {
downloadList->setColumnHidden(i, true);
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
} else {
getActionHoSCol(i)->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
}
}
return loaded;
}
void DownloadingTorrents::hideOrShowColumnName() {
hideOrShowColumn(NAME);
}
void DownloadingTorrents::hideOrShowColumnSize() {
hideOrShowColumn(SIZE);
}
void DownloadingTorrents::hideOrShowColumnProgress() {
hideOrShowColumn(PROGRESS);
}
void DownloadingTorrents::hideOrShowColumnDownSpeed() {
hideOrShowColumn(DLSPEED);
}
void DownloadingTorrents::hideOrShowColumnUpSpeed() {
hideOrShowColumn(UPSPEED);
}
void DownloadingTorrents::hideOrShowColumnSeedersLeechers() {
hideOrShowColumn(SEEDSLEECH);
}
void DownloadingTorrents::hideOrShowColumnRatio() {
hideOrShowColumn(RATIO);
}
void DownloadingTorrents::hideOrShowColumnEta() {
hideOrShowColumn(ETA);
}
void DownloadingTorrents::on_actionClearLog_triggered() { void DownloadingTorrents::on_actionClearLog_triggered() {
infoBar->clear(); infoBar->clear();
} }
// getter, return the action hide or show whose id is index
QAction* DownloadingTorrents::getActionHoSCol(int index) {
switch(index) {
case NAME :
return actionHOSColName;
break;
case SIZE :
return actionHOSColSize;
break;
case PROGRESS :
return actionHOSColProgress;
break;
case DLSPEED :
return actionHOSColDownSpeed;
break;
case UPSPEED :
return actionHOSColUpSpeed;
break;
case SEEDSLEECH :
return actionHOSColSeedersLeechers;
break;
case RATIO :
return actionHOSColRatio;
break;
case ETA :
return actionHOSColEta;
break;
default :
return NULL;
}
}
QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{ QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{
QStringList res; QStringList res;
QModelIndex index; QModelIndex index;
@@ -305,38 +488,12 @@ QStringList DownloadingTorrents::getSelectedTorrents(bool only_one) const{
return res; return res;
} }
void DownloadingTorrents::updateRatio() {
// Update ratio info
float ratio = 1.;
session_status sessionStatus = BTSession->getSessionStatus();
if(sessionStatus.total_payload_download == 0) {
if(sessionStatus.total_payload_upload == 0)
ratio = 1.;
else
ratio = 10.;
}else{
ratio = (double)sessionStatus.total_payload_upload / (double)sessionStatus.total_payload_download;
if(ratio > 10.)
ratio = 10.;
}
LCD_Ratio->display(QString(QByteArray::number(ratio, 'f', 1)));
if(ratio < 0.5) {
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/unhappy.png")));
}else{
if(ratio > 1.0) {
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/smile.png")));
}else{
lbl_ratio_icon->setPixmap(QPixmap(QString::fromUtf8(":/Icons/stare.png")));
}
}
}
void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) { void DownloadingTorrents::displayInfoBarMenu(const QPoint& pos) {
// Log Menu // Log Menu
QMenu myLogMenu(this); QMenu myLogMenu(this);
myLogMenu.addAction(actionClearLog); myLogMenu.addAction(actionClearLog);
// XXX: Why mapToGlobal() is not enough? // XXX: Why mapToGlobal() is not enough?
myLogMenu.exec(mapToGlobal(pos)+QPoint(22,383)); myLogMenu.exec(mapToGlobal(pos)+QPoint(44,305));
} }
void DownloadingTorrents::sortProgressColumnDelayed() { void DownloadingTorrents::sortProgressColumnDelayed() {
@@ -349,9 +506,6 @@ void DownloadingTorrents::sortProgressColumnDelayed() {
// get information from torrent handles and // get information from torrent handles and
// update download list accordingly // update download list accordingly
void DownloadingTorrents::updateDlList() { void DownloadingTorrents::updateDlList() {
// update global informations
LCD_UpSpeed->display(QString(QByteArray::number(BTSession->getPayloadUploadRate()/1024., 'f', 1))); // UP LCD
LCD_DownSpeed->display(QString(QByteArray::number(BTSession->getPayloadDownloadRate()/1024., 'f', 1))); // DL LCD
// browse handles // browse handles
QStringList unfinishedTorrents = BTSession->getUnfinishedTorrents(); QStringList unfinishedTorrents = BTSession->getUnfinishedTorrents();
QString hash; QString hash;
@@ -372,6 +526,12 @@ void DownloadingTorrents::updateDlList() {
Q_ASSERT(row != -1); Q_ASSERT(row != -1);
// No need to update a paused torrent // No need to update a paused torrent
if(h.is_paused()) continue; if(h.is_paused()) continue;
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) != -1) {
if(!downloadList->isColumnHidden(PROGRESS)) {
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
}
continue;
}
// Parse download state // Parse download state
// Setting download state // Setting download state
switch(h.state()) { switch(h.state()) {
@@ -384,47 +544,73 @@ void DownloadingTorrents::updateDlList() {
continue; continue;
case torrent_status::checking_files: case torrent_status::checking_files:
case torrent_status::queued_for_checking: case torrent_status::queued_for_checking:
if(BTSession->getTorrentsToPauseAfterChecking().indexOf(hash) == -1) {
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole); DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/time.png"))), Qt::DecorationRole);
setRowColor(row, QString::fromUtf8("grey")); setRowColor(row, QString::fromUtf8("grey"));
} if(!downloadList->isColumnHidden(PROGRESS)) {
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress())); DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
}
break; break;
case torrent_status::connecting_to_tracker: case torrent_status::connecting_to_tracker:
if(h.download_payload_rate() > 0) { if(h.download_payload_rate() > 0) {
// Display "Downloading" status when connecting if download speed > 0 // Display "Downloading" status when connecting if download speed > 0
if(!downloadList->isColumnHidden(ETA)) {
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash))); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
}
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole); DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
setRowColor(row, QString::fromUtf8("green")); setRowColor(row, QString::fromUtf8("green"));
}else{ }else{
if(!downloadList->isColumnHidden(ETA)) {
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1)); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
}
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/connecting.png"))), Qt::DecorationRole); DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/connecting.png"))), Qt::DecorationRole);
setRowColor(row, QString::fromUtf8("grey")); setRowColor(row, QString::fromUtf8("grey"));
} }
if(!downloadList->isColumnHidden(PROGRESS)) {
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress())); DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
}
if(!downloadList->isColumnHidden(DLSPEED)) {
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate())); DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
}
if(!downloadList->isColumnHidden(UPSPEED)) {
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate())); DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
}
break; break;
case torrent_status::downloading: case torrent_status::downloading:
case torrent_status::downloading_metadata: case torrent_status::downloading_metadata:
if(h.download_payload_rate() > 0) { if(h.download_payload_rate() > 0) {
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole); DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/downloading.png"))), Qt::DecorationRole);
if(!downloadList->isColumnHidden(ETA)) {
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash))); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)BTSession->getETA(hash)));
}
setRowColor(row, QString::fromUtf8("green")); setRowColor(row, QString::fromUtf8("green"));
}else{ }else{
DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/stalled.png"))), Qt::DecorationRole); DLListModel->setData(DLListModel->index(row, NAME), QVariant(QIcon(QString::fromUtf8(":/Icons/skin/stalled.png"))), Qt::DecorationRole);
if(!downloadList->isColumnHidden(ETA)) {
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1)); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
setRowColor(row, QString::fromUtf8("black"));
} }
setRowColor(row, QApplication::palette().color(QPalette::WindowText));
}
if(!downloadList->isColumnHidden(PROGRESS)) {
DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress())); DLListModel->setData(DLListModel->index(row, PROGRESS), QVariant((double)h.progress()));
}
if(!downloadList->isColumnHidden(DLSPEED)) {
DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate())); DLListModel->setData(DLListModel->index(row, DLSPEED), QVariant((double)h.download_payload_rate()));
}
if(!downloadList->isColumnHidden(UPSPEED)) {
DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate())); DLListModel->setData(DLListModel->index(row, UPSPEED), QVariant((double)h.upload_payload_rate()));
}
break; break;
default: default:
if(!downloadList->isColumnHidden(ETA)) {
DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1)); DLListModel->setData(DLListModel->index(row, ETA), QVariant((qlonglong)-1));
} }
}
if(!downloadList->isColumnHidden(SEEDSLEECH)) {
DLListModel->setData(DLListModel->index(row, SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+QString::fromUtf8("/")+misc::toQString(h.num_peers() - h.num_seeds(), true))); DLListModel->setData(DLListModel->index(row, SEEDSLEECH), QVariant(misc::toQString(h.num_seeds(), true)+QString::fromUtf8("/")+misc::toQString(h.num_peers() - h.num_seeds(), true)));
}
if(!downloadList->isColumnHidden(RATIO)) {
DLListModel->setData(DLListModel->index(row, RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash)))); DLListModel->setData(DLListModel->index(row, RATIO), QVariant(misc::toQString(BTSession->getRealRatio(hash))));
}
}catch(invalid_handle e) { }catch(invalid_handle e) {
continue; continue;
} }
@@ -560,11 +746,26 @@ void DownloadingTorrents::saveColWidthDLList() const{
qDebug("Saving columns width in download list"); qDebug("Saving columns width in download list");
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent")); QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
QStringList width_list; QStringList width_list;
unsigned int nbColumns = DLListModel->columnCount()-1; QStringList new_width_list;
for(unsigned int i=0; i<nbColumns; ++i) { short nbColumns = DLListModel->columnCount()-1;
width_list << misc::toQString(downloadList->columnWidth(i)); QString line = settings.value("DownloadListColsWidth", QString()).toString();
if(!line.isEmpty()) {
width_list = line.split(' ');
} }
settings.setValue(QString::fromUtf8("DownloadListColsWidth"), width_list.join(QString::fromUtf8(" "))); for(short i=0; i<nbColumns; ++i){
if(downloadList->columnWidth(i)<1 && width_list.size() == DLListModel->columnCount()-1 && width_list.at(i).toInt()>=1) {
// load the former width
new_width_list << width_list.at(i);
} else if(downloadList->columnWidth(i)>=1) {
// usual case, save the current width
new_width_list << QString::fromUtf8(misc::toString(downloadList->columnWidth(i)).c_str());
} else {
// default width
downloadList->resizeColumnToContents(i);
new_width_list << QString::fromUtf8(misc::toString(downloadList->columnWidth(i)).c_str());
}
}
settings.setValue(QString::fromUtf8("DownloadListColsWidth"), new_width_list.join(QString::fromUtf8(" ")));
qDebug("Download list columns width saved"); qDebug("Download list columns width saved");
} }
@@ -661,10 +862,10 @@ void DownloadingTorrents::portListeningFailure() {
} }
// Set the color of a row in data model // Set the color of a row in data model
void DownloadingTorrents::setRowColor(int row, QString color) { void DownloadingTorrents::setRowColor(int row, QColor color) {
unsigned int nbColumns = DLListModel->columnCount(); unsigned int nbColumns = DLListModel->columnCount()-1;
for(unsigned int i=0; i<nbColumns; ++i) { for(unsigned int i=0; i<nbColumns; ++i) {
DLListModel->setData(DLListModel->index(row, i), QVariant(QColor(color)), Qt::ForegroundRole); DLListModel->setData(DLListModel->index(row, i), QVariant(color), Qt::ForegroundRole);
} }
} }
@@ -681,5 +882,5 @@ int DownloadingTorrents::getRowFromHash(QString hash) const{
} }
void DownloadingTorrents::displayDownloadingUrlInfos(QString url) { void DownloadingTorrents::displayDownloadingUrlInfos(QString url) {
setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QString::fromUtf8("black")); setInfoBar(tr("Downloading '%1', please wait...", "e.g: Downloading 'xxx.torrent', please wait...").arg(url), QPalette::WindowText);
} }

View File

@@ -41,6 +41,10 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
bool delayedSorting; bool delayedSorting;
unsigned int nbTorrents; unsigned int nbTorrents;
Qt::SortOrder delayedSortingOrder; Qt::SortOrder delayedSortingOrder;
void hideOrShowColumn(int index);
bool loadHiddenColumns();
void saveHiddenColumns();
QAction* getActionHoSCol(int index);
public: public:
DownloadingTorrents(QObject *parent, bittorrent *BTSession); DownloadingTorrents(QObject *parent, bittorrent *BTSession);
@@ -54,7 +58,7 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
signals: signals:
void unfinishedTorrentsNumberChanged(unsigned int); void unfinishedTorrentsNumberChanged(unsigned int);
void torrentDoubleClicked(QString hash); void torrentDoubleClicked(QString hash, bool finished);
void torrentFinished(QString hash); void torrentFinished(QString hash);
protected slots: protected slots:
@@ -65,6 +69,7 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
void notifyTorrentDoubleClicked(const QModelIndex& index); void notifyTorrentDoubleClicked(const QModelIndex& index);
void on_actionSet_upload_limit_triggered(); void on_actionSet_upload_limit_triggered();
void displayDLListMenu(const QPoint& pos); void displayDLListMenu(const QPoint& pos);
void displayDLHoSMenu(const QPoint&);
void on_actionClearLog_triggered(); void on_actionClearLog_triggered();
void displayInfoBarMenu(const QPoint& pos); void displayInfoBarMenu(const QPoint& pos);
void addTorrent(QString hash); void addTorrent(QString hash);
@@ -76,21 +81,31 @@ class DownloadingTorrents : public QWidget, public Ui::downloading{
void torrentDuplicate(QString path); void torrentDuplicate(QString path);
void torrentCorrupted(QString path); void torrentCorrupted(QString path);
void portListeningFailure(); void portListeningFailure();
void setRowColor(int row, QString color); void setRowColor(int row, QColor color);
void displayDownloadingUrlInfos(QString url); void displayDownloadingUrlInfos(QString url);
void showProperties(const QModelIndex &index); void showProperties(const QModelIndex &index);
void hideOrShowColumnName();
void hideOrShowColumnSize();
void hideOrShowColumnProgress();
void hideOrShowColumnDownSpeed();
void hideOrShowColumnUpSpeed();
void hideOrShowColumnSeedersLeechers();
void hideOrShowColumnRatio();
void hideOrShowColumnEta();
void displayUPnPError(QString msg);
void displayUPnPSuccess(QString msg);
public slots: public slots:
void updateDlList(); void updateDlList();
void setInfoBar(QString info, QString color="black"); void setInfoBar(QString info, QColor color=QApplication::palette().color(QPalette::WindowText));
void pauseTorrent(QString hash); void pauseTorrent(QString hash);
void resumeTorrent(QString hash); void resumeTorrent(QString hash);
void updateRatio();
void deleteTorrent(QString hash); void deleteTorrent(QString hash);
void setBottomTabEnabled(unsigned int index, bool b); void setBottomTabEnabled(unsigned int index, bool b);
void propertiesSelection(); void propertiesSelection();
void sortProgressColumnDelayed(); void sortProgressColumnDelayed();
void updateFileSizeAndProgress(QString hash); void updateFileSizeAndProgress(QString hash);
void showPropertiesFromHash(QString hash);
}; };

125
src/engineSelect.ui Normal file
View File

@@ -0,0 +1,125 @@
<ui version="4.0" >
<class>engineSelect</class>
<widget class="QDialog" name="engineSelect" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>541</width>
<height>254</height>
</rect>
</property>
<property name="acceptDrops" >
<bool>true</bool>
</property>
<property name="windowTitle" >
<string>Search plugins</string>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="lbl_engines" >
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
<underline>true</underline>
</font>
</property>
<property name="text" >
<string>Installed search engines:</string>
</property>
</widget>
</item>
<item>
<widget class="QTreeWidget" name="pluginsTree" >
<property name="contextMenuPolicy" >
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode" >
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="uniformRowHeights" >
<bool>true</bool>
</property>
<property name="itemsExpandable" >
<bool>false</bool>
</property>
<column>
<property name="text" >
<string>Name</string>
</property>
</column>
<column>
<property name="text" >
<string>Url</string>
</property>
</column>
<column>
<property name="text" >
<string>Enabled</string>
</property>
</column>
<column>
<property name="text" >
<string/>
</property>
</column>
</widget>
</item>
<item>
<widget class="QLabel" name="getNewEngine_lbl" >
<property name="font" >
<font>
<italic>true</italic>
</font>
</property>
<property name="text" >
<string>You can get new search engine plugins here: &lt;a href="http:plugins.qbittorrent.org">http://plugins.qbittorrent.org&lt;/a></string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="installButton" >
<property name="text" >
<string>Install a new one</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="updateButton" >
<property name="text" >
<string>Check for updates</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="closeButton" >
<property name="text" >
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<action name="actionEnable" >
<property name="text" >
<string>Enable</string>
</property>
</action>
<action name="actionDisable" >
<property name="text" >
<string>Disable</string>
</property>
</action>
<action name="actionUninstall" >
<property name="text" >
<string>Uninstall</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

703
src/engineSelectDlg.cpp Normal file
View File

@@ -0,0 +1,703 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#include "engineSelectDlg.h"
#include "downloadThread.h"
#include "misc.h"
#include "pluginSource.h"
#include <QProcess>
#include <QHeaderView>
#include <QSettings>
#include <QMenu>
#include <QMessageBox>
#include <QFileDialog>
#include <QDropEvent>
#include <QInputDialog>
#ifdef HAVE_MAGICK
#include <Magick++.h>
using namespace Magick;
#endif
#ifdef HAVE_ZZIP
#include <zzip/zzip.h>
#endif
#define ENGINE_NAME 0
#define ENGINE_URL 1
#define ENGINE_STATE 2
#define ENGINE_ID 3
engineSelectDlg::engineSelectDlg(QWidget *parent) : QDialog(parent) {
setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
pluginsTree->header()->resizeSection(0, 170);
pluginsTree->header()->resizeSection(1, 220);
pluginsTree->hideColumn(ENGINE_ID);
actionEnable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_ok.png")));
actionDisable->setIcon(QIcon(QString::fromUtf8(":/Icons/button_cancel.png")));
actionUninstall->setIcon(QIcon(QString::fromUtf8(":/Icons/skin/remove.png")));
connect(actionEnable, SIGNAL(triggered()), this, SLOT(enableSelection()));
connect(actionDisable, SIGNAL(triggered()), this, SLOT(disableSelection()));
connect(pluginsTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(displayContextMenu(const QPoint&)));
downloader = new downloadThread(this);
connect(downloader, SIGNAL(downloadFinished(QString, QString)), this, SLOT(processDownloadedFile(QString, QString)));
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
loadSupportedSearchEngines(true);
connect(pluginsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(toggleEngineState(QTreeWidgetItem*, int)));
show();
}
engineSelectDlg::~engineSelectDlg() {
qDebug("Destroying engineSelectDlg");
saveSettings();
emit enginesChanged();
qDebug("Before deleting downloader");
delete downloader;
qDebug("Engine plugins dialog destroyed");
}
void engineSelectDlg::dropEvent(QDropEvent *event) {
event->acceptProposedAction();
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
QString file;
foreach(file, files) {
qDebug("dropped %s", file.toUtf8().data());
file = file.replace("file://", "");
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
downloader->downloadUrl(file);
continue;
}
if(file.endsWith(".py", Qt::CaseInsensitive)) {
QString plugin_name = file.split(QDir::separator()).last();
plugin_name.replace(".py", "");
installPlugin(file, plugin_name);
}
#ifdef HAVE_ZZIP
if(file.endsWith(".zip", Qt::CaseInsensitive)) {
installZipPlugin(file);
}
#endif
}
}
// Decode if we accept drag 'n drop or not
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
QString mime;
foreach(mime, event->mimeData()->formats()){
qDebug("mimeData: %s", mime.toUtf8().data());
}
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
event->acceptProposedAction();
}
}
void engineSelectDlg::saveSettings() {
qDebug("Saving engines settings");
QStringList known_engines;
QVariantList known_enginesEnabled;
QString engine;
foreach(engine, installed_engines.keys()) {
known_engines << engine;
known_enginesEnabled << QVariant(installed_engines.value(engine, true));
qDebug("Engine %s has state: %d", engine.toUtf8().data(), installed_engines.value(engine, true));
}
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
settings.setValue(QString::fromUtf8("SearchEngines/knownEngines"), known_engines);
settings.setValue(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), known_enginesEnabled);
}
void engineSelectDlg::on_updateButton_clicked() {
// Download version file from primary server
downloader->downloadUrl("http://www.dchris.eu/search_engine/versions.txt");
}
void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
int index = pluginsTree->indexOfTopLevelItem(item);
QString id = item->text(ENGINE_ID);
bool new_val = !installed_engines.value(id, true);
installed_engines[id] = new_val;
QString enabledTxt;
if(new_val){
enabledTxt = tr("True");
setRowColor(index, "green");
}else{
enabledTxt = tr("False");
setRowColor(index, "red");
}
item->setText(ENGINE_STATE, enabledTxt);
}
void engineSelectDlg::displayContextMenu(const QPoint& pos) {
QMenu myContextMenu(this);
QModelIndex index;
// Enable/disable pause/start action given the DL state
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
bool has_enable = false, has_disable = false;
QTreeWidgetItem *item;
foreach(item, items) {
QString id = item->text(ENGINE_ID);
if(installed_engines.value(id, true) and !has_disable) {
myContextMenu.addAction(actionDisable);
has_disable = true;
}
if(!installed_engines.value(id, true) and !has_enable) {
myContextMenu.addAction(actionEnable);
has_enable = true;
}
if(has_enable && has_disable) break;
}
myContextMenu.addSeparator();
myContextMenu.addAction(actionUninstall);
myContextMenu.exec(mapToGlobal(pos)+QPoint(12, 58));
}
void engineSelectDlg::on_closeButton_clicked() {
close();
}
void engineSelectDlg::on_actionUninstall_triggered() {
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
QTreeWidgetItem *item;
bool change = false;
bool error = false;
foreach(item, items) {
int index = pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1);
QString id = item->text(ENGINE_ID);
if(QFile::exists(":/search_engine/engines/"+id+".py")) {
error = true;
// Disable it instead
installed_engines.insert(id, false);
item->setText(ENGINE_STATE, tr("False"));
setRowColor(index, "red");
continue;
}else {
// Proceed with uninstall
// remove it from hard drive
QDir enginesFolder(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines");
QStringList filters;
filters << id+".*";
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
QString file;
foreach(file, files) {
enginesFolder.remove(file);
}
// Remove it from lists
installed_engines.remove(id);
delete item;
change = true;
}
}
if(error)
QMessageBox::warning(0, tr("Uninstall warning"), tr("Some plugins could not be uninstalled because they are included in qBittorrent.\n Only the ones you added yourself can be uninstalled.\nHowever, those plugins were disabled."));
else
QMessageBox::information(0, tr("Uninstall success"), tr("All selected plugins were uninstalled successfully"));
}
void engineSelectDlg::enableSelection() {
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
QTreeWidgetItem *item;
foreach(item, items) {
int index = pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1);
QString id = item->text(ENGINE_ID);
installed_engines.insert(id, true);
item->setText(ENGINE_STATE, tr("True"));
setRowColor(index, "green");
}
}
void engineSelectDlg::disableSelection() {
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
QTreeWidgetItem *item;
foreach(item, items) {
int index = pluginsTree->indexOfTopLevelItem(item);
Q_ASSERT(index != -1);
QString id = item->text(ENGINE_ID);
installed_engines.insert(id, false);
item->setText(ENGINE_STATE, tr("False"));
setRowColor(index, "red");
}
}
// Set the color of a row in data model
void engineSelectDlg::setRowColor(int row, QString color){
QTreeWidgetItem *item = pluginsTree->topLevelItem(row);
for(int i=0; i<pluginsTree->columnCount()-1; ++i){
item->setData(i, Qt::ForegroundRole, QVariant(QColor(color)));
}
}
bool engineSelectDlg::checkInstalled(QString plugin_name) const {
QProcess nova;
QStringList params;
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
params << "--supported_engines";
nova.start("python", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
QByteArray result = nova.readAll();
result = result.replace("\r", "");
result = result.replace("\n", "");
QList<QByteArray> plugins_list = result.split(',');
return plugins_list.contains(plugin_name.toUtf8());
}
void engineSelectDlg::loadSupportedSearchEngines(bool first) {
// Some clean up first
pluginsTree->clear();
QHash<QString, bool> old_engines;
if(first) {
QSettings settings(QString::fromUtf8("qBittorrent"), QString::fromUtf8("qBittorrent"));
QStringList known_engines = settings.value(QString::fromUtf8("SearchEngines/knownEngines"), QStringList()).toStringList();
QVariantList enabled = settings.value(QString::fromUtf8("SearchEngines/knownEnginesEnabled"), QList<QVariant>()).toList();
Q_ASSERT(known_engines.size() == enabled.size());
unsigned int nbKnownEngines = known_engines.size();
for(unsigned int i=0; i<nbKnownEngines; ++i) {
old_engines[known_engines.at(i)] = enabled.at(i).toBool();
}
} else {
old_engines = installed_engines;
}
installed_engines.clear();
QStringList params;
// Ask nova core for the supported search engines
QProcess nova;
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
params << "--supported_engines";
nova.start("python", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
QByteArray result = nova.readAll();
result = result.replace("\r", "");
result = result.replace("\n", "");
qDebug("read: %s", result.data());
QByteArray e;
QStringList supported_engines_ids;
foreach(e, result.split(',')) {
QString en = QString(e);
supported_engines_ids << en;
installed_engines[en] = old_engines.value(en, true);
}
params.clear();
params << misc::qBittorrentPath()+"search_engine"+QDir::separator()+"nova2.py";
params << "--supported_engines_infos";
nova.start("python", params, QIODevice::ReadOnly);
nova.waitForStarted();
nova.waitForFinished();
result = nova.readAll();
result = result.replace("\r", "");
result = result.replace("\n", "");
qDebug("read: %s", result.data());
unsigned int i = 0;
foreach(e, result.split(',')) {
QString id = supported_engines_ids.at(i);
QString nameUrlCouple(e);
QStringList line = nameUrlCouple.split('|');
if(line.size() != 2) continue;
QString enabledTxt;
if(installed_engines.value(id, true)) {
enabledTxt = tr("True");
} else {
enabledTxt = tr("False");
}
line << enabledTxt;
line << id;
QTreeWidgetItem *item = new QTreeWidgetItem(pluginsTree, line);
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
if(QFile::exists(iconPath)) {
// Good, we already have the icon
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
} else {
// Icon is missing, we must download it
downloader->downloadUrl(line.at(1)+"/favicon.ico");
}
if(installed_engines.value(id, true))
setRowColor(i, "green");
else
setRowColor(i, "red");
++i;
}
}
QList<QTreeWidgetItem*> engineSelectDlg::findItemsWithUrl(QString url){
QList<QTreeWidgetItem*> res;
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
if(url.startsWith(item->text(ENGINE_URL), Qt::CaseInsensitive))
res << item;
}
return res;
}
QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id){
QList<QTreeWidgetItem*> res;
for(int i=0; i<pluginsTree->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = pluginsTree->topLevelItem(i);
if(id == item->text(ENGINE_ID))
return item;
}
return 0;
}
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
float old_version = misc::getPluginVersion(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
return (new_version > old_version);
}
#ifdef HAVE_ZZIP
void engineSelectDlg::installZipPlugin(QString path) {
QStringList plugins;
QStringList favicons;
ZZIP_DIR* dir = zzip_dir_open(path.toUtf8().data(), 0);
if(!dir) {
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("Search engine plugin archive could not be read."));
return;
}
ZZIP_DIRENT dirent;
while(zzip_dir_read(dir, &dirent)) {
/* show info for first file */
QString name(dirent.d_name);
if(name.endsWith(".py", Qt::CaseInsensitive)) {
plugins << name;
} else {
if(name.endsWith(".png", Qt::CaseInsensitive)) {
favicons << name;
}
}
}
QString plugin;
std::cout << dirent.d_name << std::endl;
ZZIP_FILE* fp = zzip_file_open(dir, dirent.d_name, 0);
if (fp) {
char buf[10];
zzip_ssize_t len = zzip_file_read(fp, buf, 10);
if (len) {
/* show head of README */
std::cout << buf;
}
zzip_file_close(fp);
std::cout << std::endl;
}
foreach(plugin, plugins) {
QString plugin_name = plugin.split(QDir::separator()).last();
plugin_name.chop(3); // Remove .py extension
qDebug("Detected plugin %s in archive", plugin_name.toUtf8().data());
ZZIP_FILE* fp = zzip_file_open(dir, plugin.toUtf8().data(), 0);
if(fp) {
QTemporaryFile *tmpfile = new QTemporaryFile();
QString tmpPath;
// Write file
if(tmpfile->open()) {
tmpPath = tmpfile->fileName();
char buf[255];
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
while(len) {
tmpfile->write(buf, len);
len = zzip_file_read(fp, buf, 255);
}
zzip_file_close(fp);
tmpfile->close();
} else {
qDebug("Could not open tmp file");
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.toUtf8().data()));
delete tmpfile;
continue;
}
// Install plugin
installPlugin(tmpPath, plugin_name);
qDebug("installPlugin() finished");
delete tmpfile;
qDebug("Deleted tmpfile");
} else {
qDebug("Cannot read file in archive");
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.toUtf8().data()));
}
}
QString favicon;
foreach(favicon, favicons) {
qDebug("Detected favicon %s in archive", favicon.toUtf8().data());
// Ok we have a favicon here
QString plugin_name = favicon.split(QDir::separator()).last();
plugin_name.chop(4); // Remove .png extension
if(!QFile::exists(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py"))
continue;
// Check if we already have a favicon for this plugin
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".png";
if(QFile::exists(iconPath)) continue;
ZZIP_FILE* fp = zzip_file_open(dir, favicon.toUtf8().data(), 0);
if(fp) {
QFile dest_icon(iconPath);
// Write icon
if(dest_icon.open(QIODevice::WriteOnly | QIODevice::Text)) {
char buf[255];
zzip_ssize_t len = zzip_file_read(fp, buf, 255);
while(len) {
dest_icon.write(buf, len);
len = zzip_file_read(fp, buf, 255);
}
zzip_file_close(fp);
dest_icon.close();
// Update icon in list
QTreeWidgetItem *item = findItemWithID(plugin_name);
Q_ASSERT(item);
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
}
}
}
zzip_dir_close(dir);
}
#endif
void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
qDebug("Asked to install plugin at %s", path.toUtf8().data());
float new_version = misc::getPluginVersion(path);
qDebug("Version to be installed: %.2f", new_version);
if(!isUpdateNeeded(plugin_name, new_version)) {
qDebug("Apparently update it 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.toUtf8().data()));
return;
}
// Process with install
QString dest_path = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
bool update = false;
if(QFile::exists(dest_path)) {
// Backup in case install fails
QFile::copy(dest_path, dest_path+".bak");
QFile::remove(dest_path);
update = true;
}
// Copy the plugin
QFile::copy(path, dest_path);
// Check if this was correctly installed
if(!checkInstalled(plugin_name)) {
if(update) {
// Remove broken file
QFile::remove(dest_path);
// restore backup
QFile::copy(dest_path+".bak", dest_path);
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.toUtf8().data()));
return;
} else {
// Remove broken file
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.toUtf8().data()));
return;
}
}
// Install was successful, remove backup
if(update) {
QFile::remove(dest_path+".bak");
}
// Refresh plugin list
loadSupportedSearchEngines();
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.toUtf8().data()));
return;
} 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.toUtf8().data()));
return;
}
}
void engineSelectDlg::on_installButton_clicked() {
pluginSourceDlg *dlg = new pluginSourceDlg(this);
connect(dlg, SIGNAL(askForLocalFile()), this, SLOT(askForLocalPlugin()));
connect(dlg, SIGNAL(askForUrl()), this, SLOT(askForPluginUrl()));
}
void engineSelectDlg::askForPluginUrl() {
bool ok;
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
tr("URL:"), QLineEdit::Normal,
"http://", &ok);
if (ok && !url.isEmpty())
downloader->downloadUrl(url);
}
void engineSelectDlg::askForLocalPlugin() {
QStringList pathsList = QFileDialog::getOpenFileNames(0,
tr("Select search plugins"), QDir::homePath(),
#ifdef HAVE_ZZIP
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py *.zip)"));
#else
tr("qBittorrent search plugins")+QString::fromUtf8(" (*.py)"));
#endif
QString path;
foreach(path, pathsList) {
if(path.endsWith(".py", Qt::CaseInsensitive)) {
QString plugin_name = path.split(QDir::separator()).last();
plugin_name.replace(".py", "", Qt::CaseInsensitive);
installPlugin(path, plugin_name);
}
#ifdef HAVE_ZZIP
else {
if(path.endsWith(".zip", Qt::CaseInsensitive)) {
installZipPlugin(path);
}
}
#endif
}
}
bool engineSelectDlg::parseVersionsFile(QString versions_file, QString updateServer) {
qDebug("Checking if update is needed");
bool file_correct = false;
QFile versions(versions_file);
if(!versions.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug("* Error: Could not read versions.txt file");
return false;
}
bool updated = false;
while(!versions.atEnd()) {
QByteArray line = versions.readLine();
line.replace("\n", "");
line = line.trimmed();
if(line.isEmpty()) continue;
if(line.startsWith("#")) continue;
QList<QByteArray> list = line.split(' ');
if(list.size() != 2) continue;
QString plugin_name = QString(list.first());
if(!plugin_name.endsWith(":")) continue;
plugin_name.chop(1); // remove trailing ':'
bool ok;
float version = list.last().toFloat(&ok);
qDebug("read line %s: %.2f", plugin_name.toUtf8().data(), version);
if(!ok) continue;
file_correct = true;
if(isUpdateNeeded(plugin_name, version)) {
qDebug("Plugin: %s is outdated", plugin_name.toUtf8().data());
// Downloading update
downloader->downloadUrl(updateServer+plugin_name+".pyqBT"); // Actually this is really a .py
downloader->downloadUrl(updateServer+plugin_name+".png");
updated = true;
}else {
qDebug("Plugin: %s is up to date", plugin_name.toUtf8().data());
}
}
// Close file
versions.close();
// Clean up tmp file
QFile::remove(versions_file);
if(file_correct && !updated) {
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
}
return file_correct;
}
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
qDebug("engineSelectDlg received %s", url.toUtf8().data());
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
// Icon downloaded
QImage fileIcon;
#ifdef HAVE_MAGICK
try{
QFile::copy(filePath, filePath+".ico");
Image image(QDir::cleanPath(filePath+".ico").toUtf8().data());
// Convert to PNG since we can't read ICO format
image.magick("PNG");
// Resize to 16x16px
image.sample(Geometry(16, 16));
image.write(filePath.toUtf8().data());
QFile::remove(filePath+".ico");
}catch(Magick::Exception &error_){
qDebug("favicon conversion to PNG failure: %s", error_.what());
}
#endif
if(fileIcon.load(filePath)) {
QList<QTreeWidgetItem*> items = findItemsWithUrl(url);
QTreeWidgetItem *item;
foreach(item, items){
QString id = item->text(ENGINE_ID);
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
QFile::copy(filePath, iconPath);
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
}
}
// Delete tmp file
QFile::remove(filePath);
return;
}
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
if(!parseVersionsFile(filePath, "http://www.dchris.eu/search_engine/")) {
qDebug("Primary update server failed, try secondary");
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
}
QFile::remove(filePath);
return;
}
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
if(!parseVersionsFile(filePath, "http://hydr0g3n.free.fr/search_engine/")) {
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
}
QFile::remove(filePath);
return;
}
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
QString plugin_name = url.split('/').last();
plugin_name.replace(".pyqBT", "");
plugin_name.replace(".py", "");
installPlugin(filePath, plugin_name);
QFile::remove(filePath);
return;
}
#ifdef HAVE_ZZIP
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
installZipPlugin(filePath);
QFile::remove(filePath);
return;
}
#endif
}
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
qDebug("Could not download favicon: %s, reason: %s", url.toUtf8().data(), reason.toUtf8().data());
return;
}
if(url == "http://www.dchris.eu/search_engine/versions.txt") {
// Primary update server failed, try secondary
qDebug("Primary update server failed, try secondary");
downloader->downloadUrl("http://hydr0g3n.free.fr/search_engine/versions.txt");
return;
}
if(url == "http://hydr0g3n.free.fr/search_engine/versions.txt") {
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
return;
}
if(url.endsWith(".pyqBT", Qt::CaseInsensitive) || url.endsWith(".py", Qt::CaseInsensitive)) {
// a plugin update download has been failed
QString plugin_name = url.split('/').last();
plugin_name.replace(".pyqBT", "", 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.toUtf8().data()));
}
#ifdef HAVE_ZZIP
if(url.endsWith(".zip", Qt::CaseInsensitive)) {
QString plugin_name = url.split('/').last();
plugin_name.replace(".zip", "", 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.toUtf8().data()));
}
#endif
}

76
src/engineSelectDlg.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef ENGINE_SELECT_DLG_H
#define ENGINE_SELECT_DLG_H
#include "ui_engineSelect.h"
class downloadThread;
class QDropEvent;
class engineSelectDlg : public QDialog, public Ui::engineSelect{
Q_OBJECT
private:
// Search related
QHash<QString, bool> installed_engines;
downloadThread *downloader;
public:
engineSelectDlg(QWidget *parent);
~engineSelectDlg();
QList<QTreeWidgetItem*> findItemsWithUrl(QString url);
QTreeWidgetItem* findItemWithID(QString id);
protected:
bool parseVersionsFile(QString versions_file, QString updateServer);
bool isUpdateNeeded(QString plugin_name, float new_version) const;
bool checkInstalled(QString plugin_name) const;
signals:
void enginesChanged();
protected slots:
void saveSettings();
void on_closeButton_clicked();
void loadSupportedSearchEngines(bool first=false);
void toggleEngineState(QTreeWidgetItem*, int);
void setRowColor(int row, QString color);
void processDownloadedFile(QString url, QString filePath);
void handleDownloadFailure(QString url, QString reason);
void displayContextMenu(const QPoint& pos);
void enableSelection();
void disableSelection();
void on_actionUninstall_triggered();
void on_updateButton_clicked();
void on_installButton_clicked();
void dropEvent(QDropEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void installPlugin(QString plugin_path, QString plugin_name);
void askForLocalPlugin();
void askForPluginUrl();
#ifdef HAVE_ZZIP
void installZipPlugin(QString path);
#endif
};
#endif

186
src/eventmanager.cpp Normal file
View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "eventmanager.h"
#include "json.h"
#include <QDebug>
EventManager::EventManager(QObject *parent)
: QObject(parent)
{
revision = 0;
}
void EventManager::update(QVariantMap event)
{
revision++;
events << QPair<ulong, QVariantMap>(revision, event);
emit updated();
qDebug("Added the following event");
qDebug() << event;
/* QLinkedList<QPair<ulong, QVariantMap> >::iterator i;
for (i = events.begin(); i != events.end(); i++)
qDebug() << *i;*/
}
QVariant EventManager::querySince(ulong r) const
{
QVariantList list;
QLinkedListIterator<QPair<ulong, QVariantMap> > i(events);
i.toBack();
while (i.hasPrevious())
{
QPair<ulong, QVariantMap> pair = i.previous();
if (pair.first <= r)
break;
list.prepend(QVariant(pair.second));
}
QVariantMap map;
map["events"] = QVariant(list);
map["revision"] = QVariant((qulonglong) revision);
return QVariant(map);
}
bool EventManager::isUpdated(ulong r) const
{
return (r < revision);
}
void EventManager::addedTorrent(QString, QTorrentHandle& h)
{
QVariantMap event;
event["type"] = QVariant("add");
event["hash"] = QVariant(h.hash());
event["name"] = QVariant(h.name());
update(event);
}
void EventManager::deletedTorrent(QString hash)
{
QVariantMap event;
event["type"] = QVariant("delete");
event["hash"] = QVariant(hash);
QLinkedList<QPair<ulong, QVariantMap> >::iterator i = events.end();
bool loop = true;
while (loop && i != events.begin()) {
--i;
QVariantMap oldevent = i->second;
if(oldevent["hash"] == QVariant(hash))
{
if(oldevent["type"] == QVariant("add"))
loop = false;
i = events.erase(i);
}
}
update(event);
}
void EventManager::modifiedTorrent(QTorrentHandle h)
{
QString hash = h.hash();
QVariantMap event;
QVariant v;
if(h.is_paused())
v = QVariant("paused");
else
{
switch(h.state())
{
case torrent_status::finished:
case torrent_status::seeding:
v = QVariant("seeding");
break;
case torrent_status::checking_files:
case torrent_status::queued_for_checking:
v = QVariant("checking");
break;
case torrent_status::connecting_to_tracker:
if(h.download_payload_rate() > 0)
v = QVariant("downloading");
else
v = QVariant("connecting");
break;
case torrent_status::downloading:
case torrent_status::downloading_metadata:
if(h.download_payload_rate() > 0)
v = QVariant("downloading");
else
v = QVariant("stalled");
break;
default:
v = QVariant();
}
}
if(modify(hash, "state", v))
event["state"] = v;
v = QVariant((qlonglong)h.actual_size());
if(modify(hash, "size", v))
event["size"] = v;
v = QVariant(h.progress());
if(modify(hash, "progress", v))
event["progress"] = v;
v = QVariant(h.download_payload_rate());
if(modify(hash, "dlspeed", v))
event["dlspeed"] = v;
v = QVariant(h.upload_payload_rate());
if(modify(hash, "upspeed", v))
event["upspeed"] = v;
if(event.size() > 0)
{
event["type"] = QVariant("modify");
event["hash"] = QVariant(hash);
update(event);
}
}
bool EventManager::modify(QString hash, QString key, QVariant value)
{
QLinkedList<QPair<ulong, QVariantMap> >::iterator i = events.end();
while (i != events.begin()) {
--i;
QVariantMap event = i->second;
if(event["hash"] == QVariant(hash))
{
if(event["type"] == QVariant("add"))
return true;
if(event.contains(key))
{
if(event[key] == value)
return false;
else
{
if(event.size() <= 3)
i = events.erase(i);
else
i->second.remove(key);
return true;
}
}
}
}
return true;
}

55
src/eventmanager.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef EVENTMANAGER_H
#define EVENTMANAGER_H
#include "qtorrenthandle.h"
#include <QLinkedList>
#include <QPair>
#include <QVariant>
class EventManager : public QObject
{
Q_OBJECT
private:
ulong revision;
QLinkedList<QPair <ulong, QVariantMap> > events;
bool modify(QString hash, QString key, QVariant value);
protected:
void update(QVariantMap event);
public:
EventManager(QObject *parent = 0);
QVariant querySince(ulong r) const;
bool isUpdated(ulong r) const;
signals:
void updated();
public slots:
void addedTorrent(QString path, QTorrentHandle& h);
void deletedTorrent(QString hash);
void modifiedTorrent(QTorrentHandle h);
};
#endif

383
src/filterParserThread.h Normal file
View File

@@ -0,0 +1,383 @@
/*
* 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.
*
* Contact : chris@qbittorrent.org
*/
#ifndef FILTERPARSERTHREAD_H
#define FILTERPARSERTHREAD_H
#include <QThread>
#include <QFile>
#include <QDataStream>
#include <QMessageBox>
#include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp>
using namespace libtorrent;
using namespace std;
// P2B Stuff
#include <string.h>
#include <arpa/inet.h>
// End of P2B stuff
class FilterParserThread : public QThread {
Q_OBJECT
private:
session *s;
ip_filter filter;
bool abort;
QString filePath;
protected:
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 {
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
// PeerGuardian p2p file
parseP2BFilterFile(filePath);
} else {
// Default: eMule DAT format
parseDATFilterFile(filePath);
}
}
}
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) {
}
~FilterParserThread(){
abort = true;
wait();
}
// Parser for eMule ip filter in DAT format
void parseDATFilterFile(QString filePath) {
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;
bool IPv4 = true;
QFile file(filePath);
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(',');
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;
if(IPv4) {
//IPv4 addresses
IP = strStartIP.split('.');
address_v4 start((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
IP = strEndIP.split('.');
address_v4 last((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
// 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).toUtf8().data());
IP = strEndIP.split(':');
address_v6 last = address_v6::from_string(strEndIP.remove(':', 0).toUtf8().data());
// Apply to bittorrent session
filter.add_rule(start, last, ip_filter::blocked);
}
}
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).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
IP = strEndIP.split('.');
address_v4 last((IP.at(0).toInt() << 24) + (IP.at(1).toInt() << 16) + (IP.at(2).toInt() << 8) + IP.at(3).toInt());
// 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;
do {
int 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){
if(isRunning()) {
// Already parsing a filter, abort first
abort = true;
wait();
}
abort = false;
filePath = _filePath;
// Run it
start();
}
};
#endif

204
src/httpconnection.cpp Normal file
View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "httpconnection.h"
#include "httpserver.h"
#include "eventmanager.h"
#include "json.h"
#include <QTcpSocket>
#include <QDateTime>
#include <QStringList>
#include <QHttpRequestHeader>
#include <QHttpResponseHeader>
#include <QFile>
#include <QDebug>
HttpConnection::HttpConnection(QTcpSocket *socket, HttpServer *parent)
: QObject(parent), socket(socket), parent(parent)
{
socket->setParent(this);
connect(socket, SIGNAL(readyRead()), this, SLOT(read()));
connect(socket, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}
HttpConnection::~HttpConnection()
{
}
void HttpConnection::read()
{
QString input = socket->readAll();
qDebug(" -------");
qDebug("|REQUEST|");
qDebug(" -------");
qDebug(input.toAscii().constData());
parser.write(input);
if(parser.isError())
{
generator.setStatusLine(400, "Bad Request");
write();
}
else
if (parser.isParsable())
respond();
}
void HttpConnection::write()
{
QByteArray output = generator.toByteArray();
qDebug(" --------");
qDebug("|RESPONSE|");
qDebug(" --------");
qDebug()<<output;
socket->write(output);
socket->disconnectFromHost();
}
void HttpConnection::respond()
{
QStringList auth = parser.value("Authorization").split(" ", QString::SkipEmptyParts);
if (auth.size() != 2 || QString::compare(auth[0], "Basic", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth[1].toUtf8()))
{
generator.setStatusLine(401, "Unauthorized");
generator.setValue("WWW-Authenticate", "Basic realm=\"you know what\"");
write();
return;
}
QString url = parser.url();
QStringList list = url.split('/', QString::SkipEmptyParts);
if (list.contains(".") || list.contains(".."))
{
respondNotFound();
return;
}
if (list.size() == 0)
list.append("index.html");
if (list.size() == 2)
{
if (list[0] == "json")
{
if (list[1] == "events")
{
EventManager* manager = parent->eventManager();
uint r = parser.get("r").toUInt();
if(manager->isUpdated(r))
respondJson();
else
connect(manager, SIGNAL(updated()), this, SLOT(respondJson()));
return;
}
}
if (list[0] == "command")
{
QString command = list[1];
respondCommand(command);
generator.setStatusLine(200, "OK");
write();
return;
}
}
if (list[0] == "images")
list[0] = "Icons";
else
list.prepend("webui");
url = ":/" + list.join("/");
QFile file(url);
if(!file.open(QIODevice::ReadOnly))
{
respondNotFound();
return;
}
QString ext = list.last();
int index = ext.lastIndexOf('.') + 1;
if (index > 0)
ext.remove(0, index);
else
ext.clear();
QByteArray data = file.readAll();
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt(ext);
generator.setMessage(data);
write();
}
void HttpConnection::respondNotFound()
{
generator.setStatusLine(404, "File not found");
write();
}
void HttpConnection::respondJson()
{
EventManager* manager = parent->eventManager();
QString temp = parser.get("r");
uint r = parser.get("r").toUInt();
QVariant data = manager->querySince(r);
QString string = toJson(data);
generator.setStatusLine(200, "OK");
generator.setContentTypeByExt("js");
generator.setMessage(string);
write();
}
void HttpConnection::respondCommand(QString command)
{
if(command == "download")
{
QString urls = parser.post("urls");
QStringList list = urls.split('\n');
QStringList url_list_cleaned;
foreach(QString url, list){
url = url.trimmed();
if(!url.isEmpty()){
if(url_list_cleaned.indexOf(QRegExp(url, Qt::CaseInsensitive, QRegExp::FixedString)) < 0){
url_list_cleaned << url;
}
}
}
emit urlsReadyToBeDownloaded(url_list_cleaned);
return;
}
if(command == "resumeall")
{
emit resumeAllTorrents();
return;
}
if(command == "pauseall")
{
emit pauseAllTorrents();
return;
}
if(command == "resume")
{
emit resumeTorrent(parser.post("hash"));
return;
}
if(command == "pause")
{
emit pauseTorrent(parser.post("hash"));
return;
}
if(command == "delete")
{
emit deleteTorrent(parser.post("hash"));
return;
}
}

67
src/httpconnection.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef HTTPCONNECTION_H
#define HTTPCONNECTION_H
#include "httprequestparser.h"
#include "httpresponsegenerator.h"
#include <QObject>
class QTcpSocket;
class HttpServer;
class HttpConnection : public QObject
{
Q_OBJECT
private:
QTcpSocket *socket;
HttpServer *parent;
protected:
HttpRequestParser parser;
HttpResponseGenerator generator;
protected slots:
void write();
virtual void respond();
void respondJson();
void respondCommand(QString command);
void respondNotFound();
public:
HttpConnection(QTcpSocket *socket, HttpServer *parent);
~HttpConnection();
private slots:
void read();
signals:
void urlsReadyToBeDownloaded(const QStringList&);
void deleteTorrent(QString hash);
void resumeTorrent(QString hash);
void pauseTorrent(QString hash);
void resumeAllTorrents();
void pauseAllTorrents();
};
#endif

128
src/httprequestparser.cpp Normal file
View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "httprequestparser.h"
#include <QUrl>
#include <QDebug>
HttpRequestParser::HttpRequestParser()
{
headerDone = false;
messageDone = false;
error = false;
}
HttpRequestParser::~HttpRequestParser()
{
}
bool HttpRequestParser::isParsable() const
{
return !error && headerDone && isValid() && (messageDone || !hasContentLength() || contentLength() == 0);
}
bool HttpRequestParser::isError() const
{
return error;
}
QString HttpRequestParser::url() const
{
return path;
}
QString HttpRequestParser::message() const
{
if(isParsable())
return data;
return QString();
}
QString HttpRequestParser::get(const QString key) const
{
return getMap[key];
}
QString HttpRequestParser::post(const QString key) const
{
return postMap[key];
}
void HttpRequestParser::write(QString str)
{
while (!headerDone && str.size()>0)
{
int index = str.indexOf('\n') + 1;
if(index == 0)
{
data += str;
str.clear();
}
else
{
data += str.left(index);
str.remove(0, index);
if(data.right(4) == "\r\n\r\n")
{
QHttpRequestHeader::operator=(QHttpRequestHeader(data));
headerDone = true;
data.clear();
QUrl url = QUrl::fromEncoded(QHttpRequestHeader::path().toAscii());
path = url.path();
qDebug() << path;
QListIterator<QPair<QString, QString> > i(url.queryItems());
while (i.hasNext())
{
QPair<QString, QString> pair = i.next();
getMap[pair.first] = pair.second;
qDebug() << pair.first << "=" << get(pair.first);
}
}
}
}
if(!messageDone && str.size()>0)
{
if(hasContentLength())
{
data += str;
if(data.size() >= (int) contentLength())
{
data.resize(contentLength());
messageDone = true;
//parse POST data
if(contentType() == "application/x-www-form-urlencoded")
{
QUrl url;
url.setEncodedQuery(data.toAscii());
QListIterator<QPair<QString, QString> > i(url.queryItems());
while (i.hasNext())
{
QPair<QString, QString> pair = i.next();
postMap[pair.first] = pair.second;
qDebug() << pair.first << "=" << post(pair.first);
}
}
}
}
else
error = true;
}
}

50
src/httprequestparser.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef HTTPREQUESTPARSER_H
#define HTTPREQUESTPARSER_H
#include<QHttpRequestHeader>
class HttpRequestParser : public QHttpRequestHeader
{
private:
bool headerDone;
bool messageDone;
bool error;
QString data;
QString path;
QMap<QString, QString> postMap;
QMap<QString, QString> getMap;
public:
HttpRequestParser();
~HttpRequestParser();
bool isParsable() const;
bool isError() const;
QString url() const;
QString message() const;
QString get(const QString key) const;
QString post(const QString key) const;
void write(QString str);
};
#endif

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "httpresponsegenerator.h"
#include <QDebug>
void HttpResponseGenerator::setMessage(const QByteArray message)
{
HttpResponseGenerator::message = message;
setContentLength(message.size());
}
void HttpResponseGenerator::setMessage(const QString message)
{
setMessage(message.QString::toUtf8());
}
void HttpResponseGenerator::stripMessage()
{
message.clear();
}
QByteArray HttpResponseGenerator::toByteArray() const
{
return QHttpResponseHeader::toString().toUtf8() + message;
}
void HttpResponseGenerator::setContentTypeByExt(const QString ext)
{
if(ext == "css")
{
setContentType("text/css");
return;
}
if(ext == "gif")
{
setContentType("image/gif");
return;
}
if(ext == "htm" || ext == "html")
{
setContentType("text/html");
return;
}
if(ext == "js")
{
setContentType("text/javascript");
return;
}
if(ext == "png")
{
setContentType("image/x-png");
return;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef HTTPRESPONSEGENERATOR_H
#define HTTPRESPONSEGENERATOR_H
#include<QHttpResponseHeader>
class HttpResponseGenerator : public QHttpResponseHeader
{
private:
QByteArray message;
public:
void setMessage(const QByteArray message);
void setMessage(const QString message);
void stripMessage();
void setContentTypeByExt(const QString ext);
virtual QByteArray toByteArray() const;
};
#endif

98
src/httpserver.cpp Normal file
View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "httpserver.h"
#include "httpconnection.h"
#include "eventmanager.h"
#include "bittorrent.h"
#include <QTimer>
HttpServer::HttpServer(bittorrent *BTSession, int msec, QObject* parent) : QTcpServer(parent)
{
base64 = QByteArray(":").toBase64();
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
HttpServer::BTSession = BTSession;
manager = new EventManager(this);
//add torrents
QStringList list = BTSession->getUnfinishedTorrents() + BTSession->getFinishedTorrents();
QString hash;
foreach(hash, list)
{
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid())
manager->addedTorrent(QString(), h);
}
//connect BTSession to manager
connect(BTSession, SIGNAL(addedTorrent(QString, QTorrentHandle&, bool)), manager, SLOT(addedTorrent(QString, QTorrentHandle&)));
connect(BTSession, SIGNAL(deletedTorrent(QString)), manager, SLOT(deletedTorrent(QString)));
//set timer
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
timer->start(msec);
}
HttpServer::~HttpServer()
{
delete manager;
}
void HttpServer::newHttpConnection()
{
QTcpSocket *socket;
while((socket = nextPendingConnection()))
{
HttpConnection *connection = new HttpConnection(socket, this);
//connect connection to BTSession
connect(connection, SIGNAL(urlsReadyToBeDownloaded(const QStringList&)), BTSession, SLOT(downloadFromURLList(const QStringList&)));
connect(connection, SIGNAL(deleteTorrent(QString)), BTSession, SLOT(deleteTorrent(QString)));
connect(connection, SIGNAL(pauseTorrent(QString)), BTSession, SLOT(pauseTorrent(QString)));
connect(connection, SIGNAL(resumeTorrent(QString)), BTSession, SLOT(resumeTorrent(QString)));
connect(connection, SIGNAL(pauseAllTorrents()), BTSession, SLOT(pauseAllTorrents()));
connect(connection, SIGNAL(resumeAllTorrents()), BTSession, SLOT(resumeAllTorrents()));
}
}
void HttpServer::onTimer()
{
QStringList list = BTSession->getUnfinishedTorrents() + BTSession->getFinishedTorrents();
foreach(QString hash, list)
{
QTorrentHandle h = BTSession->getTorrentHandle(hash);
if(h.is_valid())
manager->modifiedTorrent(h);
}
}
void HttpServer::setAuthorization(QString username, QString password)
{
QString cat = username + ":" + password;
base64 = QByteArray(cat.toUtf8()).toBase64();
}
bool HttpServer::isAuthorized(QByteArray auth) const
{
return (auth == base64);
}
EventManager *HttpServer::eventManager() const
{
return manager;
}

53
src/httpserver.h Normal file
View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef HTTPSERVER_H
#define HTTPSERVER_H
#include <QTcpServer>
#include <QByteArray>
class bittorrent;
class EventManager;
class HttpServer : public QTcpServer
{
Q_OBJECT
private:
QByteArray base64;
bittorrent *BTSession;
EventManager *manager;
public:
HttpServer(bittorrent *BTSession, int msec, QObject* parent = 0);
~HttpServer();
void setAuthorization(QString username, QString password);
bool isAuthorized(QByteArray auth) const;
EventManager *eventManager() const;
private slots:
void newHttpConnection();
void onTimer();
};
#endif

View File

@@ -1,93 +1,105 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file>Icons/time.png</file>
<file>Icons/connection.png</file>
<file>Icons/locale.png</file>
<file>Icons/newmsg.png</file>
<file>Icons/url.png</file>
<file>Icons/button_ok.png</file>
<file>Icons/stare.png</file>
<file>Icons/log.png</file>
<file>Icons/qbittorrent32.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/description.png</file>
<file>Icons/qbittorrent16.png</file>
<file>Icons/exec.png</file>
<file>Icons/systemtray.png</file>
<file>Icons/unhappy.png</file>
<file>Icons/filter.png</file>
<file>Icons/sphere.png</file>
<file>Icons/mascot.png</file>
<file>Icons/encrypted.png</file>
<file>Icons/refresh.png</file>
<file>Icons/uparrow.png</file>
<file>Icons/style.png</file>
<file>Icons/wizard.png</file>
<file>Icons/password.png</file>
<file>Icons/rss.png</file>
<file>Icons/sphere2.png</file>
<file>Icons/smile.png</file>
<file>Icons/loading.png</file>
<file>Icons/button_cancel.png</file>
<file>Icons/qbittorrent22.png</file>
<file>Icons/proxy.png</file>
<file>Icons/add_folder.png</file>
<file>Icons/unavailable.png</file>
<file>Icons/add_file.png</file> <file>Icons/add_file.png</file>
<file>Icons/add_folder.png</file>
<file>Icons/bt_settings.png</file>
<file>Icons/button_cancel.png</file>
<file>Icons/button_ok.png</file>
<file>Icons/configure.png</file>
<file>Icons/connection.png</file>
<file>Icons/description.png</file>
<file>Icons/downarrow.png</file>
<file>Icons/download.png</file>
<file>Icons/edit_clear.png</file>
<file>Icons/encrypted.png</file>
<file>Icons/file.png</file>
<file>Icons/filter.png</file>
<file>Icons/folder.png</file>
<file>Icons/gear.png</file>
<file>Icons/home.png</file> <file>Icons/home.png</file>
<file>Icons/loading.png</file>
<file>Icons/locale.png</file>
<file>Icons/log.png</file>
<file>Icons/mascot.png</file>
<file>Icons/money.png</file>
<file>Icons/newmsg.png</file>
<file>Icons/password.png</file>
<file>Icons/proxy.png</file>
<file>Icons/qbittorrent16.png</file>
<file>Icons/qbittorrent22.png</file>
<file>Icons/qbittorrent32.png</file>
<file>Icons/refresh.png</file>
<file>Icons/rss16.png</file>
<file>Icons/rss32.png</file>
<file>Icons/smile.png</file>
<file>Icons/sphere.png</file>
<file>Icons/sphere2.png</file>
<file>Icons/splash.png</file> <file>Icons/splash.png</file>
<file>Icons/flags/portugal.png</file> <file>Icons/star.png</file>
<file>Icons/stare.png</file>
<file>Icons/style.png</file>
<file>Icons/subscribe.png</file>
<file>Icons/subscribe16.png</file>
<file>Icons/systemtray.png</file>
<file>Icons/time.png</file>
<file>Icons/unavailable.png</file>
<file>Icons/unhappy.png</file>
<file>Icons/unsubscribe.png</file>
<file>Icons/unsubscribe16.png</file>
<file>Icons/uparrow.png</file>
<file>Icons/url.png</file>
<file>Icons/wizard.png</file>
<file>Icons/flags/brazil.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/china.png</file>
<file>Icons/flags/denmark.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/france.png</file> <file>Icons/flags/france.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/greece.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/italy.png</file>
<file>Icons/flags/japan.png</file>
<file>Icons/flags/netherlands.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/poland.png</file>
<file>Icons/flags/portugal.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/russia.png</file>
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/south_korea.png</file>
<file>Icons/flags/spain.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/sweden.png</file>
<file>Icons/flags/turkey.png</file>
<file>Icons/flags/ukraine.png</file> <file>Icons/flags/ukraine.png</file>
<file>Icons/flags/united_kingdom.png</file> <file>Icons/flags/united_kingdom.png</file>
<file>Icons/flags/germany.png</file>
<file>Icons/flags/russia.png</file>
<file>Icons/flags/netherlands.png</file>
<file>Icons/flags/slovakia.png</file>
<file>Icons/flags/spain.png</file>
<file>Icons/flags/finland.png</file>
<file>Icons/flags/china_hong_kong.png</file>
<file>Icons/flags/spain_catalunya.png</file>
<file>Icons/flags/poland.png</file>
<file>Icons/flags/hungary.png</file>
<file>Icons/flags/norway.png</file>
<file>Icons/flags/denmark.png</file>
<file>Icons/flags/italy.png</file>
<file>Icons/flags/china.png</file>
<file>Icons/flags/brazil.png</file>
<file>Icons/flags/south_korea.png</file>
<file>Icons/flags/turkey.png</file>
<file>Icons/flags/sweden.png</file>
<file>Icons/flags/bulgaria.png</file>
<file>Icons/flags/romania.png</file>
<file>Icons/flags/japan.png</file>
<file>Icons/flags/greece.png</file>
<file>Icons/skin/properties.png</file>
<file>Icons/skin/play_all.png</file>
<file>Icons/skin/remove.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/open.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/delete.png</file>
<file>Icons/skin/connected.png</file>
<file>Icons/skin/url.png</file>
<file>Icons/skin/pause_all.png</file>
<file>Icons/skin/downloading.png</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/search.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/pause.png</file>
<file>Icons/skin/firewalled.png</file>
<file>Icons/skin/seeding.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/delete_perm.png</file>
<file>Icons/skin/connecting.png</file>
<file>Icons/skin/add.png</file> <file>Icons/skin/add.png</file>
<file>Icons/skin/stalled.png</file> <file>Icons/skin/connected.png</file>
<file>Icons/skin/new.png</file> <file>Icons/skin/connecting.png</file>
<file>Icons/skin/qb_question.png</file> <file>Icons/skin/delete.png</file>
<file>Icons/skin/delete_all.png</file> <file>Icons/skin/delete_all.png</file>
<file>Icons/skin/delete_perm.png</file>
<file>Icons/skin/disconnected.png</file>
<file>Icons/skin/downloading.png</file>
<file>Icons/skin/exit.png</file>
<file>Icons/skin/firewalled.png</file>
<file>Icons/skin/info.png</file> <file>Icons/skin/info.png</file>
<file>Icons/skin/new.png</file>
<file>Icons/skin/open.png</file>
<file>Icons/skin/pause.png</file>
<file>Icons/skin/pause_all.png</file>
<file>Icons/skin/paused.png</file>
<file>Icons/skin/play.png</file>
<file>Icons/skin/play_all.png</file>
<file>Icons/skin/preview.png</file>
<file>Icons/skin/properties.png</file>
<file>Icons/skin/qb_question.png</file>
<file>Icons/skin/remove.png</file>
<file>Icons/skin/search.png</file>
<file>Icons/skin/seeding.png</file>
<file>Icons/skin/settings.png</file>
<file>Icons/skin/stalled.png</file>
<file>Icons/skin/url.png</file>
</qresource> </qresource>
</RCC> </RCC>

103
src/json.cpp Normal file
View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "json.h"
QString toJson(QVariant v)
{
if (v.isNull())
return "null";
QString result;
switch(v.type())
{
case QVariant::Bool:
case QVariant::Double:
case QVariant::Int:
case QVariant::LongLong:
case QVariant::UInt:
case QVariant::ULongLong:
return v.value<QString>();
case QVariant::String:
{
QString s = v.value<QString>();
result = "\"";
for(int i=0; i<s.size(); i++)
{
QChar ch = s[i];
switch(ch.toAscii())
{
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
case '\"':
case '\'':
case '\\':
case '&':
result += '\\';
case '\0':
default:
result += ch;
}
}
result += "\"";
return result;
}
case QVariant::List:
{
result = "[";
QListIterator<QVariant> it(v.value<QVariantList>());
while (it.hasNext())
result += toJson(it.next()) + ",";
if(result.size() > 1)
result.chop(1);
result += "]";
return result;
}
case QVariant::Map:
{
result = "{";
QMapIterator<QString, QVariant> it(v.value<QVariantMap>());
while (it.hasNext()) {
it.next();
if(it.value().isValid())
result += toJson(QVariant(it.key())) + ":" + toJson(it.value()) + ",";
}
if(result.size() > 1)
result.chop(1);
result += "}";
return result;
}
default:
return "undefined";
}
}

29
src/json.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2007 by Ishan Arora
* ishanarora@gmail.com
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef JSON_H
#define JSON_H
#include <QVariant>
QString toJson(QVariant v);
#endif

View File

@@ -1,29 +1,29 @@
<!DOCTYPE RCC><RCC version="1.0"> <!DOCTYPE RCC><RCC version="1.0">
<qresource> <qresource>
<file>lang/qbittorrent_es.qm</file> <file>lang/qbittorrent_bg.qm</file>
<file>lang/qbittorrent_ko.qm</file> <file>lang/qbittorrent_ca.qm</file>
<file>lang/qbittorrent_hu.qm</file> <file>lang/qbittorrent_da.qm</file>
<file>lang/qbittorrent_sk.qm</file> <file>lang/qbittorrent_de.qm</file>
<file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_ro.qm</file>
<file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_el.qm</file> <file>lang/qbittorrent_el.qm</file>
<file>lang/qbittorrent_en.qm</file> <file>lang/qbittorrent_en.qm</file>
<file>lang/qbittorrent_pt_BR.qm</file> <file>lang/qbittorrent_es.qm</file>
<file>lang/qbittorrent_fi.qm</file>
<file>lang/qbittorrent_fr.qm</file> <file>lang/qbittorrent_fr.qm</file>
<file>lang/qbittorrent_hu.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_ja.qm</file>
<file>lang/qbittorrent_ko.qm</file>
<file>lang/qbittorrent_nb.qm</file>
<file>lang/qbittorrent_nl.qm</file> <file>lang/qbittorrent_nl.qm</file>
<file>lang/qbittorrent_pl.qm</file> <file>lang/qbittorrent_pl.qm</file>
<file>lang/qbittorrent_it.qm</file>
<file>lang/qbittorrent_zh.qm</file>
<file>lang/qbittorrent_ca.qm</file>
<file>lang/qbittorrent_pt.qm</file> <file>lang/qbittorrent_pt.qm</file>
<file>lang/qbittorrent_da.qm</file> <file>lang/qbittorrent_pt_BR.qm</file>
<file>lang/qbittorrent_tr.qm</file> <file>lang/qbittorrent_ro.qm</file>
<file>lang/qbittorrent_bg.qm</file> <file>lang/qbittorrent_ru.qm</file>
<file>lang/qbittorrent_de.qm</file> <file>lang/qbittorrent_sk.qm</file>
<file>lang/qbittorrent_sv.qm</file> <file>lang/qbittorrent_sv.qm</file>
<file>lang/qbittorrent_ja.qm</file> <file>lang/qbittorrent_tr.qm</file>
<file>lang/qbittorrent_fi.qm</file> <file>lang/qbittorrent_uk.qm</file>
<file>lang/qbittorrent_zh.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.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

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