mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-19 15:07:22 -06:00
Compare commits
614 Commits
release-3.
...
release-3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78c5d1c12f | ||
|
|
26fb54299b | ||
|
|
8ad82fc748 | ||
|
|
6e5aab7e98 | ||
|
|
fd39efd846 | ||
|
|
86dba7bd21 | ||
|
|
ac3b01e02e | ||
|
|
c267036427 | ||
|
|
0a0c8f307b | ||
|
|
e804e6e91a | ||
|
|
67e90d8d2a | ||
|
|
88abe2baff | ||
|
|
23748547a2 | ||
|
|
0b68c2356c | ||
|
|
0aaf4d1050 | ||
|
|
ce535c9492 | ||
|
|
3e6d76245c | ||
|
|
18f3517efa | ||
|
|
09e85900d7 | ||
|
|
6e09028682 | ||
|
|
59427ed6af | ||
|
|
340288f308 | ||
|
|
b76428420e | ||
|
|
22d9427e20 | ||
|
|
2e3cce6755 | ||
|
|
3f9b568808 | ||
|
|
a74bac20c4 | ||
|
|
ee6b968f2f | ||
|
|
00fae05507 | ||
|
|
9718b7d9ba | ||
|
|
bf0319f7b2 | ||
|
|
5b232528cc | ||
|
|
1f17a7836d | ||
|
|
e183c12bed | ||
|
|
3901f1555d | ||
|
|
84f6a82d98 | ||
|
|
100cff1738 | ||
|
|
d12ab5c048 | ||
|
|
055ad080a1 | ||
|
|
c771703c34 | ||
|
|
cf1c934083 | ||
|
|
c40bab0555 | ||
|
|
fadb208299 | ||
|
|
a6d2f5186f | ||
|
|
80f7e7868d | ||
|
|
40bdeb6c2d | ||
|
|
e0be09dd81 | ||
|
|
45dbfe80a1 | ||
|
|
db45c11d84 | ||
|
|
80a9896a4e | ||
|
|
9fefdc0fbb | ||
|
|
a16d0f8d28 | ||
|
|
472fff06dc | ||
|
|
91ffbfcf68 | ||
|
|
5cf976bbb2 | ||
|
|
e6b7a429ea | ||
|
|
12072e2b69 | ||
|
|
4cecb585bc | ||
|
|
66ce5c2557 | ||
|
|
f7f23d2ae9 | ||
|
|
e204562f4d | ||
|
|
c38987fc03 | ||
|
|
501ab07024 | ||
|
|
d0ebe08bf5 | ||
|
|
4fcd107ff3 | ||
|
|
25feee2366 | ||
|
|
a39ce57094 | ||
|
|
f7150edd44 | ||
|
|
be78188691 | ||
|
|
c75725e2d8 | ||
|
|
9b6122c328 | ||
|
|
99ba8a6a53 | ||
|
|
3a29d70dd5 | ||
|
|
2a2c947602 | ||
|
|
cd3dc3d645 | ||
|
|
f74c68e121 | ||
|
|
bfd30dcabc | ||
|
|
5d3ba06e9d | ||
|
|
e275c3ba52 | ||
|
|
3335a111e1 | ||
|
|
f7c8a499d5 | ||
|
|
ad7ad8f596 | ||
|
|
ef5b3b90c3 | ||
|
|
787b824d90 | ||
|
|
7e6dfa759c | ||
|
|
c6a6f85a5e | ||
|
|
421b45c553 | ||
|
|
0507876080 | ||
|
|
52db762677 | ||
|
|
94037cd017 | ||
|
|
a16de62103 | ||
|
|
df8def02f5 | ||
|
|
d60f2fc7df | ||
|
|
cc9ffc4767 | ||
|
|
f6a620cf6e | ||
|
|
a7cf8cea3f | ||
|
|
4cec06e67c | ||
|
|
41c953de46 | ||
|
|
986bb1a8ac | ||
|
|
a7ff38d389 | ||
|
|
f530bb7537 | ||
|
|
b5adbaef78 | ||
|
|
4a271d358f | ||
|
|
d9e51c220e | ||
|
|
d413c2061c | ||
|
|
9646794bb1 | ||
|
|
d40495a4be | ||
|
|
b63a68c9db | ||
|
|
1ba1f90058 | ||
|
|
8d025949d9 | ||
|
|
4291a08d47 | ||
|
|
3eac05b229 | ||
|
|
e39f8fafb1 | ||
|
|
50b9438fb6 | ||
|
|
357dfca3e0 | ||
|
|
8f8218c515 | ||
|
|
10e3009c46 | ||
|
|
cc5a737836 | ||
|
|
19ad58c394 | ||
|
|
8a905bed5d | ||
|
|
f781cc18a6 | ||
|
|
db174614f2 | ||
|
|
b9a061680d | ||
|
|
63df3090d5 | ||
|
|
ff8ff72eab | ||
|
|
8b547644b0 | ||
|
|
8605c4d4d3 | ||
|
|
e95fce3084 | ||
|
|
cf91685f6f | ||
|
|
e3e3f9a745 | ||
|
|
6a1497de92 | ||
|
|
fc65ba4ced | ||
|
|
73fb0a6309 | ||
|
|
2a411987db | ||
|
|
79617949c1 | ||
|
|
c3921ce2fe | ||
|
|
228f51fff9 | ||
|
|
08ee439a47 | ||
|
|
1a67282dd3 | ||
|
|
d2f1216987 | ||
|
|
b519700e33 | ||
|
|
12d9898b5d | ||
|
|
ef39fb0709 | ||
|
|
f068f63f0d | ||
|
|
57ca831d4d | ||
|
|
093c5d84d2 | ||
|
|
e922275c72 | ||
|
|
52ff53b0b0 | ||
|
|
65d3ca8c3f | ||
|
|
a16c264aa3 | ||
|
|
4642a35de7 | ||
|
|
50822a7476 | ||
|
|
f108e67dcc | ||
|
|
fb22940639 | ||
|
|
9c7bb08a03 | ||
|
|
b4680e82b7 | ||
|
|
cbf683878a | ||
|
|
fd5d3d0a48 | ||
|
|
5cb022c7b1 | ||
|
|
145950e766 | ||
|
|
8bde7d45b8 | ||
|
|
e0935fe14f | ||
|
|
c27aa7723e | ||
|
|
87b26a8dcc | ||
|
|
0b2b07e98e | ||
|
|
581d544f61 | ||
|
|
aa5fbd461f | ||
|
|
5394b77086 | ||
|
|
7028c2712a | ||
|
|
89be63da02 | ||
|
|
4f504f597d | ||
|
|
a9e9a9c835 | ||
|
|
4ae2f6c33b | ||
|
|
76d93c23b7 | ||
|
|
9d051ea523 | ||
|
|
ff279681ac | ||
|
|
5cc5552d9b | ||
|
|
42c74f9553 | ||
|
|
420fa82e8d | ||
|
|
70f2086202 | ||
|
|
ee21562426 | ||
|
|
b6c707c080 | ||
|
|
2eaeead67c | ||
|
|
7c7da44622 | ||
|
|
7c6da30705 | ||
|
|
2955bb5488 | ||
|
|
b78ccf289e | ||
|
|
b9b7786477 | ||
|
|
c3592304b6 | ||
|
|
2442411a5e | ||
|
|
f5c0cddea5 | ||
|
|
51e0e485f4 | ||
|
|
3889b4c101 | ||
|
|
247ba02bb8 | ||
|
|
25c6d8bf6b | ||
|
|
cf72318d74 | ||
|
|
a8c05ca02e | ||
|
|
1c8abd5c3b | ||
|
|
6ebe3897fb | ||
|
|
9faa44eabf | ||
|
|
f81ffdb78e | ||
|
|
2997c0fd1e | ||
|
|
193d273f00 | ||
|
|
e54cd8499e | ||
|
|
c984902fbf | ||
|
|
45cbf4bf25 | ||
|
|
2204f27de1 | ||
|
|
735b1cc6b9 | ||
|
|
a0da4a844c | ||
|
|
45ef98b077 | ||
|
|
ef7de49ec8 | ||
|
|
e937344761 | ||
|
|
f3c3912923 | ||
|
|
e728710430 | ||
|
|
ddd6533812 | ||
|
|
0d74131edd | ||
|
|
cf3a87ed55 | ||
|
|
632cba8b30 | ||
|
|
0afa4d260c | ||
|
|
3f2303a479 | ||
|
|
7bfa847d01 | ||
|
|
331219dda8 | ||
|
|
b2df917011 | ||
|
|
b727ce3b29 | ||
|
|
0e1eaa518b | ||
|
|
f893131b8d | ||
|
|
2ec1d9e39e | ||
|
|
bd2f9e436c | ||
|
|
ea38e4da4d | ||
|
|
4d66a0929c | ||
|
|
0520f01510 | ||
|
|
deaf322740 | ||
|
|
929cd30e33 | ||
|
|
eef2a0a200 | ||
|
|
fbb47ce08f | ||
|
|
f746760ccf | ||
|
|
67c0f47928 | ||
|
|
7c00e80f7a | ||
|
|
3a9cf70228 | ||
|
|
0f35bac605 | ||
|
|
1b24feb4f4 | ||
|
|
ee8a492954 | ||
|
|
e3cb9de365 | ||
|
|
a217988dbb | ||
|
|
81c6de48dc | ||
|
|
1217d8d021 | ||
|
|
3362766cb5 | ||
|
|
34da3e653d | ||
|
|
ddb5c0052d | ||
|
|
a8f0acecb7 | ||
|
|
8f1f57afe4 | ||
|
|
0209a9f41d | ||
|
|
bf3a6d1c20 | ||
|
|
256793aad7 | ||
|
|
a56c21551d | ||
|
|
fce3a2d5c0 | ||
|
|
675298bc73 | ||
|
|
6985b553af | ||
|
|
885eb64df3 | ||
|
|
2ce9aa20a5 | ||
|
|
be34f29b05 | ||
|
|
077e94342f | ||
|
|
09d7d9a360 | ||
|
|
438ee23d2c | ||
|
|
4f2d74f793 | ||
|
|
0e62a52e59 | ||
|
|
369561f8f7 | ||
|
|
69812bbf18 | ||
|
|
e33570625f | ||
|
|
2132704e1d | ||
|
|
38a6f4cc34 | ||
|
|
626a3b10d3 | ||
|
|
c7fa778732 | ||
|
|
e72cc4eaf9 | ||
|
|
c48407e038 | ||
|
|
7c7bb14a93 | ||
|
|
f9dc76928c | ||
|
|
5b7ea0e611 | ||
|
|
5437674fdf | ||
|
|
21f18d015d | ||
|
|
47d9c12f4b | ||
|
|
f6bbd9377f | ||
|
|
3baec1c327 | ||
|
|
bd657fe9ae | ||
|
|
64ebc5cfd6 | ||
|
|
84922dcdcf | ||
|
|
79976fbfce | ||
|
|
c702a7e426 | ||
|
|
336519b7b5 | ||
|
|
dfb2f8b3b3 | ||
|
|
8b99e29dc0 | ||
|
|
d6af4683bb | ||
|
|
32c813eece | ||
|
|
03f86a71de | ||
|
|
2db942e606 | ||
|
|
6668018b45 | ||
|
|
f21cafb605 | ||
|
|
3864a7fdc6 | ||
|
|
ff8f37b262 | ||
|
|
783f17ec4d | ||
|
|
46a0eb4aa4 | ||
|
|
2de5390c08 | ||
|
|
bfaa0789f0 | ||
|
|
eb73e7c1d4 | ||
|
|
63ed69789b | ||
|
|
983c19eb29 | ||
|
|
cef3c9a34d | ||
|
|
030dd9eed8 | ||
|
|
6721363f8c | ||
|
|
8bb2e98b90 | ||
|
|
58b600198f | ||
|
|
5281593bb6 | ||
|
|
0b20794672 | ||
|
|
8dd7014af6 | ||
|
|
7b736b6ae3 | ||
|
|
d475ab3881 | ||
|
|
b26724d12b | ||
|
|
3d71ea5bc0 | ||
|
|
1f4d01e512 | ||
|
|
9dbb2720b0 | ||
|
|
808f07250e | ||
|
|
ed45ff89f8 | ||
|
|
d59440f99e | ||
|
|
b80ced2b3a | ||
|
|
8873900df3 | ||
|
|
4d6bde9731 | ||
|
|
fb1772f019 | ||
|
|
cc503b36e8 | ||
|
|
0fe99fe10f | ||
|
|
e2aa8056af | ||
|
|
10bba1a632 | ||
|
|
5e400d3117 | ||
|
|
39e0c560cf | ||
|
|
bf13238ccf | ||
|
|
5b56717501 | ||
|
|
de84bb9824 | ||
|
|
496f843016 | ||
|
|
c1e2ddc843 | ||
|
|
ac74f14408 | ||
|
|
3a44b94cc7 | ||
|
|
948b2c0410 | ||
|
|
d4151c1832 | ||
|
|
365d039209 | ||
|
|
40c9727cb9 | ||
|
|
e6946042ae | ||
|
|
8e03f01518 | ||
|
|
6413537f45 | ||
|
|
e20a24ab2e | ||
|
|
47c31c8b03 | ||
|
|
f0d32e2cde | ||
|
|
62b803e268 | ||
|
|
9ea56ea5d4 | ||
|
|
7474c05209 | ||
|
|
647140c7c7 | ||
|
|
46c54e51ee | ||
|
|
9eee92b5c6 | ||
|
|
ac2c8a3d30 | ||
|
|
ee44fd54b6 | ||
|
|
0ed8ea9599 | ||
|
|
d9ef7c1b32 | ||
|
|
357334fb46 | ||
|
|
d0b54b0797 | ||
|
|
768e87b0da | ||
|
|
14187978c0 | ||
|
|
cac91737ac | ||
|
|
0b18e7bd60 | ||
|
|
61705fdd42 | ||
|
|
6dd3795b7a | ||
|
|
2e0c3f8751 | ||
|
|
e1e8471728 | ||
|
|
3c66997427 | ||
|
|
5ec2af7b5a | ||
|
|
d3b23e4b81 | ||
|
|
d76a84048b | ||
|
|
f21fbff1a8 | ||
|
|
c17a56cb8b | ||
|
|
b23608ec35 | ||
|
|
a8df699441 | ||
|
|
1b430abb16 | ||
|
|
e4062e0f35 | ||
|
|
e6f2926ccd | ||
|
|
263d45ca9d | ||
|
|
8e4b9f30bb | ||
|
|
f12b300298 | ||
|
|
2db4a661ee | ||
|
|
3c6ee3d57f | ||
|
|
e2a1211513 | ||
|
|
436b3a472a | ||
|
|
f177ecd50c | ||
|
|
e675625d0d | ||
|
|
326a74425f | ||
|
|
a217d97fda | ||
|
|
6959084f98 | ||
|
|
216bf9b477 | ||
|
|
3efb311076 | ||
|
|
3ac9b2525e | ||
|
|
831b47b8f9 | ||
|
|
ff10eafcbc | ||
|
|
38c0dbfc95 | ||
|
|
e739e81205 | ||
|
|
1bcfae5265 | ||
|
|
b249b461d7 | ||
|
|
af79ba433c | ||
|
|
14013c80c0 | ||
|
|
85a7c7303d | ||
|
|
f94214ae14 | ||
|
|
ca4fbe40dd | ||
|
|
91818417be | ||
|
|
577ace4971 | ||
|
|
776d91f663 | ||
|
|
b9ed94a098 | ||
|
|
800630a813 | ||
|
|
4854a1b2ee | ||
|
|
bcfb8f1f53 | ||
|
|
2afa282190 | ||
|
|
2b2ffca7d0 | ||
|
|
650e7018a7 | ||
|
|
1e0ac5c5f1 | ||
|
|
b9c087f46c | ||
|
|
9d5cfbcda9 | ||
|
|
ffe6292237 | ||
|
|
9d97c05889 | ||
|
|
2658b39434 | ||
|
|
f6cf07a147 | ||
|
|
4752fa5301 | ||
|
|
f40d3d1fc6 | ||
|
|
d7ffabe7ca | ||
|
|
b318f54fdf | ||
|
|
661ddd75b6 | ||
|
|
bc97208516 | ||
|
|
64c8f61bb1 | ||
|
|
7699b7ce6f | ||
|
|
347832d852 | ||
|
|
50b3a24371 | ||
|
|
4d5512dcca | ||
|
|
2e6c890883 | ||
|
|
f94d733c62 | ||
|
|
9032808112 | ||
|
|
a22137e06e | ||
|
|
973518694e | ||
|
|
d6caf1c839 | ||
|
|
6f7dca1f83 | ||
|
|
dbbf1a8fce | ||
|
|
27e18f0eb8 | ||
|
|
bbc667cb78 | ||
|
|
74d757f0f1 | ||
|
|
a224650281 | ||
|
|
54abcfcd93 | ||
|
|
627d74233b | ||
|
|
b29f6916cb | ||
|
|
7041a96fda | ||
|
|
6bc42cfd1a | ||
|
|
bd8b8a8c8f | ||
|
|
cf1a62cec3 | ||
|
|
fd6fcffa67 | ||
|
|
75fecf9645 | ||
|
|
04f3f9d29d | ||
|
|
fb7ba92f69 | ||
|
|
c259666e43 | ||
|
|
6609d3e89f | ||
|
|
f4c44ce128 | ||
|
|
a8276dd70f | ||
|
|
0c23d22472 | ||
|
|
e34cc79dad | ||
|
|
de712c6ba9 | ||
|
|
1396c63525 | ||
|
|
8cc9c64ff8 | ||
|
|
64665146e1 | ||
|
|
cdec6c9027 | ||
|
|
0172ab1f50 | ||
|
|
d76c4554e2 | ||
|
|
3d09f579d0 | ||
|
|
e95a017e33 | ||
|
|
2c91157926 | ||
|
|
d2697cd124 | ||
|
|
061fbeff56 | ||
|
|
77b3a0c4cd | ||
|
|
8340abbe63 | ||
|
|
3f8e76ce93 | ||
|
|
a2c48646ae | ||
|
|
78f5b960a0 | ||
|
|
f6236d13d1 | ||
|
|
6b56a8accf | ||
|
|
9febd608c6 | ||
|
|
e4282c10c7 | ||
|
|
575578428c | ||
|
|
6e1bfdef4c | ||
|
|
36da49667a | ||
|
|
ade1936f9c | ||
|
|
580642b08e | ||
|
|
b4a855f4c1 | ||
|
|
32a712f005 | ||
|
|
ea2b2937b9 | ||
|
|
a86961a22e | ||
|
|
2299580dc9 | ||
|
|
77786bb8bb | ||
|
|
9ee67364c2 | ||
|
|
4a76526417 | ||
|
|
f89c44e1a0 | ||
|
|
8ac83cfd42 | ||
|
|
f5b0008158 | ||
|
|
1be26cf2bd | ||
|
|
e01a61d816 | ||
|
|
25e04ee2f3 | ||
|
|
4d848e99f0 | ||
|
|
157b1eadfa | ||
|
|
5798fb959d | ||
|
|
35e04a795d | ||
|
|
e22f7570fe | ||
|
|
93bbda851a | ||
|
|
2c34aa4ea2 | ||
|
|
4f5ce16dcb | ||
|
|
df2068d734 | ||
|
|
033817f70b | ||
|
|
9c28977001 | ||
|
|
a55a27508a | ||
|
|
031d14e320 | ||
|
|
817b595222 | ||
|
|
56df8c2dd0 | ||
|
|
3715e8d2c8 | ||
|
|
ad2a18b3bc | ||
|
|
3eb61cfe83 | ||
|
|
1222dab6f8 | ||
|
|
8897001567 | ||
|
|
930161a0dd | ||
|
|
3aa0a845cd | ||
|
|
cc8419d344 | ||
|
|
f3dd93a42c | ||
|
|
13da85e859 | ||
|
|
cd5f904b53 | ||
|
|
274cde0396 | ||
|
|
3d40834c57 | ||
|
|
920aefddde | ||
|
|
7dafb384e9 | ||
|
|
2fc1487603 | ||
|
|
d6d0f422f5 | ||
|
|
bef8106d0f | ||
|
|
e502ce38ec | ||
|
|
7d73bddfd2 | ||
|
|
86a0eaf317 | ||
|
|
5f288d228d | ||
|
|
191cdc2849 | ||
|
|
427688cb34 | ||
|
|
d16d1fdb3a | ||
|
|
832272bf3a | ||
|
|
d87cea1fb7 | ||
|
|
dabbc49c20 | ||
|
|
9daf9e612b | ||
|
|
46e2e5342b | ||
|
|
c682d87a65 | ||
|
|
101b0ee58e | ||
|
|
60c0939e05 | ||
|
|
d32bb52390 | ||
|
|
4b5e7e6168 | ||
|
|
3eeed813d6 | ||
|
|
f1bce0b8e0 | ||
|
|
98dfb6302d | ||
|
|
d87a9bf7ec | ||
|
|
2b7c47c789 | ||
|
|
bafb189244 | ||
|
|
ab91fb87ea | ||
|
|
2ad10ea47e | ||
|
|
fc28f261cf | ||
|
|
a84518d69f | ||
|
|
17cba898e9 | ||
|
|
736f20538d | ||
|
|
6cee7483ba | ||
|
|
50a5e830ac | ||
|
|
81b3517166 | ||
|
|
14b5d15268 | ||
|
|
e75de01809 | ||
|
|
a29f2dcfaa | ||
|
|
2b2bc11d7d | ||
|
|
7129ad47f4 | ||
|
|
dcdb319653 | ||
|
|
c5ce99ebec | ||
|
|
e46c238f15 | ||
|
|
23ec562375 | ||
|
|
2c1d76c87f | ||
|
|
d222dece92 | ||
|
|
8b06f72a94 | ||
|
|
2e05777dc5 | ||
|
|
dd71355e8a | ||
|
|
c92eb1b17d | ||
|
|
a90ec4cce7 | ||
|
|
f1c2eae264 | ||
|
|
28fcbe9bc5 | ||
|
|
b18138bad0 | ||
|
|
0ef728d385 | ||
|
|
bd6afc2d86 | ||
|
|
de3c84bd0b | ||
|
|
e788445fe2 | ||
|
|
88237fbd1e | ||
|
|
6fba6796b8 | ||
|
|
2a2cb313cf | ||
|
|
30f21259f8 | ||
|
|
be62c34e46 | ||
|
|
e2ac4ebf9d | ||
|
|
b67a10c4a1 | ||
|
|
fc52248bf7 | ||
|
|
4725022f7e | ||
|
|
a3574d3e28 | ||
|
|
04300c11c3 | ||
|
|
1d6263385b | ||
|
|
62776df9e1 | ||
|
|
72c5bce6a4 | ||
|
|
41fa59f164 | ||
|
|
307ae459f2 | ||
|
|
027a08c92e | ||
|
|
1037d7f335 | ||
|
|
3e6706a371 | ||
|
|
48c1bd58dd | ||
|
|
cf89d892c6 | ||
|
|
d206b00a88 |
71
.travis.yml
71
.travis.yml
@@ -1,18 +1,30 @@
|
||||
language: cpp
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
osx_image: xcode7
|
||||
|
||||
env:
|
||||
matrix:
|
||||
# Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
|
||||
#- lt_branch=dist gui=true
|
||||
#- lt_branch=dist gui=false
|
||||
- lt_branch=RC_0_16 gui=true
|
||||
- lt_branch=RC_0_16 gui=false
|
||||
- lt_branch=RC_1_0 gui=true
|
||||
- lt_branch=RC_1_0 gui=false
|
||||
- lt_branch=RC_1_0 gui=true qt=4
|
||||
- lt_branch=RC_1_0 gui=true qt=5
|
||||
- lt_branch=RC_1_0 gui=false qt=4
|
||||
- lt_branch=RC_1_0 gui=false qt=5
|
||||
global:
|
||||
- secure: "OI9CUjj4lTb0HwwIZU5PbECU3hLlAL6KC8KsbwohG8/O3j5fLcnmDsK4Ad9us5cC39sS11Jcd1kDP2qRcCuST/glVNhLkcjKkiQerOfd5nQ/qL4JYfz/1mfP5mdpz9jHKzpLUIG+TXkbSTjP6VVmsb5KPT+3pKEdRFZB+Pu9+J8="
|
||||
- coverity_branch: coverity_scan
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: linux
|
||||
env: lt_branch=RC_1_0 gui=true qt=5
|
||||
- os: linux
|
||||
env: lt_branch=RC_1_0 gui=false qt=5
|
||||
|
||||
branches:
|
||||
except:
|
||||
- search_encoding_windows
|
||||
@@ -25,6 +37,7 @@ notifications:
|
||||
|
||||
# container-based builds
|
||||
sudo: false
|
||||
# TODO: osx builder does not enable cache yet, see: https://github.com/travis-ci/travis-ci/issues/4011
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
@@ -34,63 +47,75 @@ addons:
|
||||
project:
|
||||
name: "qbittorrent/qBittorrent"
|
||||
description: "Build submitted via Travis CI"
|
||||
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf"
|
||||
build_command_prepend: "./bootstrap.sh && ./configure $qbtconf && echo QMAKE_CC=$CC >> conf.pri && echo QMAKE_CXX=$CXX >> conf.pri"
|
||||
build_command: make
|
||||
branch_pattern: $coverity_branch
|
||||
notification_email: sledgehammer999@qbittorrent.org
|
||||
apt:
|
||||
sources:
|
||||
# sources list: https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
|
||||
- ubuntu-toolchain-r-test
|
||||
- boost-latest
|
||||
packages:
|
||||
# packages list: https://github.com/travis-ci/apt-package-whitelist/blob/master/ubuntu-precise
|
||||
- autoconf
|
||||
- automake
|
||||
- colormake
|
||||
- g++-4.8
|
||||
- libssl-dev
|
||||
- libboost-dev
|
||||
- libboost-system-dev
|
||||
- libgeoip-dev
|
||||
- libboost1.55-dev
|
||||
- libboost-system1.55-dev
|
||||
- libqt4-dev
|
||||
# Uncomment when Travis upgraded "Ubuntu 12.04 LTS" to a newer version whose repo will have a more up-to-date libtorrent package
|
||||
#- libtorrent-rasterbar6
|
||||
#- libboost-filesystem-dev
|
||||
|
||||
before_install:
|
||||
# Only allow specific build for coverity scan, others will stop
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [[ "$lt_branch" == "RC_1_0" && "$gui" == "true" ]]; then exit ; fi
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ] && ! [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" = "RC_1_0" -a "$gui" = true ]; then exit ; fi
|
||||
|
||||
- shopt -s expand_aliases
|
||||
- if ! [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then export CC=gcc-4.8 CXX=g++-4.8 ; fi
|
||||
- if [ "$TRAVIS_BRANCH" != "$coverity_branch" -a "$TRAVIS_OS_NAME" = "linux" ]; then dpkg-query -L ccache && export PATH="/usr/lib/ccache/:$PATH" ; fi
|
||||
- alias make="colormake -j3" # Using nprocs/2 sometimes may fail (gcc is killed by system)
|
||||
|
||||
- libt_path="$HOME/libt_install"
|
||||
- qbt_path="$HOME/qbt_install"
|
||||
- ltconf="$ltconf --prefix="$libt_path" --with-libgeoip=system"
|
||||
- qbtconf="$qbtconf --prefix="$qbt_path" PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
|
||||
- ltconf="$ltconf --prefix="$libt_path" --disable-geoip"
|
||||
- qbtconf="$qbtconf --prefix="$qbt_path" --with-qt4 PKG_CONFIG_PATH="$libt_path/lib/pkgconfig":$PKG_CONFIG_PATH"
|
||||
|
||||
# Options for specific branches
|
||||
- if [[ "$lt_branch" == "RC_0_16" ]]; then qbtconf="$qbtconf --with-libtorrent-rasterbar0.16" ; fi
|
||||
# Also setup a virtual display for after_success target when gui == true
|
||||
- if ! $gui; then qbtconf="$qbtconf --disable-gui" ; else export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ; fi
|
||||
- if [ "$gui" = false ]; then qbtconf="$qbtconf --disable-gui" ;
|
||||
elif [ "$TRAVIS_OS_NAME" = "linux" ]; then export "DISPLAY=:99.0" && /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16 ;
|
||||
fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then qbtconf="$qbtconf --disable-qt-dbus" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$qt" = 5 ]; then qbtconf="$qbtconf --with-qt4=no" ; fi
|
||||
|
||||
# Print settings
|
||||
- echo $lt_branch
|
||||
- echo $gui
|
||||
- echo $ltconf
|
||||
- echo $qbtconf
|
||||
- ccache -V && ccache --show-stats && ccache --zero-stats
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache -V && ccache --show-stats && ccache --zero-stats ; fi
|
||||
|
||||
install:
|
||||
- if ! [ "$lt_branch" == "dist" ]; then cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch --single-branch ; fi
|
||||
- if ! [ "$lt_branch" == "dist" ]; then cd libtorrent && ./autotool.sh && ./configure $ltconf && make install && cd "$TRAVIS_BUILD_DIR" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd "$HOME" && pwd && git clone --depth 1 https://github.com/arvidn/libtorrent.git --branch $lt_branch ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" -a "$lt_branch" != "dist" ]; then cd libtorrent && ./autotool.sh && ./configure $ltconf && make install && cd "$TRAVIS_BUILD_DIR" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update > /dev/null && brew install colormake libtorrent-rasterbar; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$qt" = 4 ]; then brew install qt; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$qt" = 5 ]; then brew install qt5 && brew link --force qt5; fi
|
||||
|
||||
script:
|
||||
- if [ "$TRAVIS_BRANCH" = "$coverity_branch" ]; then exit ; fi # Skip usual build when running coverity scan
|
||||
- ./bootstrap.sh && ./configure $qbtconf
|
||||
- make install
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then echo QMAKE_CC=$CC >> conf.pri && echo QMAKE_CXX=$CXX >> conf.pri ; fi
|
||||
- make && make install
|
||||
|
||||
after_success:
|
||||
- cd "$qbt_path/bin"
|
||||
- export LD_PRELOAD="$libt_path/lib/libtorrent-rasterbar.so:$LD_PRELOAD"
|
||||
- if $gui ; then ./qbittorrent --version ; else ./qbittorrent-nox --version ; fi
|
||||
- if [ "$gui" = true ]; then qbt_exe="qbittorrent" ; else qbt_exe="qbittorrent-nox" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd "$qbt_path/bin" && export LD_PRELOAD="$libt_path/lib/libtorrent-rasterbar.so:$LD_PRELOAD" ; fi
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then cd "src/$qbt_exe.app/Contents/MacOS" ; fi
|
||||
- ./$qbt_exe --version
|
||||
|
||||
after_script:
|
||||
- ccache --show-stats
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then ccache --show-stats ; fi
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[qbittorrent.qbittorrent_v3_2_x]
|
||||
[qbittorrent.qbittorrent_v3_3_x]
|
||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||
source_file = src/lang/qbittorrent_en.ts
|
||||
source_lang = en
|
||||
|
||||
@@ -10,18 +10,20 @@ int myFunction(int a)
|
||||
//code
|
||||
}
|
||||
|
||||
myClass::myClass(int *parent)
|
||||
void myFunction() {} // empty body
|
||||
|
||||
MyClass::MyClass(int *parent)
|
||||
: m_parent(parent)
|
||||
{
|
||||
//initialiaze
|
||||
//initialize
|
||||
}
|
||||
|
||||
int myClass::myMethod(int a)
|
||||
int MyClass::myMethod(int a)
|
||||
{
|
||||
//code
|
||||
}
|
||||
|
||||
class myOtherClass
|
||||
class MyOtherClass
|
||||
{
|
||||
public:
|
||||
//code
|
||||
@@ -31,10 +33,18 @@ private:
|
||||
//code
|
||||
};
|
||||
|
||||
namespace id
|
||||
namespace Name
|
||||
{
|
||||
//code
|
||||
}
|
||||
|
||||
// Lambdas
|
||||
[](int arg1, int arg2) -> bool { return arg1 < arg2; }
|
||||
|
||||
[this](int arg)
|
||||
{
|
||||
this->acc += arg;
|
||||
}
|
||||
```
|
||||
|
||||
#### b. Other code blocks ####
|
||||
@@ -75,6 +85,12 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
#### d. single-line blocks (lambdas, initializer lists etc.) ####
|
||||
```c++
|
||||
{} // empty - space before {
|
||||
{ body } // spaces around { and before }
|
||||
```
|
||||
|
||||
### 2. If blocks ###
|
||||
#### a. Multiple tests ####
|
||||
```c++
|
||||
@@ -119,16 +135,16 @@ Generally it will depend on the particular piece of code and would be determined
|
||||
|
||||
### 4. File encoding and line endings.###
|
||||
|
||||
UTF-8 and Unix-like line ending (LF). Unless some platform speficic files need other encodings/line endings.
|
||||
UTF-8 and Unix-like line ending (LF). Unless some platform specific files need other encodings/line endings.
|
||||
|
||||
### 5. Initialization lists.###
|
||||
Initialization lists should be vertical. This will allow for more easily readable diffs. The inilization colon should be indented and in its own line along with first argument. The rest of the arguments should be indented too and have the comma prepended.
|
||||
Initialization lists should be vertical. This will allow for more easily readable diffs. The initialization colon should be indented and in its own line along with first argument. The rest of the arguments should be indented too and have the comma prepended.
|
||||
```c++
|
||||
myClass::myClass(int a, int b, int c, int d)
|
||||
: priv_a(a)
|
||||
, priv_b(b)
|
||||
, priv_c(c)
|
||||
, priv_d(d)
|
||||
: m_a(a)
|
||||
, m_b(b)
|
||||
, m_c(c)
|
||||
, m_d(d)
|
||||
{
|
||||
//code
|
||||
}
|
||||
@@ -137,7 +153,7 @@ myClass::myClass(int a, int b, int c, int d)
|
||||
### 6. Enums.###
|
||||
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
|
||||
```c++
|
||||
enum days
|
||||
enum Days
|
||||
{
|
||||
Monday,
|
||||
Tuesday,
|
||||
@@ -149,7 +165,41 @@ enum days
|
||||
};
|
||||
```
|
||||
|
||||
### 7. Misc.###
|
||||
### 7. Names.###
|
||||
All names should be camelCased.
|
||||
|
||||
#### a. Type names and namespaces ####
|
||||
Type names and namespaces start with Upper case letter (except POD types).
|
||||
```c++
|
||||
class ClassName {}
|
||||
|
||||
struct StructName {}
|
||||
|
||||
enum EnumName {}
|
||||
|
||||
typedef QList<ClassName> SomeList;
|
||||
|
||||
namespace NamespaceName
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
#### b. Variable names ####
|
||||
Variable names start with lower case letter.
|
||||
```c++
|
||||
int myVar;
|
||||
```
|
||||
|
||||
#### c. Private member variable names ####
|
||||
Private member variable names start with lower case letter and should have ```m_``` prefix.
|
||||
```c++
|
||||
class MyClass
|
||||
{
|
||||
int m_myVar;
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Misc.###
|
||||
|
||||
* Line breaks for long lines with operation:
|
||||
|
||||
|
||||
158
Changelog
158
Changelog
@@ -1,110 +1,54 @@
|
||||
* Sat Aug 01 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.2
|
||||
- FEATURE: Allow to force reannounce DHT too (Chocobo1)
|
||||
- FEATURE: Implement an option to disable confirmation of torrent recheck (blaxspirit)
|
||||
- FEATURE: Allow to copy all the trackers with a keyboard shortcut (pmzqla)
|
||||
- BUGFIX: Fix torrent renaming. Closes #3398 (ngosang)
|
||||
- BUGFIX: Fix localhost address (::ffff:127.0.0.1) is not recognized when connecting to WebUI (Chocobo1)
|
||||
- BUGFIX: Fix '&' character in label name becomes accelerator key, closes #3454. (Chocobo1)
|
||||
- BUGFIX: Fix HTTP header parsing when torrent filename contains a semicolon. Closes #3511.
|
||||
- BUGFIX: Fix installing search plugin from local file. (sledgehammer999)
|
||||
- BUGFIX: Fix installing search plugin by drag-n-dropping file. (sledgehammer999)
|
||||
- COSMETIC: Update color scheme of completed.png icon. (sledgehammer999)
|
||||
- COSMETIC: Fix printing of the copyright symbol in the About dialog. (sledgehammer999)
|
||||
- COSMETIC: Minor changes in Preview File dialog (ngosang)
|
||||
- COSMETIC: Add Force Resume icon (ngosang)
|
||||
- COSMETIC: Add count of unread items to RSS tab label (pmzqla)
|
||||
- WEBUI: Ports between 1 and 65535 as in the GUI. Closes #1602 (ngosang)
|
||||
- WEBUI: Fix an error in Content tab when the torrent doesn't have metadata (ngosang)
|
||||
- WEBUI: New option Web UI port UPNP. Closes #3358 (ngosang)
|
||||
- WEBUI: Fix API Content Types. Closes #3393 (ngosang)
|
||||
- WEBUI: Fix empty trackers addition (ngosang)
|
||||
- WEBUI: Torrent download from hash. Closes #1173 (ngosang)
|
||||
- WEBUI: Fix sort by queue number (ngosang)
|
||||
- WEBUI: Open external links in a new window/tab (ngosang)
|
||||
- WEBUI: Massive increase in performance. (ngosang)
|
||||
- SEARCH: Search status per tab (DoumanAsh)
|
||||
- SEARCH: Remove the word 'torrent' in ExtraTorrent results (ngosang)
|
||||
- SEARCH: Prefer python3 over python2 on Linux and OS X (pmzqla)
|
||||
- SEARCH: Show notification if Python is not found and a search is started (pmzqla)
|
||||
- SEARCH: Update link to the Windows Python installer (pmzqla)
|
||||
- SEARCH: Improve checks for python. Print python version and path to log. (sledgehammer999)
|
||||
- SEARCH: Improve Python detection (ngosang)
|
||||
- OTHER: Improvements on the build system (Chocobo1)
|
||||
- OTHER: Bump minimum libtorrent version required to 1.0.6/0.16.19. (sledgehammer999)
|
||||
- OTHER: New translation: Slovenian
|
||||
|
||||
* Sat Jul 11 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.1
|
||||
- FEATURE: Change default preferences (ngosang)
|
||||
- FEATURE: Add "Add link to torrent" menu in TrayIconMenu. Closes #2918. (Chocobo1)
|
||||
- FEATURE: Allow to Open files from the properties pane by pressing "Enter" (pmzqla)
|
||||
- FEATURE: Add checkbox option for IpFilterTrackers. (Chocobo1)
|
||||
- FEATURE: Download-from-URL textbox change focus on tab key (Mayank Asthana)
|
||||
- FEATURE: Increase priority value of "High" (pmzqla)
|
||||
- FEATURE: Add 16 KiB, 8 MiB and 16 MiB piece sizes in Torrent Creator (ngosang)
|
||||
- BUGFIX: Disable Auto piece size when creating a new torrent (ngosang)
|
||||
- BUGFIX: Set default focus to cancel button in delete confirm dlg, closes #3085 (Chocobo1)
|
||||
- BUGFIX: Set default focus to ok button in add new torrent dlg (Chocobo1)
|
||||
- BUGFIX: Set default focus to no button in exit confirm box (Chocobo1)
|
||||
- BUGFIX: Fix Start Minimized checkbox in Options (ngosang)
|
||||
- BUGFIX: Remove limits on alternative speeds setting (LazyBui)
|
||||
- BUGFIX: Fix sorting torrents by ETA (pmzqla)
|
||||
- BUGFIX: Improve ratio calculation formula. Closes #3096. (Chocobo1)
|
||||
- BUGFIX: Clear missing files flag when resuming or force rechecking. Fixes issues in #2750. (sledgehammer999)
|
||||
- BUGFIX: Delete tempfile when downloading favicon.ico. Closes #3257. (sledgehammer999)
|
||||
- BUGFIX: Don't close downloadFromURL dialog when showing empty url warning (Chocobo1)
|
||||
- BUGFIX: Minimize to tray only if the relevant option is enabled. (sledgehammer999)
|
||||
- BUGFIX: Update disk space label after changing partition, closes #3309. (Chocobo1)
|
||||
- BUGFIX: Don't use a default upload limit. Closes #3275. (sledgehammer999)
|
||||
- BUGFIX: Fix Properties bar size when started minimized to tray. Closes #3206. (sledgehammer999)
|
||||
- COSMETIC: Change option text "Confirmation on exit when torrents are active" (Chocobo1)
|
||||
- COSMETIC: Enable to choose dark/light tray icons on all platforms. (Chocobo1)
|
||||
- COSMETIC: Use AllUppercase for label text (Chocobo1)
|
||||
- COSMETIC: changes in typography (ngosang)
|
||||
- COSMETIC: Menu revamp (Chocobo1)
|
||||
- COSMETIC: Revamp general tab info (Chocobo1)
|
||||
- COSMETIC: Better update message for users (Chocobo1)
|
||||
- COSMETIC: Fix ugly 'C++' wrapping in About dialog. (glassez)
|
||||
- WEBUI: Fix login and logout relative URLs (ngosang)
|
||||
- WEBUI: Fix resumeAll and pauseAll. Closes #3016 (ngosang)
|
||||
- WEBUI: Changes in title bar (ngosang)
|
||||
- WEBUI: Complete translatable strings (ngosang)
|
||||
- WEBUI: Minor changes in style (ngosang)
|
||||
- WEBUI: Fix Max connections and Time active in transfer information (ngosang)
|
||||
- WEBUI: New config - Global maximum number of upload slots (ngosang)
|
||||
- WEBUI: Display wasted data with friendly units. Closes #2994 (ngosang)
|
||||
- WEBUI: add delay in shutdown command in order to send out response msg (Chocobo1)
|
||||
- WEBUI: Option to hide Top Toolbar (ngosang)
|
||||
- WEBUI: Reorder the tabs/groups in Options window (ngosang)
|
||||
- WEBUI: Add new options (ngosang)
|
||||
- WEBUI: Increase API_VERSION due to changes in #3279, #3197, #3226 and #3040 (ngosang)
|
||||
- WEBUI: Add Web Seeds (HTTP Sources) tab (ngosang)
|
||||
- WEBUI: Don't update the tabs if tab's panel is collapsed (ngosang)
|
||||
- WEBUI: Fix alternative global rate limits. (ngosang)
|
||||
- SEARCH: Fix thepiratebay. Closes #3012 (ngosang)
|
||||
- SEARCH: Improve torrentz engine to return more results (ngosang)
|
||||
- SEARCH: Change width of columns in search tab. Closes #764 (ngosang)
|
||||
- SEARCH: Make strings translatable in seach engine (ngosang)
|
||||
- SEARCH: Aborting search engine process during closure. Close #2671 (DoumanAsh)
|
||||
- SEARCH: Perform searches in parallel (DoumanAsh)
|
||||
- SEARCH: Add Demonoid search engine (ngosang)
|
||||
- SEARCH: Minor fixes in search engines (ngosang)
|
||||
- SEARCH: Show the version of search engines (ngosang)
|
||||
- SEARCH: Update Legit Torrent to remove sgmllib (DoumanAsh)
|
||||
- SEARCH: Update KickassTorrents (ngosang)
|
||||
- SEARCH: Update BTDigg (ngosang)
|
||||
- SEARCH: Update Torrentz (ngosang)
|
||||
- SEARCH: Update ExtraTorrent (ngosang)
|
||||
- SEARCH: Update TorrentReactor (ngosang)
|
||||
- SEARCH: Fix Python 2 implementation of Torrentz (ngosang)
|
||||
- SEARCH: Cosmetic changes in search engine (ngosang)
|
||||
- SEARCH: Fix column sort in search engine. Closes #2621 (ngosang)
|
||||
- RSS: Update matching RSS articles while editing rules (pmzqla)
|
||||
- WINDOWS: Fix python detection from registry when multiple versions are installed (sledgehammer999)
|
||||
- LINUX: Fixes Linux issue for when the theme doesn't have a corresponding icon. (sledgehammer999)
|
||||
- OTHER: Correctly detect FreeBSD when configuring. (sledgehammer999, yurivict)
|
||||
- OTHER: Add translator to credits.
|
||||
- OTHER: New translation: Indonesian.
|
||||
- OTHER: Split Chinese locales into Chinese Simplified, Chinese Traditional(Taiwan), Chinese Traditional(Hong Kong). (sledgehammer999)
|
||||
* Sun Nov 29 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.0
|
||||
- FEATURE: Huge core code refactoring. Problems with labels, temp folders etc should be eliminated. Smoother UI should be observed too. (glassez)
|
||||
- FEATURE: Speed graph (Anton Lashkov)
|
||||
- FEATURE: Add multiple peers in Peers addition dialog. Closes #1563, #2245, #3133, #1419, #3287, #1419 (ngosang)
|
||||
- FEATURE: Allow to copy all peers with a keyboard shortcut (ngosang)
|
||||
- FEATURE: Use GeoIP2 database, allows for country resolution of IPv6 peers. It is no longer embedded in the program but downloaded and updated monthly. (glassez)
|
||||
- FEATURE: Add more "Run External Program" parameters, closes #3053, #238, #1291, #1522. (Chocobo1, glassez)
|
||||
- FEATURE: Add an option to allow the use of proxies only for torrents. Closes #2701. (pmzqla)
|
||||
- FEATURE: Detect network interface state changes. It should detect VPN connection resets. (Pawel Polewicz)
|
||||
- FEATURE: Switch to using c++11 (glassez)
|
||||
- FEATURE: Automatically add trackers to new downloads. (ngosang)
|
||||
- FEATURE: You can now choose the path to download for watched folders. (dsimakov, sledgehammer999)
|
||||
- FEATURE: Switch to Qt5 by default.
|
||||
- BUGFIX: Fix progress calculation in Content tab. Closes #2639 Closes #2752 (ngosang)
|
||||
- BUGFIX: Fix label filter. Closes #3429. (glassez)
|
||||
- BUGFIX: Fix "Run External Program Launches too Early" issue, closes #2107. (Chocobo1)
|
||||
- BUGFIX: Don't remove torrent contents parent folder, even it is empty. Closes #2244. (Chocobo1)
|
||||
- BUGFIX: Always apply filter for manually banned IPs. Related #3988. (sledgehammer999)
|
||||
- BUGFIX: Fix reporting of tracker status. Closes #3101. (sledgehammer999)
|
||||
- BUGFIX: Don't connect to "any interface" when the configured network interface is missing. Closes #3943, #2741, #1159, #844 and #143. (sledgehammer999)
|
||||
- BUGFIX: Fix reordering of first column with Qt5. Closes #2835. (sledgehammer999)
|
||||
- COSMETIC: Add back "qBittorrent" in program updater title, closes #3549. (Chocobo1)
|
||||
- COSMETIC: Use infinity symbol rather than -1 for nb_connections (pmzqla)
|
||||
- COSMETIC: Move uTP options to it's own section (Chocobo1)
|
||||
- COSMETIC: Fix availability bar & progress bar height being too small on high DPI displays (Chocobo1)
|
||||
- COSMETIC: Fix availability label & progress label clipped on high DPI displays, closes #3237. (Chocobo1)
|
||||
- COSMETIC: Add tooltips/legend for availability bar & progress bar (Chocobo1)
|
||||
- COSMETIC: Use theme color for background in PropertiesWidget (Chocobo1)
|
||||
- COSMETIC: Replace horizontal line with border in bottom panel (Chocobo1)
|
||||
- COSMETIC: Various visual changes in the side panel. (Chocobo1)
|
||||
- COSMETIC: Use thin border for transfer list (Chocobo1)
|
||||
- COSMETIC: Make URL in "Add Torrent File..." clickable. Closes #3928. (Chocobo1)
|
||||
- COSMETIC: New view for errored torrents. (sledgehammer999)
|
||||
- WEBUI: Add information in General tab (ngosang)
|
||||
- WEBUI: Reorder "Super seeding mode" option in right click menu (ngosang)
|
||||
- WEBUI: Clean up JavaScript code (ngosang)
|
||||
- WEBUI: Added labels support. #648 (Felipe Barriga Richards, ngosnag)
|
||||
- WEBUI: Fix accessing the WebUI through IPv6 (ngosang)
|
||||
- WEBUI: Bump WebUI API_VERSION to 6.
|
||||
- WEBUI: Change selected color to differentiate from the progressbar. (Daniel Peukert, ngosang)
|
||||
- SEARCH: Add "Copy description page URL" button in search tab. Closes #2371. (pmzqla)
|
||||
- SEARCH: Add https_proxy env variable. This forces Python to use the HTTP proxy for HTTPS connections. (pmzqla)
|
||||
- SEARCH: Detect new plugin URL from clipboard (ngosang)
|
||||
- SEARCH: Update Torrentz trackers (ngosang)
|
||||
- WINDOWS: Fix german translation of the installer (netswap)
|
||||
- NOX: Don't ask the user questions in nox build when in non-interactive mode. Closes #3875. (sledgehammer999)
|
||||
- OTHER: Fixed typos, spelling correction (dartraiden)
|
||||
- OTHER: Fix need for restart to enable/disable peer countries resolution. (glassez)
|
||||
- OTHER: Unload the GeoIP db when disabled. (sledgehammer999)
|
||||
- OTHER: Reduce max value of "Disk cache size" to 1536MB for 32bit. Closes to #4028. (Chocobo1)
|
||||
- OTHER: Make "Download in sequential order" and "Download first and last piece first" options independent. (glassez)
|
||||
|
||||
* Sun May 10 2015 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.2.0
|
||||
- FEATURE: Show actual protocol for listen success/failure in the log. Needs libtorrent v1.0.0 (Gelmir)
|
||||
|
||||
10
INSTALL
10
INSTALL
@@ -14,15 +14,11 @@ qBittorrent - A BitTorrent client in C++ / Qt4
|
||||
|
||||
- pkg-config executable
|
||||
|
||||
- libtorrent-rasterbar by Arvid Norberg (>= 0.15.0)
|
||||
- libtorrent-rasterbar by Arvid Norberg (>= 1.0.6)
|
||||
-> http://www.libtorrent.net
|
||||
Be careful: another library (the one used by rTorrent) uses a similar name.
|
||||
|
||||
- libboost 1.34.x (libboost-filesystem°) + libasio
|
||||
or
|
||||
- libboost >= 1.35.x (libboost-system, libboost-filesystem°)
|
||||
|
||||
°libboost-filesystem is not needed if libtorrent-rasterbar >= v0.16.x is used
|
||||
- libboost >= 1.35.x (libboost-system)
|
||||
|
||||
- python >= 2.3 (needed by search engine)
|
||||
* Run time only dependency
|
||||
@@ -44,7 +40,7 @@ qBittorrent - A BitTorrent client in C++ / Qt4
|
||||
|
||||
- pkg-config executable
|
||||
|
||||
- libtorrent-rasterbar by Arvid Norberg (>= v0.15.0)
|
||||
- libtorrent-rasterbar by Arvid Norberg (>= v1.0.6)
|
||||
-> http://www.libtorrent.net
|
||||
Be careful: another library (the one used by rTorrent) uses a similar name.
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
qBittorrent - A BitTorrent client in Qt
|
||||
------------------------------------------
|
||||
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://travis-ci.org/qbittorrent/qBittorrent)
|
||||
[](https://scan.coverity.com/projects/5494)
|
||||
********************************
|
||||
### Description:
|
||||
@@ -32,6 +32,9 @@ http://www.qbittorrent.org
|
||||
or our wiki here:
|
||||
http://wiki.qbittorrent.org
|
||||
|
||||
Use the forum for troubleshooting before reporting bugs:
|
||||
http://forum.qbittorrent.org
|
||||
|
||||
Please report any bug (or feature request) to:
|
||||
http://bugs.qbittorrent.org
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ How to build
|
||||
First you need to create the conf.pri file in the same dir as this readme.os2 is.
|
||||
the conf.pri file has the following content:
|
||||
|
||||
##### conf.pri content beginn #####
|
||||
##### conf.pri content begin #####
|
||||
BINDIR = ./bin
|
||||
INCDIR = ./include
|
||||
LIBDIR = ./lib
|
||||
|
||||
602
configure
vendored
602
configure
vendored
@@ -601,10 +601,10 @@ EXPAND_BINDIR
|
||||
EXPAND_PREFIX
|
||||
zlib_LIBS
|
||||
zlib_CFLAGS
|
||||
qjson_LIBS
|
||||
qjson_CFLAGS
|
||||
libtorrent_LIBS
|
||||
libtorrent_CFLAGS
|
||||
qjson_LIBS
|
||||
qjson_CFLAGS
|
||||
BOOST_SYSTEM_LIB
|
||||
BOOST_LDFLAGS
|
||||
BOOST_CPPFLAGS
|
||||
@@ -690,6 +690,7 @@ infodir
|
||||
docdir
|
||||
oldincludedir
|
||||
includedir
|
||||
runstatedir
|
||||
localstatedir
|
||||
sharedstatedir
|
||||
sysconfdir
|
||||
@@ -714,9 +715,7 @@ ac_user_opts='
|
||||
enable_option_checking
|
||||
enable_dependency_tracking
|
||||
enable_silent_rules
|
||||
with_qt5
|
||||
with_libtorrent_rasterbar0_16
|
||||
with_geoip_database_embedded
|
||||
with_qt4
|
||||
with_qtsingleapplication
|
||||
with_qjson
|
||||
enable_debug
|
||||
@@ -743,10 +742,10 @@ PKG_CONFIG
|
||||
PKG_CONFIG_PATH
|
||||
PKG_CONFIG_LIBDIR
|
||||
QT_QMAKE
|
||||
libtorrent_CFLAGS
|
||||
libtorrent_LIBS
|
||||
qjson_CFLAGS
|
||||
qjson_LIBS
|
||||
libtorrent_CFLAGS
|
||||
libtorrent_LIBS
|
||||
zlib_CFLAGS
|
||||
zlib_LIBS'
|
||||
|
||||
@@ -787,6 +786,7 @@ datadir='${datarootdir}'
|
||||
sysconfdir='${prefix}/etc'
|
||||
sharedstatedir='${prefix}/com'
|
||||
localstatedir='${prefix}/var'
|
||||
runstatedir='${localstatedir}/run'
|
||||
includedir='${prefix}/include'
|
||||
oldincludedir='/usr/include'
|
||||
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
|
||||
@@ -1039,6 +1039,15 @@ do
|
||||
| -silent | --silent | --silen | --sile | --sil)
|
||||
silent=yes ;;
|
||||
|
||||
-runstatedir | --runstatedir | --runstatedi | --runstated \
|
||||
| --runstate | --runstat | --runsta | --runst | --runs \
|
||||
| --run | --ru | --r)
|
||||
ac_prev=runstatedir ;;
|
||||
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
|
||||
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
|
||||
| --run=* | --ru=* | --r=*)
|
||||
runstatedir=$ac_optarg ;;
|
||||
|
||||
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
|
||||
ac_prev=sbindir ;;
|
||||
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
|
||||
@@ -1176,7 +1185,7 @@ fi
|
||||
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
|
||||
datadir sysconfdir sharedstatedir localstatedir includedir \
|
||||
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
|
||||
libdir localedir mandir
|
||||
libdir localedir mandir runstatedir
|
||||
do
|
||||
eval ac_val=\$$ac_var
|
||||
# Remove trailing slashes.
|
||||
@@ -1329,6 +1338,7 @@ Fine tuning of the installation directories:
|
||||
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
|
||||
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
|
||||
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
|
||||
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
|
||||
--libdir=DIR object code libraries [EPREFIX/lib]
|
||||
--includedir=DIR C header files [PREFIX/include]
|
||||
--oldincludedir=DIR C header files for non-gcc [/usr/include]
|
||||
@@ -1383,14 +1393,7 @@ Optional Features:
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-qt5 Compile using Qt5 (default=no)
|
||||
--with-libtorrent-rasterbar0.16
|
||||
Compile using libtorrent-rasterbar 0.16.x series
|
||||
(default=no)
|
||||
--with-geoip-database-embedded
|
||||
Embed the GeoIP database in the qBittorrent
|
||||
executable (please follow instructions in
|
||||
src/gui/geoip/README) (default=no)
|
||||
--with-qt4 Compile using Qt4 (default=no)
|
||||
--with-qtsingleapplication=[system|shipped]
|
||||
Use the shipped qtsingleapplication library or the
|
||||
system one (default=shipped)
|
||||
@@ -1426,14 +1429,14 @@ Some influential environment variables:
|
||||
directories to add to pkg-config's search path
|
||||
PKG_CONFIG_LIBDIR
|
||||
path overriding pkg-config's built-in search path
|
||||
QT_QMAKE value of moc_location for QtCore >= 4.8.0, overriding pkg-config
|
||||
QT_QMAKE value of host_bins for Qt5Core >= 5.2.0, overriding pkg-config
|
||||
qjson_CFLAGS
|
||||
C compiler flags for qjson, overriding pkg-config
|
||||
qjson_LIBS linker flags for qjson, overriding pkg-config
|
||||
libtorrent_CFLAGS
|
||||
C compiler flags for libtorrent, overriding pkg-config
|
||||
libtorrent_LIBS
|
||||
linker flags for libtorrent, overriding pkg-config
|
||||
qjson_CFLAGS
|
||||
C compiler flags for qjson, overriding pkg-config
|
||||
qjson_LIBS linker flags for qjson, overriding pkg-config
|
||||
zlib_CFLAGS C compiler flags for zlib, overriding pkg-config
|
||||
zlib_LIBS linker flags for zlib, overriding pkg-config
|
||||
|
||||
@@ -4176,29 +4179,11 @@ fi
|
||||
# Define --wth-* and --enable-* arguments
|
||||
|
||||
|
||||
# Check whether --with-qt5 was given.
|
||||
if test "${with_qt5+set}" = set; then :
|
||||
withval=$with_qt5;
|
||||
# Check whether --with-qt4 was given.
|
||||
if test "${with_qt4+set}" = set; then :
|
||||
withval=$with_qt4;
|
||||
else
|
||||
with_qt5=no
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --with-libtorrent-rasterbar0.16 was given.
|
||||
if test "${with_libtorrent_rasterbar0_16+set}" = set; then :
|
||||
withval=$with_libtorrent_rasterbar0_16;
|
||||
else
|
||||
with_libtorrent_rasterbar0_16=no
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --with-geoip-database-embedded was given.
|
||||
if test "${with_geoip_database_embedded+set}" = set; then :
|
||||
withval=$with_geoip_database_embedded;
|
||||
else
|
||||
with_geoip_database_embedded=no
|
||||
with_qt4=no
|
||||
fi
|
||||
|
||||
|
||||
@@ -4429,7 +4414,6 @@ $as_echo "yes" >&6; }
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
enable_qt_dbus=no
|
||||
enable_geoip_database=no
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui" ;; #(
|
||||
*) :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_gui" >&5
|
||||
@@ -4471,13 +4455,106 @@ $as_echo "$enable_webui" >&6; }
|
||||
as_fn_error $? "Unknown option \"$enable_webui\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Qt5 should be enabled" >&5
|
||||
$as_echo_n "checking whether Qt5 should be enabled... " >&6; }
|
||||
case "x$with_qt5" in #(
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Qt4 should be enabled" >&5
|
||||
$as_echo_n "checking whether Qt4 should be enabled... " >&6; }
|
||||
case "x$with_qt4" in #(
|
||||
"xno") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
|
||||
if test -n "$QT_QMAKE"; then
|
||||
pkg_cv_QT_QMAKE="$QT_QMAKE"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
QT_QMAKE=$pkg_cv_QT_QMAKE
|
||||
|
||||
if test "x$QT_QMAKE" = x""; then :
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
as_ac_File=`$as_echo "ac_cv_file_$QT_QMAKE/qmake" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $QT_QMAKE/qmake" >&5
|
||||
$as_echo_n "checking for $QT_QMAKE/qmake... " >&6; }
|
||||
if eval \${$as_ac_File+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "$QT_QMAKE/qmake"; then
|
||||
eval "$as_ac_File=yes"
|
||||
else
|
||||
eval "$as_ac_File=no"
|
||||
fi
|
||||
fi
|
||||
eval ac_res=\$$as_ac_File
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
|
||||
QT_QMAKE="$QT_QMAKE/qmake"
|
||||
else
|
||||
as_ac_File=`$as_echo "ac_cv_file_$QT_QMAKE/qmake-qt5" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $QT_QMAKE/qmake-qt5" >&5
|
||||
$as_echo_n "checking for $QT_QMAKE/qmake-qt5... " >&6; }
|
||||
if eval \${$as_ac_File+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "$QT_QMAKE/qmake-qt5"; then
|
||||
eval "$as_ac_File=yes"
|
||||
else
|
||||
eval "$as_ac_File=no"
|
||||
fi
|
||||
fi
|
||||
eval ac_res=\$$as_ac_File
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
|
||||
QT_QMAKE="$QT_QMAKE/qmake-qt5"
|
||||
else
|
||||
QT_QMAKE=""
|
||||
fi
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.2.0" >&5
|
||||
$as_echo_n "checking for Qt5 qmake >= 5.2.0... " >&6; }
|
||||
if test "x$QT_QMAKE" != "x"; then :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $QT_QMAKE" >&5
|
||||
$as_echo "$QT_QMAKE" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
|
||||
$as_echo "not found" >&6; }
|
||||
|
||||
fi
|
||||
;; #(
|
||||
"xyes") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"QtCore >= 4.8.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "QtCore >= 4.8.0") 2>&5
|
||||
ac_status=$?
|
||||
@@ -4589,105 +4666,12 @@ else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
|
||||
$as_echo "not found" >&6; }
|
||||
|
||||
fi
|
||||
;; #(
|
||||
"xyes") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
|
||||
if test -n "$QT_QMAKE"; then
|
||||
pkg_cv_QT_QMAKE="$QT_QMAKE"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"Qt5Core >= 5.2.0\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "Qt5Core >= 5.2.0") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_QT_QMAKE=`$PKG_CONFIG --variable="host_bins" "Qt5Core >= 5.2.0" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
QT_QMAKE=$pkg_cv_QT_QMAKE
|
||||
|
||||
if test "x$QT_QMAKE" = x""; then :
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
as_ac_File=`$as_echo "ac_cv_file_$QT_QMAKE/qmake" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $QT_QMAKE/qmake" >&5
|
||||
$as_echo_n "checking for $QT_QMAKE/qmake... " >&6; }
|
||||
if eval \${$as_ac_File+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "$QT_QMAKE/qmake"; then
|
||||
eval "$as_ac_File=yes"
|
||||
else
|
||||
eval "$as_ac_File=no"
|
||||
fi
|
||||
fi
|
||||
eval ac_res=\$$as_ac_File
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
|
||||
QT_QMAKE="$QT_QMAKE/qmake"
|
||||
else
|
||||
as_ac_File=`$as_echo "ac_cv_file_$QT_QMAKE/qmake-qt5" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $QT_QMAKE/qmake-qt5" >&5
|
||||
$as_echo_n "checking for $QT_QMAKE/qmake-qt5... " >&6; }
|
||||
if eval \${$as_ac_File+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
test "$cross_compiling" = yes &&
|
||||
as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
|
||||
if test -r "$QT_QMAKE/qmake-qt5"; then
|
||||
eval "$as_ac_File=yes"
|
||||
else
|
||||
eval "$as_ac_File=no"
|
||||
fi
|
||||
fi
|
||||
eval ac_res=\$$as_ac_File
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
|
||||
QT_QMAKE="$QT_QMAKE/qmake-qt5"
|
||||
else
|
||||
QT_QMAKE=""
|
||||
fi
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Qt5 qmake >= 5.2.0" >&5
|
||||
$as_echo_n "checking for Qt5 qmake >= 5.2.0... " >&6; }
|
||||
if test "x$QT_QMAKE" != "x"; then :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $QT_QMAKE" >&5
|
||||
$as_echo "$QT_QMAKE" >&6; }
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
|
||||
$as_echo "not found" >&6; }
|
||||
|
||||
fi
|
||||
;; #(
|
||||
*) :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_qt5" >&5
|
||||
$as_echo "$with_qt5" >&6; }
|
||||
as_fn_error $? "Unknown option \"$with_qt5\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_qt4" >&5
|
||||
$as_echo "$with_qt4" >&6; }
|
||||
as_fn_error $? "Unknown option \"$with_qt4\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||
esac
|
||||
if test "x$QT_QMAKE" = "x"; then :
|
||||
as_fn_error $? "Could not find qmake" "$LINENO" 5
|
||||
@@ -4700,7 +4684,7 @@ case "x$enable_qt_dbus" in #(
|
||||
"xyes") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
if test "x$with_qt5" = "xyes"; then :
|
||||
if test "x$with_qt4" = "xno"; then :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Qt5DBus >= 5.2.0" >&5
|
||||
$as_echo_n "checking for Qt5DBus >= 5.2.0... " >&6; }
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
@@ -4811,8 +4795,11 @@ $as_echo_n "checking for boostlib >= $boost_lib_version_req... " >&6; }
|
||||
libsubdirs="lib"
|
||||
ax_arch=`uname -m`
|
||||
case $ax_arch in
|
||||
x86_64|ppc64|s390x|sparc64|aarch64)
|
||||
libsubdirs="lib64 lib lib64"
|
||||
x86_64)
|
||||
libsubdirs="lib64 libx32 lib lib64"
|
||||
;;
|
||||
ppc64|s390x|sparc64|aarch64|ppc64le)
|
||||
libsubdirs="lib64 lib lib64 ppc64le"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -4903,6 +4890,10 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
|
||||
if test "x$succeeded" != "xyes"; then
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
BOOST_CPPFLAGS=
|
||||
BOOST_LDFLAGS=
|
||||
_version=0
|
||||
if test "$ac_boost_path" != ""; then
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
@@ -4915,6 +4906,11 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
|
||||
done
|
||||
if test -z "$BOOST_CPPFLAGS"; then
|
||||
if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if test "$cross_compiling" != yes; then
|
||||
@@ -5294,222 +5290,6 @@ $as_echo "$as_me: Boost.System LIB: $BOOST_SYSTEM_LIB" >&6;}
|
||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to compile using libtorrent-rasterbar 0.16.x" >&5
|
||||
$as_echo_n "checking whether to compile using libtorrent-rasterbar 0.16.x... " >&6; }
|
||||
case "x$with_libtorrent_rasterbar0_16" in #(
|
||||
"xno") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
|
||||
$as_echo_n "checking for libtorrent... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.0.6\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.0.6") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.0.6" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.0.6\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.0.6") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.0.6" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.0.6" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.0.6" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.0.6) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details." "$LINENO" 5
|
||||
elif test $pkg_failed = untried; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
|
||||
See \`config.log' for more details" "$LINENO" 5; }
|
||||
else
|
||||
libtorrent_CFLAGS=$pkg_cv_libtorrent_CFLAGS
|
||||
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"
|
||||
fi ;; #(
|
||||
"xyes") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
|
||||
$as_echo_n "checking for libtorrent... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 0.16.19\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 0.16.19") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 0.16.19" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 0.16.19\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 0.16.19") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 0.16.19" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 0.16.19" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 0.16.19" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 0.16.19) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details." "$LINENO" 5
|
||||
elif test $pkg_failed = untried; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
|
||||
See \`config.log' for more details" "$LINENO" 5; }
|
||||
else
|
||||
libtorrent_CFLAGS=$pkg_cv_libtorrent_CFLAGS
|
||||
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"
|
||||
fi ;; #(
|
||||
*) :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libtorrent_rasterbar0_16" >&5
|
||||
$as_echo "$with_libtorrent_rasterbar0_16" >&6; }
|
||||
as_fn_error $? "Unknown option \"$with_libtorrent_rasterbar0_16\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to embed the GeoIP database" >&5
|
||||
$as_echo_n "checking whether to embed the GeoIP database... " >&6; }
|
||||
case "x$with_geoip_database_embedded" in #(
|
||||
"xno") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED" ;; #(
|
||||
"xyes") :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED" ;; #(
|
||||
*) :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_geoip_database_embedded" >&5
|
||||
$as_echo "$with_geoip_database_embedded" >&6; }
|
||||
as_fn_error $? "Unknown option \"$with_geoip_database_embedded\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which qtsingleapplication to use" >&5
|
||||
$as_echo_n "checking which qtsingleapplication to use... " >&6; }
|
||||
case "x$with_qtsingleapplication" in #(
|
||||
@@ -5527,7 +5307,7 @@ $as_echo "$with_qtsingleapplication" >&6; }
|
||||
as_fn_error $? "Unknown option \"$with_qtsingleapplication\". Use either \"system\" or \"shipped\"." "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
if test "x$with_qt5" = "xno"; then :
|
||||
if test "x$with_qt4" = "xyes"; then :
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking which qjson to use" >&5
|
||||
$as_echo_n "checking which qjson to use... " >&6; }
|
||||
case "x$with_qjson" in #(
|
||||
@@ -5640,6 +5420,99 @@ esac
|
||||
fi
|
||||
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libtorrent" >&5
|
||||
$as_echo_n "checking for libtorrent... " >&6; }
|
||||
|
||||
if test -n "$libtorrent_CFLAGS"; then
|
||||
pkg_cv_libtorrent_CFLAGS="$libtorrent_CFLAGS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.0.6\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.0.6") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_CFLAGS=`$PKG_CONFIG --cflags "libtorrent-rasterbar >= 1.0.6" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
if test -n "$libtorrent_LIBS"; then
|
||||
pkg_cv_libtorrent_LIBS="$libtorrent_LIBS"
|
||||
elif test -n "$PKG_CONFIG"; then
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent-rasterbar >= 1.0.6\""; } >&5
|
||||
($PKG_CONFIG --exists --print-errors "libtorrent-rasterbar >= 1.0.6") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }; then
|
||||
pkg_cv_libtorrent_LIBS=`$PKG_CONFIG --libs "libtorrent-rasterbar >= 1.0.6" 2>/dev/null`
|
||||
test "x$?" != "x0" && pkg_failed=yes
|
||||
else
|
||||
pkg_failed=yes
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent-rasterbar >= 1.0.6" 2>&1`
|
||||
else
|
||||
libtorrent_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent-rasterbar >= 1.0.6" 2>&1`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$libtorrent_PKG_ERRORS" >&5
|
||||
|
||||
as_fn_error $? "Package requirements (libtorrent-rasterbar >= 1.0.6) were not met:
|
||||
|
||||
$libtorrent_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details." "$LINENO" 5
|
||||
elif test $pkg_failed = untried; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
|
||||
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
|
||||
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
Alternatively, you may set the environment variables libtorrent_CFLAGS
|
||||
and libtorrent_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
|
||||
See \`config.log' for more details" "$LINENO" 5; }
|
||||
else
|
||||
libtorrent_CFLAGS=$pkg_cv_libtorrent_CFLAGS
|
||||
libtorrent_LIBS=$pkg_cv_libtorrent_LIBS
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"
|
||||
fi
|
||||
|
||||
|
||||
pkg_failed=no
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for zlib" >&5
|
||||
$as_echo_n "checking for zlib... " >&6; }
|
||||
@@ -5808,8 +5681,11 @@ extract() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# BSD sed needs an actual newline character in the substitute command
|
||||
new_line='
|
||||
'
|
||||
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
|
||||
string=$(echo " $*" | $SED -e 's: -:\n:g' -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//')
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[:space:]*//')
|
||||
SAVEIFS=$IFS
|
||||
IFS=$(printf "\n\b")
|
||||
for i in $string; do
|
||||
|
||||
73
configure.ac
73
configure.ac
@@ -12,23 +12,11 @@ AM_INIT_AUTOMAKE
|
||||
|
||||
# Define --wth-* and --enable-* arguments
|
||||
|
||||
AC_ARG_WITH(qt5,
|
||||
[AS_HELP_STRING([--with-qt5],
|
||||
[Compile using Qt5 (default=no)])],
|
||||
AC_ARG_WITH(qt4,
|
||||
[AS_HELP_STRING([--with-qt4],
|
||||
[Compile using Qt4 (default=no)])],
|
||||
[],
|
||||
[with_qt5=no])
|
||||
|
||||
AC_ARG_WITH(libtorrent-rasterbar0.16,
|
||||
[AS_HELP_STRING([--with-libtorrent-rasterbar0.16],
|
||||
[Compile using libtorrent-rasterbar 0.16.x series (default=no)])],
|
||||
[],
|
||||
[with_libtorrent_rasterbar0_16=no])
|
||||
|
||||
AC_ARG_WITH(geoip-database-embedded,
|
||||
[AS_HELP_STRING([--with-geoip-database-embedded],
|
||||
[Embed the GeoIP database in the qBittorrent executable (please follow instructions in src/gui/geoip/README) (default=no)])],
|
||||
[],
|
||||
[with_geoip_database_embedded=no])
|
||||
[with_qt4=no])
|
||||
|
||||
AC_ARG_WITH(qtsingleapplication,
|
||||
[AS_HELP_STRING([--with-qtsingleapplication=@<:@system|shipped@:>@],
|
||||
@@ -107,7 +95,6 @@ AS_CASE(["x$enable_gui"],
|
||||
["xno"],
|
||||
[AC_MSG_RESULT([no])
|
||||
enable_qt_dbus=[no]
|
||||
enable_geoip_database=[no]
|
||||
QBT_ADD_CONFIG="$QBT_ADD_CONFIG nogui"],
|
||||
[AC_MSG_RESULT([$enable_gui])
|
||||
AC_MSG_ERROR([Unknown option "$enable_gui". Use either "yes" or "no".])])
|
||||
@@ -134,16 +121,16 @@ AS_CASE(["x$enable_webui"],
|
||||
[AC_MSG_RESULT([$enable_webui])
|
||||
AC_MSG_ERROR([Unknown option "$enable_webui". Use either "yes" or "no".])])
|
||||
|
||||
AC_MSG_CHECKING([whether Qt5 should be enabled])
|
||||
AS_CASE(["x$with_qt5"],
|
||||
AC_MSG_CHECKING([whether Qt4 should be enabled])
|
||||
AS_CASE(["x$with_qt4"],
|
||||
["xno"],
|
||||
[AC_MSG_RESULT([no])
|
||||
FIND_QT4()],
|
||||
FIND_QT5()],
|
||||
["xyes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
FIND_QT5()],
|
||||
[AC_MSG_RESULT([$with_qt5])
|
||||
AC_MSG_ERROR([Unknown option "$with_qt5". Use either "yes" or "no".])])
|
||||
FIND_QT4()],
|
||||
[AC_MSG_RESULT([$with_qt4])
|
||||
AC_MSG_ERROR([Unknown option "$with_qt4". Use either "yes" or "no".])])
|
||||
AS_IF([test "x$QT_QMAKE" = "x"],
|
||||
[AC_MSG_ERROR([Could not find qmake])
|
||||
])
|
||||
@@ -181,34 +168,6 @@ AS_IF([test "x$BOOST_SYSTEM_LIB" = "x"],
|
||||
[AC_MSG_NOTICE([Boost.System LIB: $BOOST_SYSTEM_LIB])
|
||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"])
|
||||
|
||||
AC_MSG_CHECKING([whether to compile using libtorrent-rasterbar 0.16.x])
|
||||
AS_CASE(["x$with_libtorrent_rasterbar0_16"],
|
||||
["xno"],
|
||||
[AC_MSG_RESULT([no])
|
||||
PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 1.0.6],
|
||||
[CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"])],
|
||||
["xyes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 0.16.19],
|
||||
[CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"])],
|
||||
[AC_MSG_RESULT([$with_libtorrent_rasterbar0_16])
|
||||
AC_MSG_ERROR([Unknown option "$with_libtorrent_rasterbar0_16". Use either "yes" or "no".])])
|
||||
|
||||
AC_MSG_CHECKING([whether to embed the GeoIP database])
|
||||
AS_CASE(["x$with_geoip_database_embedded"],
|
||||
["xno"],
|
||||
[AC_MSG_RESULT([no])
|
||||
QBT_REMOVE_DEFINES="$QBT_REMOVE_DEFINES WITH_GEOIP_EMBEDDED"],
|
||||
["xyes"],
|
||||
[AC_MSG_RESULT([yes])
|
||||
QBT_ADD_DEFINES="$QBT_ADD_DEFINES WITH_GEOIP_EMBEDDED"],
|
||||
[AC_MSG_RESULT([$with_geoip_database_embedded])
|
||||
AC_MSG_ERROR([Unknown option "$with_geoip_database_embedded". Use either "yes" or "no".])])
|
||||
|
||||
AC_MSG_CHECKING([which qtsingleapplication to use])
|
||||
AS_CASE(["x$with_qtsingleapplication"],
|
||||
["xshipped"],
|
||||
@@ -220,7 +179,7 @@ AS_CASE(["x$with_qtsingleapplication"],
|
||||
[AC_MSG_RESULT([$with_qtsingleapplication])
|
||||
AC_MSG_ERROR([Unknown option "$with_qtsingleapplication". Use either "system" or "shipped".])])
|
||||
|
||||
AS_IF([test "x$with_qt5" = "xno"],
|
||||
AS_IF([test "x$with_qt4" = "xyes"],
|
||||
[AC_MSG_CHECKING([which qjson to use])
|
||||
AS_CASE(["x$with_qjson"],
|
||||
["xshipped"],
|
||||
@@ -237,6 +196,11 @@ AS_IF([test "x$with_qt5" = "xno"],
|
||||
AC_MSG_ERROR([Unknown option "$with_qjson". Use either "system" or "shipped".])])
|
||||
])
|
||||
|
||||
PKG_CHECK_MODULES(libtorrent,
|
||||
[libtorrent-rasterbar >= 1.0.6],
|
||||
[CPPFLAGS="$libtorrent_CFLAGS $CPPFLAGS"
|
||||
LIBS="$libtorrent_LIBS $LIBS"])
|
||||
|
||||
PKG_CHECK_MODULES(zlib,
|
||||
[zlib],
|
||||
[CPPFLAGS="$zlib_CFLAGS $CPPFLAGS"
|
||||
@@ -258,8 +222,11 @@ extract() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# BSD sed needs an actual newline character in the substitute command
|
||||
new_line='
|
||||
'
|
||||
# Convert " -" to "\n" if not between quotes and remove possible leading white spaces
|
||||
string=$(echo " $*" | $SED -e 's: -:\n:g' -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
|
||||
string=$(echo " $*" | $SED -e "s: -:\\${new_line}:g" -e 's:"\(.*\)\n\(.*\)":\"\1 -\2":g' -e "s:'\(.*\)\n\(.*\)':\'\1 -\2':g" -e 's/^[[:space:]]*//')
|
||||
SAVEIFS=$IFS
|
||||
IFS=$(printf "\n\b")
|
||||
for i in $string; do
|
||||
|
||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.2.2</string>
|
||||
<string>3.3.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>qBit</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
||||
2
dist/windows/README.txt
vendored
2
dist/windows/README.txt
vendored
@@ -11,7 +11,7 @@ TRANSLATORS:
|
||||
5. Save the files with utf8 encoding and BOM.
|
||||
6. Submit your changes: 1) as a pull request to the official git repo or
|
||||
2) open an issue to the bugtracker and attach them or 3) via email or
|
||||
4)the same way you provide the tranlations for qbt itself
|
||||
4)the same way you provide the translations for qbt itself
|
||||
|
||||
PACKAGERS:
|
||||
|
||||
|
||||
10
dist/windows/installer-translations/german.nsi
vendored
10
dist/windows/installer-translations/german.nsi
vendored
@@ -1,9 +1,9 @@
|
||||
;Installer strings
|
||||
;Installer strings
|
||||
|
||||
;LangString inst_qbt_req ${LANG_ENGLISH} "qBittorrent (required)"
|
||||
LangString inst_qbt_req ${LANG_GERMAN} "qBittorrent (erforderlich)"
|
||||
;LangString inst_dekstop ${LANG_ENGLISH} "Create Desktop Shortcut"
|
||||
LangString inst_dekstop ${LANG_GERMAN} "Verknüpfung am Desktop erstellen"
|
||||
LangString inst_dekstop ${LANG_GERMAN} "Verknüpfung auf dem Desktop erstellen"
|
||||
;LangString inst_startmenu ${LANG_ENGLISH} "Create Start Menu Shortcut"
|
||||
LangString inst_startmenu ${LANG_GERMAN} "Eintrag im Startmenü erstellen"
|
||||
;LangString inst_torrent ${LANG_ENGLISH} "Open .torrent files with qBittorrent"
|
||||
@@ -38,12 +38,12 @@ LangString remove_conf ${LANG_GERMAN} "Einstellungsdateien entfernen"
|
||||
;LangString remove_firewall ${LANG_ENGLISH} "Remove Windows Firewall rule"
|
||||
LangString remove_firewall ${LANG_GERMAN} "Regel in der Windows Firewall entfernen"
|
||||
;LangString remove_firewallinfo ${LANG_ENGLISH} "Removing Windows Firewall rule"
|
||||
LangString remove_firewallinfo ${LANG_GERMAN} "Entferne Regel in der Windows Firewall"
|
||||
LangString remove_firewallinfo ${LANG_GERMAN} "Entferne Regel aus der Windows Firewall"
|
||||
;LangString remove_cache ${LANG_ENGLISH} "Remove torrents and cached data"
|
||||
LangString remove_cache ${LANG_GERMAN} "Torrents und zwischengespeicherte Daten entfernen"
|
||||
;LangString uninst_warning ${LANG_ENGLISH} "qBittorrent is running. Please close the application before uninstalling."
|
||||
LangString uninst_warning ${LANG_GERMAN} "qBittorrent läuft gerade. Bitte das Programm vor der Deinstallation beenden."
|
||||
;LangString uninst_tor_warn ${LANG_ENGLISH} "Not removing .torrent association. It is associated with:"
|
||||
LangString uninst_tor_warn ${LANG_GERMAN} "Dateiverknüfung mit .torrent-Dateien konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
|
||||
LangString uninst_tor_warn ${LANG_GERMAN} "Dateiverknüpfung mit .torrent-Dateien konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
|
||||
;LangString uninst_mag_warn ${LANG_ENGLISH} "Not removing magnet association. It is associated with:"
|
||||
LangString uninst_mag_warn ${LANG_GERMAN} "Dateiverknüfung mit Magnet-Links konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
|
||||
LangString uninst_mag_warn ${LANG_GERMAN} "Dateiverknüpfung mit Magnet-Links konnte nicht entfernt werden, da dieser Typ mit diesem Programm verknüpft ist:"
|
||||
|
||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -19,7 +19,7 @@ XPStyle on
|
||||
!define CSIDL_APPDATA '0x1A' ;Application Data path
|
||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||
|
||||
!define PROG_VERSION "3.2.2"
|
||||
!define PROG_VERSION "3.3.0"
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
!define MUI_FINISHPAGE_RUN_TEXT $(launch_qbt)
|
||||
|
||||
@@ -111,7 +111,7 @@ To install QBittorrent, do the following:
|
||||
=============
|
||||
|
||||
Please create bugreports at http://svn.netlabs.org/qtapps
|
||||
Only bug reports with a reproducable bug are accepted. :-)
|
||||
Only bug reports with a reproducible bug are accepted. :-)
|
||||
|
||||
|
||||
5. CREDITS
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 23
|
||||
#serial 26
|
||||
|
||||
AC_DEFUN([AX_BOOST_BASE],
|
||||
[
|
||||
@@ -92,8 +92,11 @@ if test "x$want_boost" = "xyes"; then
|
||||
libsubdirs="lib"
|
||||
ax_arch=`uname -m`
|
||||
case $ax_arch in
|
||||
x86_64|ppc64|s390x|sparc64|aarch64)
|
||||
libsubdirs="lib64 lib lib64"
|
||||
x86_64)
|
||||
libsubdirs="lib64 libx32 lib lib64"
|
||||
;;
|
||||
ppc64|s390x|sparc64|aarch64|ppc64le)
|
||||
libsubdirs="lib64 lib lib64 ppc64le"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -170,6 +173,10 @@ if test "x$want_boost" = "xyes"; then
|
||||
dnl if we found no boost with system layout we search for boost libraries
|
||||
dnl built and installed without the --layout=system option or for a staged(not installed) version
|
||||
if test "x$succeeded" != "xyes"; then
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
BOOST_CPPFLAGS=
|
||||
BOOST_LDFLAGS=
|
||||
_version=0
|
||||
if test "$ac_boost_path" != ""; then
|
||||
if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
|
||||
@@ -182,6 +189,12 @@ if test "x$want_boost" = "xyes"; then
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
|
||||
done
|
||||
dnl if nothing found search for layout used in Windows distributions
|
||||
if test -z "$BOOST_CPPFLAGS"; then
|
||||
if test -d "$ac_boost_path/boost" && test -r "$ac_boost_path/boost"; then
|
||||
BOOST_CPPFLAGS="-I$ac_boost_path"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if test "$cross_compiling" != yes; then
|
||||
|
||||
@@ -54,7 +54,7 @@ AS_IF([test "x$QT_QMAKE" != "x"],
|
||||
# Sets the HAVE_QTDBUS variable to true or false.
|
||||
# --------------------------------------
|
||||
AC_DEFUN([FIND_QTDBUS],
|
||||
[AS_IF([test "x$with_qt5" = "xyes"],
|
||||
[AS_IF([test "x$with_qt4" = "xno"],
|
||||
[AC_MSG_CHECKING([for Qt5DBus >= 5.2.0])
|
||||
PKG_CHECK_EXISTS([Qt5DBus >= 5.2.0],
|
||||
[AC_MSG_RESULT([found])
|
||||
|
||||
@@ -10,7 +10,9 @@ exists($$OUT_PWD/../conf.pri) {
|
||||
}
|
||||
|
||||
LIBS += -framework Carbon -framework IOKit
|
||||
CONFIG += c++11
|
||||
|
||||
# C++11 support
|
||||
lessThan(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -std=c++11
|
||||
|
||||
QT_LANG_PATH = ../dist/qt-translations
|
||||
DIST_PATH = ../dist/mac
|
||||
@@ -56,6 +58,3 @@ QMAKE_BUNDLE_DATA += qt_translations
|
||||
|
||||
ICON = $$DIST_PATH/qbittorrent_mac.icns
|
||||
QMAKE_INFO_PLIST = $$DIST_PATH/Info.plist
|
||||
|
||||
DEFINES += WITH_GEOIP_EMBEDDED
|
||||
message("On Mac OS X, GeoIP database must be embedded.")
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# C++11 support
|
||||
lessThan(QT_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -std=c++11
|
||||
|
||||
exists(conf.pri) {
|
||||
# to the conf.pri goes all system dependent stuff
|
||||
include(conf.pri)
|
||||
@@ -13,8 +16,4 @@ LIBS += \
|
||||
RC_FILE = qbittorrent_os2.rc
|
||||
|
||||
# LIBTORRENT DEFINES
|
||||
DEFINES += WITH_SHIPPED_GEOIP_H
|
||||
DEFINES += BOOST_ASIO_DYN_LINK
|
||||
|
||||
DEFINES += WITH_GEOIP_EMBEDDED
|
||||
message("On eCS(OS/2), GeoIP database must be embedded.")
|
||||
|
||||
@@ -27,3 +27,6 @@ strace_win {
|
||||
}
|
||||
|
||||
SOURCES += $$PWD/main.cpp
|
||||
|
||||
# upgrade code
|
||||
HEADERS += $$PWD/upgrade.h
|
||||
|
||||
@@ -32,8 +32,10 @@
|
||||
#include <QLocale>
|
||||
#include <QLibraryInfo>
|
||||
#include <QSysInfo>
|
||||
#include <QProcess>
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#include "gui/guiiconprovider.h"
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#include <QSharedMemory>
|
||||
@@ -46,26 +48,40 @@
|
||||
#endif // Q_OS_MAC
|
||||
#include "mainwindow.h"
|
||||
#include "addnewtorrentdialog.h"
|
||||
#include "shutdownconfirm.h"
|
||||
#else // DISABLE_GUI
|
||||
#include <iostream>
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
#include "../webui/webui.h"
|
||||
#include "webui/webui.h"
|
||||
#endif
|
||||
|
||||
#include "application.h"
|
||||
#include "logger.h"
|
||||
#include "preferences.h"
|
||||
#include "qbtsession.h"
|
||||
#include "torrentpersistentdata.h"
|
||||
#include "core/logger.h"
|
||||
#include "core/preferences.h"
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/misc.h"
|
||||
#include "core/iconprovider.h"
|
||||
#include "core/scanfoldersmodel.h"
|
||||
#include "core/net/smtp.h"
|
||||
#include "core/net/downloadmanager.h"
|
||||
#include "core/net/geoipmanager.h"
|
||||
#include "core/bittorrent/session.h"
|
||||
#include "core/bittorrent/torrenthandle.h"
|
||||
|
||||
static const char PARAMS_SEPARATOR[] = "|";
|
||||
|
||||
Application::Application(const QString &id, int &argc, char **argv)
|
||||
: BaseApplication(id, argc, argv)
|
||||
, m_running(false)
|
||||
#ifndef DISABLE_GUI
|
||||
, m_shutdownAct(ShutdownAction::None)
|
||||
#endif
|
||||
{
|
||||
Logger::initInstance();
|
||||
Preferences::initInstance();
|
||||
|
||||
#if defined(Q_OS_MACX) && !defined(DISABLE_GUI)
|
||||
if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
|
||||
// fix Mac OS X 10.9 (mavericks) font issue
|
||||
@@ -85,6 +101,8 @@ Application::Application(const QString &id, int &argc, char **argv)
|
||||
|
||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
||||
|
||||
Logger::instance()->addMessage(tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
|
||||
}
|
||||
|
||||
void Application::processMessage(const QString &message)
|
||||
@@ -98,6 +116,94 @@ void Application::processMessage(const QString &message)
|
||||
m_paramsQueue.append(params);
|
||||
}
|
||||
|
||||
void Application::sendNotificationEmail(BitTorrent::TorrentHandle *const torrent)
|
||||
{
|
||||
// Prepare mail content
|
||||
QString content = QObject::tr("Torrent name: %1").arg(torrent->name()) + "\n";
|
||||
content += QObject::tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + "\n";
|
||||
content += QObject::tr("Save path: %1").arg(torrent->savePath()) + "\n\n";
|
||||
content += QObject::tr("The torrent was downloaded in %1.",
|
||||
"The torrent was downloaded in 1 hour and 20 seconds")
|
||||
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n";
|
||||
content += QObject::tr("Thank you for using qBittorrent.") + "\n";
|
||||
|
||||
// Send the notification email
|
||||
Net::Smtp *sender = new Net::Smtp;
|
||||
sender->sendMail("notification@qbittorrent.org",
|
||||
Preferences::instance()->getMailNotificationEmail(),
|
||||
QObject::tr("[qBittorrent] '%1' has finished downloading").arg(torrent->name()),
|
||||
content);
|
||||
}
|
||||
|
||||
void Application::torrentFinished(BitTorrent::TorrentHandle *const torrent)
|
||||
{
|
||||
Preferences *const pref = Preferences::instance();
|
||||
|
||||
// AutoRun program
|
||||
if (pref->isAutoRunEnabled()) {
|
||||
QString program = pref->getAutoRunProgram();
|
||||
|
||||
program.replace("%N", torrent->name());
|
||||
program.replace("%L", torrent->label());
|
||||
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
|
||||
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
|
||||
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
|
||||
program.replace("%C", QString::number(torrent->filesCount()));
|
||||
program.replace("%Z", QString::number(torrent->totalSize()));
|
||||
program.replace("%T", torrent->currentTracker());
|
||||
program.replace("%I", torrent->hash());
|
||||
|
||||
QProcess::startDetached(program);
|
||||
}
|
||||
|
||||
// Mail notification
|
||||
if (pref->isMailNotificationEnabled())
|
||||
sendNotificationEmail(torrent);
|
||||
}
|
||||
|
||||
void Application::allTorrentsFinished()
|
||||
{
|
||||
#ifndef DISABLE_GUI
|
||||
Preferences *const pref = Preferences::instance();
|
||||
|
||||
bool will_shutdown = (pref->shutdownWhenDownloadsComplete()
|
||||
|| pref->shutdownqBTWhenDownloadsComplete()
|
||||
|| pref->suspendWhenDownloadsComplete()
|
||||
|| pref->hibernateWhenDownloadsComplete());
|
||||
|
||||
// Auto-Shutdown
|
||||
if (will_shutdown) {
|
||||
bool suspend = pref->suspendWhenDownloadsComplete();
|
||||
bool hibernate = pref->hibernateWhenDownloadsComplete();
|
||||
bool shutdown = pref->shutdownWhenDownloadsComplete();
|
||||
|
||||
// Confirm shutdown
|
||||
ShutdownAction action = ShutdownAction::None;
|
||||
if (suspend)
|
||||
action = ShutdownAction::Suspend;
|
||||
else if (hibernate)
|
||||
action = ShutdownAction::Hibernate;
|
||||
else if (shutdown)
|
||||
action = ShutdownAction::Shutdown;
|
||||
|
||||
if (!ShutdownConfirmDlg::askForConfirmation(action)) return;
|
||||
|
||||
// Actually shut down
|
||||
if (suspend || hibernate || shutdown) {
|
||||
qDebug("Preparing for auto-shutdown because all downloads are complete!");
|
||||
// Disabling it for next time
|
||||
pref->setShutdownWhenDownloadsComplete(false);
|
||||
pref->setSuspendWhenDownloadsComplete(false);
|
||||
pref->setHibernateWhenDownloadsComplete(false);
|
||||
// Make sure preferences are synced before exiting
|
||||
m_shutdownAct = action;
|
||||
}
|
||||
qDebug("Exiting the application");
|
||||
exit();
|
||||
}
|
||||
#endif // DISABLE_GUI
|
||||
}
|
||||
|
||||
bool Application::sendParams(const QStringList ¶ms)
|
||||
{
|
||||
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
|
||||
@@ -114,45 +220,36 @@ void Application::processParams(const QStringList ¶ms)
|
||||
m_window->activate(); // show UI
|
||||
return;
|
||||
}
|
||||
|
||||
const bool useTorrentAdditionDialog = Preferences::instance()->useAdditionDialog();
|
||||
#endif
|
||||
|
||||
foreach (QString param, params) {
|
||||
param = param.trimmed();
|
||||
if (misc::isUrl(param)) {
|
||||
QBtSession::instance()->downloadFromUrl(param);
|
||||
}
|
||||
else {
|
||||
if (param.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||
qDebug("Converting bc link to magnet link");
|
||||
param = misc::bcLinkToMagnet(param);
|
||||
}
|
||||
|
||||
if (param.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||
#ifndef DISABLE_GUI
|
||||
if (useTorrentAdditionDialog)
|
||||
AddNewTorrentDialog::showMagnet(param, m_window);
|
||||
else
|
||||
if (Preferences::instance()->useAdditionDialog())
|
||||
AddNewTorrentDialog::show(param, m_window);
|
||||
else
|
||||
#endif
|
||||
QBtSession::instance()->addMagnetUri(param);
|
||||
}
|
||||
else {
|
||||
#ifndef DISABLE_GUI
|
||||
if (useTorrentAdditionDialog)
|
||||
AddNewTorrentDialog::showTorrent(param, QString(), m_window);
|
||||
else
|
||||
#endif
|
||||
QBtSession::instance()->addTorrent(param);
|
||||
}
|
||||
}
|
||||
BitTorrent::Session::instance()->addTorrent(param);
|
||||
}
|
||||
}
|
||||
|
||||
int Application::exec(const QStringList ¶ms)
|
||||
{
|
||||
// Resume unfinished torrents
|
||||
QBtSession::instance()->startUpTorrents();
|
||||
Net::DownloadManager::initInstance();
|
||||
#ifdef DISABLE_GUI
|
||||
IconProvider::initInstance();
|
||||
#else
|
||||
GuiIconProvider::initInstance();
|
||||
#endif
|
||||
|
||||
BitTorrent::Session::initInstance();
|
||||
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
|
||||
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()));
|
||||
|
||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||
Net::GeoIPManager::initInstance();
|
||||
#endif
|
||||
ScanFoldersModel::initInstance(this);
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
m_webui = new WebUI;
|
||||
@@ -294,6 +391,8 @@ void Application::initializeTranslation()
|
||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||
void Application::shutdownCleanup(QSessionManager &manager)
|
||||
{
|
||||
Q_UNUSED(manager);
|
||||
|
||||
// This is only needed for a special case on Windows XP.
|
||||
// (but is called for every Windows version)
|
||||
// If a process takes too much time to exit during OS
|
||||
@@ -357,10 +456,16 @@ void Application::cleanup()
|
||||
#ifndef DISABLE_WEBUI
|
||||
delete m_webui;
|
||||
#endif
|
||||
QBtSession::drop();
|
||||
TorrentPersistentData::drop();
|
||||
Preferences::drop();
|
||||
Logger::drop();
|
||||
|
||||
ScanFoldersModel::freeInstance();
|
||||
BitTorrent::Session::freeInstance();
|
||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||
Net::GeoIPManager::freeInstance();
|
||||
#endif
|
||||
Preferences::freeInstance();
|
||||
Logger::freeInstance();
|
||||
IconProvider::freeInstance();
|
||||
Net::DownloadManager::freeInstance();
|
||||
#ifndef DISABLE_GUI
|
||||
#ifdef Q_OS_WIN
|
||||
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
||||
@@ -370,6 +475,9 @@ void Application::cleanup()
|
||||
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
||||
#endif // Q_OS_WIN
|
||||
delete m_window;
|
||||
if (m_shutdownAct != ShutdownAction::None) {
|
||||
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
|
||||
Utils::Misc::shutdownComputer(m_shutdownAct);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -50,10 +50,17 @@ QT_END_NAMESPACE
|
||||
typedef QtSingleCoreApplication BaseApplication;
|
||||
#endif
|
||||
|
||||
#include "core/utils/misc.h"
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
class WebUI;
|
||||
#endif
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentHandle;
|
||||
}
|
||||
|
||||
class Application : public BaseApplication
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -77,6 +84,8 @@ protected:
|
||||
|
||||
private slots:
|
||||
void processMessage(const QString &message);
|
||||
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||
void allTorrentsFinished();
|
||||
void cleanup();
|
||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||
void shutdownCleanup(QSessionManager &manager);
|
||||
@@ -87,6 +96,7 @@ private:
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
QPointer<MainWindow> m_window;
|
||||
ShutdownAction m_shutdownAct;
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
@@ -99,6 +109,7 @@ private:
|
||||
|
||||
void initializeTranslation();
|
||||
void processParams(const QStringList ¶ms);
|
||||
void sendNotificationEmail(BitTorrent::TorrentHandle *const torrent);
|
||||
};
|
||||
|
||||
#endif // APPLICATION_H
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <QScopedPointer>
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
// GUI-only includes
|
||||
#include <QFont>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
@@ -47,8 +48,13 @@ Q_IMPORT_PLUGIN(QICOPlugin)
|
||||
Q_IMPORT_PLUGIN(qico)
|
||||
#endif
|
||||
#endif // QBT_STATIC_QT
|
||||
#else // DISABLE_GUI
|
||||
|
||||
#else
|
||||
// NoGUI-only includes
|
||||
#include <cstdio>
|
||||
#ifdef Q_OS_UNIX
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
@@ -66,9 +72,10 @@ Q_IMPORT_PLUGIN(qico)
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include "application.h"
|
||||
#include "misc.h"
|
||||
#include "preferences.h"
|
||||
#include "logger.h"
|
||||
#include "core/utils/misc.h"
|
||||
#include "core/preferences.h"
|
||||
|
||||
#include "upgrade.h"
|
||||
|
||||
// Signal handlers
|
||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
||||
@@ -120,13 +127,11 @@ QBtCommandLineParameters parseCommandLine();
|
||||
// Main
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//Initialize logger singleton here to avoid threading issues
|
||||
Logger::instance()->addMessage(QObject::tr("qBittorrent %1 started", "qBittorrent v3.2.0alpha started").arg(VERSION));
|
||||
// We must save it here because QApplication constructor may change it
|
||||
bool isOneArg = (argc == 2);
|
||||
|
||||
// Create Application
|
||||
QString appId = QLatin1String("qBittorrent-") + misc::getUserIDString();
|
||||
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
|
||||
QScopedPointer<Application> app(new Application(appId, argc, argv));
|
||||
|
||||
const QBtCommandLineParameters params = parseCommandLine();
|
||||
@@ -176,8 +181,16 @@ int main(int argc, char *argv[])
|
||||
if (!qputenv("QBITTORRENT", QByteArray(VERSION)))
|
||||
std::cerr << "Couldn't set environment variable...\n";
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
if (!userAgreesWithLegalNotice())
|
||||
return EXIT_SUCCESS;
|
||||
#else
|
||||
if (!params.shouldDaemonize
|
||||
&& isatty(fileno(stdin))
|
||||
&& isatty(fileno(stdout))
|
||||
&& !userAgreesWithLegalNotice())
|
||||
return EXIT_SUCCESS;
|
||||
#endif
|
||||
|
||||
// Check if qBittorrent is already running for this user
|
||||
if (app->isRunning()) {
|
||||
@@ -191,12 +204,20 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
qDebug("qBittorrent is already running for this user.");
|
||||
|
||||
misc::msleep(300);
|
||||
Utils::Misc::msleep(300);
|
||||
app->sendParams(params.torrents);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
if (!upgrade()) return EXIT_FAILURE;
|
||||
#else
|
||||
if (!upgrade(!params.shouldDaemonize
|
||||
&& isatty(fileno(stdin))
|
||||
&& isatty(fileno(stdout)))) return EXIT_FAILURE;
|
||||
#endif
|
||||
|
||||
srand(time(0));
|
||||
#ifdef DISABLE_GUI
|
||||
if (params.shouldDaemonize) {
|
||||
@@ -398,7 +419,7 @@ void displayUsage(const QString& prg_name)
|
||||
#else
|
||||
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prg_name), QMessageBox::Ok);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(misc::screenCenter(&msgBox));
|
||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
#endif
|
||||
}
|
||||
@@ -410,7 +431,7 @@ void displayBadArgMessage(const QString& message)
|
||||
QMessageBox msgBox(QMessageBox::Critical, QObject::tr("Bad command line"),
|
||||
message + QLatin1Char('\n') + help, QMessageBox::Ok);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(misc::screenCenter(&msgBox));
|
||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
#else
|
||||
std::cerr << qPrintable(QObject::tr("Bad command line: "));
|
||||
@@ -442,7 +463,7 @@ bool userAgreesWithLegalNotice()
|
||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(misc::screenCenter(&msgBox));
|
||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||
msgBox.exec();
|
||||
if (msgBox.clickedButton() == agree_button) {
|
||||
// Save the answer
|
||||
|
||||
132
src/app/upgrade.h
Normal file
132
src/app/upgrade.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef UPGRADE_H
|
||||
#define UPGRADE_H
|
||||
|
||||
#include <libtorrent/lazy_entry.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#ifndef DISABLE_GUI
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/misc.h"
|
||||
|
||||
bool userAcceptsUpgrade()
|
||||
{
|
||||
#ifdef DISABLE_GUI
|
||||
std::cout << std::endl << "*** " << qPrintable(QObject::tr("Upgrade")) << " ***" << std::endl;
|
||||
char ret = '\0';
|
||||
do {
|
||||
std::cout << qPrintable(QObject::tr("You updated from an older version that saved things differently. You must migrate to the new saving system. You will not be able to use an older version than v3.3.0 again. Continue? [y/n]")) << std::endl;
|
||||
ret = getchar(); // Read pressed key
|
||||
}
|
||||
while ((ret != 'y') && (ret != 'Y') && (ret != 'n') && (ret != 'N'));
|
||||
|
||||
if ((ret == 'y') || (ret == 'Y'))
|
||||
return true;
|
||||
#else
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText(QObject::tr("You updated from an older version that saved things differently. You must migrate to the new saving system. If you continue, you will not be able to use an older version than v3.3.0 again."));
|
||||
msgBox.setWindowTitle(QObject::tr("Upgrade"));
|
||||
msgBox.addButton(QMessageBox::Abort);
|
||||
msgBox.addButton(QMessageBox::Ok);
|
||||
msgBox.setDefaultButton(QMessageBox::Abort);
|
||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||
if (msgBox.exec() == QMessageBox::Ok)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upgradeResumeFile(const QString &filepath)
|
||||
{
|
||||
QFile file1(filepath);
|
||||
if (!file1.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
|
||||
QByteArray data = file1.readAll();
|
||||
file1.close();
|
||||
|
||||
libtorrent::lazy_entry fastOld;
|
||||
libtorrent::error_code ec;
|
||||
libtorrent::lazy_bdecode(data.constData(), data.constData() + data.size(), fastOld, ec);
|
||||
if ((fastOld.type() != libtorrent::lazy_entry::dict_t) && !ec) return false;
|
||||
|
||||
libtorrent::entry fastNew;
|
||||
fastNew = fastOld;
|
||||
|
||||
int priority = fastOld.dict_find_int_value("qBt-queuePosition");
|
||||
QFile file2(QString("%1.%2").arg(filepath).arg(priority > 0 ? priority : 0));
|
||||
QVector<char> out;
|
||||
libtorrent::bencode(std::back_inserter(out), fastNew);
|
||||
if (file2.open(QIODevice::WriteOnly)) {
|
||||
if (file2.write(&out[0], out.size()) == out.size()) {
|
||||
Utils::Fs::forceRemove(filepath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upgrade(bool ask = true)
|
||||
{
|
||||
QString backupFolderPath = Utils::Fs::expandPathAbs(Utils::Fs::QDesktopServicesDataLocation() + "BT_backup");
|
||||
QDir backupFolderDir(backupFolderPath);
|
||||
if (!backupFolderDir.exists()) return true;
|
||||
|
||||
QStringList backupFiles = backupFolderDir.entryList(QStringList() << QLatin1String("*.fastresume"), QDir::Files, QDir::Unsorted);
|
||||
if (!backupFiles.isEmpty()) {
|
||||
if (ask && !userAcceptsUpgrade()) return false;
|
||||
|
||||
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||
foreach (QString backupFile, backupFiles) {
|
||||
if (rx.indexIn(backupFile) != -1) {
|
||||
if (!upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile)))
|
||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
|
||||
}
|
||||
else {
|
||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // UPGRADE_H
|
||||
64
src/core/bittorrent/cachestatus.cpp
Normal file
64
src/core/bittorrent/cachestatus.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "cachestatus.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
CacheStatus::CacheStatus(const libtorrent::cache_status &nativeStatus)
|
||||
: m_nativeStatus(nativeStatus)
|
||||
{
|
||||
}
|
||||
|
||||
int CacheStatus::totalUsedBuffers() const
|
||||
{
|
||||
return m_nativeStatus.total_used_buffers;
|
||||
}
|
||||
|
||||
qreal CacheStatus::readRatio() const
|
||||
{
|
||||
if (m_nativeStatus.blocks_read > 0)
|
||||
return (static_cast<qreal>(m_nativeStatus.blocks_read_hit) / m_nativeStatus.blocks_read);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CacheStatus::jobQueueLength() const
|
||||
{
|
||||
return m_nativeStatus.job_queue_length;
|
||||
}
|
||||
|
||||
int CacheStatus::averageJobTime() const
|
||||
{
|
||||
return m_nativeStatus.average_job_time;
|
||||
}
|
||||
|
||||
qlonglong CacheStatus::queuedBytes() const
|
||||
{
|
||||
return m_nativeStatus.queued_bytes;
|
||||
}
|
||||
53
src/core/bittorrent/cachestatus.h
Normal file
53
src/core/bittorrent/cachestatus.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_CACHESTATUS_H
|
||||
#define BITTORRENT_CACHESTATUS_H
|
||||
|
||||
#include <libtorrent/disk_io_thread.hpp>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class CacheStatus
|
||||
{
|
||||
public:
|
||||
CacheStatus(const libtorrent::cache_status &nativeStatus);
|
||||
|
||||
int totalUsedBuffers() const;
|
||||
qreal readRatio() const;
|
||||
int jobQueueLength() const;
|
||||
int averageJobTime() const;
|
||||
qlonglong queuedBytes() const;
|
||||
|
||||
private:
|
||||
libtorrent::cache_status m_nativeStatus;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_CACHESTATUS_H
|
||||
98
src/core/bittorrent/infohash.cpp
Normal file
98
src/core/bittorrent/infohash.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QHash>
|
||||
#include "infohash.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
InfoHash::InfoHash()
|
||||
: m_valid(false)
|
||||
{
|
||||
}
|
||||
|
||||
InfoHash::InfoHash(const libtorrent::sha1_hash &nativeHash)
|
||||
: m_valid(true)
|
||||
, m_nativeHash(nativeHash)
|
||||
{
|
||||
char out[(libtorrent::sha1_hash::size * 2) + 1];
|
||||
libtorrent::to_hex((char const*)&m_nativeHash[0], libtorrent::sha1_hash::size, out);
|
||||
m_hashString = QString(out);
|
||||
}
|
||||
|
||||
InfoHash::InfoHash(const QString &hashString)
|
||||
: m_valid(false)
|
||||
, m_hashString(hashString)
|
||||
{
|
||||
QByteArray raw = m_hashString.toLatin1();
|
||||
if (raw.size() == 40)
|
||||
m_valid = libtorrent::from_hex(raw.constData(), 40, (char*)&m_nativeHash[0]);
|
||||
}
|
||||
|
||||
|
||||
InfoHash::InfoHash(const InfoHash &other)
|
||||
: m_valid(other.m_valid)
|
||||
, m_nativeHash(other.m_nativeHash)
|
||||
, m_hashString(other.m_hashString)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool InfoHash::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
|
||||
InfoHash::operator libtorrent::sha1_hash() const
|
||||
{
|
||||
return m_nativeHash;
|
||||
}
|
||||
|
||||
|
||||
InfoHash::operator QString() const
|
||||
{
|
||||
return m_hashString;
|
||||
}
|
||||
|
||||
|
||||
bool InfoHash::operator==(const InfoHash &other) const
|
||||
{
|
||||
return (m_nativeHash == other.m_nativeHash);
|
||||
}
|
||||
|
||||
|
||||
bool InfoHash::operator!=(const InfoHash &other) const
|
||||
{
|
||||
return (m_nativeHash != other.m_nativeHash);
|
||||
}
|
||||
|
||||
uint qHash(const InfoHash &key, uint seed)
|
||||
{
|
||||
return qHash(static_cast<QString>(key), seed);
|
||||
}
|
||||
61
src/core/bittorrent/infohash.h
Normal file
61
src/core/bittorrent/infohash.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_INFOHASH_H
|
||||
#define BITTORRENT_INFOHASH_H
|
||||
|
||||
#include <libtorrent/sha1_hash.hpp>
|
||||
#include <QString>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class InfoHash
|
||||
{
|
||||
public:
|
||||
InfoHash();
|
||||
InfoHash(const libtorrent::sha1_hash &nativeHash);
|
||||
InfoHash(const QString &hashString);
|
||||
InfoHash(const InfoHash &other);
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
operator libtorrent::sha1_hash() const;
|
||||
operator QString() const;
|
||||
bool operator==(const InfoHash &other) const;
|
||||
bool operator!=(const InfoHash &other) const;
|
||||
|
||||
private:
|
||||
bool m_valid;
|
||||
libtorrent::sha1_hash m_nativeHash;
|
||||
QString m_hashString;
|
||||
};
|
||||
}
|
||||
|
||||
uint qHash(const BitTorrent::InfoHash &key, uint seed);
|
||||
|
||||
#endif // BITTORRENT_INFOHASH_H
|
||||
93
src/core/bittorrent/magneturi.cpp
Normal file
93
src/core/bittorrent/magneturi.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/error_code.hpp>
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
|
||||
#include "core/utils/string.h"
|
||||
#include "magneturi.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
MagnetUri::MagnetUri(const QString &url)
|
||||
: m_valid(false)
|
||||
, m_url(url)
|
||||
{
|
||||
if (url.isEmpty()) return;
|
||||
|
||||
libt::error_code ec;
|
||||
libt::parse_magnet_uri(url.toUtf8().constData(), m_addTorrentParams, ec);
|
||||
if (ec) return;
|
||||
|
||||
m_valid = true;
|
||||
m_hash = m_addTorrentParams.info_hash;
|
||||
m_name = Utils::String::fromStdString(m_addTorrentParams.name);
|
||||
|
||||
foreach (const std::string &tracker, m_addTorrentParams.trackers)
|
||||
m_trackers.append(Utils::String::fromStdString(tracker));
|
||||
|
||||
foreach (const std::string &urlSeed, m_addTorrentParams.url_seeds)
|
||||
m_urlSeeds.append(QUrl(urlSeed.c_str()));
|
||||
}
|
||||
|
||||
bool MagnetUri::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
InfoHash MagnetUri::hash() const
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
QString MagnetUri::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QList<TrackerEntry> MagnetUri::trackers() const
|
||||
{
|
||||
return m_trackers;
|
||||
}
|
||||
|
||||
QList<QUrl> MagnetUri::urlSeeds() const
|
||||
{
|
||||
return m_urlSeeds;
|
||||
}
|
||||
|
||||
QString MagnetUri::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
libtorrent::add_torrent_params MagnetUri::addTorrentParams() const
|
||||
{
|
||||
return m_addTorrentParams;
|
||||
}
|
||||
68
src/core/bittorrent/magneturi.h
Normal file
68
src/core/bittorrent/magneturi.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_MAGNETURI_H
|
||||
#define BITTORRENT_MAGNETURI_H
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
#include <libtorrent/add_torrent_params.hpp>
|
||||
|
||||
#include "infohash.h"
|
||||
#include "trackerentry.h"
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class MagnetUri
|
||||
{
|
||||
public:
|
||||
explicit MagnetUri(const QString &url = QString());
|
||||
|
||||
bool isValid() const;
|
||||
InfoHash hash() const;
|
||||
QString name() const;
|
||||
QList<TrackerEntry> trackers() const;
|
||||
QList<QUrl> urlSeeds() const;
|
||||
QString url() const;
|
||||
|
||||
libtorrent::add_torrent_params addTorrentParams() const;
|
||||
|
||||
private:
|
||||
bool m_valid;
|
||||
QString m_url;
|
||||
InfoHash m_hash;
|
||||
QString m_name;
|
||||
QList<TrackerEntry> m_trackers;
|
||||
QList<QUrl> m_urlSeeds;
|
||||
libtorrent::add_torrent_params m_addTorrentParams;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_MAGNETURI_H
|
||||
255
src/core/bittorrent/peerinfo.cpp
Normal file
255
src/core/bittorrent/peerinfo.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "core/net/geoipmanager.h"
|
||||
#include "core/utils/string.h"
|
||||
#include "core/unicodestrings.h"
|
||||
#include "peerinfo.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
// PeerAddress
|
||||
|
||||
PeerAddress::PeerAddress()
|
||||
: port(0)
|
||||
{
|
||||
}
|
||||
|
||||
PeerAddress::PeerAddress(QHostAddress ip, ushort port)
|
||||
: ip(ip)
|
||||
, port(port)
|
||||
{
|
||||
}
|
||||
|
||||
// PeerInfo
|
||||
|
||||
PeerInfo::PeerInfo(const libt::peer_info &nativeInfo)
|
||||
: m_nativeInfo(nativeInfo)
|
||||
{
|
||||
}
|
||||
|
||||
bool PeerInfo::fromDHT() const
|
||||
{
|
||||
return (m_nativeInfo.source & libt::peer_info::dht);
|
||||
}
|
||||
|
||||
bool PeerInfo::fromPeX() const
|
||||
{
|
||||
return (m_nativeInfo.source & libt::peer_info::pex);
|
||||
}
|
||||
|
||||
bool PeerInfo::fromLSD() const
|
||||
{
|
||||
return (m_nativeInfo.source & libt::peer_info::lsd);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||
QString PeerInfo::country() const
|
||||
{
|
||||
return Net::GeoIPManager::instance()->lookup(address().ip);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool PeerInfo::isInteresting() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::interesting);
|
||||
}
|
||||
|
||||
bool PeerInfo::isChocked() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::choked);
|
||||
}
|
||||
|
||||
bool PeerInfo::isRemoteInterested() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::remote_interested);
|
||||
}
|
||||
|
||||
bool PeerInfo::isRemoteChocked() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::remote_choked);
|
||||
}
|
||||
|
||||
|
||||
bool PeerInfo::isSupportsExtensions() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::supports_extensions);
|
||||
}
|
||||
|
||||
bool PeerInfo::isLocalConnection() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::local_connection);
|
||||
}
|
||||
|
||||
bool PeerInfo::isHandshake() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::handshake);
|
||||
}
|
||||
|
||||
|
||||
bool PeerInfo::isConnecting() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::connecting);
|
||||
}
|
||||
|
||||
bool PeerInfo::isQueued() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::queued);
|
||||
}
|
||||
|
||||
|
||||
bool PeerInfo::isOnParole() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::on_parole);
|
||||
}
|
||||
|
||||
bool PeerInfo::isSeed() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::seed);
|
||||
}
|
||||
|
||||
bool PeerInfo::optimisticUnchoke() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::optimistic_unchoke);
|
||||
}
|
||||
|
||||
|
||||
bool PeerInfo::isSnubbed() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::snubbed);
|
||||
}
|
||||
|
||||
bool PeerInfo::isUploadOnly() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::upload_only);
|
||||
}
|
||||
|
||||
bool PeerInfo::isEndgameMode() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::endgame_mode);
|
||||
}
|
||||
|
||||
|
||||
bool PeerInfo::isHolepunched() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::holepunched);
|
||||
}
|
||||
|
||||
bool PeerInfo::useI2PSocket() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::i2p_socket);
|
||||
}
|
||||
|
||||
bool PeerInfo::useUTPSocket() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::utp_socket);
|
||||
}
|
||||
|
||||
bool PeerInfo::useSSLSocket() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::ssl_socket);
|
||||
}
|
||||
|
||||
bool PeerInfo::isRC4Encrypted() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::rc4_encrypted);
|
||||
}
|
||||
|
||||
bool PeerInfo::isPlaintextEncrypted() const
|
||||
{
|
||||
return (m_nativeInfo.flags & libt::peer_info::plaintext_encrypted);
|
||||
}
|
||||
|
||||
|
||||
PeerAddress PeerInfo::address() const
|
||||
{
|
||||
return PeerAddress(QHostAddress(QString::fromStdString(m_nativeInfo.ip.address().to_string())),
|
||||
m_nativeInfo.ip.port());
|
||||
}
|
||||
|
||||
QString PeerInfo::client() const
|
||||
{
|
||||
return Utils::String::fromStdString(m_nativeInfo.client);
|
||||
}
|
||||
|
||||
|
||||
qreal PeerInfo::progress() const
|
||||
{
|
||||
return m_nativeInfo.progress;
|
||||
}
|
||||
|
||||
int PeerInfo::payloadUpSpeed() const
|
||||
{
|
||||
return m_nativeInfo.payload_up_speed;
|
||||
}
|
||||
|
||||
|
||||
int PeerInfo::payloadDownSpeed() const
|
||||
{
|
||||
return m_nativeInfo.payload_down_speed;
|
||||
}
|
||||
|
||||
qlonglong PeerInfo::totalUpload() const
|
||||
{
|
||||
return m_nativeInfo.total_upload;
|
||||
}
|
||||
|
||||
|
||||
qlonglong PeerInfo::totalDownload() const
|
||||
{
|
||||
return m_nativeInfo.total_download;
|
||||
}
|
||||
|
||||
QBitArray PeerInfo::pieces() const
|
||||
{
|
||||
QBitArray result(m_nativeInfo.pieces.size());
|
||||
|
||||
for (int i = 0; i < m_nativeInfo.pieces.size(); ++i)
|
||||
result.setBit(i, m_nativeInfo.pieces.get_bit(i));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString PeerInfo::connectionType() const
|
||||
{
|
||||
if (m_nativeInfo.flags & libt::peer_info::utp_socket)
|
||||
return QString::fromUtf8(C_UTP);
|
||||
|
||||
QString connection;
|
||||
switch(m_nativeInfo.connection_type) {
|
||||
case libt::peer_info::http_seed:
|
||||
case libt::peer_info::web_seed:
|
||||
connection = "Web";
|
||||
break;
|
||||
default:
|
||||
connection = "BT";
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
101
src/core/bittorrent/peerinfo.h
Normal file
101
src/core/bittorrent/peerinfo.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_PEERINFO_H
|
||||
#define BITTORRENT_PEERINFO_H
|
||||
|
||||
#include <libtorrent/peer_info.hpp>
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QBitArray>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct PeerAddress
|
||||
{
|
||||
QHostAddress ip;
|
||||
ushort port;
|
||||
|
||||
PeerAddress();
|
||||
PeerAddress(QHostAddress ip, ushort port);
|
||||
};
|
||||
|
||||
class PeerInfo
|
||||
{
|
||||
public:
|
||||
PeerInfo(const libtorrent::peer_info &nativeInfo);
|
||||
|
||||
bool fromDHT() const;
|
||||
bool fromPeX() const;
|
||||
bool fromLSD() const;
|
||||
|
||||
bool isInteresting() const;
|
||||
bool isChocked() const;
|
||||
bool isRemoteInterested() const;
|
||||
bool isRemoteChocked() const;
|
||||
bool isSupportsExtensions() const;
|
||||
bool isLocalConnection() const;
|
||||
|
||||
bool isHandshake() const;
|
||||
bool isConnecting() const;
|
||||
bool isQueued() const;
|
||||
bool isOnParole() const;
|
||||
bool isSeed() const;
|
||||
|
||||
bool optimisticUnchoke() const;
|
||||
bool isSnubbed() const;
|
||||
bool isUploadOnly() const;
|
||||
bool isEndgameMode() const;
|
||||
bool isHolepunched() const;
|
||||
|
||||
bool useI2PSocket() const;
|
||||
bool useUTPSocket() const;
|
||||
bool useSSLSocket() const;
|
||||
|
||||
bool isRC4Encrypted() const;
|
||||
bool isPlaintextEncrypted() const;
|
||||
|
||||
PeerAddress address() const;
|
||||
QString client() const;
|
||||
qreal progress() const;
|
||||
int payloadUpSpeed() const;
|
||||
int payloadDownSpeed() const;
|
||||
qlonglong totalUpload() const;
|
||||
qlonglong totalDownload() const;
|
||||
QBitArray pieces() const;
|
||||
QString connectionType() const;
|
||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||
QString country() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
libtorrent::peer_info m_nativeInfo;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_PEERINFO_H
|
||||
96
src/core/bittorrent/private/bandwidthscheduler.cpp
Normal file
96
src/core/bittorrent/private/bandwidthscheduler.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "core/preferences.h"
|
||||
#include "bandwidthscheduler.h"
|
||||
|
||||
BandwidthScheduler::BandwidthScheduler(QObject *parent)
|
||||
: QTimer(parent)
|
||||
{
|
||||
Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
|
||||
// Single shot, we call start() again manually
|
||||
setSingleShot(true);
|
||||
// Connect Signals/Slots
|
||||
connect(this, SIGNAL(timeout()), this, SLOT(start()));
|
||||
}
|
||||
|
||||
void BandwidthScheduler::start()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
Q_ASSERT(pref->isSchedulerEnabled());
|
||||
bool alt_bw_enabled = pref->isAltBandwidthEnabled();
|
||||
|
||||
QTime start = pref->getSchedulerStartTime();
|
||||
QTime end = pref->getSchedulerEndTime();
|
||||
QTime now = QTime::currentTime();
|
||||
int sched_days = pref->getSchedulerDays();
|
||||
int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek();
|
||||
bool new_mode = false;
|
||||
bool reverse = false;
|
||||
|
||||
if (start > end) {
|
||||
QTime temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
reverse = true;
|
||||
}
|
||||
|
||||
if ((start <= now) && (end >= now)) {
|
||||
switch(sched_days) {
|
||||
case EVERY_DAY:
|
||||
new_mode = true;
|
||||
break;
|
||||
case WEEK_ENDS:
|
||||
if ((day == 6) || (day == 7))
|
||||
new_mode = true;
|
||||
break;
|
||||
case WEEK_DAYS:
|
||||
if ((day != 6) && (day != 7))
|
||||
new_mode = true;
|
||||
break;
|
||||
default:
|
||||
if (day == (sched_days - 2))
|
||||
new_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
new_mode = !new_mode;
|
||||
|
||||
if (new_mode != alt_bw_enabled)
|
||||
emit switchToAlternativeMode(new_mode);
|
||||
|
||||
// Timeout regularly to accommodate for external system clock changes
|
||||
// eg from the user or from a timesync utility
|
||||
QTimer::start(1500);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -28,31 +28,23 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef GEOIPMANAGER_H
|
||||
#define GEOIPMANAGER_H
|
||||
#ifndef BANDWIDTHSCHEDULER_H
|
||||
#define BANDWIDTHSCHEDULER_H
|
||||
|
||||
#include <QString>
|
||||
#include <QIcon>
|
||||
#include <QTimer>
|
||||
|
||||
namespace libtorrent {
|
||||
class session;
|
||||
}
|
||||
|
||||
class GeoIPManager : public QObject {
|
||||
Q_OBJECT
|
||||
class BandwidthScheduler : public QTimer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void loadDatabase(libtorrent::session *s);
|
||||
static QIcon CountryISOCodeToIcon(const char* iso);
|
||||
static QString CountryISOCodeToName(const char* iso);
|
||||
BandwidthScheduler(QObject *parent = 0);
|
||||
|
||||
private:
|
||||
static QString geoipFolder(bool embedded=false);
|
||||
static QString geoipDBpath(bool embedded=false);
|
||||
#ifdef WITH_GEOIP_EMBEDDED
|
||||
static void exportEmbeddedDb();
|
||||
#endif
|
||||
public slots:
|
||||
void start();
|
||||
|
||||
signals:
|
||||
void switchToAlternativeMode(bool alternative);
|
||||
};
|
||||
|
||||
|
||||
#endif // GEOIP_H
|
||||
#endif // BANDWIDTHSCHEDULER_H
|
||||
439
src/core/bittorrent/private/filterparserthread.cpp
Normal file
439
src/core/bittorrent/private/filterparserthread.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libt.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
#include <QDataStream>
|
||||
#include <QStringList>
|
||||
|
||||
#include <libtorrent/session.hpp>
|
||||
#include <libtorrent/ip_filter.hpp>
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "filterparserthread.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
|
||||
FilterParserThread::FilterParserThread(libt::session *s, QObject *parent)
|
||||
: QThread(parent)
|
||||
, m_session(s)
|
||||
, m_abort(false)
|
||||
{
|
||||
}
|
||||
|
||||
FilterParserThread::~FilterParserThread()
|
||||
{
|
||||
m_abort = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
// Parser for eMule ip filter in DAT format
|
||||
int FilterParserThread::parseDATFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filter file in read mode."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
unsigned int nbLine = 0;
|
||||
while (!file.atEnd() && !m_abort) {
|
||||
++nbLine;
|
||||
QByteArray line = file.readLine();
|
||||
// Ignoring empty lines
|
||||
line = line.trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// Ignoring commented lines
|
||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||
|
||||
// Line should be split by commas
|
||||
QList<QByteArray> partsList = line.split(',');
|
||||
const uint nbElem = partsList.size();
|
||||
|
||||
// IP Range should be split by a dash
|
||||
QList<QByteArray> IPs = partsList.first().split('-');
|
||||
if (IPs.size() != 2) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Line was %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
const QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||
if (strStartIP.isEmpty()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec);
|
||||
if (ec) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||
if (strEndIP.isEmpty()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec);
|
||||
if (ec) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("One IP is IPv4 and the other is IPv6!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if there is an access value (apparently not mandatory)
|
||||
int nbAccess = 0;
|
||||
if (nbElem > 1) {
|
||||
// There is possibly one
|
||||
nbAccess = partsList.at(1).trimmed().toInt();
|
||||
}
|
||||
|
||||
if (nbAccess > 127) {
|
||||
// Ignoring this rule because access value is too high
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now Add to the filter
|
||||
try {
|
||||
filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
}
|
||||
catch(std::exception &) {
|
||||
qDebug("Bad line in filter file, avoided crash...");
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// Parser for PeerGuardian ip filter in p2p format
|
||||
int FilterParserThread::parseP2PFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filter file in read mode."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
unsigned int nbLine = 0;
|
||||
while (!file.atEnd() && !m_abort) {
|
||||
++nbLine;
|
||||
QByteArray line = file.readLine().trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// Ignoring commented lines
|
||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||
|
||||
// Line is split by :
|
||||
QList<QByteArray> partsList = line.split(':');
|
||||
if (partsList.size() < 2) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get IP range
|
||||
QList<QByteArray> IPs = partsList.last().split('-');
|
||||
if (IPs.size() != 2) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||
if (strStartIP.isEmpty()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
libt::address startAddr = libt::address::from_string(qPrintable(strStartIP), ec);
|
||||
if (ec) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||
if (strEndIP.isEmpty()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
libt::address endAddr = libt::address::from_string(qPrintable(strEndIP), ec);
|
||||
if (ec) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
filter.add_rule(startAddr, endAddr, libt::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
}
|
||||
catch(std::exception &) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
||||
{
|
||||
char c;
|
||||
int total_read = 0;
|
||||
int read;
|
||||
do {
|
||||
read = stream.readRawData(&c, 1);
|
||||
total_read += read;
|
||||
if (read > 0) {
|
||||
if (c != delim) {
|
||||
name += c;
|
||||
}
|
||||
else {
|
||||
// Delim found
|
||||
return total_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
while(read > 0);
|
||||
|
||||
return total_read;
|
||||
}
|
||||
|
||||
// Parser for PeerGuardian ip filter in p2p format
|
||||
int FilterParserThread::parseP2BFilterFile(QString m_filePath, libt::ip_filter &filter)
|
||||
{
|
||||
int ruleCount = 0;
|
||||
QFile file(m_filePath);
|
||||
if (!file.exists()) return ruleCount;
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
Logger::instance()->addMessage(tr("I/O Error: Could not open ip filter file in read mode."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
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))) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
if ((version == 1) || (version == 2)) {
|
||||
qDebug ("p2b version 1 or 2");
|
||||
unsigned int start, end;
|
||||
|
||||
std::string name;
|
||||
while(getlineInStream(stream, name, '\0') && !m_abort) {
|
||||
if (!stream.readRawData((char*)&start, sizeof(start))
|
||||
|| !stream.readRawData((char*)&end, sizeof(end))) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// Network byte order to Host byte order
|
||||
// asio address_v4 constructor expects it
|
||||
// that way
|
||||
libt::address_v4 first(ntohl(start));
|
||||
libt::address_v4 last(ntohl(end));
|
||||
// Apply to bittorrent session
|
||||
try {
|
||||
filter.add_rule(first, last, libt::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
}
|
||||
catch(std::exception &) {}
|
||||
}
|
||||
}
|
||||
else if (version == 3) {
|
||||
qDebug ("p2b version 3");
|
||||
unsigned int namecount;
|
||||
if (!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
namecount = ntohl(namecount);
|
||||
// Reading names although, we don't really care about them
|
||||
for (unsigned int i = 0; i < namecount; ++i) {
|
||||
std::string name;
|
||||
if (!getlineInStream(stream, name, '\0')) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
if (m_abort) return ruleCount;
|
||||
}
|
||||
|
||||
// Reading the ranges
|
||||
unsigned int rangecount;
|
||||
if (!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
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))) {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// Network byte order to Host byte order
|
||||
// asio address_v4 constructor expects it
|
||||
// that way
|
||||
libt::address_v4 first(ntohl(start));
|
||||
libt::address_v4 last(ntohl(end));
|
||||
// Apply to bittorrent session
|
||||
try {
|
||||
filter.add_rule(first, last, libt::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
}
|
||||
catch(std::exception &) {}
|
||||
|
||||
if (m_abort) return ruleCount;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Logger::instance()->addMessage(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||
}
|
||||
|
||||
file.close();
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// 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 FilterParserThread::processFilterFile(QString _filePath)
|
||||
{
|
||||
if (isRunning()) {
|
||||
// Already parsing a filter, m_abort first
|
||||
m_abort = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
m_abort = false;
|
||||
m_filePath = _filePath;
|
||||
// Run it
|
||||
start();
|
||||
}
|
||||
|
||||
void FilterParserThread::processFilterList(libt::session *s, const QStringList &IPs)
|
||||
{
|
||||
// First, import current filter
|
||||
libt::ip_filter filter = s->get_ip_filter();
|
||||
foreach (const QString &ip, IPs) {
|
||||
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
|
||||
boost::system::error_code ec;
|
||||
libt::address addr = libt::address::from_string(ip.toLocal8Bit().constData(), ec);
|
||||
Q_ASSERT(!ec);
|
||||
if (!ec)
|
||||
filter.add_rule(addr, addr, libt::ip_filter::blocked);
|
||||
}
|
||||
|
||||
s->set_ip_filter(filter);
|
||||
}
|
||||
|
||||
QString FilterParserThread::cleanupIPAddress(QString _ip)
|
||||
{
|
||||
QHostAddress ip(_ip.trimmed());
|
||||
if (ip.isNull()) return QString();
|
||||
|
||||
return ip.toString();
|
||||
}
|
||||
|
||||
void FilterParserThread::run()
|
||||
{
|
||||
qDebug("Processing filter file");
|
||||
libt::ip_filter filter = m_session->get_ip_filter();
|
||||
int ruleCount = 0;
|
||||
if (m_filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
||||
// PeerGuardian p2p file
|
||||
ruleCount = parseP2PFilterFile(m_filePath, filter);
|
||||
}
|
||||
else if (m_filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
|
||||
// PeerGuardian p2b file
|
||||
ruleCount = parseP2BFilterFile(m_filePath, filter);
|
||||
}
|
||||
else if (m_filePath.endsWith(".dat", Qt::CaseInsensitive)) {
|
||||
// eMule DAT format
|
||||
ruleCount = parseDATFilterFile(m_filePath, filter);
|
||||
}
|
||||
|
||||
if (m_abort) return;
|
||||
|
||||
try {
|
||||
m_session->set_ip_filter(filter);
|
||||
emit IPFilterParsed(ruleCount);
|
||||
}
|
||||
catch(std::exception &) {
|
||||
emit IPFilterError();
|
||||
}
|
||||
|
||||
qDebug("IP Filter thread: finished parsing, filter applied");
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -32,38 +32,30 @@
|
||||
#define FILTERPARSERTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QDataStream>
|
||||
#include <QStringList>
|
||||
|
||||
namespace libtorrent {
|
||||
class session;
|
||||
struct ip_filter;
|
||||
class QDataStream;
|
||||
class QStringList;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class session;
|
||||
struct ip_filter;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
// P2B Stuff
|
||||
#include <string.h>
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
// End of P2B stuff
|
||||
|
||||
class FilterParserThread : public QThread {
|
||||
class FilterParserThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FilterParserThread(QObject* parent, libtorrent::session *s);
|
||||
FilterParserThread(libtorrent::session *s, QObject *parent = 0);
|
||||
~FilterParserThread();
|
||||
|
||||
int parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
||||
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
||||
int getlineInStream(QDataStream& stream, string& name, char delim);
|
||||
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter);
|
||||
int parseDATFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||
int parseP2PFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||
int getlineInStream(QDataStream &stream, std::string &name, char delim);
|
||||
int parseP2BFilterFile(QString filePath, libtorrent::ip_filter &filter);
|
||||
void processFilterFile(QString _filePath);
|
||||
static void processFilterList(libtorrent::session *s, const QStringList& IPs);
|
||||
static void processFilterList(libtorrent::session *s, const QStringList &IPs);
|
||||
|
||||
signals:
|
||||
void IPFilterParsed(int ruleCount);
|
||||
@@ -74,9 +66,9 @@ protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
libtorrent::session *s;
|
||||
bool abort;
|
||||
QString filePath;
|
||||
libtorrent::session *m_session;
|
||||
bool m_abort;
|
||||
QString m_filePath;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // BITTORRENT_FILTERPARSERTHREAD_H
|
||||
56
src/core/bittorrent/private/speedmonitor.cpp
Normal file
56
src/core/bittorrent/private/speedmonitor.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QList>
|
||||
#include "speedmonitor.h"
|
||||
|
||||
void SpeedMonitor::addSample(const SpeedSample &sample)
|
||||
{
|
||||
m_speedSamples.push_back(sample);
|
||||
m_sum += sample;
|
||||
if (m_speedSamples.size() > MAX_SAMPLES) {
|
||||
m_sum -= m_speedSamples.front();
|
||||
m_speedSamples.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
SpeedSampleAvg SpeedMonitor::average() const
|
||||
{
|
||||
if (m_speedSamples.empty())
|
||||
return SpeedSampleAvg();
|
||||
|
||||
qreal k = qreal(1.) / m_speedSamples.size();
|
||||
return SpeedSampleAvg(m_sum.download * k, m_sum.upload * k);
|
||||
}
|
||||
|
||||
void SpeedMonitor::reset()
|
||||
{
|
||||
m_sum = SpeedSample();
|
||||
m_speedSamples.clear();
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -26,42 +27,58 @@
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef QTORRENTFILTER_H
|
||||
#define QTORRENTFILTER_H
|
||||
#ifndef SPEEDMONITOR_H
|
||||
#define SPEEDMONITOR_H
|
||||
|
||||
#include "qtorrenthandle.h"
|
||||
template<typename T> class QList;
|
||||
|
||||
class QTorrentFilter
|
||||
template<typename T>
|
||||
struct Sample
|
||||
{
|
||||
public:
|
||||
enum
|
||||
Sample()
|
||||
: download()
|
||||
, upload()
|
||||
{
|
||||
All,
|
||||
Downloading,
|
||||
Seeding,
|
||||
Completed,
|
||||
Paused,
|
||||
Resumed,
|
||||
Active,
|
||||
Inactive
|
||||
};
|
||||
}
|
||||
|
||||
// label: pass empty string for "no label" or null string (QString()) for "any label"
|
||||
QTorrentFilter(QString filter, QString label = QString());
|
||||
bool apply(const QTorrentHandle& h) const;
|
||||
Sample(T dl, T ul)
|
||||
: download(dl)
|
||||
, upload(ul)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
int type_;
|
||||
QString label_;
|
||||
Sample<T> &operator+=(const Sample<T> &other)
|
||||
{
|
||||
download += other.download;
|
||||
upload += other.upload;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isTorrentDownloading(const QTorrentHandle &h) const;
|
||||
bool isTorrentSeeding(const QTorrentHandle &h) const;
|
||||
bool isTorrentCompleted(const QTorrentHandle &h) const;
|
||||
bool isTorrentPaused(const QTorrentHandle &h) const;
|
||||
bool isTorrentResumed(const QTorrentHandle &h) const;
|
||||
bool isTorrentActive(const QTorrentHandle &h) const;
|
||||
bool isTorrentInactive(const QTorrentHandle &h) const;
|
||||
bool torrentHasLabel(const QTorrentHandle &h) const;
|
||||
Sample<T> &operator-=(const Sample<T> &other)
|
||||
{
|
||||
download -= other.download;
|
||||
upload -= other.upload;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T download;
|
||||
T upload;
|
||||
};
|
||||
|
||||
#endif // QTORRENTFILTER_H
|
||||
typedef Sample<qlonglong> SpeedSample;
|
||||
typedef Sample<qreal> SpeedSampleAvg;
|
||||
|
||||
class SpeedMonitor
|
||||
{
|
||||
public:
|
||||
void addSample(const SpeedSample &sample);
|
||||
SpeedSampleAvg average() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
static const int MAX_SAMPLES = 30;
|
||||
QList<SpeedSample> m_speedSamples;
|
||||
SpeedSample m_sum;
|
||||
};
|
||||
|
||||
#endif // SPEEDMONITOR_H
|
||||
@@ -1,67 +1,81 @@
|
||||
#include "torrentstatistics.h"
|
||||
|
||||
#include <QDateTime>
|
||||
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
#include "qbtsession.h"
|
||||
#include "qinisettings.h"
|
||||
#include "preferences.h"
|
||||
#include "core/qinisettings.h"
|
||||
#include "core/preferences.h"
|
||||
#include "core/bittorrent/sessionstatus.h"
|
||||
#include "core/bittorrent/session.h"
|
||||
#include "statistics.h"
|
||||
|
||||
TorrentStatistics::TorrentStatistics(QBtSession* session, QObject* parent)
|
||||
: QObject(parent)
|
||||
static const qint64 SAVE_INTERVAL = 15 * 60 * 1000;
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
Statistics::Statistics(Session *session)
|
||||
: QObject(session)
|
||||
, m_session(session)
|
||||
, m_sessionUL(0)
|
||||
, m_sessionDL(0)
|
||||
, m_lastWrite(0)
|
||||
, m_dirty(false)
|
||||
{
|
||||
loadStats();
|
||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gatherStats()));
|
||||
load();
|
||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
|
||||
m_timer.start(60 * 1000);
|
||||
}
|
||||
|
||||
TorrentStatistics::~TorrentStatistics() {
|
||||
Statistics::~Statistics()
|
||||
{
|
||||
if (m_dirty)
|
||||
m_lastWrite = 0;
|
||||
saveStats();
|
||||
save();
|
||||
}
|
||||
|
||||
quint64 TorrentStatistics::getAlltimeDL() const {
|
||||
quint64 Statistics::getAlltimeDL() const
|
||||
{
|
||||
return m_alltimeDL + m_sessionDL;
|
||||
}
|
||||
|
||||
quint64 TorrentStatistics::getAlltimeUL() const {
|
||||
quint64 Statistics::getAlltimeUL() const
|
||||
{
|
||||
return m_alltimeUL + m_sessionUL;
|
||||
}
|
||||
|
||||
void TorrentStatistics::gatherStats() {
|
||||
libtorrent::session_status ss = m_session->getSessionStatus();
|
||||
if (ss.total_download > m_sessionDL) {
|
||||
m_sessionDL = ss.total_download;
|
||||
void Statistics::gather()
|
||||
{
|
||||
SessionStatus ss = m_session->status();
|
||||
if (ss.totalDownload() > m_sessionDL) {
|
||||
m_sessionDL = ss.totalDownload();
|
||||
m_dirty = true;
|
||||
}
|
||||
if (ss.total_upload > m_sessionUL) {
|
||||
m_sessionUL = ss.total_upload;
|
||||
if (ss.totalUpload() > m_sessionUL) {
|
||||
m_sessionUL = ss.totalUpload();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
saveStats();
|
||||
save();
|
||||
}
|
||||
|
||||
void TorrentStatistics::saveStats() const {
|
||||
if (!(m_dirty && (QDateTime::currentMSecsSinceEpoch() - m_lastWrite >= 15*60*1000) ))
|
||||
void Statistics::save() const
|
||||
{
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
if (!m_dirty || ((now - m_lastWrite) < SAVE_INTERVAL))
|
||||
return;
|
||||
|
||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||
QVariantHash v;
|
||||
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL);
|
||||
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL);
|
||||
s.setValue("Stats/AllStats", v);
|
||||
m_dirty = false;
|
||||
m_lastWrite = QDateTime::currentMSecsSinceEpoch();
|
||||
m_lastWrite = now;
|
||||
}
|
||||
|
||||
void TorrentStatistics::loadStats() {
|
||||
void Statistics::load()
|
||||
{
|
||||
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
||||
// This code reads the data from there, writes it to the new file, and removes the keys
|
||||
// from the old file. This code should be removed after some time has passed.
|
||||
@@ -69,7 +83,7 @@ void TorrentStatistics::loadStats() {
|
||||
// Don't forget to remove:
|
||||
// 1. Preferences::getStats()
|
||||
// 2. Preferences::removeStats()
|
||||
// 3. #include "preferences.h"
|
||||
// 3. #include "core/preferences.h"
|
||||
Preferences* const pref = Preferences::instance();
|
||||
QIniSettings s("qBittorrent", "qBittorrent-data");
|
||||
QVariantHash v = pref->getStats();
|
||||
@@ -87,14 +101,15 @@ void TorrentStatistics::loadStats() {
|
||||
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong();
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
v = s.value("Stats/AllStats").toHash();
|
||||
}
|
||||
|
||||
m_alltimeDL = v["AlltimeDL"].toULongLong();
|
||||
m_alltimeUL = v["AlltimeUL"].toULongLong();
|
||||
|
||||
if (m_dirty) {
|
||||
saveStats();
|
||||
save();
|
||||
pref->removeStats();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,32 @@
|
||||
#ifndef TORRENTSTATISTICS_H
|
||||
#define TORRENTSTATISTICS_H
|
||||
#ifndef STATISTICS_H
|
||||
#define STATISTICS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
class QBtSession;
|
||||
namespace BitTorrent { class Session; }
|
||||
|
||||
class TorrentStatistics : QObject
|
||||
class Statistics : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(TorrentStatistics)
|
||||
Q_DISABLE_COPY(Statistics)
|
||||
|
||||
public:
|
||||
TorrentStatistics(QBtSession* session, QObject* parent = 0);
|
||||
~TorrentStatistics();
|
||||
Statistics(BitTorrent::Session *session);
|
||||
~Statistics();
|
||||
|
||||
quint64 getAlltimeDL() const;
|
||||
quint64 getAlltimeUL() const;
|
||||
|
||||
private slots:
|
||||
void gatherStats();
|
||||
void gather();
|
||||
|
||||
private:
|
||||
void saveStats() const;
|
||||
void loadStats();
|
||||
void save() const;
|
||||
void load();
|
||||
|
||||
private:
|
||||
QBtSession* m_session;
|
||||
BitTorrent::Session *m_session;
|
||||
// Will overflow at 15.9 EiB
|
||||
quint64 m_alltimeUL;
|
||||
quint64 m_alltimeDL;
|
||||
@@ -38,4 +38,4 @@ private:
|
||||
QTimer m_timer;
|
||||
};
|
||||
|
||||
#endif // TORRENTSTATISTICS_H
|
||||
#endif // STATISTICS_H
|
||||
2477
src/core/bittorrent/session.cpp
Normal file
2477
src/core/bittorrent/session.cpp
Normal file
File diff suppressed because it is too large
Load Diff
376
src/core/bittorrent/session.h
Normal file
376
src/core/bittorrent/session.h
Normal file
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_SESSION_H
|
||||
#define BITTORRENT_SESSION_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
#include <QVector>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QNetworkConfigurationManager>
|
||||
|
||||
#include "core/tristatebool.h"
|
||||
#include "core/types.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class session;
|
||||
class entry;
|
||||
struct add_torrent_params;
|
||||
struct pe_settings;
|
||||
struct proxy_settings;
|
||||
struct session_settings;
|
||||
struct session_status;
|
||||
|
||||
class alert;
|
||||
struct torrent_alert;
|
||||
struct state_update_alert;
|
||||
struct stats_alert;
|
||||
struct add_torrent_alert;
|
||||
struct torrent_checked_alert;
|
||||
struct torrent_finished_alert;
|
||||
struct torrent_removed_alert;
|
||||
struct torrent_deleted_alert;
|
||||
struct torrent_delete_failed_alert;
|
||||
struct torrent_paused_alert;
|
||||
struct torrent_resumed_alert;
|
||||
struct save_resume_data_alert;
|
||||
struct save_resume_data_failed_alert;
|
||||
struct file_renamed_alert;
|
||||
struct storage_moved_alert;
|
||||
struct storage_moved_failed_alert;
|
||||
struct metadata_received_alert;
|
||||
struct file_error_alert;
|
||||
struct file_completed_alert;
|
||||
struct tracker_error_alert;
|
||||
struct tracker_reply_alert;
|
||||
struct tracker_warning_alert;
|
||||
struct portmap_error_alert;
|
||||
struct portmap_alert;
|
||||
struct peer_blocked_alert;
|
||||
struct peer_ban_alert;
|
||||
struct fastresume_rejected_alert;
|
||||
struct url_seed_alert;
|
||||
struct listen_succeeded_alert;
|
||||
struct listen_failed_alert;
|
||||
struct external_ip_alert;
|
||||
}
|
||||
|
||||
class QTimer;
|
||||
class QStringList;
|
||||
class QString;
|
||||
class QUrl;
|
||||
template<typename T> class QList;
|
||||
|
||||
class FilterParserThread;
|
||||
class BandwidthScheduler;
|
||||
class Statistics;
|
||||
|
||||
typedef QPair<QString, QString> QStringPair;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class InfoHash;
|
||||
class CacheStatus;
|
||||
class SessionStatus;
|
||||
class TorrentHandle;
|
||||
class Tracker;
|
||||
class MagnetUri;
|
||||
class TrackerEntry;
|
||||
struct AddTorrentData;
|
||||
|
||||
struct AddTorrentParams
|
||||
{
|
||||
QString name;
|
||||
QString label;
|
||||
QString savePath;
|
||||
bool disableTempPath; // e.g. for imported torrents
|
||||
bool sequential;
|
||||
TriStateBool addForced;
|
||||
TriStateBool addPaused;
|
||||
QVector<int> filePriorities; // used if TorrentInfo is set
|
||||
bool ignoreShareRatio;
|
||||
bool skipChecking;
|
||||
|
||||
AddTorrentParams();
|
||||
};
|
||||
|
||||
struct TorrentStatusReport
|
||||
{
|
||||
uint nbDownloading = 0;
|
||||
uint nbSeeding = 0;
|
||||
uint nbCompleted = 0;
|
||||
uint nbActive = 0;
|
||||
uint nbInactive = 0;
|
||||
uint nbPaused = 0;
|
||||
uint nbResumed = 0;
|
||||
uint nbErrored = 0;
|
||||
};
|
||||
|
||||
class Session : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Session)
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static Session *instance();
|
||||
|
||||
bool isDHTEnabled() const;
|
||||
bool isLSDEnabled() const;
|
||||
bool isPexEnabled() const;
|
||||
bool isQueueingEnabled() const;
|
||||
qreal globalMaxRatio() const;
|
||||
bool isTempPathEnabled() const;
|
||||
bool isAppendExtensionEnabled() const;
|
||||
bool useAppendLabelToSavePath() const;
|
||||
QString defaultSavePath() const;
|
||||
QString tempPath() const;
|
||||
|
||||
TorrentHandle *findTorrent(const InfoHash &hash) const;
|
||||
QHash<InfoHash, TorrentHandle *> torrents() const;
|
||||
TorrentStatusReport torrentStatusReport() const;
|
||||
bool hasActiveTorrents() const;
|
||||
bool hasUnfinishedTorrents() const;
|
||||
SessionStatus status() const;
|
||||
CacheStatus cacheStatus() const;
|
||||
quint64 getAlltimeDL() const;
|
||||
quint64 getAlltimeUL() const;
|
||||
int downloadRateLimit() const;
|
||||
int uploadRateLimit() const;
|
||||
bool isListening() const;
|
||||
|
||||
void changeSpeedLimitMode(bool alternative);
|
||||
void setDownloadRateLimit(int rate);
|
||||
void setUploadRateLimit(int rate);
|
||||
void setGlobalMaxRatio(qreal ratio);
|
||||
void enableIPFilter(const QString &filterPath, bool force = false);
|
||||
void disableIPFilter();
|
||||
void banIP(const QString &ip);
|
||||
|
||||
bool isKnownTorrent(const InfoHash &hash) const;
|
||||
bool addTorrent(QString source, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||
bool addTorrent(const TorrentInfo &torrentInfo, const AddTorrentParams ¶ms = AddTorrentParams());
|
||||
bool deleteTorrent(const QString &hash, bool deleteLocalFiles = false);
|
||||
bool loadMetadata(const QString &magnetUri);
|
||||
bool cancelLoadMetadata(const InfoHash &hash);
|
||||
|
||||
void recursiveTorrentDownload(const InfoHash &hash);
|
||||
void increaseTorrentsPriority(const QStringList &hashes);
|
||||
void decreaseTorrentsPriority(const QStringList &hashes);
|
||||
void topTorrentsPriority(const QStringList &hashes);
|
||||
void bottomTorrentsPriority(const QStringList &hashes);
|
||||
|
||||
// TorrentHandle interface
|
||||
void handleTorrentRatioLimitChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentLabelChanged(TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void handleTorrentMetadataReceived(TorrentHandle *const torrent);
|
||||
void handleTorrentPaused(TorrentHandle *const torrent);
|
||||
void handleTorrentResumed(TorrentHandle *const torrent);
|
||||
void handleTorrentChecked(TorrentHandle *const torrent);
|
||||
void handleTorrentFinished(TorrentHandle *const torrent);
|
||||
void handleTorrentTrackersAdded(TorrentHandle *const torrent, const QList<TrackerEntry> &newTrackers);
|
||||
void handleTorrentTrackersRemoved(TorrentHandle *const torrent, const QList<TrackerEntry> &deletedTrackers);
|
||||
void handleTorrentTrackersChanged(TorrentHandle *const torrent);
|
||||
void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QList<QUrl> &newUrlSeeds);
|
||||
void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QList<QUrl> &urlSeeds);
|
||||
void handleTorrentResumeDataReady(TorrentHandle *const torrent, const libtorrent::entry &data);
|
||||
void handleTorrentResumeDataFailed(TorrentHandle *const torrent);
|
||||
void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||
void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||
void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||
void handleTorrentTrackerAuthenticationRequired(TorrentHandle *const torrent, const QString &trackerUrl);
|
||||
|
||||
signals:
|
||||
void torrentsUpdated();
|
||||
void addTorrentFailed(const QString &error);
|
||||
void torrentAdded(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentPaused(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentResumed(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentFinished(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentFinishedChecking(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentSavePathChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void torrentLabelChanged(BitTorrent::TorrentHandle *const torrent, const QString &oldLabel);
|
||||
void allTorrentsFinished();
|
||||
void metadataLoaded(const BitTorrent::TorrentInfo &info);
|
||||
void torrentMetadataLoaded(BitTorrent::TorrentHandle *const torrent);
|
||||
void fullDiskError(BitTorrent::TorrentHandle *const torrent, const QString &msg);
|
||||
void trackerSuccess(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||
void trackerWarning(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||
void trackerError(BitTorrent::TorrentHandle *const torrent, const QString &tracker);
|
||||
void trackerAuthenticationRequired(BitTorrent::TorrentHandle *const torrent);
|
||||
void recursiveTorrentDownloadPossible(BitTorrent::TorrentHandle *const torrent);
|
||||
void speedLimitModeChanged(bool alternative);
|
||||
void ipFilterParsed(bool error, int ruleCount);
|
||||
void trackersAdded(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
|
||||
void trackersRemoved(BitTorrent::TorrentHandle *const torrent, const QList<BitTorrent::TrackerEntry> &trackers);
|
||||
void trackersChanged(BitTorrent::TorrentHandle *const torrent);
|
||||
void trackerlessStateChanged(BitTorrent::TorrentHandle *const torrent, bool trackerless);
|
||||
void downloadFromUrlFailed(const QString &url, const QString &reason);
|
||||
void downloadFromUrlFinished(const QString &url);
|
||||
|
||||
private slots:
|
||||
void configure();
|
||||
void readAlerts();
|
||||
void refresh();
|
||||
void processBigRatios();
|
||||
void generateResumeData(bool final = false);
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void handleDownloadFinished(const QString &url, const QString &filePath);
|
||||
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||
void switchToAlternativeMode(bool alternative);
|
||||
|
||||
// Session reconfiguration triggers
|
||||
void networkOnlineStateChanged(const bool online);
|
||||
void networkConfigurationChange(const QNetworkConfiguration&);
|
||||
|
||||
private:
|
||||
explicit Session(QObject *parent = 0);
|
||||
~Session();
|
||||
|
||||
bool hasPerTorrentRatioLimit() const;
|
||||
|
||||
void initResumeFolder();
|
||||
|
||||
// Session configuration
|
||||
void setSessionSettings();
|
||||
void setProxySettings(libtorrent::proxy_settings proxySettings);
|
||||
void adjustLimits();
|
||||
void adjustLimits(libtorrent::session_settings &sessionSettings);
|
||||
const QStringList getListeningIPs();
|
||||
void setListeningPort();
|
||||
void setDefaultSavePath(const QString &path);
|
||||
void setDefaultTempPath(const QString &path = QString());
|
||||
void preAllocateAllFiles(bool b);
|
||||
void setMaxConnectionsPerTorrent(int max);
|
||||
void setMaxUploadsPerTorrent(int max);
|
||||
void enableLSD(bool enable);
|
||||
void enableDHT(bool enable);
|
||||
void changeSpeedLimitMode_impl(bool alternative);
|
||||
|
||||
void setAppendLabelToSavePath(bool append);
|
||||
void setAppendExtension(bool append);
|
||||
|
||||
void startUpTorrents();
|
||||
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
|
||||
const TorrentInfo &torrentInfo = TorrentInfo(),
|
||||
const QByteArray &fastresumeData = QByteArray());
|
||||
|
||||
void updateRatioTimer();
|
||||
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
||||
void exportTorrentFiles(QString path);
|
||||
void saveTorrentResumeData(TorrentHandle *const torrent);
|
||||
|
||||
void handleAlert(libtorrent::alert *a);
|
||||
void dispatchTorrentAlert(libtorrent::alert *a);
|
||||
void handleAddTorrentAlert(libtorrent::add_torrent_alert *p);
|
||||
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
|
||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
||||
void handleFileErrorAlert(libtorrent::file_error_alert *p);
|
||||
void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p);
|
||||
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p);
|
||||
void handleTorrentDeleteFailedAlert(libtorrent::torrent_delete_failed_alert *p);
|
||||
void handlePortmapWarningAlert(libtorrent::portmap_error_alert *p);
|
||||
void handlePortmapAlert(libtorrent::portmap_alert *p);
|
||||
void handlePeerBlockedAlert(libtorrent::peer_blocked_alert *p);
|
||||
void handlePeerBanAlert(libtorrent::peer_ban_alert *p);
|
||||
void handleUrlSeedAlert(libtorrent::url_seed_alert *p);
|
||||
void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p);
|
||||
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
|
||||
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
|
||||
|
||||
void saveResumeData();
|
||||
bool writeResumeDataFile(TorrentHandle *const torrent, const libtorrent::entry &data);
|
||||
|
||||
void dispatchAlerts(std::auto_ptr<libtorrent::alert> alertPtr);
|
||||
void getPendingAlerts(QVector<libtorrent::alert *> &out, ulong time = 0);
|
||||
|
||||
AddTorrentData addDataFromParams(const AddTorrentParams ¶ms);
|
||||
|
||||
// BitTorrent
|
||||
libtorrent::session *m_nativeSession;
|
||||
|
||||
bool m_LSDEnabled;
|
||||
bool m_DHTEnabled;
|
||||
bool m_PeXEnabled;
|
||||
bool m_queueingEnabled;
|
||||
bool m_torrentExportEnabled;
|
||||
bool m_finishedTorrentExportEnabled;
|
||||
bool m_preAllocateAll;
|
||||
qreal m_globalMaxRatio;
|
||||
int m_numResumeData;
|
||||
int m_extraLimit;
|
||||
bool m_appendLabelToSavePath;
|
||||
bool m_appendExtension;
|
||||
uint m_refreshInterval;
|
||||
MaxRatioAction m_highRatioAction;
|
||||
QList<BitTorrent::TrackerEntry> m_additionalTrackers;
|
||||
QString m_defaultSavePath;
|
||||
QString m_tempPath;
|
||||
QString m_filterPath;
|
||||
QString m_resumeFolderPath;
|
||||
QFile m_resumeFolderLock;
|
||||
QHash<InfoHash, QString> m_savePathsToRemove;
|
||||
|
||||
QTimer *m_refreshTimer;
|
||||
QTimer *m_bigRatioTimer;
|
||||
QTimer *m_resumeDataTimer;
|
||||
Statistics *m_statistics;
|
||||
// IP filtering
|
||||
QPointer<FilterParserThread> m_filterParser;
|
||||
QPointer<BandwidthScheduler> m_bwScheduler;
|
||||
// Tracker
|
||||
QPointer<Tracker> m_tracker;
|
||||
|
||||
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
||||
QHash<InfoHash, TorrentHandle *> m_torrents;
|
||||
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||
TorrentStatusReport m_torrentStatusReport;
|
||||
|
||||
QMutex m_alertsMutex;
|
||||
QWaitCondition m_alertsWaitCondition;
|
||||
QVector<libtorrent::alert *> m_alerts;
|
||||
|
||||
QNetworkConfigurationManager m_networkManager;
|
||||
|
||||
static Session *m_instance;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_SESSION_H
|
||||
136
src/core/bittorrent/sessionstatus.cpp
Normal file
136
src/core/bittorrent/sessionstatus.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "sessionstatus.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
SessionStatus::SessionStatus(const libtorrent::session_status &nativeStatus)
|
||||
: m_nativeStatus(nativeStatus)
|
||||
{
|
||||
}
|
||||
|
||||
bool SessionStatus::hasIncomingConnections() const
|
||||
{
|
||||
return m_nativeStatus.has_incoming_connections;
|
||||
}
|
||||
|
||||
int SessionStatus::payloadDownloadRate() const
|
||||
{
|
||||
return m_nativeStatus.payload_download_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::payloadUploadRate() const
|
||||
{
|
||||
return m_nativeStatus.payload_upload_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::downloadRate() const
|
||||
{
|
||||
return m_nativeStatus.download_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::uploadRate() const
|
||||
{
|
||||
return m_nativeStatus.upload_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::ipOverheadDownloadRate() const
|
||||
{
|
||||
return m_nativeStatus.ip_overhead_download_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::ipOverheadUploadRate() const
|
||||
{
|
||||
return m_nativeStatus.ip_overhead_upload_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::dhtDownloadRate() const
|
||||
{
|
||||
return m_nativeStatus.dht_download_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::dhtUploadRate() const
|
||||
{
|
||||
return m_nativeStatus.dht_upload_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::trackerDownloadRate() const
|
||||
{
|
||||
return m_nativeStatus.tracker_download_rate;
|
||||
}
|
||||
|
||||
int SessionStatus::trackerUploadRate() const
|
||||
{
|
||||
return m_nativeStatus.tracker_upload_rate;
|
||||
}
|
||||
|
||||
qlonglong SessionStatus::totalDownload() const
|
||||
{
|
||||
return m_nativeStatus.total_download;
|
||||
}
|
||||
|
||||
qlonglong SessionStatus::totalUpload() const
|
||||
{
|
||||
return m_nativeStatus.total_upload;
|
||||
}
|
||||
|
||||
qlonglong SessionStatus::totalPayloadDownload() const
|
||||
{
|
||||
return m_nativeStatus.total_payload_download;
|
||||
}
|
||||
|
||||
qlonglong SessionStatus::totalPayloadUpload() const
|
||||
{
|
||||
return m_nativeStatus.total_payload_upload;
|
||||
}
|
||||
|
||||
qlonglong SessionStatus::totalWasted() const
|
||||
{
|
||||
return (m_nativeStatus.total_redundant_bytes + m_nativeStatus.total_failed_bytes);
|
||||
}
|
||||
|
||||
int SessionStatus::diskReadQueue() const
|
||||
{
|
||||
return m_nativeStatus.disk_read_queue;
|
||||
}
|
||||
|
||||
int SessionStatus::diskWriteQueue() const
|
||||
{
|
||||
return m_nativeStatus.disk_write_queue;
|
||||
}
|
||||
|
||||
int SessionStatus::dhtNodes() const
|
||||
{
|
||||
return m_nativeStatus.dht_nodes;
|
||||
}
|
||||
|
||||
int SessionStatus::peersCount() const
|
||||
{
|
||||
return m_nativeStatus.num_peers;
|
||||
}
|
||||
79
src/core/bittorrent/sessionstatus.h
Normal file
79
src/core/bittorrent/sessionstatus.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_SESSIONSTATUS_H
|
||||
#define BITTORRENT_SESSIONSTATUS_H
|
||||
|
||||
#include <libtorrent/session_status.hpp>
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class SessionStatus
|
||||
{
|
||||
public:
|
||||
SessionStatus(const libtorrent::session_status &nativeStatus);
|
||||
|
||||
bool hasIncomingConnections() const;
|
||||
|
||||
// Return current download rate for the BT
|
||||
// session. Payload means that it only take into
|
||||
// account "useful" part of the rate
|
||||
int payloadDownloadRate() const;
|
||||
|
||||
// Return current upload rate for the BT
|
||||
// session. Payload means that it only take into
|
||||
// account "useful" part of the rate
|
||||
int payloadUploadRate() const;
|
||||
|
||||
// Additional download/upload rates
|
||||
int uploadRate() const;
|
||||
int downloadRate() const;
|
||||
int ipOverheadUploadRate() const;
|
||||
int ipOverheadDownloadRate() const;
|
||||
int dhtUploadRate() const;
|
||||
int dhtDownloadRate() const;
|
||||
int trackerUploadRate() const;
|
||||
int trackerDownloadRate() const;
|
||||
|
||||
qlonglong totalDownload() const;
|
||||
qlonglong totalUpload() const;
|
||||
qlonglong totalPayloadDownload() const;
|
||||
qlonglong totalPayloadUpload() const;
|
||||
qlonglong totalWasted() const;
|
||||
int diskReadQueue() const;
|
||||
int diskWriteQueue() const;
|
||||
int dhtNodes() const;
|
||||
int peersCount() const;
|
||||
|
||||
private:
|
||||
libtorrent::session_status m_nativeStatus;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_SESSIONSTATUS_H
|
||||
165
src/core/bittorrent/torrentcreatorthread.cpp
Normal file
165
src/core/bittorrent/torrentcreatorthread.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <libtorrent/entry.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <libtorrent/file.hpp>
|
||||
#include <libtorrent/storage.hpp>
|
||||
#include <libtorrent/hasher.hpp>
|
||||
#include <libtorrent/file_pool.hpp>
|
||||
#include <libtorrent/create_torrent.hpp>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/misc.h"
|
||||
#include "core/utils/string.h"
|
||||
#include "torrentcreatorthread.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
// do not include files and folders whose
|
||||
// name starts with a .
|
||||
bool fileFilter(const std::string &f)
|
||||
{
|
||||
return (libt::filename(f)[0] != '.');
|
||||
}
|
||||
|
||||
TorrentCreatorThread::TorrentCreatorThread(QObject *parent)
|
||||
: QThread(parent)
|
||||
{
|
||||
}
|
||||
|
||||
TorrentCreatorThread::~TorrentCreatorThread()
|
||||
{
|
||||
m_abort = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize)
|
||||
{
|
||||
m_inputPath = Utils::Fs::fromNativePath(inputPath);
|
||||
m_savePath = Utils::Fs::fromNativePath(savePath);
|
||||
if (QFile(m_savePath).exists())
|
||||
Utils::Fs::forceRemove(m_savePath);
|
||||
m_trackers = trackers;
|
||||
m_urlSeeds = urlSeeds;
|
||||
m_comment = comment;
|
||||
m_private = isPrivate;
|
||||
m_pieceSize = pieceSize;
|
||||
m_abort = false;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::sendProgressSignal(int numHashes, int numPieces)
|
||||
{
|
||||
emit updateProgress(static_cast<int>((numHashes * 100.) / numPieces));
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::abortCreation()
|
||||
{
|
||||
m_abort = true;
|
||||
}
|
||||
|
||||
void TorrentCreatorThread::run()
|
||||
{
|
||||
emit updateProgress(0);
|
||||
|
||||
QString creator_str("qBittorrent " VERSION);
|
||||
try {
|
||||
libt::file_storage fs;
|
||||
// Adding files to the torrent
|
||||
libt::add_files(fs, Utils::String::toStdString(Utils::Fs::toNativePath(m_inputPath)), fileFilter);
|
||||
if (m_abort) return;
|
||||
|
||||
libt::create_torrent t(fs, m_pieceSize);
|
||||
|
||||
// Add url seeds
|
||||
foreach (const QString &seed, m_urlSeeds)
|
||||
t.add_url_seed(Utils::String::toStdString(seed.trimmed()));
|
||||
|
||||
int tier = 0;
|
||||
bool newline = false;
|
||||
foreach (const QString &tracker, m_trackers) {
|
||||
if (tracker.isEmpty()) {
|
||||
if (newline)
|
||||
continue;
|
||||
++tier;
|
||||
newline = true;
|
||||
continue;
|
||||
}
|
||||
t.add_tracker(Utils::String::toStdString(tracker.trimmed()), tier);
|
||||
newline = false;
|
||||
}
|
||||
if (m_abort) return;
|
||||
|
||||
// calculate the hash for all pieces
|
||||
const QString parentPath = Utils::Fs::branchPath(m_inputPath) + "/";
|
||||
libt::set_piece_hashes(t, Utils::String::toStdString(Utils::Fs::toNativePath(parentPath)), boost::bind(&TorrentCreatorThread::sendProgressSignal, this, _1, t.num_pieces()));
|
||||
// Set qBittorrent as creator and add user comment to
|
||||
// torrent_info structure
|
||||
t.set_creator(creator_str.toUtf8().constData());
|
||||
t.set_comment(m_comment.toUtf8().constData());
|
||||
// Is private ?
|
||||
t.set_priv(m_private);
|
||||
if (m_abort) return;
|
||||
|
||||
// create the torrent and print it to out
|
||||
qDebug("Saving to %s", qPrintable(m_savePath));
|
||||
#ifdef _MSC_VER
|
||||
wchar_t *savePathW = new wchar_t[m_savePath.length() + 1];
|
||||
int len = Utils::Fs::toNativePath(m_savePath).toWCharArray(savePathW);
|
||||
savePathW[len] = L'\0';
|
||||
std::ofstream outfile(savePathW, std::ios_base::out | std::ios_base::binary);
|
||||
delete[] savePathW;
|
||||
#else
|
||||
std::ofstream outfile(Utils::Fs::toNativePath(m_savePath).toLocal8Bit().constData(), std::ios_base::out | std::ios_base::binary);
|
||||
#endif
|
||||
if (outfile.fail())
|
||||
throw std::exception();
|
||||
|
||||
libt::bencode(std::ostream_iterator<char>(outfile), t.generate());
|
||||
outfile.close();
|
||||
|
||||
emit updateProgress(100);
|
||||
emit creationSuccess(m_savePath, parentPath);
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
emit creationFailure(Utils::String::fromStdString(e.what()));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -28,46 +28,46 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef TORRENTCREATORTHREAD_H
|
||||
#define TORRENTCREATORTHREAD_H
|
||||
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
||||
#define BITTORRENT_TORRENTCREATORTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
#include <QStringList>
|
||||
#include <QDialog>
|
||||
|
||||
class TorrentCreatorThread : public QThread {
|
||||
Q_OBJECT
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TorrentCreatorThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TorrentCreatorThread(QDialog *_parent) {
|
||||
parent = _parent;
|
||||
}
|
||||
~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);
|
||||
void sendProgressSignal(int progress);
|
||||
void abortCreation() { abort = true; }
|
||||
public:
|
||||
TorrentCreatorThread(QObject *parent = 0);
|
||||
~TorrentCreatorThread();
|
||||
|
||||
protected:
|
||||
void run();
|
||||
void create(const QString &inputPath, const QString &savePath, const QStringList &trackers,
|
||||
const QStringList &urlSeeds, const QString &comment, bool isPrivate, int pieceSize);
|
||||
void abortCreation();
|
||||
|
||||
signals:
|
||||
void creationFailure(QString msg);
|
||||
void creationSuccess(QString path, QString branch_path);
|
||||
void updateProgress(int progress);
|
||||
protected:
|
||||
void run();
|
||||
|
||||
private:
|
||||
QString input_path;
|
||||
QString save_path;
|
||||
QStringList trackers;
|
||||
QStringList url_seeds;
|
||||
QString comment;
|
||||
bool is_private;
|
||||
int piece_size;
|
||||
bool abort;
|
||||
QDialog *parent;
|
||||
};
|
||||
signals:
|
||||
void creationFailure(const QString &msg);
|
||||
void creationSuccess(const QString &path, const QString &branchPath);
|
||||
void updateProgress(int progress);
|
||||
|
||||
#endif // TORRENTCREATORTHREAD_H
|
||||
private:
|
||||
void sendProgressSignal(int numHashes, int numPieces);
|
||||
|
||||
QString m_inputPath;
|
||||
QString m_savePath;
|
||||
QStringList m_trackers;
|
||||
QStringList m_urlSeeds;
|
||||
QString m_comment;
|
||||
bool m_private;
|
||||
int m_pieceSize;
|
||||
bool m_abort;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_TORRENTCREATORTHREAD_H
|
||||
1837
src/core/bittorrent/torrenthandle.cpp
Normal file
1837
src/core/bittorrent/torrenthandle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
418
src/core/bittorrent/torrenthandle.h
Normal file
418
src/core/bittorrent/torrenthandle.h
Normal file
@@ -0,0 +1,418 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_TORRENTHANDLE_H
|
||||
#define BITTORRENT_TORRENTHANDLE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
#include <QQueue>
|
||||
#include <QVector>
|
||||
#include <QHash>
|
||||
|
||||
#include <libtorrent/torrent_handle.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include "core/tristatebool.h"
|
||||
#include "private/speedmonitor.h"
|
||||
#include "infohash.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
class QBitArray;
|
||||
class QStringList;
|
||||
template<typename T, typename U> struct QPair;
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class alert;
|
||||
struct stats_alert;
|
||||
struct torrent_checked_alert;
|
||||
struct torrent_finished_alert;
|
||||
struct torrent_paused_alert;
|
||||
struct torrent_resumed_alert;
|
||||
struct save_resume_data_alert;
|
||||
struct save_resume_data_failed_alert;
|
||||
struct file_renamed_alert;
|
||||
struct file_rename_failed_alert;
|
||||
struct storage_moved_alert;
|
||||
struct storage_moved_failed_alert;
|
||||
struct metadata_received_alert;
|
||||
struct file_completed_alert;
|
||||
struct tracker_error_alert;
|
||||
struct tracker_reply_alert;
|
||||
struct tracker_warning_alert;
|
||||
struct fastresume_rejected_alert;
|
||||
struct torrent_status;
|
||||
}
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct PeerAddress;
|
||||
class Session;
|
||||
class PeerInfo;
|
||||
class TrackerEntry;
|
||||
struct AddTorrentParams;
|
||||
|
||||
struct AddTorrentData
|
||||
{
|
||||
bool resumed;
|
||||
// for both new and resumed torrents
|
||||
QString name;
|
||||
QString label;
|
||||
QString savePath;
|
||||
bool disableTempPath;
|
||||
bool sequential;
|
||||
bool hasSeedStatus;
|
||||
bool skipChecking;
|
||||
TriStateBool addForced;
|
||||
TriStateBool addPaused;
|
||||
// for new torrents
|
||||
QVector<int> filePriorities;
|
||||
// for resumed torrents
|
||||
qreal ratioLimit;
|
||||
};
|
||||
|
||||
struct TrackerInfo
|
||||
{
|
||||
QString lastMessage;
|
||||
quint32 numPeers;
|
||||
|
||||
TrackerInfo();
|
||||
};
|
||||
|
||||
class TorrentState
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Unknown = -1,
|
||||
|
||||
ForcedDownloading,
|
||||
Downloading,
|
||||
DownloadingMetadata,
|
||||
Allocating,
|
||||
StalledDownloading,
|
||||
|
||||
ForcedUploading,
|
||||
Uploading,
|
||||
StalledUploading,
|
||||
|
||||
QueuedDownloading,
|
||||
QueuedUploading,
|
||||
|
||||
CheckingUploading,
|
||||
CheckingDownloading,
|
||||
|
||||
QueuedForChecking,
|
||||
CheckingResumeData,
|
||||
|
||||
PausedDownloading,
|
||||
PausedUploading,
|
||||
|
||||
MissingFiles,
|
||||
Error
|
||||
};
|
||||
|
||||
TorrentState(int value);
|
||||
|
||||
operator int() const;
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
class TorrentHandle : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(TorrentHandle)
|
||||
|
||||
public:
|
||||
static const qreal USE_GLOBAL_RATIO;
|
||||
static const qreal NO_RATIO_LIMIT;
|
||||
|
||||
static const qreal MAX_RATIO;
|
||||
|
||||
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||
const AddTorrentData &data);
|
||||
~TorrentHandle();
|
||||
|
||||
bool isValid() const;
|
||||
InfoHash hash() const;
|
||||
QString name() const;
|
||||
QDateTime creationDate() const;
|
||||
QString creator() const;
|
||||
QString comment() const;
|
||||
bool isPrivate() const;
|
||||
qlonglong totalSize() const;
|
||||
qlonglong wantedSize() const;
|
||||
qlonglong completedSize() const;
|
||||
qlonglong incompletedSize() const;
|
||||
qlonglong pieceLength() const;
|
||||
qlonglong wastedSize() const;
|
||||
QString currentTracker() const;
|
||||
|
||||
// 1. savePath() - the path where all the files and subfolders of torrent are stored (as always).
|
||||
// 2. rootPath() - absolute path of torrent file tree (save path + first item from 1st torrent file path).
|
||||
// 3. contentPath() - absolute path of torrent content (root path for multifile torrents, absolute file path for singlefile torrents).
|
||||
//
|
||||
// These methods have 'actual' parameter (defaults to false) which allow to get actual or final path variant.
|
||||
//
|
||||
// Examples.
|
||||
// Suppose we have three torrent with following structures and save path `/home/user/torrents`:
|
||||
//
|
||||
// Torrent A (multifile)
|
||||
//
|
||||
// torrentA/
|
||||
// subdir1/
|
||||
// subdir2/
|
||||
// file1
|
||||
// file2
|
||||
// file3
|
||||
// file4
|
||||
//
|
||||
//
|
||||
// Torrent B (singlefile)
|
||||
//
|
||||
// torrentB/
|
||||
// subdir1/
|
||||
// file1
|
||||
//
|
||||
//
|
||||
// Torrent C (singlefile)
|
||||
//
|
||||
// file1
|
||||
//
|
||||
//
|
||||
// Results:
|
||||
// | | rootPath | contentPath |
|
||||
// |---|------------------------------|--------------------------------------------|
|
||||
// | A | /home/user/torrents/torrentA | /home/user/torrents/torrentA |
|
||||
// | B | /home/user/torrents/torrentB | /home/user/torrents/torrentB/subdir1/file1 |
|
||||
// | C | /home/user/torrents/file1 | /home/user/torrents/file1 |
|
||||
|
||||
QString savePath(bool actual = false) const;
|
||||
QString rootPath(bool actual = false) const;
|
||||
QString contentPath(bool actual = false) const;
|
||||
|
||||
int filesCount() const;
|
||||
int piecesCount() const;
|
||||
int piecesHave() const;
|
||||
qreal progress() const;
|
||||
QString label() const;
|
||||
QDateTime addedTime() const;
|
||||
qreal ratioLimit() const;
|
||||
|
||||
QString filePath(int index) const;
|
||||
QString fileName(int index) const;
|
||||
qlonglong fileSize(int index) const;
|
||||
QStringList absoluteFilePaths() const;
|
||||
QStringList absoluteFilePathsUnwanted() const;
|
||||
QPair<int, int> fileExtremityPieces(int index) const;
|
||||
QVector<int> filePriorities() const;
|
||||
|
||||
TorrentInfo info() const;
|
||||
bool isSeed() const;
|
||||
bool isPaused() const;
|
||||
bool isResumed() const;
|
||||
bool isQueued() const;
|
||||
bool isForced() const;
|
||||
bool isChecking() const;
|
||||
bool isDownloading() const;
|
||||
bool isUploading() const;
|
||||
bool isCompleted() const;
|
||||
bool isActive() const;
|
||||
bool isInactive() const;
|
||||
bool isErrored() const;
|
||||
bool isSequentialDownload() const;
|
||||
bool hasFirstLastPiecePriority() const;
|
||||
TorrentState state() const;
|
||||
bool hasMetadata() const;
|
||||
bool hasMissingFiles() const;
|
||||
bool hasError() const;
|
||||
bool hasFilteredPieces() const;
|
||||
int queuePosition() const;
|
||||
QList<TrackerEntry> trackers() const;
|
||||
QHash<QString, TrackerInfo> trackerInfos() const;
|
||||
QList<QUrl> urlSeeds() const;
|
||||
QString error() const;
|
||||
qlonglong totalDownload() const;
|
||||
qlonglong totalUpload() const;
|
||||
int activeTime() const;
|
||||
int finishedTime() const;
|
||||
int seedingTime() const;
|
||||
qulonglong eta() const;
|
||||
QVector<qreal> filesProgress() const;
|
||||
int seedsCount() const;
|
||||
int peersCount() const;
|
||||
int leechsCount() const;
|
||||
int totalSeedsCount() const;
|
||||
int totalPeersCount() const;
|
||||
int totalLeechersCount() const;
|
||||
int completeCount() const;
|
||||
int incompleteCount() const;
|
||||
QDateTime lastSeenComplete() const;
|
||||
QDateTime completedTime() const;
|
||||
int timeSinceUpload() const;
|
||||
int timeSinceDownload() const;
|
||||
int timeSinceActivity() const;
|
||||
int downloadLimit() const;
|
||||
int uploadLimit() const;
|
||||
bool superSeeding() const;
|
||||
QList<PeerInfo> peers() const;
|
||||
QBitArray pieces() const;
|
||||
QBitArray downloadingPieces() const;
|
||||
QVector<int> pieceAvailability() const;
|
||||
qreal distributedCopies() const;
|
||||
qreal maxRatio(bool *usesGlobalRatio = 0) const;
|
||||
qreal realRatio() const;
|
||||
int uploadPayloadRate() const;
|
||||
int downloadPayloadRate() const;
|
||||
qlonglong totalPayloadUpload() const;
|
||||
qlonglong totalPayloadDownload() const;
|
||||
int connectionsCount() const;
|
||||
int connectionsLimit() const;
|
||||
qlonglong nextAnnounce() const;
|
||||
|
||||
void setName(const QString &name);
|
||||
void setLabel(const QString &label);
|
||||
void setSequentialDownload(bool b);
|
||||
void toggleSequentialDownload();
|
||||
void setFirstLastPiecePriority(bool b);
|
||||
void toggleFirstLastPiecePriority();
|
||||
void pause();
|
||||
void resume(bool forced = false);
|
||||
void move(QString path);
|
||||
void forceReannounce(int index = -1);
|
||||
void forceDHTAnnounce();
|
||||
void forceRecheck();
|
||||
void setTrackerLogin(const QString &username, const QString &password);
|
||||
void renameFile(int index, const QString &name);
|
||||
bool saveTorrentFile(const QString &path);
|
||||
void prioritizeFiles(const QVector<int> &priorities);
|
||||
void setFilePriority(int index, int priority);
|
||||
void setRatioLimit(qreal limit);
|
||||
void setUploadLimit(int limit);
|
||||
void setDownloadLimit(int limit);
|
||||
void setSuperSeeding(bool enable);
|
||||
void flushCache();
|
||||
void addTrackers(const QList<TrackerEntry> &trackers);
|
||||
void replaceTrackers(QList<TrackerEntry> trackers);
|
||||
void addUrlSeeds(const QList<QUrl> &urlSeeds);
|
||||
void removeUrlSeeds(const QList<QUrl> &urlSeeds);
|
||||
bool connectPeer(const PeerAddress &peerAddress);
|
||||
|
||||
QString toMagnetUri() const;
|
||||
|
||||
bool needSaveResumeData() const;
|
||||
|
||||
// Session interface
|
||||
libtorrent::torrent_handle nativeHandle() const;
|
||||
|
||||
void handleAlert(libtorrent::alert *a);
|
||||
void handleStateUpdate(const libtorrent::torrent_status &nativeStatus);
|
||||
void handleTempPathChanged();
|
||||
void handleAppendExtensionToggled();
|
||||
void saveResumeData();
|
||||
|
||||
private:
|
||||
typedef boost::function<void ()> EventTrigger;
|
||||
|
||||
void initialize();
|
||||
void updateStatus();
|
||||
void updateStatus(const libtorrent::torrent_status &nativeStatus);
|
||||
void updateState();
|
||||
void updateTorrentInfo();
|
||||
|
||||
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
|
||||
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
|
||||
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p);
|
||||
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p);
|
||||
void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p);
|
||||
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p);
|
||||
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p);
|
||||
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p);
|
||||
void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p);
|
||||
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p);
|
||||
void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p);
|
||||
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p);
|
||||
void handleFileRenamedAlert(libtorrent::file_renamed_alert *p);
|
||||
void handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p);
|
||||
void handleFileCompletedAlert(libtorrent::file_completed_alert *p);
|
||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
||||
void handleStatsAlert(libtorrent::stats_alert *p);
|
||||
|
||||
bool isMoveInProgress() const;
|
||||
bool useTempPath() const;
|
||||
QString nativeActualSavePath() const;
|
||||
|
||||
void adjustActualSavePath();
|
||||
void adjustActualSavePath_impl();
|
||||
void moveStorage(const QString &newPath);
|
||||
void appendExtensionsToIncompleteFiles();
|
||||
void removeExtensionsFromIncompleteFiles();
|
||||
bool addTracker(const TrackerEntry &tracker);
|
||||
bool addUrlSeed(const QUrl &urlSeed);
|
||||
bool removeUrlSeed(const QUrl &urlSeed);
|
||||
|
||||
Session *const m_session;
|
||||
libtorrent::torrent_handle m_nativeHandle;
|
||||
libtorrent::torrent_status m_nativeStatus;
|
||||
TorrentState m_state;
|
||||
TorrentInfo m_torrentInfo;
|
||||
SpeedMonitor m_speedMonitor;
|
||||
|
||||
InfoHash m_hash;
|
||||
|
||||
QString m_oldPath;
|
||||
QString m_newPath;
|
||||
// m_queuedPath is where files should be moved to,
|
||||
// when current moving is completed
|
||||
QString m_queuedPath;
|
||||
// m_moveFinishedTriggers is activated only when the following conditions are met:
|
||||
// all file rename jobs complete, all file move jobs complete
|
||||
QQueue<EventTrigger> m_moveFinishedTriggers;
|
||||
int m_renameCount;
|
||||
|
||||
// Persistent data
|
||||
QString m_name;
|
||||
QString m_savePath;
|
||||
QString m_label;
|
||||
bool m_hasSeedStatus;
|
||||
qreal m_ratioLimit;
|
||||
bool m_tempPathDisabled;
|
||||
bool m_hasMissingFiles;
|
||||
|
||||
bool m_pauseAfterRecheck;
|
||||
bool m_needSaveResumeData;
|
||||
QHash<QString, TrackerInfo> m_trackerInfos;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_TORRENTHANDLE_H
|
||||
223
src/core/bittorrent/torrentinfo.cpp
Normal file
223
src/core/bittorrent/torrentinfo.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <libtorrent/error_code.hpp>
|
||||
|
||||
#include "core/utils/misc.h"
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/string.h"
|
||||
#include "infohash.h"
|
||||
#include "trackerentry.h"
|
||||
#include "torrentinfo.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace BitTorrent;
|
||||
|
||||
TorrentInfo::TorrentInfo(boost::intrusive_ptr<const libt::torrent_info> nativeInfo)
|
||||
: m_nativeInfo(const_cast<libt::torrent_info *>(nativeInfo.get()))
|
||||
{
|
||||
}
|
||||
|
||||
TorrentInfo::TorrentInfo(const TorrentInfo &other)
|
||||
: m_nativeInfo(other.m_nativeInfo)
|
||||
{
|
||||
}
|
||||
|
||||
TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
||||
{
|
||||
m_nativeInfo = other.m_nativeInfo;
|
||||
return *this;
|
||||
}
|
||||
|
||||
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
|
||||
{
|
||||
error.clear();
|
||||
libt::error_code ec;
|
||||
TorrentInfo info(new libt::torrent_info(Utils::String::toStdString(Utils::Fs::toNativePath(path)), ec));
|
||||
if (ec) {
|
||||
error = QString::fromUtf8(ec.message().c_str());
|
||||
qDebug("Cannot load .torrent file: %s", qPrintable(error));
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
TorrentInfo TorrentInfo::loadFromFile(const QString &path)
|
||||
{
|
||||
QString error;
|
||||
return loadFromFile(path, error);
|
||||
}
|
||||
|
||||
bool TorrentInfo::isValid() const
|
||||
{
|
||||
return (m_nativeInfo && m_nativeInfo->is_valid() && (m_nativeInfo->num_files() > 0));
|
||||
}
|
||||
|
||||
InfoHash TorrentInfo::hash() const
|
||||
{
|
||||
if (!isValid()) return InfoHash();
|
||||
return m_nativeInfo->info_hash();
|
||||
}
|
||||
|
||||
QString TorrentInfo::name() const
|
||||
{
|
||||
if (!isValid()) return QString();
|
||||
return Utils::String::fromStdString(m_nativeInfo->name());
|
||||
}
|
||||
|
||||
QDateTime TorrentInfo::creationDate() const
|
||||
{
|
||||
if (!isValid()) return QDateTime();
|
||||
boost::optional<time_t> t = m_nativeInfo->creation_date();
|
||||
return t ? QDateTime::fromTime_t(*t) : QDateTime();
|
||||
}
|
||||
|
||||
QString TorrentInfo::creator() const
|
||||
{
|
||||
if (!isValid()) return QString();
|
||||
return Utils::String::fromStdString(m_nativeInfo->creator());
|
||||
}
|
||||
|
||||
QString TorrentInfo::comment() const
|
||||
{
|
||||
if (!isValid()) return QString();
|
||||
return Utils::String::fromStdString(m_nativeInfo->comment());
|
||||
}
|
||||
|
||||
bool TorrentInfo::isPrivate() const
|
||||
{
|
||||
if (!isValid()) return false;
|
||||
return m_nativeInfo->priv();
|
||||
}
|
||||
|
||||
qlonglong TorrentInfo::totalSize() const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->total_size();
|
||||
}
|
||||
|
||||
int TorrentInfo::filesCount() const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->num_files();
|
||||
}
|
||||
|
||||
int TorrentInfo::pieceLength() const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->piece_length();
|
||||
}
|
||||
|
||||
int TorrentInfo::piecesCount() const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->num_pieces();
|
||||
}
|
||||
|
||||
QString TorrentInfo::filePath(int index) const
|
||||
{
|
||||
if (!isValid()) return QString();
|
||||
return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->files().file_path(index)));
|
||||
}
|
||||
|
||||
QStringList TorrentInfo::filePaths() const
|
||||
{
|
||||
QStringList list;
|
||||
for (int i = 0; i < filesCount(); ++i)
|
||||
list << filePath(i);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QString TorrentInfo::fileName(int index) const
|
||||
{
|
||||
return Utils::Fs::fileName(filePath(index));
|
||||
}
|
||||
|
||||
QString TorrentInfo::origFilePath(int index) const
|
||||
{
|
||||
if (!isValid()) return QString();
|
||||
return Utils::Fs::fromNativePath(Utils::String::fromStdString(m_nativeInfo->orig_files().file_path(index)));
|
||||
}
|
||||
|
||||
qlonglong TorrentInfo::fileSize(int index) const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->files().file_size(index);
|
||||
}
|
||||
|
||||
qlonglong TorrentInfo::fileOffset(int index) const
|
||||
{
|
||||
if (!isValid()) return -1;
|
||||
return m_nativeInfo->file_at(index).offset;
|
||||
}
|
||||
|
||||
QList<TrackerEntry> TorrentInfo::trackers() const
|
||||
{
|
||||
if (!isValid()) return QList<TrackerEntry>();
|
||||
|
||||
QList<TrackerEntry> trackers;
|
||||
foreach (const libt::announce_entry &tracker, m_nativeInfo->trackers())
|
||||
trackers.append(tracker);
|
||||
|
||||
return trackers;
|
||||
}
|
||||
|
||||
QList<QUrl> TorrentInfo::urlSeeds() const
|
||||
{
|
||||
if (!isValid()) return QList<QUrl>();
|
||||
|
||||
QList<QUrl> urlSeeds;
|
||||
foreach (const libt::web_seed_entry &webSeed, m_nativeInfo->web_seeds())
|
||||
if (webSeed.type == libt::web_seed_entry::url_seed)
|
||||
urlSeeds.append(QUrl(webSeed.url.c_str()));
|
||||
|
||||
return urlSeeds;
|
||||
}
|
||||
|
||||
QByteArray TorrentInfo::metadata() const
|
||||
{
|
||||
if (!isValid()) return QByteArray();
|
||||
return QByteArray(m_nativeInfo->metadata().get(), m_nativeInfo->metadata_size());
|
||||
}
|
||||
|
||||
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
||||
{
|
||||
if (!isValid()) return;
|
||||
m_nativeInfo->rename_file(index, Utils::String::toStdString(newPath));
|
||||
}
|
||||
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> TorrentInfo::nativeInfo() const
|
||||
{
|
||||
return m_nativeInfo;
|
||||
}
|
||||
87
src/core/bittorrent/torrentinfo.h
Normal file
87
src/core/bittorrent/torrentinfo.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_TORRENTINFO_H
|
||||
#define BITTORRENT_TORRENTINFO_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
|
||||
class QString;
|
||||
class QUrl;
|
||||
class QDateTime;
|
||||
class QStringList;
|
||||
class QByteArray;
|
||||
template<typename T> class QList;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class InfoHash;
|
||||
class TrackerEntry;
|
||||
|
||||
class TorrentInfo
|
||||
{
|
||||
public:
|
||||
explicit TorrentInfo(boost::intrusive_ptr<const libtorrent::torrent_info> nativeInfo = boost::intrusive_ptr<const libtorrent::torrent_info>());
|
||||
TorrentInfo(const TorrentInfo &other);
|
||||
|
||||
static TorrentInfo loadFromFile(const QString &path, QString &error);
|
||||
static TorrentInfo loadFromFile(const QString &path);
|
||||
|
||||
TorrentInfo &operator=(const TorrentInfo &other);
|
||||
|
||||
bool isValid() const;
|
||||
InfoHash hash() const;
|
||||
QString name() const;
|
||||
QDateTime creationDate() const;
|
||||
QString creator() const;
|
||||
QString comment() const;
|
||||
bool isPrivate() const;
|
||||
qlonglong totalSize() const;
|
||||
int filesCount() const;
|
||||
int pieceLength() const;
|
||||
int piecesCount() const;
|
||||
QString filePath(int index) const;
|
||||
QStringList filePaths() const;
|
||||
QString fileName(int index) const;
|
||||
QString origFilePath(int index) const;
|
||||
qlonglong fileSize(int index) const;
|
||||
qlonglong fileOffset(int index) const;
|
||||
QList<TrackerEntry> trackers() const;
|
||||
QList<QUrl> urlSeeds() const;
|
||||
QByteArray metadata() const;
|
||||
|
||||
void renameFile(uint index, const QString &newPath);
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> nativeInfo() const;
|
||||
|
||||
private:
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> m_nativeInfo;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_TORRENTINFO_H
|
||||
@@ -31,59 +31,68 @@
|
||||
|
||||
#include <vector>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#include "preferences.h"
|
||||
#include "http/server.h"
|
||||
#include "qtracker.h"
|
||||
#include "core/preferences.h"
|
||||
#include "core/http/server.h"
|
||||
#include "core/utils/string.h"
|
||||
#include "tracker.h"
|
||||
|
||||
// QPeer
|
||||
bool QPeer::operator!=(const QPeer &other) const
|
||||
// static limits
|
||||
static const int MAX_TORRENTS = 100;
|
||||
static const int MAX_PEERS_PER_TORRENT = 1000;
|
||||
static const int ANNOUNCE_INTERVAL = 1800; // 30min
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
// Peer
|
||||
bool Peer::operator!=(const Peer &other) const
|
||||
{
|
||||
return qhash() != other.qhash();
|
||||
return uid() != other.uid();
|
||||
}
|
||||
|
||||
bool QPeer::operator==(const QPeer &other) const
|
||||
bool Peer::operator==(const Peer &other) const
|
||||
{
|
||||
return qhash() == other.qhash();
|
||||
return uid() == other.uid();
|
||||
}
|
||||
|
||||
QString QPeer::qhash() const
|
||||
QString Peer::uid() const
|
||||
{
|
||||
return ip + ":" + QString::number(port);
|
||||
}
|
||||
|
||||
libtorrent::entry QPeer::toEntry(bool no_peer_id) const
|
||||
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
||||
{
|
||||
libtorrent::entry::dictionary_type peer_map;
|
||||
if (!no_peer_id)
|
||||
peer_map["id"] = libtorrent::entry(peer_id.toStdString());
|
||||
peer_map["ip"] = libtorrent::entry(ip.toStdString());
|
||||
peer_map["port"] = libtorrent::entry(port);
|
||||
libtorrent::entry::dictionary_type peerMap;
|
||||
if (!noPeerId)
|
||||
peerMap["id"] = libtorrent::entry(Utils::String::toStdString(peerId));
|
||||
peerMap["ip"] = libtorrent::entry(Utils::String::toStdString(ip));
|
||||
peerMap["port"] = libtorrent::entry(port);
|
||||
|
||||
return libtorrent::entry(peer_map);
|
||||
return libtorrent::entry(peerMap);
|
||||
}
|
||||
|
||||
// QTracker
|
||||
// Tracker
|
||||
|
||||
QTracker::QTracker(QObject *parent)
|
||||
Tracker::Tracker(QObject *parent)
|
||||
: Http::ResponseBuilder(parent)
|
||||
, m_server(new Http::Server(this, this))
|
||||
{
|
||||
}
|
||||
|
||||
QTracker::~QTracker()
|
||||
Tracker::~Tracker()
|
||||
{
|
||||
if (m_server->isListening())
|
||||
qDebug("Shutting down the embedded tracker...");
|
||||
// TODO: Store the torrent list
|
||||
}
|
||||
|
||||
bool QTracker::start()
|
||||
bool Tracker::start()
|
||||
{
|
||||
const int listen_port = Preferences::instance()->getTrackerPort();
|
||||
const int listenPort = Preferences::instance()->getTrackerPort();
|
||||
|
||||
if (m_server->isListening()) {
|
||||
if (m_server->serverPort() == listen_port) {
|
||||
if (m_server->serverPort() == listenPort) {
|
||||
// Already listening on the right port, just return
|
||||
return true;
|
||||
}
|
||||
@@ -93,21 +102,21 @@ bool QTracker::start()
|
||||
|
||||
qDebug("Starting the embedded tracker...");
|
||||
// Listen on the predefined port
|
||||
return m_server->listen(QHostAddress::Any, listen_port);
|
||||
return m_server->listen(QHostAddress::Any, listenPort);
|
||||
}
|
||||
|
||||
Http::Response QTracker::processRequest(const Http::Request &request, const Http::Environment &env)
|
||||
Http::Response Tracker::processRequest(const Http::Request &request, const Http::Environment &env)
|
||||
{
|
||||
clear(); // clear response
|
||||
|
||||
//qDebug("QTracker received the following request:\n%s", qPrintable(parser.toString()));
|
||||
//qDebug("Tracker received the following request:\n%s", qPrintable(parser.toString()));
|
||||
// Is request a GET request?
|
||||
if (request.method != "GET") {
|
||||
qDebug("QTracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
||||
qDebug("Tracker: Unsupported HTTP request: %s", qPrintable(request.method));
|
||||
status(100, "Invalid request type");
|
||||
}
|
||||
else if (!request.path.startsWith("/announce", Qt::CaseInsensitive)) {
|
||||
qDebug("QTracker: Unrecognized path: %s", qPrintable(request.path));
|
||||
qDebug("Tracker: Unrecognized path: %s", qPrintable(request.path));
|
||||
status(100, "Invalid request type");
|
||||
}
|
||||
else {
|
||||
@@ -120,85 +129,85 @@ Http::Response QTracker::processRequest(const Http::Request &request, const Http
|
||||
return response();
|
||||
}
|
||||
|
||||
void QTracker::respondToAnnounceRequest()
|
||||
void Tracker::respondToAnnounceRequest()
|
||||
{
|
||||
const QStringMap &gets = m_request.gets;
|
||||
TrackerAnnounceRequest annonce_req;
|
||||
TrackerAnnounceRequest annonceReq;
|
||||
|
||||
// IP
|
||||
annonce_req.peer.ip = m_env.clientAddress.toString();
|
||||
annonceReq.peer.ip = m_env.clientAddress.toString();
|
||||
|
||||
// 1. Get info_hash
|
||||
if (!gets.contains("info_hash")) {
|
||||
qDebug("QTracker: Missing info_hash");
|
||||
qDebug("Tracker: Missing info_hash");
|
||||
status(101, "Missing info_hash");
|
||||
return;
|
||||
}
|
||||
annonce_req.info_hash = gets.value("info_hash");
|
||||
annonceReq.infoHash = gets.value("info_hash");
|
||||
// info_hash cannot be longer than 20 bytes
|
||||
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
||||
qDebug("QTracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
||||
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qPrintable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
||||
status(150, "Invalid infohash");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// 2. Get peer ID
|
||||
if (!gets.contains("peer_id")) {
|
||||
qDebug("QTracker: Missing peer_id");
|
||||
qDebug("Tracker: Missing peer_id");
|
||||
status(102, "Missing peer_id");
|
||||
return;
|
||||
}
|
||||
annonce_req.peer.peer_id = gets.value("peer_id");
|
||||
annonceReq.peer.peerId = gets.value("peer_id");
|
||||
// peer_id cannot be longer than 20 bytes
|
||||
/*if (annonce_req.peer.peer_id.length() > 20) {
|
||||
qDebug("QTracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
||||
qDebug("Tracker: peer_id is not 20 byte long: %s", qPrintable(annonce_req.peer.peer_id));
|
||||
status(151, "Invalid peerid");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// 3. Get port
|
||||
if (!gets.contains("port")) {
|
||||
qDebug("QTracker: Missing port");
|
||||
qDebug("Tracker: Missing port");
|
||||
status(103, "Missing port");
|
||||
return;
|
||||
}
|
||||
bool ok = false;
|
||||
annonce_req.peer.port = gets.value("port").toInt(&ok);
|
||||
if (!ok || annonce_req.peer.port < 1 || annonce_req.peer.port > 65535) {
|
||||
qDebug("QTracker: Invalid port number (%d)", annonce_req.peer.port);
|
||||
annonceReq.peer.port = gets.value("port").toInt(&ok);
|
||||
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
|
||||
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
|
||||
status(103, "Missing port");
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. Get event
|
||||
annonce_req.event = "";
|
||||
annonceReq.event = "";
|
||||
if (gets.contains("event")) {
|
||||
annonce_req.event = gets.value("event");
|
||||
qDebug("QTracker: event is %s", qPrintable(annonce_req.event));
|
||||
annonceReq.event = gets.value("event");
|
||||
qDebug("Tracker: event is %s", qPrintable(annonceReq.event));
|
||||
}
|
||||
|
||||
// 5. Get numwant
|
||||
annonce_req.numwant = 50;
|
||||
annonceReq.numwant = 50;
|
||||
if (gets.contains("numwant")) {
|
||||
int tmp = gets.value("numwant").toInt();
|
||||
if (tmp > 0) {
|
||||
qDebug("QTracker: numwant = %d", tmp);
|
||||
annonce_req.numwant = tmp;
|
||||
qDebug("Tracker: numwant = %d", tmp);
|
||||
annonceReq.numwant = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. no_peer_id (extension)
|
||||
annonce_req.no_peer_id = false;
|
||||
annonceReq.noPeerId = false;
|
||||
if (gets.contains("no_peer_id"))
|
||||
annonce_req.no_peer_id = true;
|
||||
annonceReq.noPeerId = true;
|
||||
|
||||
// 7. TODO: support "compact" extension
|
||||
|
||||
// Done parsing, now let's reply
|
||||
if (m_torrents.contains(annonce_req.info_hash)) {
|
||||
if (annonce_req.event == "stopped") {
|
||||
qDebug("QTracker: Peer stopped downloading, deleting it from the list");
|
||||
m_torrents[annonce_req.info_hash].remove(annonce_req.peer.qhash());
|
||||
if (m_torrents.contains(annonceReq.infoHash)) {
|
||||
if (annonceReq.event == "stopped") {
|
||||
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
|
||||
m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -210,36 +219,36 @@ void QTracker::respondToAnnounceRequest()
|
||||
}
|
||||
}
|
||||
// Register the user
|
||||
PeerList peers = m_torrents.value(annonce_req.info_hash);
|
||||
PeerList peers = m_torrents.value(annonceReq.infoHash);
|
||||
if (peers.size() == MAX_PEERS_PER_TORRENT) {
|
||||
// Too many peers, remove a random one
|
||||
peers.erase(peers.begin());
|
||||
}
|
||||
peers[annonce_req.peer.qhash()] = annonce_req.peer;
|
||||
m_torrents[annonce_req.info_hash] = peers;
|
||||
peers[annonceReq.peer.uid()] = annonceReq.peer;
|
||||
m_torrents[annonceReq.infoHash] = peers;
|
||||
|
||||
// Reply
|
||||
replyWithPeerList(annonce_req);
|
||||
replyWithPeerList(annonceReq);
|
||||
}
|
||||
|
||||
void QTracker::replyWithPeerList(const TrackerAnnounceRequest &annonce_req)
|
||||
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
||||
{
|
||||
// Prepare the entry for bencoding
|
||||
libtorrent::entry::dictionary_type reply_dict;
|
||||
reply_dict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
||||
QList<QPeer> peers = m_torrents.value(annonce_req.info_hash).values();
|
||||
libtorrent::entry::list_type peer_list;
|
||||
foreach (const QPeer &p, peers) {
|
||||
libtorrent::entry::dictionary_type replyDict;
|
||||
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
||||
QList<Peer> peers = m_torrents.value(annonceReq.infoHash).values();
|
||||
libtorrent::entry::list_type peerList;
|
||||
foreach (const Peer &p, peers) {
|
||||
//if (p != annonce_req.peer)
|
||||
peer_list.push_back(p.toEntry(annonce_req.no_peer_id));
|
||||
peerList.push_back(p.toEntry(annonceReq.noPeerId));
|
||||
}
|
||||
reply_dict["peers"] = libtorrent::entry(peer_list);
|
||||
libtorrent::entry reply_entry(reply_dict);
|
||||
replyDict["peers"] = libtorrent::entry(peerList);
|
||||
libtorrent::entry replyEntry(replyDict);
|
||||
// bencode
|
||||
std::vector<char> buf;
|
||||
libtorrent::bencode(std::back_inserter(buf), reply_entry);
|
||||
libtorrent::bencode(std::back_inserter(buf), replyEntry);
|
||||
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
|
||||
qDebug("QTracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||
|
||||
// HTTP reply
|
||||
print(reply, Http::CONTENT_TYPE_TXT);
|
||||
103
src/core/bittorrent/tracker.h
Normal file
103
src/core/bittorrent/tracker.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_TRACKER_H
|
||||
#define BITTORRENT_TRACKER_H
|
||||
|
||||
#include <QHash>
|
||||
#include "core/http/types.h"
|
||||
#include "core/http/responsebuilder.h"
|
||||
#include "core/http/irequesthandler.h"
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class entry;
|
||||
}
|
||||
|
||||
namespace Http
|
||||
{
|
||||
class Server;
|
||||
}
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
struct Peer
|
||||
{
|
||||
QString ip;
|
||||
QString peerId;
|
||||
int port;
|
||||
|
||||
bool operator!=(const Peer &other) const;
|
||||
bool operator==(const Peer &other) const;
|
||||
QString uid() const;
|
||||
libtorrent::entry toEntry(bool noPeerId) const;
|
||||
};
|
||||
|
||||
struct TrackerAnnounceRequest
|
||||
{
|
||||
QString infoHash;
|
||||
QString event;
|
||||
int numwant;
|
||||
Peer peer;
|
||||
// Extensions
|
||||
bool noPeerId;
|
||||
};
|
||||
|
||||
typedef QHash<QString, Peer> PeerList;
|
||||
typedef QHash<QString, PeerList> TorrentList;
|
||||
|
||||
/* Basic Bittorrent tracker implementation in Qt */
|
||||
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
||||
class Tracker : public Http::ResponseBuilder, public Http::IRequestHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Tracker)
|
||||
|
||||
public:
|
||||
explicit Tracker(QObject *parent = 0);
|
||||
~Tracker();
|
||||
|
||||
bool start();
|
||||
Http::Response processRequest(const Http::Request &request, const Http::Environment &env);
|
||||
|
||||
private:
|
||||
void respondToAnnounceRequest();
|
||||
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
|
||||
|
||||
Http::Server *m_server;
|
||||
TorrentList m_torrents;
|
||||
|
||||
Http::Request m_request;
|
||||
Http::Environment m_env;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_TRACKER_H
|
||||
95
src/core/bittorrent/trackerentry.cpp
Normal file
95
src/core/bittorrent/trackerentry.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "core/utils/misc.h"
|
||||
#include "core/utils/string.h"
|
||||
#include "trackerentry.h"
|
||||
|
||||
using namespace BitTorrent;
|
||||
|
||||
TrackerEntry::TrackerEntry(const QString &url)
|
||||
: m_nativeEntry(libtorrent::announce_entry(Utils::String::toStdString(url)))
|
||||
{
|
||||
}
|
||||
|
||||
TrackerEntry::TrackerEntry(const libtorrent::announce_entry &nativeEntry)
|
||||
: m_nativeEntry(nativeEntry)
|
||||
{
|
||||
}
|
||||
|
||||
TrackerEntry::TrackerEntry(const TrackerEntry &other)
|
||||
: m_nativeEntry(other.m_nativeEntry)
|
||||
{
|
||||
}
|
||||
|
||||
QString TrackerEntry::url() const
|
||||
{
|
||||
return Utils::String::fromStdString(m_nativeEntry.url);
|
||||
}
|
||||
|
||||
int TrackerEntry::tier() const
|
||||
{
|
||||
return m_nativeEntry.tier;
|
||||
}
|
||||
|
||||
TrackerEntry::Status TrackerEntry::status() const
|
||||
{
|
||||
// libtorrent::announce_entry::is_working() returns
|
||||
// true when the tracker hasn't been tried yet.
|
||||
if (m_nativeEntry.verified && m_nativeEntry.is_working())
|
||||
return Working;
|
||||
else if ((m_nativeEntry.fails == 0) && m_nativeEntry.updating)
|
||||
return Updating;
|
||||
else if (m_nativeEntry.fails == 0)
|
||||
return NotContacted;
|
||||
else
|
||||
return NotWorking;
|
||||
}
|
||||
|
||||
void TrackerEntry::setTier(int value)
|
||||
{
|
||||
m_nativeEntry.tier = value;
|
||||
}
|
||||
|
||||
TrackerEntry &TrackerEntry::operator=(const TrackerEntry &other)
|
||||
{
|
||||
this->m_nativeEntry = other.m_nativeEntry;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool TrackerEntry::operator==(const TrackerEntry &other)
|
||||
{
|
||||
return (QUrl(url()) == QUrl(other.url()));
|
||||
}
|
||||
|
||||
libtorrent::announce_entry TrackerEntry::nativeEntry() const
|
||||
{
|
||||
return m_nativeEntry;
|
||||
}
|
||||
68
src/core/bittorrent/trackerentry.h
Normal file
68
src/core/bittorrent/trackerentry.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef BITTORRENT_TRACKERENTRY_H
|
||||
#define BITTORRENT_TRACKERENTRY_H
|
||||
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
|
||||
class QString;
|
||||
|
||||
namespace BitTorrent
|
||||
{
|
||||
class TrackerEntry
|
||||
{
|
||||
public:
|
||||
enum Status
|
||||
{
|
||||
NotContacted,
|
||||
Working,
|
||||
Updating,
|
||||
NotWorking
|
||||
};
|
||||
|
||||
TrackerEntry(const QString &url);
|
||||
TrackerEntry(const libtorrent::announce_entry &nativeEntry);
|
||||
TrackerEntry(const TrackerEntry &other);
|
||||
|
||||
QString url() const;
|
||||
int tier() const;
|
||||
Status status() const;
|
||||
|
||||
void setTier(int value);
|
||||
TrackerEntry &operator=(const TrackerEntry &other);
|
||||
bool operator==(const TrackerEntry &other);
|
||||
|
||||
libtorrent::announce_entry nativeEntry() const;
|
||||
|
||||
private:
|
||||
libtorrent::announce_entry m_nativeEntry;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BITTORRENT_TRACKERENTRY_H
|
||||
@@ -1,22 +1,11 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
unix:!macx:dbus: include(qtnotify/qtnotify.pri)
|
||||
|
||||
include(qtlibtorrent/qtlibtorrent.pri)
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/misc.h \
|
||||
$$PWD/fs_utils.h \
|
||||
$$PWD/downloadthread.h \
|
||||
$$PWD/torrentpersistentdata.h \
|
||||
$$PWD/types.h \
|
||||
$$PWD/tristatebool.h \
|
||||
$$PWD/filesystemwatcher.h \
|
||||
$$PWD/scannedfoldersmodel.h \
|
||||
$$PWD/qinisettings.h \
|
||||
$$PWD/smtp.h \
|
||||
$$PWD/dnsupdater.h \
|
||||
$$PWD/logger.h \
|
||||
$$PWD/preferences.h \
|
||||
$$PWD/qtracker.h \
|
||||
$$PWD/iconprovider.h \
|
||||
$$PWD/http/irequesthandler.h \
|
||||
$$PWD/http/connection.h \
|
||||
$$PWD/http/requestparser.h \
|
||||
@@ -24,21 +13,74 @@ HEADERS += \
|
||||
$$PWD/http/server.h \
|
||||
$$PWD/http/types.h \
|
||||
$$PWD/http/responsebuilder.h \
|
||||
$$PWD/unicodestrings.h
|
||||
$$PWD/net/dnsupdater.h \
|
||||
$$PWD/net/downloadmanager.h \
|
||||
$$PWD/net/downloadhandler.h \
|
||||
$$PWD/net/geoipmanager.h \
|
||||
$$PWD/net/portforwarder.h \
|
||||
$$PWD/net/reverseresolution.h \
|
||||
$$PWD/net/smtp.h \
|
||||
$$PWD/net/private/geoipdatabase.h \
|
||||
$$PWD/bittorrent/infohash.h \
|
||||
$$PWD/bittorrent/session.h \
|
||||
$$PWD/bittorrent/sessionstatus.h \
|
||||
$$PWD/bittorrent/cachestatus.h \
|
||||
$$PWD/bittorrent/magneturi.h \
|
||||
$$PWD/bittorrent/torrentinfo.h \
|
||||
$$PWD/bittorrent/torrenthandle.h \
|
||||
$$PWD/bittorrent/peerinfo.h \
|
||||
$$PWD/bittorrent/trackerentry.h \
|
||||
$$PWD/bittorrent/tracker.h \
|
||||
$$PWD/bittorrent/torrentcreatorthread.h \
|
||||
$$PWD/bittorrent/private/speedmonitor.h \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.h \
|
||||
$$PWD/bittorrent/private/filterparserthread.h \
|
||||
$$PWD/bittorrent/private/statistics.h \
|
||||
$$PWD/utils/fs.h \
|
||||
$$PWD/utils/gzip.h \
|
||||
$$PWD/utils/misc.h \
|
||||
$$PWD/utils/string.h \
|
||||
$$PWD/unicodestrings.h \
|
||||
$$PWD/torrentfilter.h \
|
||||
$$PWD/scanfoldersmodel.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/downloadthread.cpp \
|
||||
$$PWD/scannedfoldersmodel.cpp \
|
||||
$$PWD/torrentpersistentdata.cpp \
|
||||
$$PWD/misc.cpp \
|
||||
$$PWD/fs_utils.cpp \
|
||||
$$PWD/smtp.cpp \
|
||||
$$PWD/dnsupdater.cpp \
|
||||
$$PWD/tristatebool.cpp \
|
||||
$$PWD/filesystemwatcher.cpp \
|
||||
$$PWD/logger.cpp \
|
||||
$$PWD/preferences.cpp \
|
||||
$$PWD/qtracker.cpp \
|
||||
$$PWD/iconprovider.cpp \
|
||||
$$PWD/http/connection.cpp \
|
||||
$$PWD/http/requestparser.cpp \
|
||||
$$PWD/http/responsegenerator.cpp \
|
||||
$$PWD/http/server.cpp \
|
||||
$$PWD/http/responsebuilder.cpp
|
||||
$$PWD/http/responsebuilder.cpp \
|
||||
$$PWD/net/dnsupdater.cpp \
|
||||
$$PWD/net/downloadmanager.cpp \
|
||||
$$PWD/net/downloadhandler.cpp \
|
||||
$$PWD/net/geoipmanager.cpp \
|
||||
$$PWD/net/portforwarder.cpp \
|
||||
$$PWD/net/reverseresolution.cpp \
|
||||
$$PWD/net/smtp.cpp \
|
||||
$$PWD/net/private/geoipdatabase.cpp \
|
||||
$$PWD/bittorrent/infohash.cpp \
|
||||
$$PWD/bittorrent/session.cpp \
|
||||
$$PWD/bittorrent/sessionstatus.cpp \
|
||||
$$PWD/bittorrent/cachestatus.cpp \
|
||||
$$PWD/bittorrent/magneturi.cpp \
|
||||
$$PWD/bittorrent/torrentinfo.cpp \
|
||||
$$PWD/bittorrent/torrenthandle.cpp \
|
||||
$$PWD/bittorrent/peerinfo.cpp \
|
||||
$$PWD/bittorrent/trackerentry.cpp \
|
||||
$$PWD/bittorrent/tracker.cpp \
|
||||
$$PWD/bittorrent/torrentcreatorthread.cpp \
|
||||
$$PWD/bittorrent/private/speedmonitor.cpp \
|
||||
$$PWD/bittorrent/private/bandwidthscheduler.cpp \
|
||||
$$PWD/bittorrent/private/filterparserthread.cpp \
|
||||
$$PWD/bittorrent/private/statistics.cpp \
|
||||
$$PWD/utils/fs.cpp \
|
||||
$$PWD/utils/gzip.cpp \
|
||||
$$PWD/utils/misc.cpp \
|
||||
$$PWD/utils/string.cpp \
|
||||
$$PWD/torrentfilter.cpp \
|
||||
$$PWD/scanfoldersmodel.cpp
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QDebug>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QUrlQuery>
|
||||
#endif
|
||||
#include "dnsupdater.h"
|
||||
#include "logger.h"
|
||||
|
||||
DNSUpdater::DNSUpdater(QObject *parent) :
|
||||
QObject(parent), m_state(OK), m_service(DNS::NONE)
|
||||
{
|
||||
updateCredentials();
|
||||
|
||||
// Load saved settings from previous session
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
m_lastIPCheckTime = pref->getDNSLastUpd();
|
||||
m_lastIP = QHostAddress(pref->getDNSLastIP());
|
||||
|
||||
// Start IP checking timer
|
||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
||||
m_ipCheckTimer.start();
|
||||
|
||||
// Check lastUpdate to avoid flooding
|
||||
if (!m_lastIPCheckTime.isValid() ||
|
||||
m_lastIPCheckTime.secsTo(QDateTime::currentDateTime())*1000 > IP_CHECK_INTERVAL_MS) {
|
||||
checkPublicIP();
|
||||
}
|
||||
}
|
||||
|
||||
DNSUpdater::~DNSUpdater() {
|
||||
// Save lastupdate time and last ip
|
||||
Preferences* const pref = Preferences::instance();
|
||||
pref->setDNSLastUpd(m_lastIPCheckTime);
|
||||
pref->setDNSLastIP(m_lastIP.toString());
|
||||
}
|
||||
|
||||
void DNSUpdater::checkPublicIP()
|
||||
{
|
||||
Q_ASSERT(m_state == OK);
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
||||
SLOT(ipRequestFinished(QNetworkReply*)));
|
||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||
QNetworkRequest request;
|
||||
request.setUrl(QUrl("http://checkip.dyndns.org"));
|
||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION);
|
||||
manager->get(request);
|
||||
}
|
||||
|
||||
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
if (reply->error()) {
|
||||
// Error
|
||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||
} else {
|
||||
// Parse response
|
||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
||||
QString ret = reply->readAll();
|
||||
if (ipregex.indexIn(ret) >= 0) {
|
||||
QString ip_str = ipregex.cap(1);
|
||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
||||
QHostAddress new_ip(ip_str);
|
||||
if (!new_ip.isNull()) {
|
||||
if (m_lastIP != new_ip) {
|
||||
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
||||
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
||||
m_lastIP = new_ip;
|
||||
updateDNSService();
|
||||
}
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
||||
}
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Regular expression failed ot capture the IP address";
|
||||
}
|
||||
}
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void DNSUpdater::updateDNSService()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// Prepare request
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply*)),
|
||||
SLOT(ipUpdateFinished(QNetworkReply*)));
|
||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||
QNetworkRequest request;
|
||||
request.setUrl(getUpdateUrl());
|
||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION);
|
||||
manager->get(request);
|
||||
}
|
||||
|
||||
QUrl DNSUpdater::getUpdateUrl() const
|
||||
{
|
||||
QUrl url;
|
||||
#ifdef QT_NO_OPENSSL
|
||||
url.setScheme("http");
|
||||
#else
|
||||
url.setScheme("https");
|
||||
#endif
|
||||
url.setUserName(m_username);
|
||||
url.setPassword(m_password);
|
||||
|
||||
Q_ASSERT(!m_lastIP.isNull());
|
||||
// Service specific
|
||||
switch(m_service) {
|
||||
case DNS::DYNDNS:
|
||||
url.setHost("members.dyndns.org");
|
||||
break;
|
||||
case DNS::NOIP:
|
||||
url.setHost("dynupdate.no-ip.com");
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unrecognized Dynamic DNS service!";
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
url.setPath("/nic/update");
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
url.addQueryItem("hostname", m_domain);
|
||||
url.addQueryItem("myip", m_lastIP.toString());
|
||||
#else
|
||||
QUrlQuery urlQuery(url);
|
||||
urlQuery.addQueryItem("hostname", m_domain);
|
||||
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
||||
url.setQuery(urlQuery);
|
||||
#endif
|
||||
Q_ASSERT(url.isValid());
|
||||
|
||||
qDebug() << Q_FUNC_INFO << url.toString();
|
||||
return url;
|
||||
}
|
||||
|
||||
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error()) {
|
||||
// Error
|
||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||
} else {
|
||||
// Pase reply
|
||||
processIPUpdateReply(reply->readAll());
|
||||
}
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void DNSUpdater::processIPUpdateReply(const QString &reply)
|
||||
{
|
||||
Logger* const logger = Logger::instance();
|
||||
qDebug() << Q_FUNC_INFO << reply;
|
||||
QString code = reply.split(" ").first();
|
||||
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
||||
if (code == "good" || code == "nochg") {
|
||||
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
|
||||
return;
|
||||
}
|
||||
if (code == "911" || code == "dnserr") {
|
||||
logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
// It will retry in 30 minutes because the timer was not stopped
|
||||
return;
|
||||
}
|
||||
// Everything bellow is an error, stop updating until the user updates something
|
||||
m_ipCheckTimer.stop();
|
||||
m_lastIP.clear();
|
||||
if (code == "nohost") {
|
||||
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
if (code == "badauth") {
|
||||
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
if (code == "badagent") {
|
||||
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
if (code == "!donator") {
|
||||
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
if (code == "abuse") {
|
||||
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DNSUpdater::updateCredentials()
|
||||
{
|
||||
if (m_state == FATAL) return;
|
||||
Preferences* const pref = Preferences::instance();
|
||||
Logger* const logger = Logger::instance();
|
||||
bool change = false;
|
||||
// Get DNS service information
|
||||
if (m_service != pref->getDynDNSService()) {
|
||||
m_service = pref->getDynDNSService();
|
||||
change = true;
|
||||
}
|
||||
if (m_domain != pref->getDynDomainName()) {
|
||||
m_domain = pref->getDynDomainName();
|
||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
||||
if (domain_regex.indexIn(m_domain) < 0) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
if (m_username != pref->getDynDNSUsername()) {
|
||||
m_username = pref->getDynDNSUsername();
|
||||
if (m_username.length() < 4) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
if (m_password != pref->getDynDNSPassword()) {
|
||||
m_password = pref->getDynDNSPassword();
|
||||
if (m_password.length() < 4) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
|
||||
if (m_state == INVALID_CREDS && change) {
|
||||
m_state = OK; // Try again
|
||||
m_ipCheckTimer.start();
|
||||
checkPublicIP();
|
||||
}
|
||||
}
|
||||
|
||||
QUrl DNSUpdater::getRegistrationUrl(int service)
|
||||
{
|
||||
switch(service) {
|
||||
case DNS::DYNDNS:
|
||||
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
||||
case DNS::NOIP:
|
||||
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
||||
default:
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
return QUrl();
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QTemporaryFile>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QDebug>
|
||||
|
||||
#include "downloadthread.h"
|
||||
#include "preferences.h"
|
||||
#include "qinisettings.h"
|
||||
#include "fs_utils.h"
|
||||
#include <zlib.h>
|
||||
|
||||
/** Download Thread **/
|
||||
|
||||
DownloadThread::DownloadThread(QObject* parent) : QObject(parent) {
|
||||
connect(&m_networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
|
||||
#ifndef QT_NO_OPENSSL
|
||||
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList<QSslError>)));
|
||||
#endif
|
||||
}
|
||||
|
||||
QByteArray DownloadThread::gUncompress(Bytef *inData, size_t len) {
|
||||
if (len <= 4) {
|
||||
qWarning("gUncompress: Input data is truncated");
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray result;
|
||||
|
||||
z_stream strm;
|
||||
static const int CHUNK_SIZE = 1024;
|
||||
char out[CHUNK_SIZE];
|
||||
|
||||
/* allocate inflate state */
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = len;
|
||||
strm.next_in = inData;
|
||||
|
||||
const int windowBits = 15;
|
||||
const int ENABLE_ZLIB_GZIP = 32;
|
||||
|
||||
int ret = inflateInit2(&strm, windowBits|ENABLE_ZLIB_GZIP ); // gzip decoding
|
||||
if (ret != Z_OK)
|
||||
return QByteArray();
|
||||
|
||||
// run inflate()
|
||||
do {
|
||||
strm.avail_out = CHUNK_SIZE;
|
||||
strm.next_out = reinterpret_cast<unsigned char*>(out);
|
||||
|
||||
ret = inflate(&strm, Z_NO_FLUSH);
|
||||
Q_ASSERT(ret != Z_STREAM_ERROR); // state not clobbered
|
||||
|
||||
switch (ret) {
|
||||
case Z_NEED_DICT:
|
||||
case Z_DATA_ERROR:
|
||||
case Z_MEM_ERROR:
|
||||
(void) inflateEnd(&strm);
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
result.append(out, CHUNK_SIZE - strm.avail_out);
|
||||
} while (!strm.avail_out);
|
||||
|
||||
// clean up and return
|
||||
inflateEnd(&strm);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DownloadThread::processDlFinished(QNetworkReply* reply) {
|
||||
QString url = reply->url().toString();
|
||||
qDebug("Download finished: %s", qPrintable(url));
|
||||
// Check if the request was successful
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// Failure
|
||||
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error())));
|
||||
emit downloadFailure(url, errorCodeToString(reply->error()));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
// Check if the server ask us to redirect somewhere lese
|
||||
const QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (redirection.isValid()) {
|
||||
// We should redirect
|
||||
QUrl newUrl = redirection.toUrl();
|
||||
// Resolve relative urls
|
||||
if (newUrl.isRelative())
|
||||
newUrl = reply->url().resolved(newUrl);
|
||||
const QString newUrlString = newUrl.toString();
|
||||
qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(newUrlString));
|
||||
// Redirect to magnet workaround
|
||||
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||
qDebug("Magnet redirect detected.");
|
||||
reply->abort();
|
||||
emit magnetRedirect(newUrlString, url);
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
m_redirectMapping.insert(newUrlString, url);
|
||||
// redirecting with first cookies
|
||||
downloadUrl(newUrlString, m_networkManager.cookieJar()->cookiesForUrl(url));
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
// Checking if it was redirected, restoring initial URL
|
||||
if (m_redirectMapping.contains(url)) {
|
||||
url = m_redirectMapping.take(url);
|
||||
}
|
||||
// Success
|
||||
QTemporaryFile *tmpfile = new QTemporaryFile;
|
||||
if (tmpfile->open()) {
|
||||
tmpfile->setAutoRemove(false);
|
||||
QString filePath = tmpfile->fileName();
|
||||
qDebug("Temporary filename is: %s", qPrintable(filePath));
|
||||
if (reply->isOpen() || reply->open(QIODevice::ReadOnly)) {
|
||||
QByteArray replyData = reply->readAll();
|
||||
if (reply->rawHeader("Content-Encoding") == "gzip") {
|
||||
// uncompress gzip reply
|
||||
replyData = gUncompress(reinterpret_cast<unsigned char*>(replyData.data()), replyData.length());
|
||||
}
|
||||
tmpfile->write(replyData);
|
||||
tmpfile->close();
|
||||
// XXX: tmpfile needs to be deleted on Windows before using the file
|
||||
// or it will complain that the file is used by another process.
|
||||
delete tmpfile;
|
||||
// Send finished signal
|
||||
emit downloadFinished(url, filePath);
|
||||
} else {
|
||||
delete tmpfile;
|
||||
fsutils::forceRemove(filePath);
|
||||
// Error when reading the request
|
||||
emit downloadFailure(url, tr("I/O Error"));
|
||||
}
|
||||
} else {
|
||||
delete tmpfile;
|
||||
emit downloadFailure(url, tr("I/O Error"));
|
||||
}
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void DownloadThread::downloadTorrentUrl(const QString &url, const QList<QNetworkCookie>& cookies)
|
||||
{
|
||||
// Process request
|
||||
QNetworkReply *reply = downloadUrl(url, cookies);
|
||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
||||
}
|
||||
|
||||
QNetworkReply* DownloadThread::downloadUrl(const QString &url, const QList<QNetworkCookie>& cookies) {
|
||||
// Update proxy settings
|
||||
applyProxySettings();
|
||||
// Set cookies
|
||||
if (!cookies.empty()) {
|
||||
qDebug("Setting %d cookies for url: %s", cookies.size(), qPrintable(url));
|
||||
m_networkManager.cookieJar()->setCookiesFromUrl(cookies, url);
|
||||
}
|
||||
// Process download request
|
||||
qDebug("url is %s", qPrintable(url));
|
||||
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
|
||||
QNetworkRequest request(qurl);
|
||||
// Spoof Firefox 38 user agent to avoid web server banning
|
||||
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0");
|
||||
qDebug("Downloading %s...", request.url().toEncoded().data());
|
||||
qDebug("%d cookies for this URL", m_networkManager.cookieJar()->cookiesForUrl(url).size());
|
||||
for (int i=0; i<m_networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
|
||||
qDebug("%s=%s", m_networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), m_networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data());
|
||||
qDebug("Domain: %s, Path: %s", qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(m_networkManager.cookieJar()->cookiesForUrl(url).at(i).path()));
|
||||
}
|
||||
// accept gzip
|
||||
request.setRawHeader("Accept-Encoding", "gzip");
|
||||
return m_networkManager.get(request);
|
||||
}
|
||||
|
||||
void DownloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) {
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if (!reply) return;
|
||||
if (bytesTotal > 0) {
|
||||
// Total number of bytes is available
|
||||
if (bytesTotal > 1048576*10) {
|
||||
// More than 10MB, this is probably not a torrent file, aborting...
|
||||
reply->abort();
|
||||
reply->deleteLater();
|
||||
} else {
|
||||
disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
||||
}
|
||||
} else {
|
||||
if (bytesReceived > 1048576*10) {
|
||||
// More than 10MB, this is probably not a torrent file, aborting...
|
||||
reply->abort();
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadThread::applyProxySettings() {
|
||||
QNetworkProxy proxy;
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
if (pref->isProxyEnabled()) {
|
||||
// Proxy enabled
|
||||
proxy.setHostName(pref->getProxyIp());
|
||||
proxy.setPort(pref->getProxyPort());
|
||||
// Default proxy type is HTTP, we must change if it is SOCKS5
|
||||
const int proxy_type = pref->getProxyType();
|
||||
if (proxy_type == Proxy::SOCKS5 || proxy_type == Proxy::SOCKS5_PW) {
|
||||
qDebug() << Q_FUNC_INFO << "using SOCKS proxy";
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "using HTTP proxy";
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
}
|
||||
// Authentication?
|
||||
if (pref->isProxyAuthEnabled()) {
|
||||
qDebug("Proxy requires authentication, authenticating");
|
||||
proxy.setUser(pref->getProxyUsername());
|
||||
proxy.setPassword(pref->getProxyPassword());
|
||||
}
|
||||
} else {
|
||||
proxy.setType(QNetworkProxy::NoProxy);
|
||||
}
|
||||
m_networkManager.setProxy(proxy);
|
||||
}
|
||||
|
||||
QString DownloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
|
||||
switch(status) {
|
||||
case QNetworkReply::HostNotFoundError:
|
||||
return tr("The remote host name was not found (invalid hostname)");
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return tr("The operation was canceled");
|
||||
case QNetworkReply::RemoteHostClosedError:
|
||||
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
||||
case QNetworkReply::TimeoutError:
|
||||
return tr("The connection to the remote server timed out");
|
||||
case QNetworkReply::SslHandshakeFailedError:
|
||||
return tr("SSL/TLS handshake failed");
|
||||
case QNetworkReply::ConnectionRefusedError:
|
||||
return tr("The remote server refused the connection");
|
||||
case QNetworkReply::ProxyConnectionRefusedError:
|
||||
return tr("The connection to the proxy server was refused");
|
||||
case QNetworkReply::ProxyConnectionClosedError:
|
||||
return tr("The proxy server closed the connection prematurely");
|
||||
case QNetworkReply::ProxyNotFoundError:
|
||||
return tr("The proxy host name was not found");
|
||||
case QNetworkReply::ProxyTimeoutError:
|
||||
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||
return tr("The proxy requires authentication in order to honour the request but did not accept any credentials offered");
|
||||
case QNetworkReply::ContentAccessDenied:
|
||||
return tr("The access to the remote content was denied (401)");
|
||||
case QNetworkReply::ContentOperationNotPermittedError:
|
||||
return tr("The operation requested on the remote content is not permitted");
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
return tr("The remote content was not found at the server (404)");
|
||||
case QNetworkReply::AuthenticationRequiredError:
|
||||
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
||||
case QNetworkReply::ProtocolUnknownError:
|
||||
return tr("The Network Access API cannot honor the request because the protocol is not known");
|
||||
case QNetworkReply::ProtocolInvalidOperationError:
|
||||
return tr("The requested operation is invalid for this protocol");
|
||||
case QNetworkReply::UnknownNetworkError:
|
||||
return tr("An unknown network-related error was detected");
|
||||
case QNetworkReply::UnknownProxyError:
|
||||
return tr("An unknown proxy-related error was detected");
|
||||
case QNetworkReply::UnknownContentError:
|
||||
return tr("An unknown error related to the remote content was detected");
|
||||
case QNetworkReply::ProtocolFailure:
|
||||
return tr("A breakdown in protocol was detected");
|
||||
default:
|
||||
return tr("Unknown error");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void DownloadThread::ignoreSslErrors(QNetworkReply* reply, const QList<QSslError> &errors) {
|
||||
Q_UNUSED(errors)
|
||||
// Ignore all SSL errors
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
#endif
|
||||
278
src/core/filesystemwatcher.cpp
Normal file
278
src/core/filesystemwatcher.cpp
Normal file
@@ -0,0 +1,278 @@
|
||||
#include <QtGlobal>
|
||||
#ifndef Q_OS_WIN
|
||||
#include <QSet>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <string.h>
|
||||
#elif !defined Q_OS_HAIKU
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "core/preferences.h"
|
||||
#include "core/bittorrent/torrentinfo.h"
|
||||
#include "core/bittorrent/magneturi.h"
|
||||
#include "filesystemwatcher.h"
|
||||
|
||||
#ifndef CIFS_MAGIC_NUMBER
|
||||
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
||||
#endif
|
||||
|
||||
#ifndef NFS_SUPER_MAGIC
|
||||
#define NFS_SUPER_MAGIC 0x6969
|
||||
#endif
|
||||
|
||||
#ifndef SMB_SUPER_MAGIC
|
||||
#define SMB_SUPER_MAGIC 0x517B
|
||||
#endif
|
||||
|
||||
const int WATCH_INTERVAL = 10000; // 10 sec
|
||||
const int MAX_PARTIAL_RETRIES = 5;
|
||||
|
||||
FileSystemWatcher::FileSystemWatcher(QObject *parent)
|
||||
: QFileSystemWatcher(parent)
|
||||
{
|
||||
m_filters << "*.torrent" << "*.magnet";
|
||||
connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString)));
|
||||
}
|
||||
|
||||
FileSystemWatcher::~FileSystemWatcher()
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
if (m_watchTimer)
|
||||
delete m_watchTimer;
|
||||
#endif
|
||||
if (m_partialTorrentTimer)
|
||||
delete m_partialTorrentTimer;
|
||||
}
|
||||
|
||||
QStringList FileSystemWatcher::directories() const
|
||||
{
|
||||
QStringList dirs;
|
||||
#ifndef Q_OS_WIN
|
||||
if (m_watchTimer) {
|
||||
foreach (const QDir &dir, m_watchedFolders)
|
||||
dirs << dir.canonicalPath();
|
||||
}
|
||||
#endif
|
||||
dirs << QFileSystemWatcher::directories();
|
||||
return dirs;
|
||||
}
|
||||
|
||||
void FileSystemWatcher::addPath(const QString &path)
|
||||
{
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
QDir dir(path);
|
||||
if (!dir.exists()) return;
|
||||
|
||||
// Check if the path points to a network file system or not
|
||||
if (isNetworkFileSystem(path)) {
|
||||
// Network mode
|
||||
qDebug("Network folder detected: %s", qPrintable(path));
|
||||
qDebug("Using file polling mode instead of inotify...");
|
||||
m_watchedFolders << dir;
|
||||
// Set up the watch timer
|
||||
if (!m_watchTimer) {
|
||||
m_watchTimer = new QTimer(this);
|
||||
connect(m_watchTimer, SIGNAL(timeout()), SLOT(scanNetworkFolders()));
|
||||
m_watchTimer->start(WATCH_INTERVAL); // 5 sec
|
||||
}
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
// Normal mode
|
||||
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
||||
QFileSystemWatcher::addPath(path);
|
||||
scanLocalFolder(path);
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileSystemWatcher::removePath(const QString &path)
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
QDir dir(path);
|
||||
for (int i = 0; i < m_watchedFolders.count(); ++i) {
|
||||
if (QDir(m_watchedFolders.at(i)) == dir) {
|
||||
m_watchedFolders.removeAt(i);
|
||||
if (m_watchedFolders.isEmpty())
|
||||
delete m_watchTimer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Normal mode
|
||||
QFileSystemWatcher::removePath(path);
|
||||
}
|
||||
|
||||
void FileSystemWatcher::scanLocalFolder(QString path)
|
||||
{
|
||||
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
||||
QStringList torrents;
|
||||
// Local folders scan
|
||||
addTorrentsFromDir(QDir(path), torrents);
|
||||
// Report detected torrent files
|
||||
if (!torrents.empty()) {
|
||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||
emit torrentsAdded(torrents);
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemWatcher::scanNetworkFolders()
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
qDebug("scanNetworkFolders() called");
|
||||
QStringList torrents;
|
||||
// Network folders scan
|
||||
foreach (const QDir &dir, m_watchedFolders) {
|
||||
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
||||
addTorrentsFromDir(dir, torrents);
|
||||
}
|
||||
// Report detected torrent files
|
||||
if (!torrents.empty()) {
|
||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||
emit torrentsAdded(torrents);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileSystemWatcher::processPartialTorrents()
|
||||
{
|
||||
QStringList noLongerPartial;
|
||||
|
||||
// Check which torrents are still partial
|
||||
foreach (const QString &torrentPath, m_partialTorrents.keys()) {
|
||||
if (!QFile::exists(torrentPath)) {
|
||||
m_partialTorrents.remove(torrentPath);
|
||||
}
|
||||
else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
|
||||
noLongerPartial << torrentPath;
|
||||
m_partialTorrents.remove(torrentPath);
|
||||
}
|
||||
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
|
||||
m_partialTorrents.remove(torrentPath);
|
||||
QFile::rename(torrentPath, torrentPath + ".invalid");
|
||||
}
|
||||
else {
|
||||
++m_partialTorrents[torrentPath];
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the partial timer if necessary
|
||||
if (m_partialTorrents.empty()) {
|
||||
m_partialTorrentTimer->stop();
|
||||
m_partialTorrentTimer->deleteLater();
|
||||
qDebug("No longer any partial torrent.");
|
||||
}
|
||||
else {
|
||||
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
|
||||
// Notify of new torrents
|
||||
if (!noLongerPartial.isEmpty())
|
||||
emit torrentsAdded(noLongerPartial);
|
||||
}
|
||||
|
||||
void FileSystemWatcher::startPartialTorrentTimer()
|
||||
{
|
||||
Q_ASSERT(!m_partialTorrents.isEmpty());
|
||||
if (!m_partialTorrentTimer) {
|
||||
m_partialTorrentTimer = new QTimer();
|
||||
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
||||
m_partialTorrentTimer->setSingleShot(true);
|
||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemWatcher::addTorrentsFromDir(const QDir &dir, QStringList &torrents)
|
||||
{
|
||||
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
||||
foreach (const QString &file, files) {
|
||||
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||
if (fileAbsPath.endsWith(".magnet")) {
|
||||
QFile f(fileAbsPath);
|
||||
if (f.open(QIODevice::ReadOnly)
|
||||
&& !BitTorrent::MagnetUri(QString::fromLocal8Bit(f.readAll())).isValid()) {
|
||||
torrents << fileAbsPath;
|
||||
}
|
||||
}
|
||||
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
|
||||
torrents << fileAbsPath;
|
||||
}
|
||||
else if (!m_partialTorrents.contains(fileAbsPath)) {
|
||||
qDebug("Partial torrent detected at: %s", qPrintable(fileAbsPath));
|
||||
qDebug("Delay the file's processing...");
|
||||
m_partialTorrents.insert(fileAbsPath, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_partialTorrents.empty())
|
||||
startPartialTorrentTimer();
|
||||
}
|
||||
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
bool FileSystemWatcher::isNetworkFileSystem(QString path)
|
||||
{
|
||||
QString file = path;
|
||||
if (!file.endsWith("/"))
|
||||
file += "/";
|
||||
file += ".";
|
||||
struct statfs buf;
|
||||
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
||||
#ifdef Q_OS_MAC
|
||||
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
||||
return ((strcmp(buf.f_fstypename, "nfs") == 0) || (strcmp(buf.f_fstypename, "cifs") == 0) || (strcmp(buf.f_fstypename, "smbfs") == 0));
|
||||
#else
|
||||
return ((buf.f_type == (long)CIFS_MAGIC_NUMBER) || (buf.f_type == (long)NFS_SUPER_MAGIC) || (buf.f_type == (long)SMB_SUPER_MAGIC));
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
||||
switch(errno) {
|
||||
case EACCES:
|
||||
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
||||
break;
|
||||
case EFAULT:
|
||||
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
||||
break;
|
||||
case EINTR:
|
||||
std::cerr << "This call was interrupted by a signal" << std::endl;
|
||||
break;
|
||||
case EIO:
|
||||
std::cerr << "I/O Error" << std::endl;
|
||||
break;
|
||||
case ELOOP:
|
||||
std::cerr << "Too many symlinks" << std::endl;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
std::cerr << "path is too long" << std::endl;
|
||||
break;
|
||||
case ENOENT:
|
||||
std::cerr << "The file referred by path does not exist" << std::endl;
|
||||
break;
|
||||
case ENOMEM:
|
||||
std::cerr << "Insufficient kernel memory" << std::endl;
|
||||
break;
|
||||
case ENOSYS:
|
||||
std::cerr << "The file system does not detect this call" << std::endl;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
std::cerr << "A component of the path is not a directory" << std::endl;
|
||||
break;
|
||||
case EOVERFLOW:
|
||||
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Unknown error" << std::endl;
|
||||
}
|
||||
|
||||
std::cerr << "Errno: " << errno << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -8,290 +8,45 @@
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#include <QSet>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <string.h>
|
||||
#elif !defined Q_OS_HAIKU
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "fs_utils.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef CIFS_MAGIC_NUMBER
|
||||
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
||||
#endif
|
||||
|
||||
#ifndef NFS_SUPER_MAGIC
|
||||
#define NFS_SUPER_MAGIC 0x6969
|
||||
#endif
|
||||
|
||||
#ifndef SMB_SUPER_MAGIC
|
||||
#define SMB_SUPER_MAGIC 0x517B
|
||||
#endif
|
||||
|
||||
const int WATCH_INTERVAL = 10000; // 10 sec
|
||||
const int MAX_PARTIAL_RETRIES = 5;
|
||||
|
||||
/*
|
||||
* Subclassing QFileSystemWatcher in order to support Network File
|
||||
* System watching (NFS, CIFS) on Linux and Mac OS.
|
||||
*/
|
||||
class FileSystemWatcher: public QFileSystemWatcher {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
#ifndef Q_OS_WIN
|
||||
QList<QDir> watched_folders;
|
||||
QPointer<QTimer> watch_timer;
|
||||
#endif
|
||||
QStringList m_filters;
|
||||
// Partial torrents
|
||||
QHash<QString, int> m_partialTorrents;
|
||||
QPointer<QTimer> m_partialTorrentTimer;
|
||||
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
private:
|
||||
static bool isNetworkFileSystem(QString path) {
|
||||
QString file = path;
|
||||
if (!file.endsWith("/"))
|
||||
file += "/";
|
||||
file += ".";
|
||||
struct statfs buf;
|
||||
if (!statfs(file.toLocal8Bit().constData(), &buf)) {
|
||||
#ifdef Q_OS_MAC
|
||||
// XXX: should we make sure HAVE_STRUCT_FSSTAT_F_FSTYPENAME is defined?
|
||||
return (strcmp(buf.f_fstypename, "nfs") == 0 || strcmp(buf.f_fstypename, "cifs") == 0 || strcmp(buf.f_fstypename, "smbfs") == 0);
|
||||
#else
|
||||
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC || buf.f_type == (long)SMB_SUPER_MAGIC);
|
||||
#endif
|
||||
} else {
|
||||
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
||||
switch(errno) {
|
||||
case EACCES:
|
||||
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
||||
break;
|
||||
case EFAULT:
|
||||
std::cerr << "Buf or path points to an invalid address" << std::endl;
|
||||
break;
|
||||
case EINTR:
|
||||
std::cerr << "This call was interrupted by a signal" << std::endl;
|
||||
break;
|
||||
case EIO:
|
||||
std::cerr << "I/O Error" << std::endl;
|
||||
break;
|
||||
case ELOOP:
|
||||
std::cerr << "Too many symlinks" << std::endl;
|
||||
break;
|
||||
case ENAMETOOLONG:
|
||||
std::cerr << "path is too long" << std::endl;
|
||||
break;
|
||||
case ENOENT:
|
||||
std::cerr << "The file referred by path does not exist" << std::endl;
|
||||
break;
|
||||
case ENOMEM:
|
||||
std::cerr << "Insufficient kernel memory" << std::endl;
|
||||
break;
|
||||
case ENOSYS:
|
||||
std::cerr << "The file system does not detect this call" << std::endl;
|
||||
break;
|
||||
case ENOTDIR:
|
||||
std::cerr << "A component of the path is not a directory" << std::endl;
|
||||
break;
|
||||
case EOVERFLOW:
|
||||
std::cerr << "Some values were too large to be represented in the struct" << std::endl;
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Unknown error" << std::endl;
|
||||
}
|
||||
std::cerr << "Errno: " << errno << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
class FileSystemWatcher : public QFileSystemWatcher
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
|
||||
m_filters << "*.torrent" << "*.magnet";
|
||||
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString)));
|
||||
}
|
||||
explicit FileSystemWatcher(QObject *parent = 0);
|
||||
~FileSystemWatcher();
|
||||
|
||||
~FileSystemWatcher() {
|
||||
#ifndef Q_OS_WIN
|
||||
if (watch_timer)
|
||||
delete watch_timer;
|
||||
#endif
|
||||
if (m_partialTorrentTimer)
|
||||
delete m_partialTorrentTimer;
|
||||
}
|
||||
|
||||
QStringList directories() const {
|
||||
QStringList dirs;
|
||||
#ifndef Q_OS_WIN
|
||||
if (watch_timer) {
|
||||
foreach (const QDir &dir, watched_folders)
|
||||
dirs << dir.canonicalPath();
|
||||
}
|
||||
#endif
|
||||
dirs << QFileSystemWatcher::directories();
|
||||
return dirs;
|
||||
}
|
||||
|
||||
void addPath(const QString & path) {
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
QDir dir(path);
|
||||
if (!dir.exists())
|
||||
return;
|
||||
// Check if the path points to a network file system or not
|
||||
if (isNetworkFileSystem(path)) {
|
||||
// Network mode
|
||||
qDebug("Network folder detected: %s", qPrintable(path));
|
||||
qDebug("Using file polling mode instead of inotify...");
|
||||
watched_folders << dir;
|
||||
// Set up the watch timer
|
||||
if (!watch_timer) {
|
||||
watch_timer = new QTimer(this);
|
||||
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders()));
|
||||
watch_timer->start(WATCH_INTERVAL); // 5 sec
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
// Normal mode
|
||||
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
||||
QFileSystemWatcher::addPath(path);
|
||||
scanLocalFolder(path);
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void removePath(const QString & path) {
|
||||
#ifndef Q_OS_WIN
|
||||
QDir dir(path);
|
||||
for (int i = 0; i < watched_folders.count(); ++i) {
|
||||
if (QDir(watched_folders.at(i)) == dir) {
|
||||
watched_folders.removeAt(i);
|
||||
if (watched_folders.isEmpty())
|
||||
delete watch_timer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// Normal mode
|
||||
QFileSystemWatcher::removePath(path);
|
||||
}
|
||||
|
||||
protected slots:
|
||||
void scanLocalFolder(QString path) {
|
||||
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
||||
QStringList torrents;
|
||||
// Local folders scan
|
||||
addTorrentsFromDir(QDir(path), torrents);
|
||||
// Report detected torrent files
|
||||
if (!torrents.empty()) {
|
||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||
emit torrentsAdded(torrents);
|
||||
}
|
||||
}
|
||||
|
||||
void scanNetworkFolders() {
|
||||
#ifndef Q_OS_WIN
|
||||
qDebug("scanNetworkFolders() called");
|
||||
QStringList torrents;
|
||||
// Network folders scan
|
||||
foreach (const QDir &dir, watched_folders) {
|
||||
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
||||
addTorrentsFromDir(dir, torrents);
|
||||
}
|
||||
// Report detected torrent files
|
||||
if (!torrents.empty()) {
|
||||
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||
emit torrentsAdded(torrents);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void processPartialTorrents() {
|
||||
QStringList no_longer_partial;
|
||||
|
||||
// Check which torrents are still partial
|
||||
foreach (const QString& torrent_path, m_partialTorrents.keys()) {
|
||||
if (!QFile::exists(torrent_path)) {
|
||||
m_partialTorrents.remove(torrent_path);
|
||||
continue;
|
||||
}
|
||||
if (fsutils::isValidTorrentFile(torrent_path)) {
|
||||
no_longer_partial << torrent_path;
|
||||
m_partialTorrents.remove(torrent_path);
|
||||
} else {
|
||||
if (m_partialTorrents[torrent_path] >= MAX_PARTIAL_RETRIES) {
|
||||
m_partialTorrents.remove(torrent_path);
|
||||
QFile::rename(torrent_path, torrent_path+".invalid");
|
||||
} else {
|
||||
m_partialTorrents[torrent_path]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the partial timer if necessary
|
||||
if (m_partialTorrents.empty()) {
|
||||
m_partialTorrentTimer->stop();
|
||||
m_partialTorrentTimer->deleteLater();
|
||||
qDebug("No longer any partial torrent.");
|
||||
} else {
|
||||
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
// Notify of new torrents
|
||||
if (!no_longer_partial.isEmpty())
|
||||
emit torrentsAdded(no_longer_partial);
|
||||
}
|
||||
QStringList directories() const;
|
||||
void addPath(const QString &path);
|
||||
void removePath(const QString &path);
|
||||
|
||||
signals:
|
||||
void torrentsAdded(QStringList &pathList);
|
||||
void torrentsAdded(const QStringList &pathList);
|
||||
|
||||
protected slots:
|
||||
void scanLocalFolder(QString path);
|
||||
void scanNetworkFolders();
|
||||
void processPartialTorrents();
|
||||
|
||||
private:
|
||||
void startPartialTorrentTimer() {
|
||||
Q_ASSERT(!m_partialTorrents.isEmpty());
|
||||
if (!m_partialTorrentTimer) {
|
||||
m_partialTorrentTimer = new QTimer();
|
||||
connect(m_partialTorrentTimer, SIGNAL(timeout()), SLOT(processPartialTorrents()));
|
||||
m_partialTorrentTimer->setSingleShot(true);
|
||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
|
||||
const QStringList files = dir.entryList(m_filters, QDir::Files, QDir::Unsorted);
|
||||
foreach (const QString &file, files) {
|
||||
const QString file_abspath = dir.absoluteFilePath(file);
|
||||
if (file_abspath.endsWith(".magnet")) {
|
||||
QFile f(file_abspath);
|
||||
if (f.open(QIODevice::ReadOnly)
|
||||
&& !misc::magnetUriToHash(QString::fromLocal8Bit(f.readAll())).isEmpty()) {
|
||||
torrents << file_abspath;
|
||||
}
|
||||
} else if (fsutils::isValidTorrentFile(file_abspath)) {
|
||||
torrents << file_abspath;
|
||||
} else {
|
||||
if (!m_partialTorrents.contains(file_abspath)) {
|
||||
qDebug("Partial torrent detected at: %s", qPrintable(file_abspath));
|
||||
qDebug("Delay the file's processing...");
|
||||
m_partialTorrents.insert(file_abspath, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_partialTorrents.empty())
|
||||
startPartialTorrentTimer();
|
||||
}
|
||||
void startPartialTorrentTimer();
|
||||
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
|
||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||
static bool isNetworkFileSystem(QString path);
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
QList<QDir> m_watchedFolders;
|
||||
QPointer<QTimer> m_watchTimer;
|
||||
#endif
|
||||
QStringList m_filters;
|
||||
// Partial torrents
|
||||
QHash<QString, int> m_partialTorrents;
|
||||
QPointer<QTimer> m_partialTorrentTimer;
|
||||
};
|
||||
|
||||
#endif // FILESYSTEMWATCHER_H
|
||||
|
||||
@@ -1,552 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2012 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "fs_utils.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
#ifdef DISABLE_GUI
|
||||
#include <QCoreApplication>
|
||||
#else
|
||||
#include <QApplication>
|
||||
#endif
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#endif
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#elif defined(Q_OS_HAIKU)
|
||||
#include <kernel/fs_info.h>
|
||||
#else
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
#else
|
||||
#include <shlobj.h>
|
||||
#include <winbase.h>
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QDesktopServices>
|
||||
#else
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace libtorrent;
|
||||
|
||||
/**
|
||||
* Converts a path to a string suitable for display.
|
||||
* This function makes sure the directory separator used is consistent
|
||||
* with the OS being run.
|
||||
*/
|
||||
QString fsutils::toNativePath(const QString& path) {
|
||||
return QDir::toNativeSeparators(path);
|
||||
}
|
||||
|
||||
QString fsutils::fromNativePath(const QString &path) {
|
||||
return QDir::fromNativeSeparators(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file extension part of a file name.
|
||||
*/
|
||||
QString fsutils::fileExtension(const QString &filename) {
|
||||
QString ext = QString(filename).remove(".!qB");
|
||||
const int point_index = ext.lastIndexOf(".");
|
||||
return (point_index >= 0) ? ext.mid(point_index + 1) : QString();
|
||||
}
|
||||
|
||||
QString fsutils::fileName(const QString& file_path) {
|
||||
QString path = fsutils::fromNativePath(file_path);
|
||||
const int slash_index = path.lastIndexOf("/");
|
||||
if (slash_index == -1)
|
||||
return path;
|
||||
return path.mid(slash_index + 1);
|
||||
}
|
||||
|
||||
QString fsutils::folderName(const QString& file_path) {
|
||||
QString path = fsutils::fromNativePath(file_path);
|
||||
const int slash_index = path.lastIndexOf("/");
|
||||
if (slash_index == -1)
|
||||
return path;
|
||||
return path.left(slash_index);
|
||||
}
|
||||
|
||||
bool fsutils::isValidTorrentFile(const QString& torrent_path) {
|
||||
try {
|
||||
boost::intrusive_ptr<libtorrent::torrent_info> t = new torrent_info(fsutils::toNativePath(torrent_path).toUtf8().constData());
|
||||
if (!t->is_valid() || t->num_files() == 0)
|
||||
return false;
|
||||
} catch(std::exception&) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an empty folder tree.
|
||||
*
|
||||
* This function will also remove .DS_Store files on Mac OS and
|
||||
* Thumbs.db on Windows.
|
||||
*/
|
||||
bool fsutils::smartRemoveEmptyFolderTree(const QString& dir_path) {
|
||||
qDebug() << Q_FUNC_INFO << dir_path;
|
||||
if (dir_path.isEmpty())
|
||||
return false;
|
||||
|
||||
QDir dir(dir_path);
|
||||
if (!dir.exists())
|
||||
return true;
|
||||
|
||||
// Remove Files created by the OS
|
||||
#if defined Q_OS_MAC
|
||||
fsutils::forceRemove(dir_path + QLatin1String("/.DS_Store"));
|
||||
#elif defined Q_OS_WIN
|
||||
fsutils::forceRemove(dir_path + QLatin1String("/Thumbs.db"));
|
||||
#endif
|
||||
|
||||
QFileInfoList sub_files = dir.entryInfoList();
|
||||
foreach (const QFileInfo& info, sub_files) {
|
||||
QString sub_name = info.fileName();
|
||||
if (sub_name == "." || sub_name == "..")
|
||||
continue;
|
||||
|
||||
QString sub_path = info.absoluteFilePath();
|
||||
qDebug() << Q_FUNC_INFO << "sub file: " << sub_path;
|
||||
if (info.isDir()) {
|
||||
if (!smartRemoveEmptyFolderTree(sub_path)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to remove folder: " << sub_path;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (info.isHidden()) {
|
||||
qDebug() << Q_FUNC_INFO << "Removing hidden file: " << sub_path;
|
||||
if (!fsutils::forceRemove(sub_path)) {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to remove " << sub_path;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Folder is not empty, aborting. Found: " << sub_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "Calling rmdir on " << dir_path;
|
||||
return QDir().rmdir(dir_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the file with the given file_path.
|
||||
*
|
||||
* This function will try to fix the file permissions before removing it.
|
||||
*/
|
||||
bool fsutils::forceRemove(const QString& file_path) {
|
||||
QFile f(file_path);
|
||||
if (!f.exists())
|
||||
return true;
|
||||
// Make sure we have read/write permissions
|
||||
f.setPermissions(f.permissions()|QFile::ReadOwner|QFile::WriteOwner|QFile::ReadUser|QFile::WriteUser);
|
||||
// Remove the file
|
||||
return f.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes directory and its content recursively.
|
||||
*
|
||||
*/
|
||||
void fsutils::removeDirRecursive(const QString& dirName) {
|
||||
QDir dir(dirName);
|
||||
|
||||
if (!dir.exists()) return;
|
||||
|
||||
Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot |
|
||||
QDir::System |
|
||||
QDir::Hidden |
|
||||
QDir::AllDirs |
|
||||
QDir::Files, QDir::DirsFirst)) {
|
||||
if (info.isDir()) removeDirRecursive(info.absoluteFilePath());
|
||||
else forceRemove(info.absoluteFilePath());
|
||||
}
|
||||
|
||||
dir.rmdir(dirName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of a file.
|
||||
* If the file is a folder, it will compute its size based on its content.
|
||||
*
|
||||
* Returns -1 in case of error.
|
||||
*/
|
||||
qint64 fsutils::computePathSize(const QString& path) {
|
||||
// Check if it is a file
|
||||
QFileInfo fi(path);
|
||||
if (!fi.exists()) return -1;
|
||||
if (fi.isFile()) return fi.size();
|
||||
// Compute folder size based on its content
|
||||
qint64 size = 0;
|
||||
foreach (const QFileInfo &subfi, QDir(path).entryInfoList(QDir::Dirs|QDir::Files)) {
|
||||
if (subfi.fileName().startsWith(".")) continue;
|
||||
if (subfi.isDir())
|
||||
size += fsutils::computePathSize(subfi.absoluteFilePath());
|
||||
else
|
||||
size += subfi.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes deep comparison of two files to make sure they are identical.
|
||||
*/
|
||||
bool fsutils::sameFiles(const QString& path1, const QString& path2) {
|
||||
QFile f1(path1), f2(path2);
|
||||
if (!f1.exists() || !f2.exists()) return false;
|
||||
if (f1.size() != f2.size()) return false;
|
||||
if (!f1.open(QIODevice::ReadOnly)) return false;
|
||||
if (!f2.open(QIODevice::ReadOnly)) {
|
||||
f1.close();
|
||||
return false;
|
||||
}
|
||||
bool same = true;
|
||||
while(!f1.atEnd() && !f2.atEnd()) {
|
||||
if (f1.read(1024) != f2.read(1024)) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
f1.close(); f2.close();
|
||||
return same;
|
||||
}
|
||||
|
||||
QString fsutils::updateLabelInSavePath(const QString& defaultSavePath, const QString& save_path, const QString& old_label, const QString& new_label) {
|
||||
if (old_label == new_label) return fsutils::fromNativePath(save_path);
|
||||
QString defaultPath = fsutils::fromNativePath(defaultSavePath);
|
||||
QString path = fsutils::fromNativePath(save_path);
|
||||
qDebug("UpdateLabelInSavePath(%s, %s, %s)", qPrintable(path), qPrintable(old_label), qPrintable(new_label));
|
||||
if (!path.startsWith(defaultPath)) return path;
|
||||
QString new_save_path = path;
|
||||
new_save_path.remove(defaultPath);
|
||||
QStringList path_parts = new_save_path.split("/", QString::SkipEmptyParts);
|
||||
if (path_parts.empty()) {
|
||||
if (!new_label.isEmpty())
|
||||
path_parts << new_label;
|
||||
} else {
|
||||
if (old_label.isEmpty() || path_parts.first() != old_label) {
|
||||
if (path_parts.first() != new_label)
|
||||
path_parts.prepend(new_label);
|
||||
} else {
|
||||
if (new_label.isEmpty()) {
|
||||
path_parts.removeAt(0);
|
||||
} else {
|
||||
if (path_parts.first() != new_label)
|
||||
path_parts.replace(0, new_label);
|
||||
}
|
||||
}
|
||||
}
|
||||
new_save_path = defaultPath;
|
||||
if (!new_save_path.endsWith("/")) new_save_path += "/";
|
||||
new_save_path += path_parts.join("/");
|
||||
qDebug("New save path is %s", qPrintable(new_save_path));
|
||||
return new_save_path;
|
||||
}
|
||||
|
||||
QString fsutils::toValidFileSystemName(QString filename) {
|
||||
qDebug("toValidFSName: %s", qPrintable(filename));
|
||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
filename.replace(regex, " ");
|
||||
qDebug("toValidFSName, result: %s", qPrintable(filename));
|
||||
return filename.trimmed();
|
||||
}
|
||||
|
||||
bool fsutils::isValidFileSystemName(const QString& filename) {
|
||||
if (filename.isEmpty()) return false;
|
||||
const QRegExp regex("[\\\\/:?\"*<>|]");
|
||||
return !filename.contains(regex);
|
||||
}
|
||||
|
||||
long long fsutils::freeDiskSpaceOnPath(QString path) {
|
||||
if (path.isEmpty()) return -1;
|
||||
QDir dir_path(path);
|
||||
if (!dir_path.exists()) {
|
||||
QStringList parts = path.split("/");
|
||||
while (parts.size() > 1 && !QDir(parts.join("/")).exists()) {
|
||||
parts.removeLast();
|
||||
}
|
||||
dir_path = QDir(parts.join("/"));
|
||||
if (!dir_path.exists()) return -1;
|
||||
}
|
||||
Q_ASSERT(dir_path.exists());
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
unsigned long long available;
|
||||
#ifdef Q_OS_HAIKU
|
||||
const QString statfs_path = dir_path.path()+"/.";
|
||||
dev_t device = dev_for_path (qPrintable(statfs_path));
|
||||
if (device >= 0) {
|
||||
fs_info info;
|
||||
if(fs_stat_dev(device, &info)==B_OK){
|
||||
available = ((unsigned long long)(info.free_blocks*info.block_size));
|
||||
return available;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
#else
|
||||
struct statfs stats;
|
||||
const QString statfs_path = dir_path.path()+"/.";
|
||||
const int ret = statfs (qPrintable(statfs_path), &stats) ;
|
||||
if (ret == 0) {
|
||||
available = ((unsigned long long)stats.f_bavail) *
|
||||
((unsigned long long)stats.f_bsize) ;
|
||||
return available;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
typedef BOOL (WINAPI *GetDiskFreeSpaceEx_t)(LPCTSTR,
|
||||
PULARGE_INTEGER,
|
||||
PULARGE_INTEGER,
|
||||
PULARGE_INTEGER);
|
||||
GetDiskFreeSpaceEx_t
|
||||
pGetDiskFreeSpaceEx = (GetDiskFreeSpaceEx_t)::GetProcAddress
|
||||
(
|
||||
::GetModuleHandle(TEXT("kernel32.dll")),
|
||||
"GetDiskFreeSpaceExW"
|
||||
);
|
||||
if ( pGetDiskFreeSpaceEx )
|
||||
{
|
||||
ULARGE_INTEGER bytesFree, bytesTotal;
|
||||
unsigned long long *ret;
|
||||
if (pGetDiskFreeSpaceEx((LPCTSTR)(fsutils::toNativePath(dir_path.path())).utf16(), &bytesFree, &bytesTotal, NULL)) {
|
||||
ret = (unsigned long long*)&bytesFree;
|
||||
return *ret;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QString fsutils::branchPath(const QString& file_path, QString* removed) {
|
||||
QString ret = fsutils::fromNativePath(file_path);
|
||||
if (ret.endsWith("/"))
|
||||
ret.chop(1);
|
||||
const int slashIndex = ret.lastIndexOf("/");
|
||||
if (slashIndex >= 0) {
|
||||
if (removed)
|
||||
*removed = ret.mid(slashIndex + 1);
|
||||
ret = ret.left(slashIndex);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool fsutils::sameFileNames(const QString &first, const QString &second) {
|
||||
#if defined(Q_OS_UNIX) || defined(Q_WS_QWS)
|
||||
return QString::compare(first, second, Qt::CaseSensitive) == 0;
|
||||
#else
|
||||
return QString::compare(first, second, Qt::CaseInsensitive) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString fsutils::expandPath(const QString &path) {
|
||||
QString ret = fsutils::fromNativePath(path.trimmed());
|
||||
if (ret.isEmpty())
|
||||
return ret;
|
||||
|
||||
return QDir::cleanPath(ret);
|
||||
}
|
||||
|
||||
QString fsutils::expandPathAbs(const QString& path) {
|
||||
QString ret = fsutils::expandPath(path);
|
||||
|
||||
if (!QDir::isAbsolutePath(ret))
|
||||
ret = QDir(ret).absolutePath();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString fsutils::QDesktopServicesDataLocation() {
|
||||
QString result;
|
||||
#ifdef Q_OS_WIN
|
||||
LPWSTR path=new WCHAR[256];
|
||||
if (SHGetSpecialFolderPath(0, path, CSIDL_LOCAL_APPDATA, FALSE))
|
||||
result = fsutils::fromNativePath(QString::fromWCharArray(path));
|
||||
if (!QCoreApplication::applicationName().isEmpty())
|
||||
result += QLatin1String("/") + qApp->applicationName();
|
||||
#else
|
||||
#ifdef Q_OS_MAC
|
||||
FSRef ref;
|
||||
OSErr err = FSFindFolder(kUserDomain, kApplicationSupportFolderType, false, &ref);
|
||||
if (err)
|
||||
return QString();
|
||||
QString path;
|
||||
QByteArray ba(2048, 0);
|
||||
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
||||
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
||||
result += QLatin1Char('/') + qApp->applicationName();
|
||||
#else
|
||||
QString xdgDataHome = QLatin1String(qgetenv("XDG_DATA_HOME"));
|
||||
if (xdgDataHome.isEmpty())
|
||||
xdgDataHome = QDir::homePath() + QLatin1String("/.local/share");
|
||||
xdgDataHome += QLatin1String("/data/")
|
||||
+ qApp->applicationName();
|
||||
result = xdgDataHome;
|
||||
#endif
|
||||
#endif
|
||||
if (!result.endsWith("/"))
|
||||
result += "/";
|
||||
return result;
|
||||
}
|
||||
|
||||
QString fsutils::QDesktopServicesCacheLocation() {
|
||||
QString result;
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||
result = QDesktopServicesDataLocation() + QLatin1String("cache");
|
||||
#else
|
||||
#ifdef Q_OS_MAC
|
||||
// http://developer.apple.com/documentation/Carbon/Reference/Folder_Manager/Reference/reference.html
|
||||
FSRef ref;
|
||||
OSErr err = FSFindFolder(kUserDomain, kCachedDataFolderType, false, &ref);
|
||||
if (err)
|
||||
return QString();
|
||||
QByteArray ba(2048, 0);
|
||||
if (FSRefMakePath(&ref, reinterpret_cast<UInt8 *>(ba.data()), ba.size()) == noErr)
|
||||
result = QString::fromUtf8(ba).normalized(QString::NormalizationForm_C);
|
||||
result += QLatin1Char('/') + qApp->applicationName();
|
||||
#else
|
||||
QString xdgCacheHome = QLatin1String(qgetenv("XDG_CACHE_HOME"));
|
||||
if (xdgCacheHome.isEmpty())
|
||||
xdgCacheHome = QDir::homePath() + QLatin1String("/.cache");
|
||||
xdgCacheHome += QLatin1Char('/') + QCoreApplication::applicationName();
|
||||
result = xdgCacheHome;
|
||||
#endif
|
||||
#endif
|
||||
if (!result.endsWith("/"))
|
||||
result += "/";
|
||||
return result;
|
||||
}
|
||||
|
||||
QString fsutils::QDesktopServicesDownloadLocation() {
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_OS2)
|
||||
// as long as it stays WinXP like we do the same on OS/2
|
||||
// TODO: Use IKnownFolderManager to get path of FOLDERID_Downloads
|
||||
// instead of hardcoding "Downloads"
|
||||
// Unfortunately, this would break compatibility with WinXP
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
return QDir(QDesktopServices::storageLocation(QDesktopServices::DocumentsLocation)).absoluteFilePath(
|
||||
QCoreApplication::translate("fsutils", "Downloads"));
|
||||
#else
|
||||
return QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).absoluteFilePath(
|
||||
QCoreApplication::translate("fsutils", "Downloads"));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
QString save_path;
|
||||
// Default save path on Linux
|
||||
QString config_path = QString::fromLocal8Bit(qgetenv("XDG_CONFIG_HOME").constData());
|
||||
if (config_path.isEmpty())
|
||||
config_path = QDir::home().absoluteFilePath(".config");
|
||||
|
||||
QString user_dirs_file = config_path + "/user-dirs.dirs";
|
||||
if (QFile::exists(user_dirs_file)) {
|
||||
QSettings settings(user_dirs_file, QSettings::IniFormat);
|
||||
// We need to force UTF-8 encoding here since this is not
|
||||
// the default for Ini files.
|
||||
settings.setIniCodec("UTF-8");
|
||||
QString xdg_download_dir = settings.value("XDG_DOWNLOAD_DIR").toString();
|
||||
if (!xdg_download_dir.isEmpty()) {
|
||||
// Resolve $HOME environment variables
|
||||
xdg_download_dir.replace("$HOME", QDir::homePath());
|
||||
save_path = xdg_download_dir;
|
||||
qDebug() << Q_FUNC_INFO << "SUCCESS: Using XDG path for downloads: " << save_path;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
if (!save_path.isEmpty() && !QFile::exists(save_path)) {
|
||||
QDir().mkpath(save_path);
|
||||
}
|
||||
|
||||
if (save_path.isEmpty() || !QFile::exists(save_path)) {
|
||||
save_path = QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
||||
qDebug() << Q_FUNC_INFO << "using" << save_path << "as fallback since the XDG detection did not work";
|
||||
}
|
||||
|
||||
return save_path;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// TODO: How to support this on Mac OS X?
|
||||
#endif
|
||||
|
||||
// Fallback
|
||||
return QDir::home().absoluteFilePath(QCoreApplication::translate("fsutils", "Downloads"));
|
||||
}
|
||||
|
||||
QString fsutils::searchEngineLocation() {
|
||||
QString folder = "nova";
|
||||
if (misc::pythonVersion() >= 3)
|
||||
folder = "nova3";
|
||||
const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation()
|
||||
+ folder);
|
||||
QDir locationDir(location);
|
||||
if (!locationDir.exists())
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
return location;
|
||||
}
|
||||
|
||||
QString fsutils::BTBackupLocation() {
|
||||
const QString location = fsutils::expandPathAbs(QDesktopServicesDataLocation()
|
||||
+ "BT_backup");
|
||||
QDir locationDir(location);
|
||||
if (!locationDir.exists())
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
return location;
|
||||
}
|
||||
|
||||
QString fsutils::cacheLocation() {
|
||||
QString location = fsutils::expandPathAbs(QDesktopServicesCacheLocation());
|
||||
QDir locationDir(location);
|
||||
if (!locationDir.exists())
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
return location;
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2012 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef FS_UTILS_H
|
||||
#define FS_UTILS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* Utility functions related to file system.
|
||||
*/
|
||||
namespace fsutils
|
||||
{
|
||||
|
||||
QString toNativePath(const QString& path);
|
||||
QString fromNativePath(const QString& path);
|
||||
QString fileExtension(const QString& filename);
|
||||
QString fileName(const QString& file_path);
|
||||
QString folderName(const QString& file_path);
|
||||
qint64 computePathSize(const QString& path);
|
||||
bool sameFiles(const QString& path1, const QString& path2);
|
||||
QString updateLabelInSavePath(const QString &defaultSavePath, const QString &save_path, const QString& old_label, const QString& new_label);
|
||||
QString toValidFileSystemName(QString filename);
|
||||
bool isValidFileSystemName(const QString& filename);
|
||||
long long freeDiskSpaceOnPath(QString path);
|
||||
QString branchPath(const QString& file_path, QString* removed = 0);
|
||||
bool sameFileNames(const QString& first, const QString& second);
|
||||
QString expandPath(const QString& path);
|
||||
QString expandPathAbs(const QString& path);
|
||||
bool isValidTorrentFile(const QString& path);
|
||||
bool smartRemoveEmptyFolderTree(const QString& dir_path);
|
||||
bool forceRemove(const QString& file_path);
|
||||
void removeDirRecursive(const QString& dirName);
|
||||
|
||||
/* Ported from Qt4 to drop dependency on QtGui */
|
||||
QString QDesktopServicesDataLocation();
|
||||
QString QDesktopServicesCacheLocation();
|
||||
QString QDesktopServicesDownloadLocation();
|
||||
/* End of Qt4 code */
|
||||
QString searchEngineLocation();
|
||||
QString BTBackupLocation();
|
||||
QString cacheLocation();
|
||||
|
||||
}
|
||||
|
||||
#endif // FS_UTILS_H
|
||||
|
||||
@@ -42,30 +42,28 @@ QT_END_NAMESPACE
|
||||
|
||||
namespace Http
|
||||
{
|
||||
class IRequestHandler;
|
||||
|
||||
class IRequestHandler;
|
||||
class Connection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Connection)
|
||||
|
||||
class Connection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Connection)
|
||||
public:
|
||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Connection();
|
||||
|
||||
public:
|
||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Connection();
|
||||
private slots:
|
||||
void read();
|
||||
|
||||
private slots:
|
||||
void read();
|
||||
|
||||
private:
|
||||
static bool acceptsGzipEncoding(const QString &encoding);
|
||||
void sendResponse(const Response &response);
|
||||
|
||||
QTcpSocket *m_socket;
|
||||
IRequestHandler *m_requestHandler;
|
||||
QByteArray m_receivedData;
|
||||
};
|
||||
private:
|
||||
static bool acceptsGzipEncoding(const QString &encoding);
|
||||
void sendResponse(const Response &response);
|
||||
|
||||
QTcpSocket *m_socket;
|
||||
IRequestHandler *m_requestHandler;
|
||||
QByteArray m_receivedData;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_CONNECTION_H
|
||||
|
||||
@@ -33,14 +33,12 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class IRequestHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IRequestHandler() {}
|
||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||
};
|
||||
|
||||
class IRequestHandler
|
||||
{
|
||||
public:
|
||||
virtual ~IRequestHandler() {}
|
||||
virtual Response processRequest(const Request &request, const Environment &env) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_IREQUESTHANDLER_H
|
||||
|
||||
@@ -36,39 +36,37 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class RequestParser
|
||||
{
|
||||
public:
|
||||
enum ErrorCode
|
||||
class RequestParser
|
||||
{
|
||||
NoError = 0,
|
||||
IncompleteRequest,
|
||||
BadRequest
|
||||
public:
|
||||
enum ErrorCode
|
||||
{
|
||||
NoError = 0,
|
||||
IncompleteRequest,
|
||||
BadRequest
|
||||
};
|
||||
|
||||
// when result != NoError parsed request is undefined
|
||||
// Warning! Header names are converted to lower-case.
|
||||
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
||||
|
||||
private:
|
||||
RequestParser(uint maxContentLength);
|
||||
|
||||
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
||||
|
||||
bool parseHttpHeader(const QByteArray &data);
|
||||
bool parseStartingLine(const QString &line);
|
||||
bool parseContent(const QByteArray &data);
|
||||
bool parseFormData(const QByteArray &data);
|
||||
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
||||
|
||||
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
||||
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
||||
|
||||
const uint m_maxContentLength;
|
||||
Request m_request;
|
||||
};
|
||||
|
||||
// when result != NoError parsed request is undefined
|
||||
// Warning! Header names are converted to lower-case.
|
||||
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
||||
|
||||
private:
|
||||
RequestParser(uint maxContentLength);
|
||||
|
||||
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
||||
|
||||
bool parseHttpHeader(const QByteArray &data);
|
||||
bool parseStartingLine(const QString &line);
|
||||
bool parseContent(const QByteArray &data);
|
||||
bool parseFormData(const QByteArray &data);
|
||||
QList<QByteArray> splitMultipartData(const QByteArray &data, const QByteArray &boundary);
|
||||
|
||||
static bool parseHeaderLine(const QString &line, QPair<QString, QString> &out);
|
||||
static bool parseHeaderValue(const QString &value, QStringMap &out);
|
||||
|
||||
const uint m_maxContentLength;
|
||||
Request m_request;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_REQUESTPARSER_H
|
||||
|
||||
@@ -34,27 +34,25 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
class ResponseBuilder : public QObject
|
||||
{
|
||||
public:
|
||||
explicit ResponseBuilder(QObject *parent = 0);
|
||||
|
||||
class ResponseBuilder : public QObject
|
||||
{
|
||||
public:
|
||||
explicit ResponseBuilder(QObject *parent = 0);
|
||||
protected:
|
||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||
void header(const QString &name, const QString &value);
|
||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||
void header(const QString &name, const QString &value);
|
||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||
void clear();
|
||||
Response response() const;
|
||||
|
||||
Response response() const;
|
||||
|
||||
private:
|
||||
void print_impl(const QByteArray &data, const QString &type);
|
||||
|
||||
Response m_response;
|
||||
};
|
||||
private:
|
||||
void print_impl(const QByteArray &data, const QString &type);
|
||||
|
||||
Response m_response;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_RESPONSEBUILDER_H
|
||||
|
||||
@@ -29,11 +29,9 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <zlib.h>
|
||||
#include "core/utils/gzip.h"
|
||||
#include "responsegenerator.h"
|
||||
|
||||
bool gCompress(QByteArray data, QByteArray& dest_buffer);
|
||||
|
||||
using namespace Http;
|
||||
|
||||
QByteArray ResponseGenerator::generate(Response response)
|
||||
@@ -44,7 +42,7 @@ QByteArray ResponseGenerator::generate(Response response)
|
||||
// So we only benefit from gzip if the message is bigger than 23+26 = 49
|
||||
// If the message is smaller than 49 bytes we actually send MORE data if we gzip
|
||||
QByteArray dest_buf;
|
||||
if ((response.content.size() > 49) && (gCompress(response.content, dest_buf)))
|
||||
if ((response.content.size() > 49) && (Utils::Gzip::compress(response.content, dest_buf)))
|
||||
response.content = dest_buf;
|
||||
else
|
||||
response.headers.remove(HEADER_CONTENT_ENCODING);
|
||||
@@ -67,58 +65,3 @@ QByteArray ResponseGenerator::generate(Response response)
|
||||
|
||||
return ret.toUtf8() + response.content;
|
||||
}
|
||||
|
||||
bool gCompress(QByteArray data, QByteArray& dest_buffer)
|
||||
{
|
||||
static const int BUFSIZE = 128 * 1024;
|
||||
char tmp_buf[BUFSIZE];
|
||||
int ret;
|
||||
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.next_in = reinterpret_cast<unsigned char*>(data.data());
|
||||
strm.avail_in = data.length();
|
||||
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
|
||||
strm.avail_out = BUFSIZE;
|
||||
|
||||
//windowBits = 15+16 to enable gzip
|
||||
//From the zlib manual: windowBits can also be greater than 15 for optional gzip encoding. Add 16 to windowBits
|
||||
//to write a simple gzip header and trailer around the compressed data instead of a zlib wrapper.
|
||||
ret = deflateInit2(&strm, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
while (strm.avail_in != 0) {
|
||||
ret = deflate(&strm, Z_NO_FLUSH);
|
||||
if (ret != Z_OK)
|
||||
return false;
|
||||
|
||||
if (strm.avail_out == 0) {
|
||||
dest_buffer.append(tmp_buf, BUFSIZE);
|
||||
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
|
||||
strm.avail_out = BUFSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
int deflate_res = Z_OK;
|
||||
while (deflate_res == Z_OK) {
|
||||
if (strm.avail_out == 0) {
|
||||
dest_buffer.append(tmp_buf, BUFSIZE);
|
||||
strm.next_out = reinterpret_cast<unsigned char*>(tmp_buf);
|
||||
strm.avail_out = BUFSIZE;
|
||||
}
|
||||
|
||||
deflate_res = deflate(&strm, Z_FINISH);
|
||||
}
|
||||
|
||||
if (deflate_res != Z_STREAM_END)
|
||||
return false;
|
||||
|
||||
dest_buffer.append(tmp_buf, BUFSIZE - strm.avail_out);
|
||||
deflateEnd(&strm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -37,13 +37,11 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
|
||||
class ResponseGenerator
|
||||
{
|
||||
public:
|
||||
static QByteArray generate(Response response);
|
||||
};
|
||||
|
||||
class ResponseGenerator
|
||||
{
|
||||
public:
|
||||
static QByteArray generate(Response response);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_RESPONSEGENERATOR_H
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
using namespace Http;
|
||||
|
||||
Server::Server(IRequestHandler *requestHandler, QObject* parent)
|
||||
Server::Server(IRequestHandler *requestHandler, QObject *parent)
|
||||
: QTcpServer(parent)
|
||||
, m_requestHandler(requestHandler)
|
||||
#ifndef QT_NO_OPENSSL
|
||||
|
||||
@@ -41,40 +41,38 @@
|
||||
|
||||
namespace Http
|
||||
{
|
||||
class IRequestHandler;
|
||||
class Connection;
|
||||
|
||||
class IRequestHandler;
|
||||
class Connection;
|
||||
class Server : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Server)
|
||||
|
||||
class Server : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(Server)
|
||||
public:
|
||||
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Server();
|
||||
|
||||
public:
|
||||
Server(IRequestHandler *requestHandler, QObject *parent = 0);
|
||||
~Server();
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||
void disableHttps();
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void enableHttps(const QSslCertificate &certificate, const QSslKey &key);
|
||||
void disableHttps();
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
void incomingConnection(qintptr socketDescriptor);
|
||||
#else
|
||||
void incomingConnection(int socketDescriptor);
|
||||
#endif
|
||||
|
||||
private:
|
||||
IRequestHandler *m_requestHandler;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
bool m_https;
|
||||
QSslCertificate m_certificate;
|
||||
QSslKey m_key;
|
||||
#endif
|
||||
};
|
||||
private:
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
void incomingConnection(qintptr socketDescriptor);
|
||||
#else
|
||||
void incomingConnection(int socketDescriptor);
|
||||
#endif
|
||||
|
||||
private:
|
||||
IRequestHandler *m_requestHandler;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
bool m_https;
|
||||
QSslCertificate m_certificate;
|
||||
QSslKey m_key;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_SERVER_H
|
||||
|
||||
@@ -37,60 +37,58 @@ typedef QMap<QString, QString> QStringMap;
|
||||
|
||||
namespace Http
|
||||
{
|
||||
const QString HEADER_SET_COOKIE = "Set-Cookie";
|
||||
const QString HEADER_CONTENT_TYPE = "Content-Type";
|
||||
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||
const QString HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
const QString HEADER_CACHE_CONTROL = "Cache-Control";
|
||||
|
||||
const QString HEADER_SET_COOKIE = "Set-Cookie";
|
||||
const QString HEADER_CONTENT_TYPE = "Content-Type";
|
||||
const QString HEADER_CONTENT_ENCODING = "Content-Encoding";
|
||||
const QString HEADER_CONTENT_LENGTH = "Content-Length";
|
||||
const QString HEADER_CACHE_CONTROL = "Cache-Control";
|
||||
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_GIF = "image/gif";
|
||||
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_JS = "application/javascript; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_JSON = "application/json";
|
||||
const QString CONTENT_TYPE_PNG = "image/png";
|
||||
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
|
||||
|
||||
const QString CONTENT_TYPE_CSS = "text/css; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_GIF = "image/gif";
|
||||
const QString CONTENT_TYPE_HTML = "text/html; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_JS = "application/javascript; charset=UTF-8";
|
||||
const QString CONTENT_TYPE_JSON = "application/json";
|
||||
const QString CONTENT_TYPE_PNG = "image/png";
|
||||
const QString CONTENT_TYPE_TXT = "text/plain; charset=UTF-8";
|
||||
struct Environment
|
||||
{
|
||||
QHostAddress clientAddress;
|
||||
};
|
||||
|
||||
struct Environment
|
||||
{
|
||||
QHostAddress clientAddress;
|
||||
};
|
||||
struct UploadedFile
|
||||
{
|
||||
QString filename; // original filename
|
||||
QString type; // MIME type
|
||||
QByteArray data; // File data
|
||||
};
|
||||
|
||||
struct UploadedFile
|
||||
{
|
||||
QString filename; // original filename
|
||||
QString type; // MIME type
|
||||
QByteArray data; // File data
|
||||
};
|
||||
struct Request
|
||||
{
|
||||
QString method;
|
||||
QString path;
|
||||
QStringMap headers;
|
||||
QStringMap gets;
|
||||
QStringMap posts;
|
||||
QMap<QString, UploadedFile> files;
|
||||
};
|
||||
|
||||
struct Request
|
||||
{
|
||||
QString method;
|
||||
QString path;
|
||||
QStringMap headers;
|
||||
QStringMap gets;
|
||||
QStringMap posts;
|
||||
QMap<QString, UploadedFile> files;
|
||||
};
|
||||
struct ResponseStatus
|
||||
{
|
||||
uint code;
|
||||
QString text;
|
||||
|
||||
struct ResponseStatus
|
||||
{
|
||||
uint code;
|
||||
QString text;
|
||||
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||
};
|
||||
|
||||
ResponseStatus(uint code = 200, const QString& text = "OK"): code(code), text(text) {}
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
ResponseStatus status;
|
||||
QStringMap headers;
|
||||
QByteArray content;
|
||||
|
||||
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
||||
};
|
||||
struct Response
|
||||
{
|
||||
ResponseStatus status;
|
||||
QStringMap headers;
|
||||
QByteArray content;
|
||||
|
||||
Response(uint code = 200, const QString& text = "OK"): status(code, text) {}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // HTTP_TYPES_H
|
||||
|
||||
64
src/core/iconprovider.cpp
Normal file
64
src/core/iconprovider.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
#include "iconprovider.h"
|
||||
|
||||
IconProvider::IconProvider(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
IconProvider::~IconProvider() {}
|
||||
|
||||
void IconProvider::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new IconProvider;
|
||||
}
|
||||
|
||||
void IconProvider::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
m_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
IconProvider *IconProvider::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
QString IconProvider::getIconPath(const QString &iconId)
|
||||
{
|
||||
return ":/icons/oxygen/" + iconId + ".png";
|
||||
}
|
||||
|
||||
IconProvider *IconProvider::m_instance = 0;
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2011 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -24,40 +25,31 @@
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef ICONPROVIDER_H
|
||||
#define ICONPROVIDER_H
|
||||
|
||||
#include <QIcon>
|
||||
#include <QString>
|
||||
#include <QObject>
|
||||
|
||||
class IconProvider
|
||||
class QString;
|
||||
|
||||
class IconProvider : public QObject
|
||||
{
|
||||
Q_DISABLE_COPY(IconProvider)
|
||||
|
||||
private:
|
||||
explicit IconProvider();
|
||||
static IconProvider* m_instance;
|
||||
Q_DISABLE_COPY(IconProvider)
|
||||
|
||||
public:
|
||||
static IconProvider* instance();
|
||||
static void drop();
|
||||
QIcon getIcon(const QString& iconId);
|
||||
QString getIconPath(const QString& iconId);
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static IconProvider *instance();
|
||||
|
||||
#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC))
|
||||
public:
|
||||
void useSystemIconTheme(bool enable);
|
||||
virtual QString getIconPath(const QString &iconId);
|
||||
|
||||
private:
|
||||
QIcon generateDifferentSizes(const QIcon& icon);
|
||||
protected:
|
||||
explicit IconProvider(QObject *parent = 0);
|
||||
~IconProvider();
|
||||
|
||||
private:
|
||||
bool m_useSystemTheme;
|
||||
#endif
|
||||
static IconProvider *m_instance;
|
||||
};
|
||||
|
||||
#endif // ICONPROVIDER_H
|
||||
@@ -16,18 +16,12 @@ namespace Log
|
||||
|
||||
Peer::Peer() {}
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
Peer::Peer(int id, const QString &ip, bool blocked)
|
||||
#else
|
||||
Peer::Peer(int id, const QString &ip, bool blocked, const QString &reason)
|
||||
#endif
|
||||
: id(id)
|
||||
, timestamp(QDateTime::currentMSecsSinceEpoch())
|
||||
, ip(ip)
|
||||
, blocked(blocked)
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
, reason(reason)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,15 +38,18 @@ Logger::Logger()
|
||||
|
||||
Logger::~Logger() {}
|
||||
|
||||
Logger * Logger::instance()
|
||||
Logger *Logger::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new Logger;
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void Logger::drop()
|
||||
void Logger::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new Logger;
|
||||
}
|
||||
|
||||
void Logger::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
@@ -73,19 +70,11 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
||||
emit newLogMessage(temp);
|
||||
}
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
void Logger::addPeer(const QString &ip, bool blocked)
|
||||
#else
|
||||
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
||||
#endif
|
||||
{
|
||||
QWriteLocker locker(&lock);
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
Log::Peer temp(peerCounter++, ip, blocked);
|
||||
#else
|
||||
Log::Peer temp(peerCounter++, ip, blocked, reason);
|
||||
#endif
|
||||
m_peers.push_back(temp);
|
||||
|
||||
if (m_peers.size() >= MAX_LOG_MESSAGES)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <QVector>
|
||||
#include <QReadWriteLock>
|
||||
#include <QObject>
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
const int MAX_LOG_MESSAGES = 1000;
|
||||
|
||||
@@ -31,19 +30,13 @@ namespace Log
|
||||
|
||||
struct Peer
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
Peer(int id, const QString &ip, bool blocked);
|
||||
#else
|
||||
Peer(int id, const QString &ip, bool blocked, const QString &reason);
|
||||
#endif
|
||||
Peer();
|
||||
int id;
|
||||
qint64 timestamp;
|
||||
QString ip;
|
||||
bool blocked;
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
QString reason;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
@@ -53,16 +46,12 @@ class Logger : public QObject
|
||||
Q_DISABLE_COPY(Logger)
|
||||
|
||||
public:
|
||||
static Logger* instance();
|
||||
static void drop();
|
||||
~Logger();
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static Logger *instance();
|
||||
|
||||
void addMessage(const QString &message, const Log::MsgType &type = Log::NORMAL);
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
void addPeer(const QString &ip, bool blocked);
|
||||
#else
|
||||
void addPeer(const QString &ip, bool blocked, const QString &reason = QString());
|
||||
#endif
|
||||
QVector<Log::Msg> getMessages(int lastKnownId = -1) const;
|
||||
QVector<Log::Peer> getPeers(int lastKnownId = -1) const;
|
||||
|
||||
@@ -72,6 +61,8 @@ signals:
|
||||
|
||||
private:
|
||||
Logger();
|
||||
~Logger();
|
||||
|
||||
static Logger* m_instance;
|
||||
QVector<Log::Msg> m_messages;
|
||||
QVector<Log::Peer> m_peers;
|
||||
|
||||
120
src/core/misc.h
120
src/core/misc.h
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef MISC_H
|
||||
#define MISC_H
|
||||
|
||||
#include <vector>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <ctime>
|
||||
#include <QPoint>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
#ifndef DISABLE_GUI
|
||||
#include <QIcon>
|
||||
#endif
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <libtorrent/error_code.hpp>
|
||||
|
||||
namespace libtorrent {
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
class big_number;
|
||||
typedef big_number sha1_hash;
|
||||
#else
|
||||
class sha1_hash;
|
||||
#endif
|
||||
struct lazy_entry;
|
||||
}
|
||||
|
||||
const qlonglong MAX_ETA = 8640000;
|
||||
enum shutDownAction { NO_SHUTDOWN, SHUTDOWN_COMPUTER, SUSPEND_COMPUTER, HIBERNATE_COMPUTER };
|
||||
|
||||
/* Miscellaneaous functions that can be useful */
|
||||
namespace misc
|
||||
{
|
||||
QString toQString(const std::string &str);
|
||||
QString toQString(const char* str);
|
||||
QString toQString(time_t t, Qt::DateFormat f = Qt::DefaultLocaleLongDate);
|
||||
QString toQStringU(const std::string &str);
|
||||
QString toQStringU(const char* str);
|
||||
QString toQString(const libtorrent::sha1_hash &hash);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
void shutdownComputer(shutDownAction action = SHUTDOWN_COMPUTER);
|
||||
#endif
|
||||
|
||||
QString parseHtmlLinks(const QString &raw_text);
|
||||
|
||||
bool isUrl(const QString &s);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
// Get screen center
|
||||
QPoint screenCenter(QWidget *win);
|
||||
#endif
|
||||
int pythonVersion();
|
||||
QString pythonExecutable();
|
||||
QString pythonVersionComplete();
|
||||
// return best userfriendly storage unit (B, KiB, MiB, GiB, TiB)
|
||||
// use Binary prefix standards from IEC 60027-2
|
||||
// see http://en.wikipedia.org/wiki/Kilobyte
|
||||
// value must be given in bytes
|
||||
QString friendlyUnit(qreal val, bool is_speed = false);
|
||||
bool isPreviewable(const QString& extension);
|
||||
QString magnetUriToName(const QString& magnet_uri);
|
||||
QString magnetUriToHash(const QString& magnet_uri);
|
||||
QString bcLinkToMagnet(QString bc_link);
|
||||
// Take a number of seconds and return an user-friendly
|
||||
// time duration like "1d 2h 10m".
|
||||
QString userFriendlyDuration(qlonglong seconds);
|
||||
QString getUserIDString();
|
||||
|
||||
// Convert functions
|
||||
QStringList toStringList(const QList<bool> &l);
|
||||
QList<int> intListfromStringList(const QStringList &l);
|
||||
QList<bool> boolListfromStringList(const QStringList &l);
|
||||
|
||||
QString accurateDoubleToString(const double &n, const int &precision);
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
bool naturalSort(QString left, QString right, bool& result);
|
||||
#endif
|
||||
|
||||
// Implements constant-time comparison to protect against timing attacks
|
||||
// Taken from https://crackstation.net/hashing-security.htm
|
||||
bool slowEquals(const QByteArray &a, const QByteArray &b);
|
||||
void loadBencodedFile(const QString &filename, std::vector<char> &buffer, libtorrent::lazy_entry &entry, libtorrent::error_code &ec);
|
||||
|
||||
void msleep(unsigned long msecs);
|
||||
}
|
||||
|
||||
#endif
|
||||
305
src/core/net/dnsupdater.cpp
Normal file
305
src/core/net/dnsupdater.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QDebug>
|
||||
#include <QRegExp>
|
||||
#include <QStringList>
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
#include <QUrlQuery>
|
||||
#endif
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "dnsupdater.h"
|
||||
|
||||
using namespace Net;
|
||||
|
||||
DNSUpdater::DNSUpdater(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_state(OK)
|
||||
, m_service(DNS::NONE)
|
||||
{
|
||||
updateCredentials();
|
||||
|
||||
// Load saved settings from previous session
|
||||
const Preferences *const pref = Preferences::instance();
|
||||
m_lastIPCheckTime = pref->getDNSLastUpd();
|
||||
m_lastIP = QHostAddress(pref->getDNSLastIP());
|
||||
|
||||
// Start IP checking timer
|
||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
||||
m_ipCheckTimer.start();
|
||||
|
||||
// Check lastUpdate to avoid flooding
|
||||
if (!m_lastIPCheckTime.isValid()
|
||||
|| (m_lastIPCheckTime.secsTo(QDateTime::currentDateTime()) * 1000 > IP_CHECK_INTERVAL_MS)) {
|
||||
checkPublicIP();
|
||||
}
|
||||
}
|
||||
|
||||
DNSUpdater::~DNSUpdater()
|
||||
{
|
||||
// Save lastupdate time and last ip
|
||||
Preferences *const pref = Preferences::instance();
|
||||
pref->setDNSLastUpd(m_lastIPCheckTime);
|
||||
pref->setDNSLastIP(m_lastIP.toString());
|
||||
}
|
||||
|
||||
void DNSUpdater::checkPublicIP()
|
||||
{
|
||||
Q_ASSERT(m_state == OK);
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipRequestFinished(QNetworkReply *)));
|
||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||
QNetworkRequest request;
|
||||
request.setUrl(QUrl("http://checkip.dyndns.org"));
|
||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION);
|
||||
manager->get(request);
|
||||
}
|
||||
|
||||
void DNSUpdater::ipRequestFinished(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
if (reply->error()) {
|
||||
// Error
|
||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||
}
|
||||
else {
|
||||
// Parse response
|
||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
||||
QString ret = reply->readAll();
|
||||
if (ipregex.indexIn(ret) >= 0) {
|
||||
QString ip_str = ipregex.cap(1);
|
||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ip_str;
|
||||
QHostAddress new_ip(ip_str);
|
||||
if (!new_ip.isNull()) {
|
||||
if (m_lastIP != new_ip) {
|
||||
qDebug() << Q_FUNC_INFO << "The IP address changed, report the change to DynDNS...";
|
||||
qDebug() << m_lastIP.toString() << "->" << new_ip.toString();
|
||||
m_lastIP = new_ip;
|
||||
updateDNSService();
|
||||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << Q_FUNC_INFO << "Failed to construct a QHostAddress from the IP string";
|
||||
}
|
||||
}
|
||||
else {
|
||||
qWarning() << Q_FUNC_INFO << "Regular expression failed to capture the IP address";
|
||||
}
|
||||
}
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void DNSUpdater::updateDNSService()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// Prepare request
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
|
||||
connect(manager, SIGNAL(finished(QNetworkReply *)), SLOT(ipUpdateFinished(QNetworkReply *)));
|
||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||
QNetworkRequest request;
|
||||
request.setUrl(getUpdateUrl());
|
||||
request.setRawHeader("User-Agent", "qBittorrent/" VERSION);
|
||||
manager->get(request);
|
||||
}
|
||||
|
||||
QUrl DNSUpdater::getUpdateUrl() const
|
||||
{
|
||||
QUrl url;
|
||||
#ifdef QT_NO_OPENSSL
|
||||
url.setScheme("http");
|
||||
#else
|
||||
url.setScheme("https");
|
||||
#endif
|
||||
url.setUserName(m_username);
|
||||
url.setPassword(m_password);
|
||||
|
||||
Q_ASSERT(!m_lastIP.isNull());
|
||||
// Service specific
|
||||
switch(m_service) {
|
||||
case DNS::DYNDNS:
|
||||
url.setHost("members.dyndns.org");
|
||||
break;
|
||||
case DNS::NOIP:
|
||||
url.setHost("dynupdate.no-ip.com");
|
||||
break;
|
||||
default:
|
||||
qWarning() << "Unrecognized Dynamic DNS service!";
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
url.setPath("/nic/update");
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
url.addQueryItem("hostname", m_domain);
|
||||
url.addQueryItem("myip", m_lastIP.toString());
|
||||
#else
|
||||
QUrlQuery urlQuery(url);
|
||||
urlQuery.addQueryItem("hostname", m_domain);
|
||||
urlQuery.addQueryItem("myip", m_lastIP.toString());
|
||||
url.setQuery(urlQuery);
|
||||
#endif
|
||||
Q_ASSERT(url.isValid());
|
||||
|
||||
qDebug() << Q_FUNC_INFO << url.toString();
|
||||
return url;
|
||||
}
|
||||
|
||||
void DNSUpdater::ipUpdateFinished(QNetworkReply *reply)
|
||||
{
|
||||
if (reply->error()) {
|
||||
// Error
|
||||
qWarning() << Q_FUNC_INFO << "Error:" << reply->errorString();
|
||||
}
|
||||
else {
|
||||
// Parse reply
|
||||
processIPUpdateReply(reply->readAll());
|
||||
}
|
||||
// Clean up
|
||||
reply->deleteLater();
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
||||
void DNSUpdater::processIPUpdateReply(const QString &reply)
|
||||
{
|
||||
Logger *const logger = Logger::instance();
|
||||
qDebug() << Q_FUNC_INFO << reply;
|
||||
QString code = reply.split(" ").first();
|
||||
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
||||
if (code == "good" || code == "nochg") {
|
||||
logger->addMessage(tr("Your dynamic DNS was successfully updated."), Log::INFO);
|
||||
return;
|
||||
}
|
||||
if ((code == "911") || (code == "dnserr")) {
|
||||
logger->addMessage(tr("Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
// It will retry in 30 minutes because the timer was not stopped
|
||||
return;
|
||||
}
|
||||
// Everything bellow is an error, stop updating until the user updates something
|
||||
m_ipCheckTimer.stop();
|
||||
m_lastIP.clear();
|
||||
if (code == "nohost") {
|
||||
logger->addMessage(tr("Dynamic DNS error: hostname supplied does not exist under specified account."), Log::CRITICAL);
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
if (code == "badauth") {
|
||||
logger->addMessage(tr("Dynamic DNS error: Invalid username/password."), Log::CRITICAL);
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
if (code == "badagent") {
|
||||
logger->addMessage(tr("Dynamic DNS error: qBittorrent was blacklisted by the service, please report a bug at http://bugs.qbittorrent.org."),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
if (code == "!donator") {
|
||||
logger->addMessage(tr("Dynamic DNS error: %1 was returned by the service, please report a bug at http://bugs.qbittorrent.org.").arg("!donator"),
|
||||
Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
if (code == "abuse") {
|
||||
logger->addMessage(tr("Dynamic DNS error: Your username was blocked due to abuse."), Log::CRITICAL);
|
||||
m_state = FATAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void DNSUpdater::updateCredentials()
|
||||
{
|
||||
if (m_state == FATAL) return;
|
||||
Preferences *const pref = Preferences::instance();
|
||||
Logger *const logger = Logger::instance();
|
||||
bool change = false;
|
||||
// Get DNS service information
|
||||
if (m_service != pref->getDynDNSService()) {
|
||||
m_service = pref->getDynDNSService();
|
||||
change = true;
|
||||
}
|
||||
if (m_domain != pref->getDynDomainName()) {
|
||||
m_domain = pref->getDynDomainName();
|
||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
||||
if (domain_regex.indexIn(m_domain) < 0) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
if (m_username != pref->getDynDNSUsername()) {
|
||||
m_username = pref->getDynDNSUsername();
|
||||
if (m_username.length() < 4) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied username is too short."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
if (m_password != pref->getDynDNSPassword()) {
|
||||
m_password = pref->getDynDNSPassword();
|
||||
if (m_password.length() < 4) {
|
||||
logger->addMessage(tr("Dynamic DNS error: supplied password is too short."), Log::CRITICAL);
|
||||
m_lastIP.clear();
|
||||
m_ipCheckTimer.stop();
|
||||
m_state = INVALID_CREDS;
|
||||
return;
|
||||
}
|
||||
change = true;
|
||||
}
|
||||
|
||||
if ((m_state == INVALID_CREDS) && change) {
|
||||
m_state = OK; // Try again
|
||||
m_ipCheckTimer.start();
|
||||
checkPublicIP();
|
||||
}
|
||||
}
|
||||
|
||||
QUrl DNSUpdater::getRegistrationUrl(int service)
|
||||
{
|
||||
switch(service) {
|
||||
case DNS::DYNDNS:
|
||||
return QUrl("https://www.dyndns.com/account/services/hosts/add.html");
|
||||
case DNS::NOIP:
|
||||
return QUrl("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html");
|
||||
default:
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
return QUrl();
|
||||
}
|
||||
@@ -36,46 +36,55 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include "preferences.h"
|
||||
#include "core/preferences.h"
|
||||
|
||||
/*!
|
||||
* Based on http://www.dyndns.com/developers/specs/
|
||||
*/
|
||||
class DNSUpdater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DNSUpdater(QObject *parent = 0);
|
||||
~DNSUpdater();
|
||||
static QUrl getRegistrationUrl(int service);
|
||||
namespace Net
|
||||
{
|
||||
// Based on http://www.dyndns.com/developers/specs/
|
||||
class DNSUpdater : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void updateCredentials();
|
||||
public:
|
||||
explicit DNSUpdater(QObject *parent = 0);
|
||||
~DNSUpdater();
|
||||
|
||||
private slots:
|
||||
void checkPublicIP();
|
||||
void ipRequestFinished(QNetworkReply* reply);
|
||||
void updateDNSService();
|
||||
void ipUpdateFinished(QNetworkReply* reply);
|
||||
static QUrl getRegistrationUrl(int service);
|
||||
|
||||
private:
|
||||
QUrl getUpdateUrl() const;
|
||||
void processIPUpdateReply(const QString &reply);
|
||||
public slots:
|
||||
void updateCredentials();
|
||||
|
||||
private:
|
||||
QHostAddress m_lastIP;
|
||||
QDateTime m_lastIPCheckTime;
|
||||
QTimer m_ipCheckTimer;
|
||||
int m_state;
|
||||
// Service creds
|
||||
DNS::Service m_service;
|
||||
QString m_domain;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
private slots:
|
||||
void checkPublicIP();
|
||||
void ipRequestFinished(QNetworkReply *reply);
|
||||
void updateDNSService();
|
||||
void ipUpdateFinished(QNetworkReply *reply);
|
||||
|
||||
private:
|
||||
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
||||
enum State { OK, INVALID_CREDS, FATAL };
|
||||
};
|
||||
private:
|
||||
QUrl getUpdateUrl() const;
|
||||
void processIPUpdateReply(const QString &reply);
|
||||
|
||||
private:
|
||||
QHostAddress m_lastIP;
|
||||
QDateTime m_lastIPCheckTime;
|
||||
QTimer m_ipCheckTimer;
|
||||
int m_state;
|
||||
// Service creds
|
||||
DNS::Service m_service;
|
||||
QString m_domain;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
|
||||
private:
|
||||
static const int IP_CHECK_INTERVAL_MS = 1800000; // 30 min
|
||||
|
||||
enum State
|
||||
{
|
||||
OK,
|
||||
INVALID_CREDS,
|
||||
FATAL
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DNSUPDATER_H
|
||||
248
src/core/net/downloadhandler.cpp
Normal file
248
src/core/net/downloadhandler.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QTemporaryFile>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookie>
|
||||
#include <QUrl>
|
||||
#include <QDebug>
|
||||
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/gzip.h"
|
||||
#include "core/utils/misc.h"
|
||||
#include "downloadmanager.h"
|
||||
#include "downloadhandler.h"
|
||||
|
||||
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||
|
||||
using namespace Net;
|
||||
|
||||
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
|
||||
: QObject(manager)
|
||||
, m_reply(reply)
|
||||
, m_manager(manager)
|
||||
, m_saveToFile(saveToFile)
|
||||
, m_sizeLimit(limit)
|
||||
, m_handleRedirectToMagnet(handleRedirectToMagnet)
|
||||
, m_url(reply->url().toString())
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
DownloadHandler::~DownloadHandler()
|
||||
{
|
||||
if (m_reply)
|
||||
delete m_reply;
|
||||
}
|
||||
|
||||
// Returns original url
|
||||
QString DownloadHandler::url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
void DownloadHandler::processFinishedDownload()
|
||||
{
|
||||
QString url = m_reply->url().toString();
|
||||
qDebug("Download finished: %s", qPrintable(url));
|
||||
// Check if the request was successful
|
||||
if (m_reply->error() != QNetworkReply::NoError) {
|
||||
// Failure
|
||||
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(m_reply->error())));
|
||||
emit downloadFailed(m_url, errorCodeToString(m_reply->error()));
|
||||
this->deleteLater();
|
||||
}
|
||||
else {
|
||||
// Check if the server ask us to redirect somewhere else
|
||||
const QVariant redirection = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (redirection.isValid()) {
|
||||
// We should redirect
|
||||
handleRedirection(redirection.toUrl());
|
||||
}
|
||||
else {
|
||||
// Success
|
||||
QByteArray replyData = m_reply->readAll();
|
||||
if (m_reply->rawHeader("Content-Encoding") == "gzip") {
|
||||
// uncompress gzip reply
|
||||
Utils::Gzip::uncompress(replyData, replyData);
|
||||
}
|
||||
|
||||
if (m_saveToFile) {
|
||||
QString filePath;
|
||||
if (saveToFile(replyData, filePath))
|
||||
emit downloadFinished(m_url, filePath);
|
||||
else
|
||||
emit downloadFailed(m_url, tr("I/O Error"));
|
||||
}
|
||||
else {
|
||||
emit downloadFinished(m_url, replyData);
|
||||
}
|
||||
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
||||
|
||||
if (bytesTotal > 0) {
|
||||
// Total number of bytes is available
|
||||
if (bytesTotal > m_sizeLimit) {
|
||||
m_reply->abort();
|
||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
||||
}
|
||||
else {
|
||||
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
||||
}
|
||||
}
|
||||
else if (bytesReceived > m_sizeLimit) {
|
||||
m_reply->abort();
|
||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
||||
}
|
||||
}
|
||||
|
||||
void DownloadHandler::init()
|
||||
{
|
||||
m_reply->setParent(this);
|
||||
if (m_sizeLimit > 0)
|
||||
connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
||||
connect(m_reply, SIGNAL(finished()), this, SLOT(processFinishedDownload()));
|
||||
}
|
||||
|
||||
bool DownloadHandler::saveToFile(const QByteArray &replyData, QString &filePath)
|
||||
{
|
||||
QTemporaryFile *tmpfile = new QTemporaryFile;
|
||||
if (!tmpfile->open()) {
|
||||
delete tmpfile;
|
||||
return false;
|
||||
}
|
||||
|
||||
tmpfile->setAutoRemove(false);
|
||||
filePath = tmpfile->fileName();
|
||||
qDebug("Temporary filename is: %s", qPrintable(filePath));
|
||||
if (m_reply->isOpen() || m_reply->open(QIODevice::ReadOnly)) {
|
||||
tmpfile->write(replyData);
|
||||
tmpfile->close();
|
||||
// XXX: tmpfile needs to be deleted on Windows before using the file
|
||||
// or it will complain that the file is used by another process.
|
||||
delete tmpfile;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
delete tmpfile;
|
||||
Utils::Fs::forceRemove(filePath);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DownloadHandler::handleRedirection(QUrl newUrl)
|
||||
{
|
||||
// Resolve relative urls
|
||||
if (newUrl.isRelative())
|
||||
newUrl = m_reply->url().resolved(newUrl);
|
||||
|
||||
const QString newUrlString = newUrl.toString();
|
||||
qDebug("Redirecting from %s to %s", qPrintable(m_reply->url().toString()), qPrintable(newUrlString));
|
||||
|
||||
// Redirect to magnet workaround
|
||||
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||
qDebug("Magnet redirect detected.");
|
||||
m_reply->abort();
|
||||
if (m_handleRedirectToMagnet)
|
||||
emit redirectedToMagnet(m_url, newUrlString);
|
||||
else
|
||||
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
|
||||
|
||||
this->deleteLater();
|
||||
}
|
||||
else {
|
||||
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet);
|
||||
m_reply->deleteLater();
|
||||
m_reply = tmp->m_reply;
|
||||
init();
|
||||
tmp->m_reply = 0;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
QString errorCodeToString(QNetworkReply::NetworkError status)
|
||||
{
|
||||
switch(status) {
|
||||
case QNetworkReply::HostNotFoundError:
|
||||
return QObject::tr("The remote host name was not found (invalid hostname)");
|
||||
case QNetworkReply::OperationCanceledError:
|
||||
return QObject::tr("The operation was canceled");
|
||||
case QNetworkReply::RemoteHostClosedError:
|
||||
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
||||
case QNetworkReply::TimeoutError:
|
||||
return QObject::tr("The connection to the remote server timed out");
|
||||
case QNetworkReply::SslHandshakeFailedError:
|
||||
return QObject::tr("SSL/TLS handshake failed");
|
||||
case QNetworkReply::ConnectionRefusedError:
|
||||
return QObject::tr("The remote server refused the connection");
|
||||
case QNetworkReply::ProxyConnectionRefusedError:
|
||||
return QObject::tr("The connection to the proxy server was refused");
|
||||
case QNetworkReply::ProxyConnectionClosedError:
|
||||
return QObject::tr("The proxy server closed the connection prematurely");
|
||||
case QNetworkReply::ProxyNotFoundError:
|
||||
return QObject::tr("The proxy host name was not found");
|
||||
case QNetworkReply::ProxyTimeoutError:
|
||||
return QObject::tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||
return QObject::tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
|
||||
case QNetworkReply::ContentAccessDenied:
|
||||
return QObject::tr("The access to the remote content was denied (401)");
|
||||
case QNetworkReply::ContentOperationNotPermittedError:
|
||||
return QObject::tr("The operation requested on the remote content is not permitted");
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
return QObject::tr("The remote content was not found at the server (404)");
|
||||
case QNetworkReply::AuthenticationRequiredError:
|
||||
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
||||
case QNetworkReply::ProtocolUnknownError:
|
||||
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known");
|
||||
case QNetworkReply::ProtocolInvalidOperationError:
|
||||
return QObject::tr("The requested operation is invalid for this protocol");
|
||||
case QNetworkReply::UnknownNetworkError:
|
||||
return QObject::tr("An unknown network-related error was detected");
|
||||
case QNetworkReply::UnknownProxyError:
|
||||
return QObject::tr("An unknown proxy-related error was detected");
|
||||
case QNetworkReply::UnknownContentError:
|
||||
return QObject::tr("An unknown error related to the remote content was detected");
|
||||
case QNetworkReply::ProtocolFailure:
|
||||
return QObject::tr("A breakdown in protocol was detected");
|
||||
default:
|
||||
return QObject::tr("Unknown error");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -24,54 +25,55 @@
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOADTHREAD_H
|
||||
#define DOWNLOADTHREAD_H
|
||||
#ifndef NET_DOWNLOADHANDLER_H
|
||||
#define NET_DOWNLOADHANDLER_H
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkCookie>
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QSslError>
|
||||
#include <zlib.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class DownloadThread : public QObject {
|
||||
Q_OBJECT
|
||||
namespace Net
|
||||
{
|
||||
class DownloadManager;
|
||||
|
||||
public:
|
||||
DownloadThread(QObject* parent = 0);
|
||||
QNetworkReply* downloadUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
||||
void downloadTorrentUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
||||
//void setProxy(QString IP, int port, QString username, QString password);
|
||||
class DownloadHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
signals:
|
||||
void downloadFinished(const QString &url, const QString &file_path);
|
||||
void downloadFailure(const QString &url, const QString &reason);
|
||||
void magnetRedirect(const QString &url_new, const QString &url_old);
|
||||
public:
|
||||
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
|
||||
~DownloadHandler();
|
||||
|
||||
private slots:
|
||||
void processDlFinished(QNetworkReply* reply);
|
||||
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void ignoreSslErrors(QNetworkReply*,const QList<QSslError>&);
|
||||
#endif
|
||||
QString url() const;
|
||||
|
||||
private:
|
||||
static QByteArray gUncompress(Bytef *inData, size_t len);
|
||||
QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||
void applyProxySettings();
|
||||
signals:
|
||||
void downloadFinished(const QString &url, const QByteArray &data);
|
||||
void downloadFinished(const QString &url, const QString &filePath);
|
||||
void downloadFailed(const QString &url, const QString &reason);
|
||||
void redirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager m_networkManager;
|
||||
QHash<QString, QString> m_redirectMapping;
|
||||
private slots:
|
||||
void processFinishedDownload();
|
||||
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||
|
||||
};
|
||||
private:
|
||||
void init();
|
||||
bool saveToFile(const QByteArray &replyData, QString &filePath);
|
||||
void handleRedirection(QUrl newUrl);
|
||||
|
||||
#endif
|
||||
QNetworkReply *m_reply;
|
||||
DownloadManager *m_manager;
|
||||
bool m_saveToFile;
|
||||
qint64 m_sizeLimit;
|
||||
bool m_handleRedirectToMagnet;
|
||||
QString m_url;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_DOWNLOADHANDLER_H
|
||||
151
src/core/net/downloadmanager.cpp
Normal file
151
src/core/net/downloadmanager.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkCookie>
|
||||
#include <QSslError>
|
||||
#include <QUrl>
|
||||
#include <QDebug>
|
||||
|
||||
#include "core/preferences.h"
|
||||
#include "downloadhandler.h"
|
||||
#include "downloadmanager.h"
|
||||
|
||||
using namespace Net;
|
||||
|
||||
DownloadManager *DownloadManager::m_instance = 0;
|
||||
|
||||
DownloadManager::DownloadManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
#ifndef QT_NO_OPENSSL
|
||||
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
|
||||
#endif
|
||||
}
|
||||
|
||||
DownloadManager::~DownloadManager()
|
||||
{
|
||||
}
|
||||
|
||||
void DownloadManager::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new DownloadManager;
|
||||
}
|
||||
|
||||
void DownloadManager::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
m_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DownloadManager *DownloadManager::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
|
||||
{
|
||||
// Update proxy settings
|
||||
applyProxySettings();
|
||||
|
||||
// Process download request
|
||||
qDebug("url is %s", qPrintable(url));
|
||||
const QUrl qurl = QUrl::fromEncoded(url.toUtf8());
|
||||
QNetworkRequest request(qurl);
|
||||
|
||||
// Spoof Firefox 38 user agent to avoid web server banning
|
||||
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0");
|
||||
|
||||
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
||||
request.setRawHeader("Referer", request.url().toEncoded().data());
|
||||
|
||||
qDebug("Downloading %s...", request.url().toEncoded().data());
|
||||
// accept gzip
|
||||
request.setRawHeader("Accept-Encoding", "gzip");
|
||||
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QString &url) const
|
||||
{
|
||||
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
||||
}
|
||||
|
||||
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
||||
{
|
||||
qDebug("Setting %d cookies for url: %s", cookieList.size(), qPrintable(url.toString()));
|
||||
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
||||
}
|
||||
|
||||
void DownloadManager::applyProxySettings()
|
||||
{
|
||||
QNetworkProxy proxy;
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
|
||||
if (pref->isProxyEnabled() && !pref->isProxyOnlyForTorrents()) {
|
||||
// Proxy enabled
|
||||
proxy.setHostName(pref->getProxyIp());
|
||||
proxy.setPort(pref->getProxyPort());
|
||||
// Default proxy type is HTTP, we must change if it is SOCKS5
|
||||
const int proxyType = pref->getProxyType();
|
||||
if ((proxyType == Proxy::SOCKS5) || (proxyType == Proxy::SOCKS5_PW)) {
|
||||
qDebug() << Q_FUNC_INFO << "using SOCKS proxy";
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
}
|
||||
else {
|
||||
qDebug() << Q_FUNC_INFO << "using HTTP proxy";
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
}
|
||||
// Authentication?
|
||||
if (pref->isProxyAuthEnabled()) {
|
||||
qDebug("Proxy requires authentication, authenticating");
|
||||
proxy.setUser(pref->getProxyUsername());
|
||||
proxy.setPassword(pref->getProxyPassword());
|
||||
}
|
||||
}
|
||||
else {
|
||||
proxy.setType(QNetworkProxy::NoProxy);
|
||||
}
|
||||
|
||||
m_networkManager.setProxy(proxy);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||
{
|
||||
Q_UNUSED(errors)
|
||||
// Ignore all SSL errors
|
||||
reply->ignoreSslErrors();
|
||||
}
|
||||
#endif
|
||||
76
src/core/net/downloadmanager.h
Normal file
76
src/core/net/downloadmanager.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef NET_DOWNLOADMANAGER_H
|
||||
#define NET_DOWNLOADMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QNetworkReply;
|
||||
class QNetworkCookie;
|
||||
class QSslError;
|
||||
class QUrl;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Net
|
||||
{
|
||||
class DownloadHandler;
|
||||
|
||||
class DownloadManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static DownloadManager *instance();
|
||||
|
||||
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
|
||||
QList<QNetworkCookie> cookiesForUrl(const QString &url) const;
|
||||
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||
|
||||
private slots:
|
||||
#ifndef QT_NO_OPENSSL
|
||||
void ignoreSslErrors(QNetworkReply *,const QList<QSslError> &);
|
||||
#endif
|
||||
|
||||
private:
|
||||
DownloadManager(QObject *parent = 0);
|
||||
~DownloadManager();
|
||||
|
||||
void applyProxySettings();
|
||||
|
||||
static DownloadManager *m_instance;
|
||||
QNetworkAccessManager m_networkManager;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_DOWNLOADMANAGER_H
|
||||
461
src/core/net/geoipmanager.cpp
Normal file
461
src/core/net/geoipmanager.cpp
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QHostAddress>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "core/preferences.h"
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/gzip.h"
|
||||
#include "downloadmanager.h"
|
||||
#include "downloadhandler.h"
|
||||
#include "private/geoipdatabase.h"
|
||||
#include "geoipmanager.h"
|
||||
|
||||
static const char DATABASE_URL[] = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz";
|
||||
static const char GEOIP_FOLDER[] = "GeoIP";
|
||||
static const char GEOIP_FILENAME[] = "GeoLite2-Country.mmdb";
|
||||
static const int CACHE_SIZE = 1000;
|
||||
static const int UPDATE_INTERVAL = 30; // Days between database updates
|
||||
|
||||
using namespace Net;
|
||||
|
||||
// GeoIPManager
|
||||
|
||||
GeoIPManager *GeoIPManager::m_instance = 0;
|
||||
|
||||
GeoIPManager::GeoIPManager()
|
||||
: m_enabled(false)
|
||||
, m_geoIPDatabase(0)
|
||||
{
|
||||
configure();
|
||||
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
||||
}
|
||||
|
||||
GeoIPManager::~GeoIPManager()
|
||||
{
|
||||
if (m_geoIPDatabase)
|
||||
delete m_geoIPDatabase;
|
||||
}
|
||||
|
||||
void GeoIPManager::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new GeoIPManager;
|
||||
}
|
||||
|
||||
void GeoIPManager::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
m_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GeoIPManager *GeoIPManager::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void GeoIPManager::loadDatabase()
|
||||
{
|
||||
if (m_geoIPDatabase) {
|
||||
delete m_geoIPDatabase;
|
||||
m_geoIPDatabase = 0;
|
||||
}
|
||||
|
||||
QString filepath = Utils::Fs::expandPathAbs(
|
||||
QString("%1%2/%3").arg(Utils::Fs::QDesktopServicesDataLocation())
|
||||
.arg(GEOIP_FOLDER).arg(GEOIP_FILENAME));
|
||||
|
||||
QString error;
|
||||
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
||||
if (m_geoIPDatabase)
|
||||
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
||||
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
|
||||
Log::INFO);
|
||||
else
|
||||
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
|
||||
|
||||
manageDatabaseUpdate();
|
||||
}
|
||||
|
||||
void GeoIPManager::manageDatabaseUpdate()
|
||||
{
|
||||
if (!m_geoIPDatabase || (m_geoIPDatabase->buildEpoch().daysTo(QDateTime::currentDateTimeUtc()) >= UPDATE_INTERVAL))
|
||||
downloadDatabaseFile();
|
||||
}
|
||||
|
||||
void GeoIPManager::downloadDatabaseFile()
|
||||
{
|
||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(DATABASE_URL);
|
||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(downloadFinished(QString, QByteArray)));
|
||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(downloadFailed(QString, QString)));
|
||||
}
|
||||
|
||||
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
|
||||
{
|
||||
if (m_enabled && m_geoIPDatabase)
|
||||
return m_geoIPDatabase->lookup(hostAddr);
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
|
||||
QString GeoIPManager::CountryName(const QString &countryISOCode)
|
||||
{
|
||||
static QHash<QString, QString> countries;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
countries[QString()] = tr("N/A");
|
||||
countries["AP"] = tr("Asia/Pacific Region");
|
||||
countries["EU"] = tr("Europe");
|
||||
countries["AD"] = tr("Andorra");
|
||||
countries["AE"] = tr("United Arab Emirates");
|
||||
countries["AF"] = tr("Afghanistan");
|
||||
countries["AG"] = tr("Antigua and Barbuda");
|
||||
countries["AI"] = tr("Anguilla");
|
||||
countries["AL"] = tr("Albania");
|
||||
countries["AM"] = tr("Armenia");
|
||||
countries["AN"] = tr("Netherlands Antilles");
|
||||
countries["AO"] = tr("Angola");
|
||||
countries["AQ"] = tr("Antarctica");
|
||||
countries["AR"] = tr("Argentina");
|
||||
countries["AS"] = tr("American Samoa");
|
||||
countries["AT"] = tr("Austria");
|
||||
countries["AU"] = tr("Australia");
|
||||
countries["AW"] = tr("Aruba");
|
||||
countries["AZ"] = tr("Azerbaijan");
|
||||
countries["BA"] = tr("Bosnia and Herzegovina");
|
||||
countries["BB"] = tr("Barbados");
|
||||
countries["BD"] = tr("Bangladesh");
|
||||
countries["BE"] = tr("Belgium");
|
||||
countries["BF"] = tr("Burkina Faso");
|
||||
countries["BG"] = tr("Bulgaria");
|
||||
countries["BH"] = tr("Bahrain");
|
||||
countries["BI"] = tr("Burundi");
|
||||
countries["BJ"] = tr("Benin");
|
||||
countries["BM"] = tr("Bermuda");
|
||||
countries["BN"] = tr("Brunei Darussalam");
|
||||
countries["BO"] = tr("Bolivia");
|
||||
countries["BR"] = tr("Brazil");
|
||||
countries["BS"] = tr("Bahamas");
|
||||
countries["BT"] = tr("Bhutan");
|
||||
countries["BV"] = tr("Bouvet Island");
|
||||
countries["BW"] = tr("Botswana");
|
||||
countries["BY"] = tr("Belarus");
|
||||
countries["BZ"] = tr("Belize");
|
||||
countries["CA"] = tr("Canada");
|
||||
countries["CC"] = tr("Cocos (Keeling) Islands");
|
||||
countries["CD"] = tr("Congo, The Democratic Republic of the");
|
||||
countries["CF"] = tr("Central African Republic");
|
||||
countries["CG"] = tr("Congo");
|
||||
countries["CH"] = tr("Switzerland");
|
||||
countries["CI"] = tr("Cote D'Ivoire");
|
||||
countries["CK"] = tr("Cook Islands");
|
||||
countries["CL"] = tr("Chile");
|
||||
countries["CM"] = tr("Cameroon");
|
||||
countries["CN"] = tr("China");
|
||||
countries["CO"] = tr("Colombia");
|
||||
countries["CR"] = tr("Costa Rica");
|
||||
countries["CU"] = tr("Cuba");
|
||||
countries["CV"] = tr("Cape Verde");
|
||||
countries["CX"] = tr("Christmas Island");
|
||||
countries["CY"] = tr("Cyprus");
|
||||
countries["CZ"] = tr("Czech Republic");
|
||||
countries["DE"] = tr("Germany");
|
||||
countries["DJ"] = tr("Djibouti");
|
||||
countries["DK"] = tr("Denmark");
|
||||
countries["DM"] = tr("Dominica");
|
||||
countries["DO"] = tr("Dominican Republic");
|
||||
countries["DZ"] = tr("Algeria");
|
||||
countries["EC"] = tr("Ecuador");
|
||||
countries["EE"] = tr("Estonia");
|
||||
countries["EG"] = tr("Egypt");
|
||||
countries["EH"] = tr("Western Sahara");
|
||||
countries["ER"] = tr("Eritrea");
|
||||
countries["ES"] = tr("Spain");
|
||||
countries["ET"] = tr("Ethiopia");
|
||||
countries["FI"] = tr("Finland");
|
||||
countries["FJ"] = tr("Fiji");
|
||||
countries["FK"] = tr("Falkland Islands (Malvinas)");
|
||||
countries["FM"] = tr("Micronesia, Federated States of");
|
||||
countries["FO"] = tr("Faroe Islands");
|
||||
countries["FR"] = tr("France");
|
||||
countries["FX"] = tr("France, Metropolitan");
|
||||
countries["GA"] = tr("Gabon");
|
||||
countries["GB"] = tr("United Kingdom");
|
||||
countries["GD"] = tr("Grenada");
|
||||
countries["GE"] = tr("Georgia");
|
||||
countries["GF"] = tr("French Guiana");
|
||||
countries["GH"] = tr("Ghana");
|
||||
countries["GI"] = tr("Gibraltar");
|
||||
countries["GL"] = tr("Greenland");
|
||||
countries["GM"] = tr("Gambia");
|
||||
countries["GN"] = tr("Guinea");
|
||||
countries["GP"] = tr("Guadeloupe");
|
||||
countries["GQ"] = tr("Equatorial Guinea");
|
||||
countries["GR"] = tr("Greece");
|
||||
countries["GS"] = tr("South Georgia and the South Sandwich Islands");
|
||||
countries["GT"] = tr("Guatemala");
|
||||
countries["GU"] = tr("Guam");
|
||||
countries["GW"] = tr("Guinea-Bissau");
|
||||
countries["GY"] = tr("Guyana");
|
||||
countries["HK"] = tr("Hong Kong");
|
||||
countries["HM"] = tr("Heard Island and McDonald Islands");
|
||||
countries["HN"] = tr("Honduras");
|
||||
countries["HR"] = tr("Croatia");
|
||||
countries["HT"] = tr("Haiti");
|
||||
countries["HU"] = tr("Hungary");
|
||||
countries["ID"] = tr("Indonesia");
|
||||
countries["IE"] = tr("Ireland");
|
||||
countries["IL"] = tr("Israel");
|
||||
countries["IN"] = tr("India");
|
||||
countries["IO"] = tr("British Indian Ocean Territory");
|
||||
countries["IQ"] = tr("Iraq");
|
||||
countries["IR"] = tr("Iran, Islamic Republic of");
|
||||
countries["IS"] = tr("Iceland");
|
||||
countries["IT"] = tr("Italy");
|
||||
countries["JM"] = tr("Jamaica");
|
||||
countries["JO"] = tr("Jordan");
|
||||
countries["JP"] = tr("Japan");
|
||||
countries["KE"] = tr("Kenya");
|
||||
countries["KG"] = tr("Kyrgyzstan");
|
||||
countries["KH"] = tr("Cambodia");
|
||||
countries["KI"] = tr("Kiribati");
|
||||
countries["KM"] = tr("Comoros");
|
||||
countries["KN"] = tr("Saint Kitts and Nevis");
|
||||
countries["KP"] = tr("Korea, Democratic People's Republic of");
|
||||
countries["KR"] = tr("Korea, Republic of");
|
||||
countries["KW"] = tr("Kuwait");
|
||||
countries["KY"] = tr("Cayman Islands");
|
||||
countries["KZ"] = tr("Kazakhstan");
|
||||
countries["LA"] = tr("Lao People's Democratic Republic");
|
||||
countries["LB"] = tr("Lebanon");
|
||||
countries["LC"] = tr("Saint Lucia");
|
||||
countries["LI"] = tr("Liechtenstein");
|
||||
countries["LK"] = tr("Sri Lanka");
|
||||
countries["LR"] = tr("Liberia");
|
||||
countries["LS"] = tr("Lesotho");
|
||||
countries["LT"] = tr("Lithuania");
|
||||
countries["LU"] = tr("Luxembourg");
|
||||
countries["LV"] = tr("Latvia");
|
||||
countries["LY"] = tr("Libyan Arab Jamahiriya");
|
||||
countries["MA"] = tr("Morocco");
|
||||
countries["MC"] = tr("Monaco");
|
||||
countries["MD"] = tr("Moldova, Republic of");
|
||||
countries["MG"] = tr("Madagascar");
|
||||
countries["MH"] = tr("Marshall Islands");
|
||||
countries["MK"] = tr("Macedonia");
|
||||
countries["ML"] = tr("Mali");
|
||||
countries["MM"] = tr("Myanmar");
|
||||
countries["MN"] = tr("Mongolia");
|
||||
countries["MO"] = tr("Macau");
|
||||
countries["MP"] = tr("Northern Mariana Islands");
|
||||
countries["MQ"] = tr("Martinique");
|
||||
countries["MR"] = tr("Mauritania");
|
||||
countries["MS"] = tr("Montserrat");
|
||||
countries["MT"] = tr("Malta");
|
||||
countries["MU"] = tr("Mauritius");
|
||||
countries["MV"] = tr("Maldives");
|
||||
countries["MW"] = tr("Malawi");
|
||||
countries["MX"] = tr("Mexico");
|
||||
countries["MY"] = tr("Malaysia");
|
||||
countries["MZ"] = tr("Mozambique");
|
||||
countries["NA"] = tr("Namibia");
|
||||
countries["NC"] = tr("New Caledonia");
|
||||
countries["NE"] = tr("Niger");
|
||||
countries["NF"] = tr("Norfolk Island");
|
||||
countries["NG"] = tr("Nigeria");
|
||||
countries["NI"] = tr("Nicaragua");
|
||||
countries["NL"] = tr("Netherlands");
|
||||
countries["NO"] = tr("Norway");
|
||||
countries["NP"] = tr("Nepal");
|
||||
countries["NR"] = tr("Nauru");
|
||||
countries["NU"] = tr("Niue");
|
||||
countries["NZ"] = tr("New Zealand");
|
||||
countries["OM"] = tr("Oman");
|
||||
countries["PA"] = tr("Panama");
|
||||
countries["PE"] = tr("Peru");
|
||||
countries["PF"] = tr("French Polynesia");
|
||||
countries["PG"] = tr("Papua New Guinea");
|
||||
countries["PH"] = tr("Philippines");
|
||||
countries["PK"] = tr("Pakistan");
|
||||
countries["PL"] = tr("Poland");
|
||||
countries["PM"] = tr("Saint Pierre and Miquelon");
|
||||
countries["PN"] = tr("Pitcairn Islands");
|
||||
countries["PR"] = tr("Puerto Rico");
|
||||
countries["PS"] = tr("Palestinian Territory");
|
||||
countries["PT"] = tr("Portugal");
|
||||
countries["PW"] = tr("Palau");
|
||||
countries["PY"] = tr("Paraguay");
|
||||
countries["QA"] = tr("Qatar");
|
||||
countries["RE"] = tr("Reunion");
|
||||
countries["RO"] = tr("Romania");
|
||||
countries["RU"] = tr("Russian Federation");
|
||||
countries["RW"] = tr("Rwanda");
|
||||
countries["SA"] = tr("Saudi Arabia");
|
||||
countries["SB"] = tr("Solomon Islands");
|
||||
countries["SC"] = tr("Seychelles");
|
||||
countries["SD"] = tr("Sudan");
|
||||
countries["SE"] = tr("Sweden");
|
||||
countries["SG"] = tr("Singapore");
|
||||
countries["SH"] = tr("Saint Helena");
|
||||
countries["SI"] = tr("Slovenia");
|
||||
countries["SJ"] = tr("Svalbard and Jan Mayen");
|
||||
countries["SK"] = tr("Slovakia");
|
||||
countries["SL"] = tr("Sierra Leone");
|
||||
countries["SM"] = tr("San Marino");
|
||||
countries["SN"] = tr("Senegal");
|
||||
countries["SO"] = tr("Somalia");
|
||||
countries["SR"] = tr("Suriname");
|
||||
countries["ST"] = tr("Sao Tome and Principe");
|
||||
countries["SV"] = tr("El Salvador");
|
||||
countries["SY"] = tr("Syrian Arab Republic");
|
||||
countries["SZ"] = tr("Swaziland");
|
||||
countries["TC"] = tr("Turks and Caicos Islands");
|
||||
countries["TD"] = tr("Chad");
|
||||
countries["TF"] = tr("French Southern Territories");
|
||||
countries["TG"] = tr("Togo");
|
||||
countries["TH"] = tr("Thailand");
|
||||
countries["TJ"] = tr("Tajikistan");
|
||||
countries["TK"] = tr("Tokelau");
|
||||
countries["TM"] = tr("Turkmenistan");
|
||||
countries["TN"] = tr("Tunisia");
|
||||
countries["TO"] = tr("Tonga");
|
||||
countries["TL"] = tr("Timor-Leste");
|
||||
countries["TR"] = tr("Turkey");
|
||||
countries["TT"] = tr("Trinidad and Tobago");
|
||||
countries["TV"] = tr("Tuvalu");
|
||||
countries["TW"] = tr("Taiwan");
|
||||
countries["TZ"] = tr("Tanzania, United Republic of");
|
||||
countries["UA"] = tr("Ukraine");
|
||||
countries["UG"] = tr("Uganda");
|
||||
countries["UM"] = tr("United States Minor Outlying Islands");
|
||||
countries["US"] = tr("United States");
|
||||
countries["UY"] = tr("Uruguay");
|
||||
countries["UZ"] = tr("Uzbekistan");
|
||||
countries["VA"] = tr("Holy See (Vatican City State)");
|
||||
countries["VC"] = tr("Saint Vincent and the Grenadines");
|
||||
countries["VE"] = tr("Venezuela");
|
||||
countries["VG"] = tr("Virgin Islands, British");
|
||||
countries["VI"] = tr("Virgin Islands, U.S.");
|
||||
countries["VN"] = tr("Vietnam");
|
||||
countries["VU"] = tr("Vanuatu");
|
||||
countries["WF"] = tr("Wallis and Futuna");
|
||||
countries["WS"] = tr("Samoa");
|
||||
countries["YE"] = tr("Yemen");
|
||||
countries["YT"] = tr("Mayotte");
|
||||
countries["RS"] = tr("Serbia");
|
||||
countries["ZA"] = tr("South Africa");
|
||||
countries["ZM"] = tr("Zambia");
|
||||
countries["ME"] = tr("Montenegro");
|
||||
countries["ZW"] = tr("Zimbabwe");
|
||||
countries["A1"] = tr("Anonymous Proxy");
|
||||
countries["A2"] = tr("Satellite Provider");
|
||||
countries["O1"] = tr("Other");
|
||||
countries["AX"] = tr("Aland Islands");
|
||||
countries["GG"] = tr("Guernsey");
|
||||
countries["IM"] = tr("Isle of Man");
|
||||
countries["JE"] = tr("Jersey");
|
||||
countries["BL"] = tr("Saint Barthelemy");
|
||||
countries["MF"] = tr("Saint Martin");
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return countries.value(countryISOCode, tr("N/A"));
|
||||
}
|
||||
|
||||
void GeoIPManager::configure()
|
||||
{
|
||||
const bool enabled = Preferences::instance()->resolvePeerCountries();
|
||||
if (m_enabled != enabled) {
|
||||
m_enabled = enabled;
|
||||
if (m_enabled && !m_geoIPDatabase) {
|
||||
loadDatabase();
|
||||
}
|
||||
else if (!m_enabled && m_geoIPDatabase) {
|
||||
delete m_geoIPDatabase;
|
||||
m_geoIPDatabase = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
|
||||
{
|
||||
Q_UNUSED(url);
|
||||
|
||||
if (!Utils::Gzip::uncompress(data, data)) {
|
||||
Logger::instance()->addMessage(tr("Could not uncompress GeoIP database file."), Log::WARNING);
|
||||
return;
|
||||
}
|
||||
|
||||
QString error;
|
||||
GeoIPDatabase *geoIPDatabase = GeoIPDatabase::load(data, error);
|
||||
if (geoIPDatabase) {
|
||||
if (!m_geoIPDatabase || (geoIPDatabase->buildEpoch() > m_geoIPDatabase->buildEpoch())) {
|
||||
if (m_geoIPDatabase)
|
||||
delete m_geoIPDatabase;
|
||||
m_geoIPDatabase = geoIPDatabase;
|
||||
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
||||
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
|
||||
Log::INFO);
|
||||
QString targetPath = Utils::Fs::expandPathAbs(
|
||||
Utils::Fs::QDesktopServicesDataLocation() + GEOIP_FOLDER);
|
||||
if (!QDir(targetPath).exists())
|
||||
QDir().mkpath(targetPath);
|
||||
QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME));
|
||||
if (!targetFile.open(QFile::WriteOnly) || (targetFile.write(data) == -1)) {
|
||||
Logger::instance()->addMessage(
|
||||
tr("Couldn't save downloaded GeoIP database file."), Log::WARNING);
|
||||
}
|
||||
else {
|
||||
Logger::instance()->addMessage(tr("Successfully updated GeoIP database."), Log::INFO);
|
||||
}
|
||||
}
|
||||
else {
|
||||
delete geoIPDatabase;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
void GeoIPManager::downloadFailed(const QString &url, const QString &reason)
|
||||
{
|
||||
Q_UNUSED(url);
|
||||
Logger::instance()->addMessage(tr("Couldn't download GeoIP database file. Reason: %1").arg(reason), Log::WARNING);
|
||||
}
|
||||
76
src/core/net/geoipmanager.h
Normal file
76
src/core/net/geoipmanager.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef NET_GEOIPMANAGER_H
|
||||
#define NET_GEOIPMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QCache>
|
||||
|
||||
class QHostAddress;
|
||||
class QString;
|
||||
|
||||
class GeoIPDatabase;
|
||||
|
||||
namespace Net
|
||||
{
|
||||
class GeoIPManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static GeoIPManager *instance();
|
||||
|
||||
QString lookup(const QHostAddress &hostAddr) const;
|
||||
|
||||
static QString CountryName(const QString &countryISOCode);
|
||||
|
||||
private slots:
|
||||
void configure();
|
||||
void downloadFinished(const QString &url, QByteArray data);
|
||||
void downloadFailed(const QString &url, const QString &reason);
|
||||
|
||||
private:
|
||||
GeoIPManager();
|
||||
~GeoIPManager();
|
||||
|
||||
void loadDatabase();
|
||||
void manageDatabaseUpdate();
|
||||
void downloadDatabaseFile();
|
||||
|
||||
bool m_enabled;
|
||||
GeoIPDatabase *m_geoIPDatabase;
|
||||
|
||||
static GeoIPManager *m_instance;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_GEOIPMANAGER_H
|
||||
122
src/core/net/portforwarder.cpp
Normal file
122
src/core/net/portforwarder.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <libtorrent/session.hpp>
|
||||
|
||||
#include "core/logger.h"
|
||||
#include "core/preferences.h"
|
||||
#include "portforwarder.h"
|
||||
|
||||
namespace libt = libtorrent;
|
||||
using namespace Net;
|
||||
|
||||
PortForwarder::PortForwarder(libtorrent::session *provider, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_active(false)
|
||||
, m_provider(provider)
|
||||
{
|
||||
configure();
|
||||
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
||||
}
|
||||
|
||||
PortForwarder::~PortForwarder()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void PortForwarder::initInstance(libtorrent::session *const provider)
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new PortForwarder(provider);
|
||||
}
|
||||
|
||||
void PortForwarder::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
m_instance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PortForwarder *PortForwarder::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void PortForwarder::addPort(qint16 port)
|
||||
{
|
||||
if (!m_mappedPorts.contains(port)) {
|
||||
m_mappedPorts.insert(port, 0);
|
||||
if (m_active)
|
||||
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
|
||||
}
|
||||
}
|
||||
|
||||
void PortForwarder::deletePort(qint16 port)
|
||||
{
|
||||
if (m_mappedPorts.contains(port)) {
|
||||
if (m_active)
|
||||
m_provider->delete_port_mapping(m_mappedPorts[port]);
|
||||
m_mappedPorts.remove(port);
|
||||
}
|
||||
}
|
||||
|
||||
void PortForwarder::configure()
|
||||
{
|
||||
bool enable = Preferences::instance()->isUPnPEnabled();
|
||||
if (m_active != enable) {
|
||||
if (enable)
|
||||
start();
|
||||
else
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void PortForwarder::start()
|
||||
{
|
||||
qDebug("Enabling UPnP / NAT-PMP");
|
||||
m_provider->start_upnp();
|
||||
m_provider->start_natpmp();
|
||||
foreach (qint16 port, m_mappedPorts.keys())
|
||||
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
|
||||
m_active = true;
|
||||
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
|
||||
}
|
||||
|
||||
void PortForwarder::stop()
|
||||
{
|
||||
qDebug("Disabling UPnP / NAT-PMP");
|
||||
m_provider->stop_upnp();
|
||||
m_provider->stop_natpmp();
|
||||
m_active = false;
|
||||
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [OFF]"), Log::INFO);
|
||||
}
|
||||
|
||||
PortForwarder *PortForwarder::m_instance = 0;
|
||||
73
src/core/net/portforwarder.h
Normal file
73
src/core/net/portforwarder.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef NET_PORTFORWARDER_H
|
||||
#define NET_PORTFORWARDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
namespace libtorrent
|
||||
{
|
||||
class session;
|
||||
}
|
||||
|
||||
namespace Net
|
||||
{
|
||||
class PortForwarder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(PortForwarder)
|
||||
|
||||
public:
|
||||
static void initInstance(libtorrent::session *const provider);
|
||||
static void freeInstance();
|
||||
static PortForwarder *instance();
|
||||
|
||||
void addPort(qint16 port);
|
||||
void deletePort(qint16 port);
|
||||
|
||||
private slots:
|
||||
void configure();
|
||||
|
||||
private:
|
||||
explicit PortForwarder(libtorrent::session *const provider, QObject *parent = 0);
|
||||
~PortForwarder();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
bool m_active;
|
||||
libtorrent::session *m_provider;
|
||||
QHash<qint16, int> m_mappedPorts;
|
||||
|
||||
static PortForwarder *m_instance;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_PORTFORWARDER_H
|
||||
520
src/core/net/private/geoipdatabase.cpp
Normal file
520
src/core/net/private/geoipdatabase.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QVariant>
|
||||
#include <QHash>
|
||||
#include <QHostAddress>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "geoipdatabase.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const quint32 __ENDIAN_TEST__ = 0x00000001;
|
||||
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__ENDIAN_TEST__)[0] == 0x01);
|
||||
const int MAX_FILE_SIZE = 10485760; // 10MB
|
||||
const char DB_TYPE[] = "GeoLite2-Country";
|
||||
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
||||
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
||||
const char DATA_SECTION_SEPARATOR[16] = { 0 };
|
||||
|
||||
enum class DataType
|
||||
{
|
||||
Unknown = 0,
|
||||
Pointer = 1,
|
||||
String = 2,
|
||||
Double = 3,
|
||||
Bytes = 4,
|
||||
Integer16 = 5,
|
||||
Integer32 = 6,
|
||||
Map = 7,
|
||||
SignedInteger32 = 8,
|
||||
Integer64 = 9,
|
||||
Integer128 = 10,
|
||||
Array = 11,
|
||||
DataCacheContainer = 12,
|
||||
EndMarker = 13,
|
||||
Boolean = 14,
|
||||
Float = 15
|
||||
};
|
||||
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
Q_IPV6ADDR createMappedAddress(quint32 ip4);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct DataFieldDescriptor
|
||||
{
|
||||
DataType fieldType;
|
||||
union
|
||||
{
|
||||
quint32 fieldSize;
|
||||
quint32 offset; // Pointer
|
||||
};
|
||||
};
|
||||
|
||||
GeoIPDatabase::GeoIPDatabase(quint32 size)
|
||||
: m_ipVersion(0)
|
||||
, m_recordSize(0)
|
||||
, m_nodeCount(0)
|
||||
, m_nodeSize(0)
|
||||
, m_indexSize(0)
|
||||
, m_recordBytes(0)
|
||||
, m_size(size)
|
||||
, m_data(new uchar[size])
|
||||
{
|
||||
}
|
||||
|
||||
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
||||
{
|
||||
GeoIPDatabase *db = 0;
|
||||
QFile file(filename);
|
||||
if (file.size() > MAX_FILE_SIZE) {
|
||||
error = tr("Unsupported database file size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
error = file.errorString();
|
||||
return 0;
|
||||
}
|
||||
|
||||
db = new GeoIPDatabase(file.size());
|
||||
|
||||
if (file.read((char *)db->m_data, db->m_size) != db->m_size) {
|
||||
error = file.errorString();
|
||||
delete db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
||||
delete db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
||||
{
|
||||
GeoIPDatabase *db = 0;
|
||||
if (data.size() > MAX_FILE_SIZE) {
|
||||
error = tr("Unsupported database file size.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
db = new GeoIPDatabase(data.size());
|
||||
|
||||
memcpy((char *)db->m_data, data.constData(), db->m_size);
|
||||
|
||||
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
||||
delete db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
GeoIPDatabase::~GeoIPDatabase()
|
||||
{
|
||||
delete [] m_data;
|
||||
}
|
||||
|
||||
QString GeoIPDatabase::type() const
|
||||
{
|
||||
return DB_TYPE;
|
||||
}
|
||||
|
||||
quint16 GeoIPDatabase::ipVersion() const
|
||||
{
|
||||
return m_ipVersion;
|
||||
}
|
||||
|
||||
QDateTime GeoIPDatabase::buildEpoch() const
|
||||
{
|
||||
return m_buildEpoch;
|
||||
}
|
||||
|
||||
QString GeoIPDatabase::lookup(const QHostAddress &hostAddr) const
|
||||
{
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
Q_IPV6ADDR addr = hostAddr.protocol() == QAbstractSocket::IPv4Protocol
|
||||
? createMappedAddress(hostAddr.toIPv4Address())
|
||||
: hostAddr.toIPv6Address();
|
||||
#else
|
||||
Q_IPV6ADDR addr = hostAddr.toIPv6Address();
|
||||
#endif
|
||||
|
||||
const uchar *ptr = m_data;
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 8; ++j) {
|
||||
bool right = static_cast<bool>((addr[i] >> (7 - j)) & 1);
|
||||
// Interpret the left/right record as number
|
||||
if (right)
|
||||
ptr += m_recordBytes;
|
||||
|
||||
quint32 id = 0;
|
||||
uchar *idPtr = reinterpret_cast<uchar *>(&id);
|
||||
memcpy(&idPtr[4 - m_recordBytes], ptr, m_recordBytes);
|
||||
fromBigEndian(idPtr, 4);
|
||||
|
||||
if (id == m_nodeCount) {
|
||||
return QString();
|
||||
}
|
||||
else if (id > m_nodeCount) {
|
||||
QString country = m_countries.value(id);
|
||||
if (country.isEmpty()) {
|
||||
const quint32 offset = id - m_nodeCount - sizeof(DATA_SECTION_SEPARATOR);
|
||||
quint32 tmp = offset + m_indexSize + sizeof(DATA_SECTION_SEPARATOR);
|
||||
QVariant val = readDataField(tmp);
|
||||
if (val.userType() == QMetaType::QVariantHash) {
|
||||
country = val.toHash()["country"].toHash()["iso_code"].toString();
|
||||
m_countries[id] = country;
|
||||
}
|
||||
}
|
||||
return country;
|
||||
}
|
||||
else {
|
||||
ptr = m_data + (id * m_nodeSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
#define CHECK_METADATA_REQ(key, type) \
|
||||
if (!metadata.contains(#key)) { \
|
||||
error = errMsgNotFound.arg(#key); \
|
||||
return false; \
|
||||
} \
|
||||
else if (metadata.value(#key).userType() != QMetaType::type) { \
|
||||
error = errMsgInvalid.arg(#key); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define CHECK_METADATA_OPT(key, type) \
|
||||
if (metadata.contains(#key)) { \
|
||||
if (metadata.value(#key).userType() != QMetaType::type) { \
|
||||
error = errMsgInvalid.arg(#key); \
|
||||
return false; \
|
||||
} \
|
||||
}
|
||||
|
||||
bool GeoIPDatabase::parseMetadata(const QVariantHash &metadata, QString &error)
|
||||
{
|
||||
const QString errMsgNotFound = tr("Metadata error: '%1' entry not found.");
|
||||
const QString errMsgInvalid = tr("Metadata error: '%1' entry has invalid type.");
|
||||
|
||||
qDebug() << "Parsing MaxMindDB metadata...";
|
||||
|
||||
CHECK_METADATA_REQ(binary_format_major_version, UShort);
|
||||
CHECK_METADATA_REQ(binary_format_minor_version, UShort);
|
||||
uint versionMajor = metadata.value("binary_format_major_version").toUInt();
|
||||
uint versionMinor = metadata.value("binary_format_minor_version").toUInt();
|
||||
if (versionMajor != 2) {
|
||||
error = tr("Unsupported database version: %1.%2").arg(versionMajor).arg(versionMinor);
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_METADATA_REQ(ip_version, UShort);
|
||||
m_ipVersion = metadata.value("ip_version").value<quint16>();
|
||||
if (m_ipVersion != 6) {
|
||||
error = tr("Unsupported IP version: %1").arg(m_ipVersion);
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_METADATA_REQ(record_size, UShort);
|
||||
m_recordSize = metadata.value("record_size").value<quint16>();
|
||||
if (m_recordSize != 24) {
|
||||
error = tr("Unsupported record size: %1").arg(m_recordSize);
|
||||
return false;
|
||||
}
|
||||
m_nodeSize = m_recordSize / 4;
|
||||
m_recordBytes = m_nodeSize / 2;
|
||||
|
||||
CHECK_METADATA_REQ(node_count, UInt);
|
||||
m_nodeCount = metadata.value("node_count").value<quint32>();
|
||||
m_indexSize = m_nodeCount * m_nodeSize;
|
||||
|
||||
CHECK_METADATA_REQ(database_type, QString);
|
||||
QString dbType = metadata.value("database_type").toString();
|
||||
if (dbType != DB_TYPE) {
|
||||
error = tr("Invalid database type: %1").arg(dbType);
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_METADATA_REQ(build_epoch, ULongLong);
|
||||
m_buildEpoch = QDateTime::fromTime_t(metadata.value("build_epoch").toULongLong());
|
||||
|
||||
CHECK_METADATA_OPT(languages, QVariantList);
|
||||
CHECK_METADATA_OPT(description, QVariantHash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeoIPDatabase::loadDB(QString &error) const
|
||||
{
|
||||
qDebug() << "Parsing MaxMindDB index tree...";
|
||||
|
||||
const int nodeSize = m_recordSize / 4; // in bytes
|
||||
const int indexSize = m_nodeCount * nodeSize;
|
||||
if ((m_size < (indexSize + sizeof(DATA_SECTION_SEPARATOR)))
|
||||
|| (memcmp(m_data + indexSize, DATA_SECTION_SEPARATOR, sizeof(DATA_SECTION_SEPARATOR)) != 0)) {
|
||||
error = tr("Database corrupted: no data section found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariantHash GeoIPDatabase::readMetadata() const
|
||||
{
|
||||
const char *ptr = reinterpret_cast<const char *>(m_data);
|
||||
quint32 size = m_size;
|
||||
if (m_size > MAX_METADATA_SIZE) {
|
||||
ptr += m_size - MAX_METADATA_SIZE;
|
||||
size = MAX_METADATA_SIZE;
|
||||
}
|
||||
|
||||
const QByteArray data = QByteArray::fromRawData(ptr, size);
|
||||
int index = data.lastIndexOf(METADATA_BEGIN_MARK);
|
||||
if (index >= 0) {
|
||||
if (m_size > MAX_METADATA_SIZE)
|
||||
index += (m_size - MAX_METADATA_SIZE); // from begin of all data
|
||||
quint32 offset = static_cast<quint32>(index + strlen(METADATA_BEGIN_MARK));
|
||||
QVariant metadata = readDataField(offset);
|
||||
if (metadata.userType() == QMetaType::QVariantHash)
|
||||
return metadata.toHash();
|
||||
}
|
||||
|
||||
return QVariantHash();
|
||||
}
|
||||
|
||||
QVariant GeoIPDatabase::readDataField(quint32 &offset) const
|
||||
{
|
||||
DataFieldDescriptor descr;
|
||||
if (!readDataFieldDescriptor(offset, descr))
|
||||
return QVariant();
|
||||
|
||||
quint32 locOffset = offset;
|
||||
bool usePointer = false;
|
||||
if (descr.fieldType == DataType::Pointer) {
|
||||
usePointer = true;
|
||||
// convert offset from data section to global
|
||||
locOffset = descr.offset + (m_nodeCount * m_recordSize / 4) + sizeof(DATA_SECTION_SEPARATOR);
|
||||
if (!readDataFieldDescriptor(locOffset, descr))
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant fieldValue;
|
||||
switch (descr.fieldType) {
|
||||
case DataType::Pointer:
|
||||
qDebug() << "* Illegal Pointer using";
|
||||
break;
|
||||
case DataType::String:
|
||||
fieldValue = QString::fromUtf8(reinterpret_cast<const char *>(m_data + locOffset), descr.fieldSize);
|
||||
locOffset += descr.fieldSize;
|
||||
break;
|
||||
case DataType::Double:
|
||||
if (descr.fieldSize == 8)
|
||||
fieldValue = readPlainValue<double>(locOffset, descr.fieldSize);
|
||||
else
|
||||
qDebug() << "* Invalid field size for type: Double";
|
||||
break;
|
||||
case DataType::Bytes:
|
||||
fieldValue = QByteArray(reinterpret_cast<const char *>(m_data + locOffset), descr.fieldSize);
|
||||
locOffset += descr.fieldSize;
|
||||
break;
|
||||
case DataType::Integer16:
|
||||
fieldValue = readPlainValue<quint16>(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::Integer32:
|
||||
fieldValue = readPlainValue<quint32>(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::Map:
|
||||
fieldValue = readMapValue(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::SignedInteger32:
|
||||
fieldValue = readPlainValue<qint32>(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::Integer64:
|
||||
fieldValue = readPlainValue<quint64>(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::Integer128:
|
||||
qDebug() << "* Unsupported data type: Integer128";
|
||||
break;
|
||||
case DataType::Array:
|
||||
fieldValue = readArrayValue(locOffset, descr.fieldSize);
|
||||
break;
|
||||
case DataType::DataCacheContainer:
|
||||
qDebug() << "* Unsupported data type: DataCacheContainer";
|
||||
break;
|
||||
case DataType::EndMarker:
|
||||
qDebug() << "* Unsupported data type: EndMarker";
|
||||
break;
|
||||
case DataType::Boolean:
|
||||
fieldValue = QVariant::fromValue(static_cast<bool>(descr.fieldSize));
|
||||
break;
|
||||
case DataType::Float:
|
||||
if (descr.fieldSize == 4)
|
||||
fieldValue = readPlainValue<float>(locOffset, descr.fieldSize);
|
||||
else
|
||||
qDebug() << "* Invalid field size for type: Float";
|
||||
break;
|
||||
default:
|
||||
qDebug() << "* Unsupported data type: Unknown";
|
||||
}
|
||||
|
||||
if (!usePointer)
|
||||
offset = locOffset;
|
||||
return fieldValue;
|
||||
}
|
||||
|
||||
bool GeoIPDatabase::readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out) const
|
||||
{
|
||||
const uchar *dataPtr = m_data + offset;
|
||||
int availSize = m_size - offset;
|
||||
if (availSize < 1) return false;
|
||||
|
||||
out.fieldType = static_cast<DataType>((dataPtr[0] & 0xE0) >> 5);
|
||||
if (out.fieldType == DataType::Pointer) {
|
||||
int size = ((dataPtr[0] & 0x18) >> 3);
|
||||
if (availSize < (size + 2)) return false;
|
||||
|
||||
if (size == 0)
|
||||
out.offset = ((dataPtr[0] & 0x07) << 8) + dataPtr[1];
|
||||
else if (size == 1)
|
||||
out.offset = ((dataPtr[0] & 0x07) << 16) + (dataPtr[1] << 8) + dataPtr[2] + 2048;
|
||||
else if (size == 2)
|
||||
out.offset = ((dataPtr[0] & 0x07) << 24) + (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 526336;
|
||||
else if (size == 3)
|
||||
out.offset = (dataPtr[1] << 24) + (dataPtr[2] << 16) + (dataPtr[3] << 8) + dataPtr[4];
|
||||
|
||||
offset += size + 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
out.fieldSize = dataPtr[0] & 0x1F;
|
||||
if (out.fieldSize <= 28) {
|
||||
if (out.fieldType == DataType::Unknown) {
|
||||
out.fieldType = static_cast<DataType>(dataPtr[1] + 7);
|
||||
if ((out.fieldType <= DataType::Map) || (out.fieldType > DataType::Float) || (availSize < 3))
|
||||
return false;
|
||||
offset += 2;
|
||||
}
|
||||
else {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
else if (out.fieldSize == 29) {
|
||||
if (availSize < 2) return false;
|
||||
out.fieldSize = dataPtr[1] + 29;
|
||||
offset += 2;
|
||||
}
|
||||
else if (out.fieldSize == 30) {
|
||||
if (availSize < 3) return false;
|
||||
out.fieldSize = (dataPtr[1] << 8) + dataPtr[2] + 285;
|
||||
offset += 3;
|
||||
}
|
||||
else if (out.fieldSize == 31) {
|
||||
if (availSize < 4) return false;
|
||||
out.fieldSize = (dataPtr[1] << 16) + (dataPtr[2] << 8) + dataPtr[3] + 65821;
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
|
||||
{
|
||||
if (__IS_LITTLE_ENDIAN__)
|
||||
std::reverse(buf, buf + len);
|
||||
}
|
||||
|
||||
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
|
||||
{
|
||||
QVariantHash map;
|
||||
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
QVariant field = readDataField(offset);
|
||||
if (field.userType() != QMetaType::QString)
|
||||
return QVariant();
|
||||
|
||||
QString key = field.toString();
|
||||
field = readDataField(offset);
|
||||
if (field.userType() == QVariant::Invalid)
|
||||
return QVariant();
|
||||
|
||||
map[key] = field;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
QVariant GeoIPDatabase::readArrayValue(quint32 &offset, quint32 count) const
|
||||
{
|
||||
QVariantList array;
|
||||
|
||||
for (quint32 i = 0; i < count; ++i) {
|
||||
QVariant field = readDataField(offset);
|
||||
if (field.userType() == QVariant::Invalid)
|
||||
return QVariant();
|
||||
|
||||
array.append(field);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
|
||||
Q_IPV6ADDR createMappedAddress(quint32 ip4)
|
||||
{
|
||||
Q_IPV6ADDR ip6;
|
||||
memset(&ip6, 0, sizeof(ip6));
|
||||
|
||||
int i;
|
||||
for (i = 15; ip4 != 0; i--) {
|
||||
ip6[i] = ip4 & 0xFF;
|
||||
ip4 >>= 8;
|
||||
}
|
||||
|
||||
ip6[11] = 0xFF;
|
||||
ip6[10] = 0xFF;
|
||||
|
||||
return ip6;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
102
src/core/net/private/geoipdatabase.h
Normal file
102
src/core/net/private/geoipdatabase.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#ifndef GEOIPDATABASE_H
|
||||
#define GEOIPDATABASE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCoreApplication>
|
||||
|
||||
class QHostAddress;
|
||||
class QString;
|
||||
class QByteArray;
|
||||
class QDateTime;
|
||||
|
||||
struct DataFieldDescriptor;
|
||||
|
||||
class GeoIPDatabase
|
||||
{
|
||||
Q_DECLARE_TR_FUNCTIONS(GeoIPDatabase)
|
||||
|
||||
public:
|
||||
static GeoIPDatabase *load(const QString &filename, QString &error);
|
||||
static GeoIPDatabase *load(const QByteArray &data, QString &error);
|
||||
|
||||
~GeoIPDatabase();
|
||||
|
||||
QString type() const;
|
||||
quint16 ipVersion() const;
|
||||
QDateTime buildEpoch() const;
|
||||
QString lookup(const QHostAddress &hostAddr) const;
|
||||
|
||||
private:
|
||||
GeoIPDatabase(quint32 size);
|
||||
|
||||
bool parseMetadata(const QVariantHash &metadata, QString &error);
|
||||
bool loadDB(QString &error) const;
|
||||
QVariantHash readMetadata() const;
|
||||
|
||||
QVariant readDataField(quint32 &offset) const;
|
||||
bool readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor &out) const;
|
||||
void fromBigEndian(uchar *buf, quint32 len) const;
|
||||
QVariant readMapValue(quint32 &offset, quint32 count) const;
|
||||
QVariant readArrayValue(quint32 &offset, quint32 count) const;
|
||||
|
||||
template<typename T>
|
||||
QVariant readPlainValue(quint32 &offset, quint8 len) const
|
||||
{
|
||||
T value = 0;
|
||||
const uchar *const data = m_data + offset;
|
||||
const quint32 availSize = m_size - offset;
|
||||
|
||||
if ((len > 0) && (len <= sizeof(T) && (availSize >= len))) {
|
||||
// copy input data to last 'len' bytes of 'value'
|
||||
uchar *dst = reinterpret_cast<uchar *>(&value) + (sizeof(T) - len);
|
||||
memcpy(dst, data, len);
|
||||
fromBigEndian(reinterpret_cast<uchar *>(&value), sizeof(T));
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return QVariant::fromValue(value);
|
||||
}
|
||||
|
||||
// Metadata
|
||||
quint16 m_ipVersion;
|
||||
quint16 m_recordSize;
|
||||
quint32 m_nodeCount;
|
||||
int m_nodeSize;
|
||||
int m_indexSize;
|
||||
int m_recordBytes;
|
||||
QDateTime m_buildEpoch;
|
||||
// Search data
|
||||
mutable QHash<quint32, QString> m_countries;
|
||||
quint32 m_size;
|
||||
const uchar *m_data;
|
||||
};
|
||||
|
||||
#endif // GEOIPDATABASE_H
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -28,74 +28,63 @@
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef REVERSERESOLUTION_H
|
||||
#define REVERSERESOLUTION_H
|
||||
|
||||
#include <QList>
|
||||
#include <QCache>
|
||||
#include <QDebug>
|
||||
#include <QHostInfo>
|
||||
#include "misc.h"
|
||||
#include <QString>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
|
||||
#include "reverseresolution.h"
|
||||
|
||||
const int CACHE_SIZE = 500;
|
||||
|
||||
class ReverseResolution: public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ReverseResolution)
|
||||
using namespace Net;
|
||||
|
||||
public:
|
||||
explicit ReverseResolution(QObject* parent): QObject(parent) {
|
||||
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
||||
{
|
||||
return (!hostname.isEmpty() && hostname != ip);
|
||||
}
|
||||
|
||||
ReverseResolution::ReverseResolution(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_cache.setMaxCost(CACHE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
~ReverseResolution() {
|
||||
ReverseResolution::~ReverseResolution()
|
||||
{
|
||||
qDebug("Deleting host name resolver...");
|
||||
}
|
||||
}
|
||||
|
||||
void resolve(const QString &ip) {
|
||||
void ReverseResolution::resolve(const QString &ip)
|
||||
{
|
||||
if (m_cache.contains(ip)) {
|
||||
const QString& hostname = *m_cache.object(ip);
|
||||
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
|
||||
if (isUsefulHostName(hostname, ip))
|
||||
emit ip_resolved(ip, hostname);
|
||||
return;
|
||||
const QString &hostname = *m_cache.object(ip);
|
||||
qDebug() << "Resolved host name using cache: " << ip << " -> " << hostname;
|
||||
if (isUsefulHostName(hostname, ip))
|
||||
emit ipResolved(ip, hostname);
|
||||
}
|
||||
// Actually resolve the ip
|
||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||
}
|
||||
else {
|
||||
// Actually resolve the ip
|
||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||
}
|
||||
}
|
||||
|
||||
signals:
|
||||
void ip_resolved(const QString &ip, const QString &hostname);
|
||||
|
||||
private slots:
|
||||
void hostResolved(const QHostInfo& host) {
|
||||
const QString& ip = m_lookups.take(host.lookupId());
|
||||
void ReverseResolution::hostResolved(const QHostInfo &host)
|
||||
{
|
||||
const QString &ip = m_lookups.take(host.lookupId());
|
||||
Q_ASSERT(!ip.isNull());
|
||||
|
||||
if (host.error() != QHostInfo::NoError) {
|
||||
qDebug() << "DNS Reverse resolution error: " << host.errorString();
|
||||
return;
|
||||
qDebug() << "DNS Reverse resolution error: " << host.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
const QString& hostname = host.hostName();
|
||||
const QString &hostname = host.hostName();
|
||||
|
||||
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
||||
m_cache.insert(ip, new QString(hostname));
|
||||
if (isUsefulHostName(hostname, ip))
|
||||
emit ip_resolved(ip, hostname);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline bool isUsefulHostName(const QString& hostname, const QString& ip) {
|
||||
return (!hostname.isEmpty() && hostname != ip);
|
||||
}
|
||||
|
||||
QHash<int /* LookupID */, QString /* IP */> m_lookups;
|
||||
QCache<QString /* IP */, QString /* HostName */> m_cache;
|
||||
};
|
||||
|
||||
|
||||
#endif // REVERSERESOLUTION_H
|
||||
emit ipResolved(ip, hostname);
|
||||
}
|
||||
67
src/core/net/reverseresolution.h
Normal file
67
src/core/net/reverseresolution.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef NET_REVERSERESOLUTION_H
|
||||
#define NET_REVERSERESOLUTION_H
|
||||
|
||||
#include <QCache>
|
||||
#include <QObject>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QHostInfo;
|
||||
class QString;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Net
|
||||
{
|
||||
class ReverseResolution : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ReverseResolution)
|
||||
|
||||
public:
|
||||
explicit ReverseResolution(QObject *parent = 0);
|
||||
~ReverseResolution();
|
||||
|
||||
void resolve(const QString &ip);
|
||||
|
||||
signals:
|
||||
void ipResolved(const QString &ip, const QString &hostname);
|
||||
|
||||
private slots:
|
||||
void hostResolved(const QHostInfo &host);
|
||||
|
||||
private:
|
||||
QHash<int /* LookupID */, QString /* IP */> m_lookups;
|
||||
QCache<QString /* IP */, QString /* HostName */> m_cache;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // NET_REVERSERESOLUTION_H
|
||||
502
src/core/net/smtp.cpp
Normal file
502
src/core/net/smtp.cpp
Normal file
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on QxtSmtp from libqxt (http://libqxt.org)
|
||||
*/
|
||||
|
||||
#include "smtp.h"
|
||||
#include "core/preferences.h"
|
||||
#include "core/logger.h"
|
||||
|
||||
#include <QTextStream>
|
||||
#ifndef QT_NO_OPENSSL
|
||||
#include <QSslSocket>
|
||||
#else
|
||||
#include <QTcpSocket>
|
||||
#endif
|
||||
#include <QTextCodec>
|
||||
#include <QDebug>
|
||||
#include <QHostAddress>
|
||||
#include <QHostInfo>
|
||||
#include <QNetworkInterface>
|
||||
#include <QCryptographicHash>
|
||||
#include <QStringList>
|
||||
|
||||
namespace
|
||||
{
|
||||
const short DEFAULT_PORT = 25;
|
||||
const short DEFAULT_PORT_SSL = 465;
|
||||
|
||||
QByteArray hmacMD5(QByteArray key, const QByteArray &msg)
|
||||
{
|
||||
const int blockSize = 64; // HMAC-MD5 block size
|
||||
if (key.length() > blockSize) { // if key is longer than block size (64), reduce key length with MD5 compression
|
||||
key = QCryptographicHash::hash(key, QCryptographicHash::Md5);
|
||||
}
|
||||
|
||||
QByteArray innerPadding(blockSize, char(0x36)); // initialize inner padding with char "6"
|
||||
QByteArray outerPadding(blockSize, char(0x5c)); // initialize outer padding with char "\"
|
||||
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
||||
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
||||
|
||||
for (int i = 0; i < key.length(); i++) {
|
||||
innerPadding[i] = innerPadding[i] ^ key.at(i); // XOR operation between every byte in key and innerpadding, of key length
|
||||
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
||||
}
|
||||
|
||||
// result = hash ( outerPadding CONCAT hash ( innerPadding CONCAT baseString ) ).toBase64
|
||||
QByteArray total = outerPadding;
|
||||
QByteArray part = innerPadding;
|
||||
part.append(msg);
|
||||
total.append(QCryptographicHash::hash(part, QCryptographicHash::Md5));
|
||||
return QCryptographicHash::hash(total, QCryptographicHash::Md5);
|
||||
}
|
||||
|
||||
QByteArray determineFQDN()
|
||||
{
|
||||
QString hostname = QHostInfo::localHostName();
|
||||
if (hostname.isEmpty())
|
||||
hostname = "localhost";
|
||||
|
||||
return hostname.toLocal8Bit();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using namespace Net;
|
||||
|
||||
Smtp::Smtp(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_state(Init)
|
||||
, m_useSsl(false)
|
||||
{
|
||||
#ifndef QT_NO_OPENSSL
|
||||
m_socket = new QSslSocket(this);
|
||||
#else
|
||||
m_socket = new QTcpSocket(this);
|
||||
#endif
|
||||
|
||||
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
|
||||
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
||||
|
||||
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
||||
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
||||
== "750c783e6ab0b503eaa86e310a5db738");
|
||||
Q_ASSERT(hmacMD5(QByteArray::fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), "Hi There").toHex()
|
||||
== "9294727a3638bb1c13f48ef8158bfc9d");
|
||||
}
|
||||
|
||||
Smtp::~Smtp()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
||||
m_message = "";
|
||||
m_message += encodeMimeHeader("Date", QDateTime::currentDateTime().toUTC().toString("ddd, d MMM yyyy hh:mm:ss UT"), latin1);
|
||||
m_message += encodeMimeHeader("From", from, latin1);
|
||||
m_message += encodeMimeHeader("Subject", subject, latin1);
|
||||
m_message += encodeMimeHeader("To", to, latin1);
|
||||
m_message += "MIME-Version: 1.0\r\n";
|
||||
m_message += "Content-Type: text/plain; charset=UTF-8\r\n";
|
||||
m_message += "Content-Transfer-Encoding: base64\r\n";
|
||||
m_message += "\r\n";
|
||||
// Encode the body in base64
|
||||
QString crlf_body = body;
|
||||
QByteArray b = crlf_body.replace("\n","\r\n").toUtf8().toBase64();
|
||||
int ct = b.length();
|
||||
for (int i = 0; i < ct; i += 78)
|
||||
m_message += b.mid(i, 78);
|
||||
m_from = from;
|
||||
m_rcpt = to;
|
||||
// Authentication
|
||||
if (pref->getMailNotificationSMTPAuth()) {
|
||||
m_username = pref->getMailNotificationSMTPUsername();
|
||||
m_password = pref->getMailNotificationSMTPPassword();
|
||||
}
|
||||
|
||||
// Connect to SMTP server
|
||||
#ifndef QT_NO_OPENSSL
|
||||
if (pref->getMailNotificationSMTPSSL()) {
|
||||
m_socket->connectToHostEncrypted(pref->getMailNotificationSMTP(), DEFAULT_PORT_SSL);
|
||||
m_useSsl = true;
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||
m_useSsl = false;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Smtp::readyRead()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
// SMTP is line-oriented
|
||||
m_buffer += m_socket->readAll();
|
||||
while (true) {
|
||||
int pos = m_buffer.indexOf("\r\n");
|
||||
if (pos < 0) return; // Loop exit condition
|
||||
QByteArray line = m_buffer.left(pos);
|
||||
m_buffer = m_buffer.mid(pos + 2);
|
||||
qDebug() << "Response line:" << line;
|
||||
// Extract response code
|
||||
QByteArray code = line.left(3);
|
||||
|
||||
switch (m_state) {
|
||||
case Init: {
|
||||
if (code[0] == '2') {
|
||||
// The server may send a multiline greeting/INIT/220 response.
|
||||
// We wait until it finishes.
|
||||
if (line[3] != ' ')
|
||||
break;
|
||||
// Connection was successful
|
||||
ehlo();
|
||||
}
|
||||
else {
|
||||
logError("Connection failed, unrecognized reply: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EhloSent:
|
||||
case HeloSent:
|
||||
case EhloGreetReceived:
|
||||
parseEhloResponse(code, line[3] != ' ', line.mid(4));
|
||||
break;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
case StartTLSSent:
|
||||
if (code == "220") {
|
||||
m_socket->startClientEncryption();
|
||||
ehlo();
|
||||
}
|
||||
else {
|
||||
authenticate();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case AuthRequestSent:
|
||||
case AuthUsernameSent:
|
||||
if (m_authType == AuthPlain) authPlain();
|
||||
else if (m_authType == AuthLogin) authLogin();
|
||||
else authCramMD5(line.mid(4));
|
||||
break;
|
||||
case AuthSent:
|
||||
case Authenticated:
|
||||
if (code[0] == '2') {
|
||||
qDebug() << "Sending <mail from>...";
|
||||
m_socket->write("mail from:<" + m_from.toLatin1() + ">\r\n");
|
||||
m_socket->flush();
|
||||
m_state = Rcpt;
|
||||
}
|
||||
else {
|
||||
// Authentication failed!
|
||||
logError("Authentication failed, msg: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
case Rcpt:
|
||||
if (code[0] == '2') {
|
||||
m_socket->write("rcpt to:<" + m_rcpt.toLatin1() + ">\r\n");
|
||||
m_socket->flush();
|
||||
m_state = Data;
|
||||
}
|
||||
else {
|
||||
logError("<mail from> was rejected by server, msg: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
case Data:
|
||||
if (code[0] == '2') {
|
||||
m_socket->write("data\r\n");
|
||||
m_socket->flush();
|
||||
m_state = Body;
|
||||
}
|
||||
else {
|
||||
logError("<Rcpt to> was rejected by server, msg: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
case Body:
|
||||
if (code[0] == '3') {
|
||||
m_socket->write(m_message + "\r\n.\r\n");
|
||||
m_socket->flush();
|
||||
m_state = Quit;
|
||||
}
|
||||
else {
|
||||
logError("<data> was rejected by server, msg: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
case Quit:
|
||||
if (code[0] == '2') {
|
||||
m_socket->write("QUIT\r\n");
|
||||
m_socket->flush();
|
||||
// here, we just close.
|
||||
m_state = Close;
|
||||
}
|
||||
else {
|
||||
logError("Message was rejected by the server, error: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qDebug() << "Disconnecting from host";
|
||||
m_socket->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix)
|
||||
{
|
||||
QByteArray rv = "";
|
||||
QByteArray line = key.toLatin1() + ": ";
|
||||
if (!prefix.isEmpty()) line += prefix;
|
||||
if (!value.contains("=?") && latin1->canEncode(value)) {
|
||||
bool firstWord = true;
|
||||
foreach (const QByteArray& word, value.toLatin1().split(' ')) {
|
||||
if (line.size() > 78) {
|
||||
rv = rv + line + "\r\n";
|
||||
line.clear();
|
||||
}
|
||||
if (firstWord)
|
||||
line += word;
|
||||
else
|
||||
line += " " + word;
|
||||
firstWord = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The text cannot be losslessly encoded as Latin-1. Therefore, we
|
||||
// must use base64 encoding.
|
||||
QByteArray utf8 = value.toUtf8();
|
||||
// Use base64 encoding
|
||||
QByteArray base64 = utf8.toBase64();
|
||||
int ct = base64.length();
|
||||
line += "=?utf-8?b?";
|
||||
for (int i = 0; i < ct; i += 4) {
|
||||
/*if (line.length() > 72) {
|
||||
rv += line + "?\n\r";
|
||||
line = " =?utf-8?b?";
|
||||
}*/
|
||||
line = line + base64.mid(i, 4);
|
||||
}
|
||||
line += "?="; // end encoded-word atom
|
||||
}
|
||||
return rv + line + "\r\n";
|
||||
}
|
||||
|
||||
void Smtp::ehlo()
|
||||
{
|
||||
QByteArray address = determineFQDN();
|
||||
m_socket->write("ehlo " + address + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = EhloSent;
|
||||
}
|
||||
|
||||
void Smtp::helo()
|
||||
{
|
||||
QByteArray address = determineFQDN();
|
||||
m_socket->write("helo " + address + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = HeloSent;
|
||||
}
|
||||
|
||||
void Smtp::parseEhloResponse(const QByteArray &code, bool continued, const QString &line)
|
||||
{
|
||||
if (code != "250") {
|
||||
// Error
|
||||
if (m_state == EhloSent) {
|
||||
// try to send HELO instead of EHLO
|
||||
qDebug() << "EHLO failed, trying HELO instead...";
|
||||
helo();
|
||||
}
|
||||
else {
|
||||
// Both EHLO and HELO failed, chances are this is NOT
|
||||
// a SMTP server
|
||||
logError("Both EHLO and HELO failed, msg: "+line);
|
||||
m_state = Close;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state != EhloGreetReceived) {
|
||||
if (!continued) {
|
||||
// greeting only, no extensions
|
||||
qDebug() << "No extension";
|
||||
m_state = EhloDone;
|
||||
}
|
||||
else {
|
||||
// greeting followed by extensions
|
||||
m_state = EhloGreetReceived;
|
||||
qDebug () << "EHLO greet received";
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
qDebug() << Q_FUNC_INFO << "Supported extension: " << line.section(' ', 0, 0).toUpper()
|
||||
<< line.section(' ', 1);
|
||||
m_extensions[line.section(' ', 0, 0).toUpper()] = line.section(' ', 1);
|
||||
if (!continued)
|
||||
m_state = EhloDone;
|
||||
}
|
||||
|
||||
if (m_state != EhloDone) return;
|
||||
|
||||
if (m_extensions.contains("STARTTLS") && m_useSsl) {
|
||||
qDebug() << "STARTTLS";
|
||||
startTLS();
|
||||
}
|
||||
else {
|
||||
authenticate();
|
||||
}
|
||||
}
|
||||
|
||||
void Smtp::authenticate()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
if (!m_extensions.contains("AUTH") ||
|
||||
m_username.isEmpty() || m_password.isEmpty()) {
|
||||
// Skip authentication
|
||||
qDebug() << "Skipping authentication...";
|
||||
m_state = Authenticated;
|
||||
// At this point the server will not send any response
|
||||
// So fill the buffer with a fake one to pass the tests
|
||||
// in readyRead()
|
||||
m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
||||
return;
|
||||
}
|
||||
// AUTH extension is supported, check which
|
||||
// authentication modes are supported by
|
||||
// the server
|
||||
QStringList auth = m_extensions["AUTH"].toUpper().split(' ', QString::SkipEmptyParts);
|
||||
if (auth.contains("CRAM-MD5")) {
|
||||
qDebug() << "Using CRAM-MD5 authentication...";
|
||||
authCramMD5();
|
||||
}
|
||||
else if (auth.contains("PLAIN")) {
|
||||
qDebug() << "Using PLAIN authentication...";
|
||||
authPlain();
|
||||
}
|
||||
else if (auth.contains("LOGIN")) {
|
||||
qDebug() << "Using LOGIN authentication...";
|
||||
authLogin();
|
||||
}
|
||||
else {
|
||||
// Skip authentication
|
||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
||||
"knowing it is likely to fail... Server Auth Modes: "+auth.join("|"));
|
||||
m_state = Authenticated;
|
||||
// At this point the server will not send any response
|
||||
// So fill the buffer with a fake one to pass the tests
|
||||
// in readyRead()
|
||||
m_buffer.push_front("250 QBT FAKE RESPONSE\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Smtp::startTLS()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
m_socket->write("starttls\r\n");
|
||||
m_socket->flush();
|
||||
m_state = StartTLSSent;
|
||||
#else
|
||||
authenticate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Smtp::authCramMD5(const QByteArray& challenge)
|
||||
{
|
||||
if (m_state != AuthRequestSent) {
|
||||
m_socket->write("auth cram-md5\r\n");
|
||||
m_socket->flush();
|
||||
m_authType = AuthCramMD5;
|
||||
m_state = AuthRequestSent;
|
||||
}
|
||||
else {
|
||||
QByteArray response = m_username.toLatin1() + ' '
|
||||
+ hmacMD5(m_password.toLatin1(), QByteArray::fromBase64(challenge)).toHex();
|
||||
m_socket->write(response.toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthSent;
|
||||
}
|
||||
}
|
||||
|
||||
void Smtp::authPlain()
|
||||
{
|
||||
if (m_state != AuthRequestSent) {
|
||||
m_authType = AuthPlain;
|
||||
// Prepare Auth string
|
||||
QByteArray auth;
|
||||
auth += '\0';
|
||||
auth += m_username.toLatin1();
|
||||
qDebug() << "username: " << m_username.toLatin1();
|
||||
auth += '\0';
|
||||
auth += m_password.toLatin1();
|
||||
qDebug() << "password: " << m_password.toLatin1();
|
||||
// Send it
|
||||
m_socket->write("auth plain "+ auth.toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthSent;
|
||||
}
|
||||
}
|
||||
|
||||
void Smtp::authLogin()
|
||||
{
|
||||
if ((m_state != AuthRequestSent) && (m_state != AuthUsernameSent)) {
|
||||
m_socket->write("auth login\r\n");
|
||||
m_socket->flush();
|
||||
m_authType = AuthLogin;
|
||||
m_state = AuthRequestSent;
|
||||
}
|
||||
else if (m_state == AuthRequestSent) {
|
||||
m_socket->write(m_username.toLatin1().toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthUsernameSent;
|
||||
}
|
||||
else {
|
||||
m_socket->write(m_password.toLatin1().toBase64() + "\r\n");
|
||||
m_socket->flush();
|
||||
m_state = AuthSent;
|
||||
}
|
||||
}
|
||||
|
||||
void Smtp::logError(const QString &msg)
|
||||
{
|
||||
qDebug() << "Email Notification Error:" << msg;
|
||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2011 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
@@ -50,50 +50,76 @@ class QTcpSocket;
|
||||
class QTextCodec;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class Smtp : public QObject {
|
||||
Q_OBJECT
|
||||
namespace Net
|
||||
{
|
||||
class Smtp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Smtp(QObject *parent = 0);
|
||||
~Smtp();
|
||||
void sendMail(const QString &from, const QString &to, const QString &subject, const QString &body);
|
||||
public:
|
||||
Smtp(QObject *parent = 0);
|
||||
~Smtp();
|
||||
|
||||
private slots:
|
||||
void readyRead();
|
||||
void sendMail(const QString &m_from, const QString &to, const QString &subject, const QString &body);
|
||||
|
||||
private:
|
||||
QByteArray encode_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix=QByteArray());
|
||||
void ehlo();
|
||||
void helo();
|
||||
void parseEhloResponse(const QByteArray& code, bool continued, const QString& line);
|
||||
void authenticate();
|
||||
void startTLS();
|
||||
void authCramMD5(const QByteArray& challenge = QByteArray());
|
||||
void authPlain();
|
||||
void authLogin();
|
||||
void logError(const QString &msg);
|
||||
private slots:
|
||||
void readyRead();
|
||||
|
||||
private:
|
||||
enum states { Rcpt, EhloSent, HeloSent, EhloDone, EhloGreetReceived, AuthRequestSent, AuthSent,
|
||||
AuthUsernameSent, Authenticated, StartTLSSent, Data, Init, Body, Quit, Close };
|
||||
enum AuthType { AuthPlain, AuthLogin, AuthCramMD5 };
|
||||
private:
|
||||
enum States
|
||||
{
|
||||
Rcpt,
|
||||
EhloSent,
|
||||
HeloSent,
|
||||
EhloDone,
|
||||
EhloGreetReceived,
|
||||
AuthRequestSent,
|
||||
AuthSent,
|
||||
AuthUsernameSent,
|
||||
Authenticated,
|
||||
StartTLSSent,
|
||||
Data,
|
||||
Init,
|
||||
Body,
|
||||
Quit,
|
||||
Close
|
||||
};
|
||||
|
||||
private:
|
||||
QByteArray message;
|
||||
enum AuthType
|
||||
{
|
||||
AuthPlain,
|
||||
AuthLogin,
|
||||
AuthCramMD5
|
||||
};
|
||||
|
||||
QByteArray encodeMimeHeader(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix = QByteArray());
|
||||
void ehlo();
|
||||
void helo();
|
||||
void parseEhloResponse(const QByteArray &code, bool continued, const QString &line);
|
||||
void authenticate();
|
||||
void startTLS();
|
||||
void authCramMD5(const QByteArray &challenge = QByteArray());
|
||||
void authPlain();
|
||||
void authLogin();
|
||||
void logError(const QString &msg);
|
||||
|
||||
QByteArray m_message;
|
||||
#ifndef QT_NO_OPENSSL
|
||||
QSslSocket *socket;
|
||||
QSslSocket *m_socket;
|
||||
#else
|
||||
QTcpSocket *socket;
|
||||
QTcpSocket *m_socket;
|
||||
#endif
|
||||
QString from;
|
||||
QString rcpt;
|
||||
QString response;
|
||||
int state;
|
||||
QHash<QString, QString> extensions;
|
||||
QByteArray buffer;
|
||||
bool use_ssl;
|
||||
AuthType authType;
|
||||
QString username;
|
||||
QString password;
|
||||
};
|
||||
QString m_from;
|
||||
QString m_rcpt;
|
||||
QString m_response;
|
||||
int m_state;
|
||||
QHash<QString, QString> m_extensions;
|
||||
QByteArray m_buffer;
|
||||
bool m_useSsl;
|
||||
AuthType m_authType;
|
||||
QString m_username;
|
||||
QString m_password;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "preferences.h"
|
||||
#include "qinisettings.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QPair>
|
||||
@@ -50,8 +51,9 @@
|
||||
#include <winreg.h>
|
||||
#endif
|
||||
|
||||
#include "misc.h"
|
||||
#include "fs_utils.h"
|
||||
#include <cstdlib>
|
||||
#include "core/utils/fs.h"
|
||||
#include "core/utils/misc.h"
|
||||
|
||||
|
||||
Preferences* Preferences::m_instance = 0;
|
||||
@@ -61,18 +63,20 @@ Preferences::Preferences()
|
||||
, dirty(false)
|
||||
, lock(QReadWriteLock::Recursive)
|
||||
{
|
||||
qRegisterMetaTypeStreamOperators<MaxRatioAction>("MaxRatioAction");
|
||||
|
||||
QIniSettings *settings = new QIniSettings;
|
||||
#ifndef Q_OS_MAC
|
||||
QIniSettings *settings_new = new QIniSettings("qBittorrent", "qBittorrent_new");
|
||||
QStringList keys = settings_new->allKeys();
|
||||
bool use_new = false;
|
||||
|
||||
|
||||
// This means that the PC closed either due to power outage
|
||||
// or because the disk was full. In any case the settings weren't transfered
|
||||
// in their final position. So assume that qbittorrent_new.ini/qbittorrent_new.conf
|
||||
// contains the most recent settings.
|
||||
if (!keys.isEmpty()) {
|
||||
Logger::instance()->addMessage(tr("Detected unclean program exit. Using fallback file to restore settings."), Log::WARNING);
|
||||
use_new = true;
|
||||
dirty = true;
|
||||
}
|
||||
@@ -100,7 +104,9 @@ Preferences::Preferences()
|
||||
//Ensures sync to disk before we attempt to manipulate the files from save().
|
||||
delete settings;
|
||||
#ifndef Q_OS_MAC
|
||||
QString new_path = settings_new->fileName();
|
||||
delete settings_new;
|
||||
Utils::Fs::forceRemove(new_path);
|
||||
|
||||
if (use_new)
|
||||
save();
|
||||
@@ -116,15 +122,18 @@ Preferences::~Preferences()
|
||||
save();
|
||||
}
|
||||
|
||||
Preferences * Preferences::instance()
|
||||
Preferences *Preferences::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new Preferences;
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void Preferences::drop()
|
||||
void Preferences::initInstance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new Preferences;
|
||||
}
|
||||
|
||||
void Preferences::freeInstance()
|
||||
{
|
||||
if (m_instance) {
|
||||
delete m_instance;
|
||||
@@ -132,12 +141,11 @@ void Preferences::drop()
|
||||
}
|
||||
}
|
||||
|
||||
void Preferences::save()
|
||||
bool Preferences::save()
|
||||
{
|
||||
QReadLocker locker(&lock);
|
||||
QWriteLocker locker(&lock);
|
||||
|
||||
if (!dirty)
|
||||
return;
|
||||
if (!dirty) return false;
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
// QSettings delete the file before writing it out. This can result in problems
|
||||
@@ -158,22 +166,30 @@ void Preferences::save()
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
settings->sync(); // Important to get error status
|
||||
if (settings->status() == QSettings::AccessError) {
|
||||
delete settings;
|
||||
return;
|
||||
}
|
||||
QString new_path = settings->fileName();
|
||||
QSettings::Status status = settings->status();
|
||||
|
||||
if (status != QSettings::NoError) {
|
||||
if (status == QSettings::AccessError)
|
||||
Logger::instance()->addMessage(tr("An access error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
else
|
||||
Logger::instance()->addMessage(tr("A format error occurred while trying to write the configuration file."), Log::CRITICAL);
|
||||
|
||||
delete settings;
|
||||
Utils::Fs::forceRemove(new_path);
|
||||
return false;
|
||||
}
|
||||
delete settings;
|
||||
QString final_path = new_path;
|
||||
int index = final_path.lastIndexOf("_new", -1, Qt::CaseInsensitive);
|
||||
final_path.remove(index, 4);
|
||||
fsutils::forceRemove(final_path);
|
||||
Utils::Fs::forceRemove(final_path);
|
||||
QFile::rename(new_path, final_path);
|
||||
#else
|
||||
delete settings;
|
||||
#endif
|
||||
|
||||
emit changed();
|
||||
return true;
|
||||
}
|
||||
|
||||
const QVariant Preferences::value(const QString &key, const QVariant &defaultValue) const
|
||||
@@ -345,7 +361,7 @@ void Preferences::setWinStartup(bool b)
|
||||
{
|
||||
QSettings settings("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", QSettings::NativeFormat);
|
||||
if (b) {
|
||||
const QString bin_path = "\"" + fsutils::toNativePath(qApp->applicationFilePath()) + "\"";
|
||||
const QString bin_path = "\"" + Utils::Fs::toNativePath(qApp->applicationFilePath()) + "\"";
|
||||
settings.setValue("qBittorrent", bin_path);
|
||||
}
|
||||
else {
|
||||
@@ -359,13 +375,13 @@ QString Preferences::getSavePath() const
|
||||
{
|
||||
QString save_path = value("Preferences/Downloads/SavePath").toString();
|
||||
if (!save_path.isEmpty())
|
||||
return fsutils::fromNativePath(save_path);
|
||||
return fsutils::QDesktopServicesDownloadLocation();
|
||||
return Utils::Fs::fromNativePath(save_path);
|
||||
return Utils::Fs::QDesktopServicesDownloadLocation();
|
||||
}
|
||||
|
||||
void Preferences::setSavePath(const QString &save_path)
|
||||
{
|
||||
setValue("Preferences/Downloads/SavePath", fsutils::fromNativePath(save_path));
|
||||
setValue("Preferences/Downloads/SavePath", Utils::Fs::fromNativePath(save_path));
|
||||
}
|
||||
|
||||
bool Preferences::isTempPathEnabled() const
|
||||
@@ -381,12 +397,12 @@ void Preferences::setTempPathEnabled(bool enabled)
|
||||
QString Preferences::getTempPath() const
|
||||
{
|
||||
const QString temp = QDir(getSavePath()).absoluteFilePath("temp");
|
||||
return fsutils::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TempPath", temp).toString());
|
||||
}
|
||||
|
||||
void Preferences::setTempPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/TempPath", fsutils::fromNativePath(path));
|
||||
setValue("Preferences/Downloads/TempPath", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
bool Preferences::useIncompleteFilesExtension() const
|
||||
@@ -411,12 +427,12 @@ void Preferences::setAppendTorrentLabel(bool b)
|
||||
|
||||
QString Preferences::lastLocationPath() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/LastLocationPath").toString());
|
||||
}
|
||||
|
||||
void Preferences::setLastLocationPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/LastLocationPath", fsutils::fromNativePath(path));
|
||||
setValue("Preferences/Downloads/LastLocationPath", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
bool Preferences::preAllocateAllFiles() const
|
||||
@@ -467,7 +483,7 @@ QStringList Preferences::getScanDirs() const
|
||||
|
||||
QStringList newList;
|
||||
foreach (const QString& s, originalList)
|
||||
newList << fsutils::fromNativePath(s);
|
||||
newList << Utils::Fs::fromNativePath(s);
|
||||
return newList;
|
||||
}
|
||||
|
||||
@@ -477,28 +493,38 @@ void Preferences::setScanDirs(const QStringList &dirs)
|
||||
QStringList newList;
|
||||
if (!dirs.isEmpty())
|
||||
foreach (const QString& s, dirs)
|
||||
newList << fsutils::fromNativePath(s);
|
||||
newList << Utils::Fs::fromNativePath(s);
|
||||
setValue("Preferences/Downloads/ScanDirs", newList);
|
||||
}
|
||||
|
||||
QList<bool> Preferences::getDownloadInScanDirs() const
|
||||
{
|
||||
return misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList());
|
||||
return Utils::Misc::boolListfromStringList(value("Preferences/Downloads/DownloadInScanDirs").toStringList());
|
||||
}
|
||||
|
||||
void Preferences::setDownloadInScanDirs(const QList<bool> &list)
|
||||
{
|
||||
setValue("Preferences/Downloads/DownloadInScanDirs", misc::toStringList(list));
|
||||
setValue("Preferences/Downloads/ScanDirsDownloadPaths", Utils::Misc::toStringList(list));
|
||||
}
|
||||
|
||||
void Preferences::setScanDirsDownloadPaths(const QStringList &downloadpaths)
|
||||
{
|
||||
setValue("Preferences/Downloads/ScanDirsDownloadPaths", downloadpaths);
|
||||
}
|
||||
|
||||
QStringList Preferences::getScanDirsDownloadPaths() const
|
||||
{
|
||||
return value("Preferences/Downloads/DownloadPaths").toStringList();
|
||||
}
|
||||
|
||||
QString Preferences::getScanDirsLastPath() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/ScanDirsLastPath").toString());
|
||||
}
|
||||
|
||||
void Preferences::setScanDirsLastPath(const QString &path)
|
||||
{
|
||||
setValue("Preferences/Downloads/ScanDirsLastPath", fsutils::fromNativePath(path));
|
||||
setValue("Preferences/Downloads/ScanDirsLastPath", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
bool Preferences::isTorrentExportEnabled() const
|
||||
@@ -508,12 +534,12 @@ bool Preferences::isTorrentExportEnabled() const
|
||||
|
||||
QString Preferences::getTorrentExportDir() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/TorrentExportDir").toString());
|
||||
}
|
||||
|
||||
void Preferences::setTorrentExportDir(QString path)
|
||||
{
|
||||
setValue("Preferences/Downloads/TorrentExportDir", fsutils::fromNativePath(path.trimmed()));
|
||||
setValue("Preferences/Downloads/TorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
|
||||
}
|
||||
|
||||
bool Preferences::isFinishedTorrentExportEnabled() const
|
||||
@@ -523,12 +549,12 @@ bool Preferences::isFinishedTorrentExportEnabled() const
|
||||
|
||||
QString Preferences::getFinishedTorrentExportDir() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/Downloads/FinishedTorrentExportDir").toString());
|
||||
}
|
||||
|
||||
void Preferences::setFinishedTorrentExportDir(QString path)
|
||||
{
|
||||
setValue("Preferences/Downloads/FinishedTorrentExportDir", fsutils::fromNativePath(path.trimmed()));
|
||||
setValue("Preferences/Downloads/FinishedTorrentExportDir", Utils::Fs::fromNativePath(path.trimmed()));
|
||||
}
|
||||
|
||||
bool Preferences::isMailNotificationEnabled() const
|
||||
@@ -819,7 +845,6 @@ void Preferences::setProxyPeerConnections(bool enabled)
|
||||
setValue("Preferences/Connection/ProxyPeerConnections", enabled);
|
||||
}
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
bool Preferences::getForceProxy() const
|
||||
{
|
||||
return value("Preferences/Connection/ProxyForce", true).toBool();
|
||||
@@ -829,7 +854,16 @@ void Preferences::setForceProxy(bool enabled)
|
||||
{
|
||||
setValue("Preferences/Connection/ProxyForce", enabled);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Preferences::setProxyOnlyForTorrents(bool enabled)
|
||||
{
|
||||
setValue("Preferences/Connection/ProxyOnlyForTorrents", enabled);
|
||||
}
|
||||
|
||||
bool Preferences::isProxyOnlyForTorrents() const
|
||||
{
|
||||
return value("Preferences/Connection/ProxyOnlyForTorrents", false).toBool();
|
||||
}
|
||||
|
||||
// Bittorrent options
|
||||
int Preferences::getMaxConnecs() const
|
||||
@@ -940,6 +974,26 @@ void Preferences::setEncryptionSetting(int val)
|
||||
setValue("Preferences/Bittorrent/Encryption", val);
|
||||
}
|
||||
|
||||
bool Preferences::isAddTrackersEnabled() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/AddTrackers", false).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setAddTrackersEnabled(bool enabled)
|
||||
{
|
||||
setValue("Preferences/Bittorrent/AddTrackers", enabled);
|
||||
}
|
||||
|
||||
QString Preferences::getTrackersList() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/TrackersList").toString();
|
||||
}
|
||||
|
||||
void Preferences::setTrackersList(const QString &val)
|
||||
{
|
||||
setValue("Preferences/Bittorrent/TrackersList", val);
|
||||
}
|
||||
|
||||
qreal Preferences::getGlobalMaxRatio() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/MaxRatio", -1).toDouble();
|
||||
@@ -950,14 +1004,14 @@ void Preferences::setGlobalMaxRatio(qreal ratio)
|
||||
setValue("Preferences/Bittorrent/MaxRatio", ratio);
|
||||
}
|
||||
|
||||
int Preferences::getMaxRatioAction() const
|
||||
MaxRatioAction Preferences::getMaxRatioAction() const
|
||||
{
|
||||
return value("Preferences/Bittorrent/MaxRatioAction", PAUSE_ACTION).toInt();
|
||||
return value("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(MaxRatioAction::Pause)).value<MaxRatioAction>();
|
||||
}
|
||||
|
||||
void Preferences::setMaxRatioAction(int act)
|
||||
void Preferences::setMaxRatioAction(MaxRatioAction act)
|
||||
{
|
||||
setValue("Preferences/Bittorrent/MaxRatioAction", act);
|
||||
setValue("Preferences/Bittorrent/MaxRatioAction", QVariant::fromValue(act));
|
||||
}
|
||||
|
||||
// IP Filter
|
||||
@@ -983,12 +1037,12 @@ void Preferences::setFilteringTrackerEnabled(bool enabled)
|
||||
|
||||
QString Preferences::getFilter() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("Preferences/IPFilter/File").toString());
|
||||
return Utils::Fs::fromNativePath(value("Preferences/IPFilter/File").toString());
|
||||
}
|
||||
|
||||
void Preferences::setFilter(const QString &path)
|
||||
{
|
||||
setValue("Preferences/IPFilter/File", fsutils::fromNativePath(path));
|
||||
setValue("Preferences/IPFilter/File", Utils::Fs::fromNativePath(path));
|
||||
}
|
||||
|
||||
QStringList Preferences::bannedIPs() const
|
||||
@@ -1283,12 +1337,12 @@ void Preferences::setAutoRunEnabled(bool enabled)
|
||||
|
||||
QString Preferences::getAutoRunProgram() const
|
||||
{
|
||||
return fsutils::fromNativePath(value("AutoRun/program").toString());
|
||||
return Utils::Fs::fromNativePath(value("AutoRun/program").toString());
|
||||
}
|
||||
|
||||
void Preferences::setAutoRunProgram(const QString &program)
|
||||
{
|
||||
setValue("AutoRun/program", fsutils::fromNativePath(program));
|
||||
setValue("AutoRun/program", Utils::Fs::fromNativePath(program));
|
||||
}
|
||||
|
||||
bool Preferences::shutdownWhenDownloadsComplete() const
|
||||
@@ -1334,37 +1388,26 @@ void Preferences::setShutdownqBTWhenDownloadsComplete(bool shutdown)
|
||||
uint Preferences::diskCacheSize() const
|
||||
{
|
||||
uint size = value("Preferences/Downloads/DiskWriteCacheSize", 0).toUInt();
|
||||
|
||||
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes.
|
||||
// These macros may not be available on compilers other than MSVC and GCC
|
||||
#if !defined(_M_X64) && !defined(__amd64__)
|
||||
//1800MiB to leave 248MiB room to the rest of program data in RAM
|
||||
if (size > 1800)
|
||||
size = 1800;
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
size = qMin(size, (uint) 4096); // 4GiB
|
||||
#else
|
||||
// 4GiB
|
||||
if (size > 4 * 1024)
|
||||
size = 4 * 1024;
|
||||
// When build as 32bit binary, set the maximum at less than 2GB to prevent crashes
|
||||
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
|
||||
size = qMin(size, (uint) 1536);
|
||||
#endif
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void Preferences::setDiskCacheSize(uint size)
|
||||
{
|
||||
uint size0 = size;
|
||||
|
||||
#if !defined(_M_X64) && !defined(__amd64__)
|
||||
//1800MiB to leave 248MiB room to the rest of program data in RAM
|
||||
if (size0 > 1800)
|
||||
size0 = 1800;
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
size = qMin(size, (uint) 4096); // 4GiB
|
||||
#else
|
||||
// 4GiB
|
||||
if (size0 > 4 * 1024)
|
||||
size0 = 4 * 1024;
|
||||
// allocate 1536MiB and leave 512MiB to the rest of program data in RAM
|
||||
size = qMin(size, (uint) 1536);
|
||||
#endif
|
||||
|
||||
setValue("Preferences/Downloads/DiskWriteCacheSize", size0);
|
||||
setValue("Preferences/Downloads/DiskWriteCacheSize", size);
|
||||
}
|
||||
|
||||
uint Preferences::diskCacheTTL() const
|
||||
@@ -1417,12 +1460,12 @@ void Preferences::setOutgoingPortsMax(uint val)
|
||||
setValue("Preferences/Advanced/OutgoingPortsMax", val);
|
||||
}
|
||||
|
||||
bool Preferences::ignoreLimitsOnLAN() const
|
||||
bool Preferences::getIgnoreLimitsOnLAN() const
|
||||
{
|
||||
return value("Preferences/Advanced/IgnoreLimitsLAN", true).toBool();
|
||||
}
|
||||
|
||||
void Preferences::ignoreLimitsOnLAN(bool ignore)
|
||||
void Preferences::setIgnoreLimitsOnLAN(bool ignore)
|
||||
{
|
||||
setValue("Preferences/Advanced/IgnoreLimitsLAN", ignore);
|
||||
}
|
||||
@@ -1594,6 +1637,13 @@ void Preferences::setTorrentLabels(const QStringList& labels)
|
||||
setValue("TransferListFilters/customLabels", labels);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabelExternal(const QString &label)
|
||||
{
|
||||
addTorrentLabel(label);
|
||||
QString toEmit = label;
|
||||
emit externalLabelAdded(toEmit);
|
||||
}
|
||||
|
||||
void Preferences::addTorrentLabel(const QString& label)
|
||||
{
|
||||
QStringList labels = value("TransferListFilters/customLabels").toStringList();
|
||||
@@ -1797,12 +1847,12 @@ bool Preferences::isMagnetLinkAssocSet()
|
||||
|
||||
// Check magnet link assoc
|
||||
QRegExp exe_reg("\"([^\"]+)\".*");
|
||||
QString shell_command = fsutils::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
||||
QString shell_command = Utils::Fs::toNativePath(settings.value("magnet/shell/open/command/Default", "").toString());
|
||||
if (exe_reg.indexIn(shell_command) < 0)
|
||||
return false;
|
||||
QString assoc_exe = exe_reg.cap(1);
|
||||
qDebug("exe: %s", qPrintable(assoc_exe));
|
||||
if (assoc_exe.compare(fsutils::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
||||
if (assoc_exe.compare(Utils::Fs::toNativePath(qApp->applicationFilePath()), Qt::CaseInsensitive) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -1838,9 +1888,9 @@ void Preferences::setMagnetLinkAssoc(bool set)
|
||||
settings.setValue("magnet/Default", "URL:Magnet link");
|
||||
settings.setValue("magnet/Content Type", "application/x-magnet");
|
||||
settings.setValue("magnet/URL Protocol", "");
|
||||
settings.setValue("magnet/DefaultIcon/Default", fsutils::toNativePath(icon_str));
|
||||
settings.setValue("magnet/DefaultIcon/Default", Utils::Fs::toNativePath(icon_str));
|
||||
settings.setValue("magnet/shell/Default", "open");
|
||||
settings.setValue("magnet/shell/open/command/Default", fsutils::toNativePath(command_str));
|
||||
settings.setValue("magnet/shell/open/command/Default", Utils::Fs::toNativePath(command_str));
|
||||
}
|
||||
else if (isMagnetLinkAssocSet()) {
|
||||
settings.remove("magnet");
|
||||
@@ -2197,36 +2247,36 @@ void Preferences::setRssOpenFolders(const QStringList &folders)
|
||||
QByteArray Preferences::getRssHSplitterState() const
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
return value("rss/qt5/splitter_h").toByteArray();
|
||||
return value("Rss/qt5/splitter_h").toByteArray();
|
||||
#else
|
||||
return value("rss/splitter_h").toByteArray();
|
||||
return value("Rss/splitter_h").toByteArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setRssHSplitterState(const QByteArray &state)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
setValue("rss/qt5/splitter_h", state);
|
||||
setValue("Rss/qt5/splitter_h", state);
|
||||
#else
|
||||
setValue("rss/splitter_h", state);
|
||||
setValue("Rss/splitter_h", state);
|
||||
#endif
|
||||
}
|
||||
|
||||
QByteArray Preferences::getRssVSplitterState() const
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
return value("rss/qt5/splitter_v").toByteArray();
|
||||
return value("Rss/qt5/splitter_v").toByteArray();
|
||||
#else
|
||||
return value("rss/splitter_v").toByteArray();
|
||||
return value("Rss/splitter_v").toByteArray();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Preferences::setRssVSplitterState(const QByteArray &state)
|
||||
{
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
setValue("rss/qt5/splitter_v", state);
|
||||
setValue("Rss/qt5/splitter_v", state);
|
||||
#else
|
||||
setValue("rss/splitter_v", state);
|
||||
setValue("Rss/splitter_v", state);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2498,3 +2548,28 @@ void Preferences::setHostNameCookies(const QString &host_name, const QList<QByte
|
||||
hosts_table.insert(host_name, raw_cookies);
|
||||
setValue("Rss/hosts_cookies", hosts_table);
|
||||
}
|
||||
|
||||
int Preferences::getSpeedWidgetPeriod() const {
|
||||
return value("SpeedWidget/period", 1).toInt();
|
||||
}
|
||||
|
||||
void Preferences::setSpeedWidgetPeriod(const int period) {
|
||||
setValue("SpeedWidget/period", period);
|
||||
}
|
||||
|
||||
bool Preferences::getSpeedWidgetGraphEnable(int id) const
|
||||
{
|
||||
// UP and DOWN graphs enabled by default
|
||||
return value("SpeedWidget/graph_enable_" + QString::number(id), (id == 0 || id == 1)).toBool();
|
||||
}
|
||||
|
||||
void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable)
|
||||
{
|
||||
setValue("SpeedWidget/graph_enable_" + QString::number(id), enable);
|
||||
}
|
||||
|
||||
void Preferences::apply()
|
||||
{
|
||||
if (save())
|
||||
emit changed();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include <QNetworkCookie>
|
||||
#include <QVariant>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
#include "core/types.h"
|
||||
|
||||
enum scheduler_days
|
||||
{
|
||||
@@ -57,12 +57,6 @@ enum scheduler_days
|
||||
SUN
|
||||
};
|
||||
|
||||
enum maxRatioAction
|
||||
{
|
||||
PAUSE_ACTION,
|
||||
REMOVE_ACTION
|
||||
};
|
||||
|
||||
namespace Proxy
|
||||
{
|
||||
enum ProxyType
|
||||
@@ -101,7 +95,9 @@ class Preferences: public QObject
|
||||
Q_DISABLE_COPY(Preferences)
|
||||
|
||||
private:
|
||||
explicit Preferences();
|
||||
Preferences();
|
||||
~Preferences();
|
||||
|
||||
static Preferences* m_instance;
|
||||
QHash<QString, QVariant> m_data;
|
||||
int m_randomPort;
|
||||
@@ -111,16 +107,17 @@ private:
|
||||
const QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
|
||||
void setValue(const QString &key, const QVariant &value);
|
||||
|
||||
private slots:
|
||||
bool save();
|
||||
|
||||
signals:
|
||||
void changed();
|
||||
|
||||
public slots:
|
||||
void save();
|
||||
void externalLabelAdded(QString&);
|
||||
|
||||
public:
|
||||
static void initInstance();
|
||||
static void freeInstance();
|
||||
static Preferences* instance();
|
||||
static void drop();
|
||||
~Preferences();
|
||||
|
||||
// General options
|
||||
QString getLocale() const;
|
||||
@@ -182,6 +179,8 @@ public:
|
||||
QList<bool> getDownloadInScanDirs() const;
|
||||
void setDownloadInScanDirs(const QList<bool> &list);
|
||||
QString getScanDirsLastPath() const;
|
||||
void setScanDirsDownloadPaths(const QStringList &downloadpaths);
|
||||
QStringList getScanDirsDownloadPaths() const;
|
||||
void setScanDirsLastPath(const QString &path);
|
||||
bool isTorrentExportEnabled() const;
|
||||
QString getTorrentExportDir() const;
|
||||
@@ -248,10 +247,10 @@ public:
|
||||
void setProxyType(int type);
|
||||
bool proxyPeerConnections() const;
|
||||
void setProxyPeerConnections(bool enabled);
|
||||
#if LIBTORRENT_VERSION_NUM >= 10000
|
||||
bool getForceProxy() const;
|
||||
void setForceProxy(bool enabled);
|
||||
#endif
|
||||
void setProxyOnlyForTorrents(bool enabled);
|
||||
bool isProxyOnlyForTorrents() const;
|
||||
|
||||
// Bittorrent options
|
||||
int getMaxConnecs() const;
|
||||
@@ -274,10 +273,14 @@ public:
|
||||
void setLSDEnabled(bool enabled);
|
||||
int getEncryptionSetting() const;
|
||||
void setEncryptionSetting(int val);
|
||||
bool isAddTrackersEnabled() const;
|
||||
void setAddTrackersEnabled(bool enabled);
|
||||
QString getTrackersList() const;
|
||||
void setTrackersList(const QString &val);
|
||||
qreal getGlobalMaxRatio() const;
|
||||
void setGlobalMaxRatio(qreal ratio);
|
||||
int getMaxRatioAction() const;
|
||||
void setMaxRatioAction(int act);
|
||||
MaxRatioAction getMaxRatioAction() const;
|
||||
void setMaxRatioAction(MaxRatioAction act);
|
||||
|
||||
// IP Filter
|
||||
bool isFilteringEnabled() const;
|
||||
@@ -367,8 +370,8 @@ public:
|
||||
void setOutgoingPortsMin(uint val);
|
||||
uint outgoingPortsMax() const;
|
||||
void setOutgoingPortsMax(uint val);
|
||||
bool ignoreLimitsOnLAN() const;
|
||||
void ignoreLimitsOnLAN(bool ignore);
|
||||
bool getIgnoreLimitsOnLAN() const;
|
||||
void setIgnoreLimitsOnLAN(bool ignore);
|
||||
bool includeOverheadInLimits() const;
|
||||
void includeOverheadInLimits(bool include);
|
||||
bool trackerExchangeEnabled() const;
|
||||
@@ -403,6 +406,7 @@ public:
|
||||
#endif
|
||||
QStringList getTorrentLabels() const;
|
||||
void setTorrentLabels(const QStringList& labels);
|
||||
void addTorrentLabelExternal(const QString &label);
|
||||
void addTorrentLabel(const QString& label);
|
||||
void removeTorrentLabel(const QString& label);
|
||||
bool recursiveDownloadDisabled() const;
|
||||
@@ -536,10 +540,18 @@ public:
|
||||
QList<QNetworkCookie> getHostNameQNetworkCookies(const QString& host_name) const;
|
||||
void setHostNameCookies(const QString &host_name, const QList<QByteArray> &cookies);
|
||||
|
||||
// SpeedWidget
|
||||
int getSpeedWidgetPeriod() const;
|
||||
void setSpeedWidgetPeriod(const int period);
|
||||
bool getSpeedWidgetGraphEnable(int id) const;
|
||||
void setSpeedWidgetGraphEnable(int id, const bool enable);
|
||||
|
||||
public slots:
|
||||
void setStatusFilterState(bool checked);
|
||||
void setLabelFilterState(bool checked);
|
||||
void setTrackerFilterState(bool checked);
|
||||
|
||||
void apply();
|
||||
};
|
||||
|
||||
#endif // PREFERENCES_H
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2014 Ivan Sorokin
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : vanyacpp@gmail.com
|
||||
*/
|
||||
|
||||
#include "alertdispatcher.h"
|
||||
|
||||
#include <libtorrent/session.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <QMutexLocker>
|
||||
|
||||
const size_t DEFAULT_ALERTS_CAPACITY = 32;
|
||||
|
||||
struct QAlertDispatcher::Tag {
|
||||
Tag(QAlertDispatcher* dispatcher);
|
||||
|
||||
QAlertDispatcher* dispatcher;
|
||||
QMutex alerts_mutex;
|
||||
};
|
||||
|
||||
QAlertDispatcher::Tag::Tag(QAlertDispatcher* dispatcher)
|
||||
: dispatcher(dispatcher)
|
||||
{}
|
||||
|
||||
QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_session(session)
|
||||
, current_tag(new Tag(this))
|
||||
, event_posted(false)
|
||||
{
|
||||
alerts.reserve(DEFAULT_ALERTS_CAPACITY);
|
||||
m_session->set_alert_dispatch(boost::bind(&QAlertDispatcher::dispatch, current_tag, _1));
|
||||
}
|
||||
|
||||
QAlertDispatcher::~QAlertDispatcher() {
|
||||
// When QAlertDispatcher is destoyed, libtorrent still can call
|
||||
// QAlertDispatcher::dispatch a few times after destruction. This is
|
||||
// handled by passing a "tag". A tag is a object that references QAlertDispatch.
|
||||
// Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag
|
||||
// and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called
|
||||
// with invalid tag it simply discard an alert.
|
||||
|
||||
{
|
||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
||||
current_tag->dispatcher = 0;
|
||||
current_tag.clear();
|
||||
}
|
||||
|
||||
typedef boost::function<void (std::auto_ptr<libtorrent::alert>)> dispatch_function_t;
|
||||
m_session->set_alert_dispatch(dispatch_function_t());
|
||||
}
|
||||
|
||||
void QAlertDispatcher::getPendingAlertsNoWait(std::vector<libtorrent::alert*>& out) {
|
||||
Q_ASSERT(out.empty());
|
||||
out.reserve(DEFAULT_ALERTS_CAPACITY);
|
||||
|
||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
||||
alerts.swap(out);
|
||||
event_posted = false;
|
||||
}
|
||||
|
||||
void QAlertDispatcher::getPendingAlerts(std::vector<libtorrent::alert*>& out, unsigned long time) {
|
||||
Q_ASSERT(out.empty());
|
||||
out.reserve(DEFAULT_ALERTS_CAPACITY);
|
||||
|
||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
||||
|
||||
while (alerts.empty())
|
||||
alerts_condvar.wait(¤t_tag->alerts_mutex, time);
|
||||
|
||||
alerts.swap(out);
|
||||
event_posted = false;
|
||||
}
|
||||
|
||||
void QAlertDispatcher::dispatch(QSharedPointer<Tag> tag,
|
||||
std::auto_ptr<libtorrent::alert> alert_ptr) {
|
||||
QMutexLocker lock(&(tag->alerts_mutex));
|
||||
QAlertDispatcher* that = tag->dispatcher;
|
||||
if (!that)
|
||||
return;
|
||||
|
||||
bool was_empty = that->alerts.empty();
|
||||
that->alerts.push_back(alert_ptr.release());
|
||||
if (was_empty)
|
||||
that->alerts_condvar.wakeAll();
|
||||
|
||||
that->enqueueToMainThread();
|
||||
|
||||
Q_ASSERT(that->current_tag == tag);
|
||||
}
|
||||
|
||||
void QAlertDispatcher::enqueueToMainThread() {
|
||||
if (!event_posted) {
|
||||
event_posted = true;
|
||||
QMetaObject::invokeMethod(this, "deliverSignal", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QAlertDispatcher::deliverSignal() {
|
||||
emit alertsReceived();
|
||||
|
||||
QMutexLocker lock(¤t_tag->alerts_mutex);
|
||||
event_posted = false;
|
||||
|
||||
if (!alerts.empty())
|
||||
enqueueToMainThread();
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
#ifndef BANDWIDTHSCHEDULER_H
|
||||
#define BANDWIDTHSCHEDULER_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QTime>
|
||||
#include <QDateTime>
|
||||
#include "preferences.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
class BandwidthScheduler: public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BandwidthScheduler(QObject *parent): QTimer(parent) {
|
||||
Q_ASSERT(Preferences::instance()->isSchedulerEnabled());
|
||||
// Signal shot, we call start() again manually
|
||||
setSingleShot(true);
|
||||
// Connect Signals/Slots
|
||||
connect(this, SIGNAL(timeout()), this, SLOT(start()));
|
||||
}
|
||||
|
||||
public slots:
|
||||
void start() {
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
Q_ASSERT(pref->isSchedulerEnabled());
|
||||
bool alt_bw_enabled = pref->isAltBandwidthEnabled();
|
||||
|
||||
QTime start = pref->getSchedulerStartTime();
|
||||
QTime end = pref->getSchedulerEndTime();
|
||||
QTime now = QTime::currentTime();
|
||||
int sched_days = pref->getSchedulerDays();
|
||||
int day = QDateTime::currentDateTime().toLocalTime().date().dayOfWeek();
|
||||
bool new_mode = false;
|
||||
bool reverse = false;
|
||||
|
||||
if (start > end) {
|
||||
QTime temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
reverse = true;
|
||||
}
|
||||
|
||||
if (start <= now && end >= now) {
|
||||
switch(sched_days) {
|
||||
case EVERY_DAY:
|
||||
new_mode = true;
|
||||
break;
|
||||
case WEEK_ENDS:
|
||||
if (day == 6 || day == 7)
|
||||
new_mode = true;
|
||||
break;
|
||||
case WEEK_DAYS:
|
||||
if (day != 6 && day != 7)
|
||||
new_mode = true;
|
||||
break;
|
||||
default:
|
||||
if (day == sched_days - 2)
|
||||
new_mode = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reverse)
|
||||
new_mode = !new_mode;
|
||||
|
||||
if (new_mode != alt_bw_enabled)
|
||||
emit switchToAlternativeMode(new_mode);
|
||||
|
||||
// Timeout regularly to accomodate for external system clock changes
|
||||
// eg from the user or from a timesync utility
|
||||
QTimer::start(1500);
|
||||
}
|
||||
|
||||
signals:
|
||||
void switchToAlternativeMode(bool alternative);
|
||||
};
|
||||
|
||||
#endif // BANDWIDTHSCHEDULER_H
|
||||
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include "filterparserthread.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
|
||||
#include <libtorrent/session.hpp>
|
||||
#include <libtorrent/ip_filter.hpp>
|
||||
|
||||
FilterParserThread::FilterParserThread(QObject* parent, libtorrent::session *s) : QThread(parent), s(s), abort(false) {
|
||||
|
||||
}
|
||||
|
||||
FilterParserThread::~FilterParserThread() {
|
||||
abort = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
// Parser for eMule ip filter in DAT format
|
||||
int FilterParserThread::parseDATFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
||||
int ruleCount = 0;
|
||||
QFile file(filePath);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
unsigned int nbLine = 0;
|
||||
while (!file.atEnd() && !abort) {
|
||||
++nbLine;
|
||||
QByteArray line = file.readLine();
|
||||
// Ignoring empty lines
|
||||
line = line.trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// Ignoring commented lines
|
||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||
|
||||
// Line should be splitted by commas
|
||||
QList<QByteArray> partsList = line.split(',');
|
||||
const uint nbElem = partsList.size();
|
||||
|
||||
// IP Range should be splitted by a dash
|
||||
QList<QByteArray> IPs = partsList.first().split('-');
|
||||
if (IPs.size() != 2) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Line was %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
boost::system::error_code ec;
|
||||
const QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||
if (strStartIP.isEmpty()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
|
||||
if (ec) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP of the range is malformated: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
const QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||
if (strEndIP.isEmpty()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||
continue;
|
||||
}
|
||||
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
|
||||
if (ec) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("End IP of the range is malformated: %s", qPrintable(strEndIP));
|
||||
continue;
|
||||
}
|
||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||
qDebug("Ipfilter.dat: line %d is malformed.", nbLine);
|
||||
qDebug("One IP is IPv4 and the other is IPv6!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if there is an access value (apparently not mandatory)
|
||||
int nbAccess = 0;
|
||||
if (nbElem > 1) {
|
||||
// There is possibly one
|
||||
nbAccess = partsList.at(1).trimmed().toInt();
|
||||
}
|
||||
|
||||
if (nbAccess > 127) {
|
||||
// Ignoring this rule because access value is too high
|
||||
continue;
|
||||
}
|
||||
// Now Add to the filter
|
||||
try {
|
||||
filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
}catch(exception) {
|
||||
qDebug("Bad line in filter file, avoided crash...");
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// Parser for PeerGuardian ip filter in p2p format
|
||||
int FilterParserThread::parseP2PFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
||||
int ruleCount = 0;
|
||||
QFile file(filePath);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
unsigned int nbLine = 0;
|
||||
while (!file.atEnd() && !abort) {
|
||||
++nbLine;
|
||||
QByteArray line = file.readLine().trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
// Ignoring commented lines
|
||||
if (line.startsWith('#') || line.startsWith("//")) continue;
|
||||
// Line is splitted by :
|
||||
QList<QByteArray> partsList = line.split(':');
|
||||
if (partsList.size() < 2) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
continue;
|
||||
}
|
||||
// Get IP range
|
||||
QList<QByteArray> IPs = partsList.last().split('-');
|
||||
if (IPs.size() != 2) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
QString strStartIP = cleanupIPAddress(IPs.at(0));
|
||||
if (strStartIP.isEmpty()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
libtorrent::address startAddr = libtorrent::address::from_string(qPrintable(strStartIP), ec);
|
||||
if (ec) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Start IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
QString strEndIP = cleanupIPAddress(IPs.at(1));
|
||||
if (strEndIP.isEmpty()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
libtorrent::address endAddr = libtorrent::address::from_string(qPrintable(strEndIP), ec);
|
||||
if (ec) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("End IP is invalid: %s", qPrintable(strStartIP));
|
||||
continue;
|
||||
}
|
||||
if (startAddr.is_v4() != endAddr.is_v4()) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
filter.add_rule(startAddr, endAddr, libtorrent::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
} catch(std::exception&) {
|
||||
qDebug("p2p file: line %d is malformed.", nbLine);
|
||||
qDebug("Line was: %s", line.constData());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
int FilterParserThread::getlineInStream(QDataStream& stream, string& name, char delim) {
|
||||
char c;
|
||||
int total_read = 0;
|
||||
int read;
|
||||
do {
|
||||
read = stream.readRawData(&c, 1);
|
||||
total_read += read;
|
||||
if (read > 0) {
|
||||
if (c != delim) {
|
||||
name += c;
|
||||
} else {
|
||||
// Delim found
|
||||
return total_read;
|
||||
}
|
||||
}
|
||||
} while(read > 0);
|
||||
return total_read;
|
||||
}
|
||||
|
||||
// Parser for PeerGuardian ip filter in p2p format
|
||||
int FilterParserThread::parseP2BFilterFile(QString filePath, libtorrent::ip_filter& filter) {
|
||||
int ruleCount = 0;
|
||||
QFile file(filePath);
|
||||
if (file.exists()) {
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
QDataStream stream(&file);
|
||||
// Read header
|
||||
char buf[7];
|
||||
unsigned char version;
|
||||
if (
|
||||
!stream.readRawData(buf, sizeof(buf)) ||
|
||||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
|
||||
!stream.readRawData((char*)&version, sizeof(version))
|
||||
) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
if (version==1 || version==2) {
|
||||
qDebug ("p2b version 1 or 2");
|
||||
unsigned int start, end;
|
||||
|
||||
string name;
|
||||
while(getlineInStream(stream, name, '\0') && !abort) {
|
||||
if (
|
||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||
!stream.readRawData((char*)&end, sizeof(end))
|
||||
) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
// Network byte order to Host byte order
|
||||
// asio address_v4 contructor expects it
|
||||
// that way
|
||||
libtorrent::address_v4 first(ntohl(start));
|
||||
libtorrent::address_v4 last(ntohl(end));
|
||||
// Apply to bittorrent session
|
||||
try {
|
||||
filter.add_rule(first, last, libtorrent::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
} catch(std::exception&) {}
|
||||
}
|
||||
}
|
||||
else if (version==3) {
|
||||
qDebug ("p2b version 3");
|
||||
unsigned int namecount;
|
||||
if (!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
namecount=ntohl(namecount);
|
||||
// Reading names although, we don't really care about them
|
||||
for (unsigned int i=0; i<namecount; i++) {
|
||||
string name;
|
||||
if (!getlineInStream(stream, name, '\0')) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
if (abort) return ruleCount;
|
||||
}
|
||||
// Reading the ranges
|
||||
unsigned int rangecount;
|
||||
if (!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
rangecount=ntohl(rangecount);
|
||||
|
||||
unsigned int name, start, end;
|
||||
|
||||
for (unsigned int i=0; i<rangecount; i++) {
|
||||
if (
|
||||
!stream.readRawData((char*)&name, sizeof(name)) ||
|
||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||
!stream.readRawData((char*)&end, sizeof(end))
|
||||
) {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
// Network byte order to Host byte order
|
||||
// asio address_v4 contructor expects it
|
||||
// that way
|
||||
libtorrent::address_v4 first(ntohl(start));
|
||||
libtorrent::address_v4 last(ntohl(end));
|
||||
// Apply to bittorrent session
|
||||
try {
|
||||
filter.add_rule(first, last, libtorrent::ip_filter::blocked);
|
||||
++ruleCount;
|
||||
} catch(std::exception&) {}
|
||||
if (abort) return ruleCount;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||
return ruleCount;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
return ruleCount;
|
||||
}
|
||||
|
||||
// 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 FilterParserThread::processFilterFile(QString _filePath) {
|
||||
if (isRunning()) {
|
||||
// Already parsing a filter, abort first
|
||||
abort = true;
|
||||
wait();
|
||||
}
|
||||
abort = false;
|
||||
filePath = _filePath;
|
||||
// Run it
|
||||
start();
|
||||
}
|
||||
|
||||
void FilterParserThread::processFilterList(libtorrent::session *s, const QStringList& IPs) {
|
||||
// First, import current filter
|
||||
libtorrent::ip_filter filter = s->get_ip_filter();
|
||||
foreach (const QString &ip, IPs) {
|
||||
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
|
||||
boost::system::error_code ec;
|
||||
libtorrent::address addr = libtorrent::address::from_string(ip.toLocal8Bit().constData(), ec);
|
||||
Q_ASSERT(!ec);
|
||||
if (!ec)
|
||||
filter.add_rule(addr, addr, libtorrent::ip_filter::blocked);
|
||||
}
|
||||
s->set_ip_filter(filter);
|
||||
}
|
||||
|
||||
QString FilterParserThread::cleanupIPAddress(QString _ip) {
|
||||
QHostAddress ip(_ip.trimmed());
|
||||
if (ip.isNull()) {
|
||||
return QString();
|
||||
}
|
||||
return ip.toString();
|
||||
}
|
||||
|
||||
void FilterParserThread::run() {
|
||||
qDebug("Processing filter file");
|
||||
libtorrent::ip_filter filter = s->get_ip_filter();
|
||||
int ruleCount = 0;
|
||||
if (filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
||||
// PeerGuardian p2p file
|
||||
ruleCount = parseP2PFilterFile(filePath, filter);
|
||||
} else {
|
||||
if (filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
|
||||
// PeerGuardian p2b file
|
||||
ruleCount = parseP2BFilterFile(filePath, filter);
|
||||
} else {
|
||||
// Default: eMule DAT format
|
||||
ruleCount = parseDATFilterFile(filePath, filter);
|
||||
}
|
||||
}
|
||||
if (abort)
|
||||
return;
|
||||
try {
|
||||
s->set_ip_filter(filter);
|
||||
emit IPFilterParsed(ruleCount);
|
||||
} catch(std::exception&) {
|
||||
emit IPFilterError();
|
||||
}
|
||||
qDebug("IP Filter thread: finished parsing, filter applied");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
#ifndef __BITTORRENT_H__
|
||||
#define __BITTORRENT_H__
|
||||
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QUrl>
|
||||
#include <QStringList>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
#include <libtorrent/version.hpp>
|
||||
|
||||
#include "qtorrenthandle.h"
|
||||
#include "trackerinfos.h"
|
||||
#include "misc.h"
|
||||
|
||||
#ifndef DISABLE_GUI
|
||||
#include "rssdownloadrule.h"
|
||||
#endif
|
||||
|
||||
namespace libtorrent {
|
||||
struct add_torrent_params;
|
||||
struct pe_settings;
|
||||
struct proxy_settings;
|
||||
class session;
|
||||
struct session_status;
|
||||
|
||||
class alert;
|
||||
struct torrent_finished_alert;
|
||||
struct save_resume_data_alert;
|
||||
struct file_renamed_alert;
|
||||
struct torrent_deleted_alert;
|
||||
struct storage_moved_alert;
|
||||
struct storage_moved_failed_alert;
|
||||
struct metadata_received_alert;
|
||||
struct file_error_alert;
|
||||
struct file_completed_alert;
|
||||
struct torrent_paused_alert;
|
||||
struct tracker_error_alert;
|
||||
struct tracker_reply_alert;
|
||||
struct tracker_warning_alert;
|
||||
struct portmap_error_alert;
|
||||
struct portmap_alert;
|
||||
struct peer_blocked_alert;
|
||||
struct peer_ban_alert;
|
||||
struct fastresume_rejected_alert;
|
||||
struct url_seed_alert;
|
||||
struct listen_succeeded_alert;
|
||||
struct listen_failed_alert;
|
||||
struct torrent_checked_alert;
|
||||
struct external_ip_alert;
|
||||
struct state_update_alert;
|
||||
struct stats_alert;
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
class upnp;
|
||||
class natpmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
class DownloadThread;
|
||||
class FilterParserThread;
|
||||
class BandwidthScheduler;
|
||||
class ScanFoldersModel;
|
||||
class TorrentSpeedMonitor;
|
||||
class TorrentStatistics;
|
||||
class QAlertDispatcher;
|
||||
|
||||
enum TorrentExportFolder {
|
||||
RegularTorrentExportFolder,
|
||||
FinishedTorrentExportFolder
|
||||
};
|
||||
|
||||
class QTracker;
|
||||
|
||||
class QBtSession : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(QBtSession)
|
||||
|
||||
public:
|
||||
static const qreal MAX_RATIO;
|
||||
|
||||
private:
|
||||
explicit QBtSession();
|
||||
static QBtSession* m_instance;
|
||||
|
||||
public:
|
||||
static QBtSession* instance();
|
||||
static void drop();
|
||||
~QBtSession();
|
||||
QTorrentHandle getTorrentHandle(const QString &hash) const;
|
||||
std::vector<libtorrent::torrent_handle> getTorrents() const;
|
||||
qreal getPayloadDownloadRate() const;
|
||||
qreal getPayloadUploadRate() const;
|
||||
libtorrent::session_status getSessionStatus() const;
|
||||
int getListenPort() const;
|
||||
qreal getRealRatio(const libtorrent::torrent_status &status) const;
|
||||
QHash<QString, TrackerInfos> getTrackersInfo(const QString &hash) const;
|
||||
bool hasActiveTorrents() const;
|
||||
bool hasDownloadingTorrents() const;
|
||||
//int getMaximumActiveDownloads() const;
|
||||
//int getMaximumActiveTorrents() const;
|
||||
inline libtorrent::session* getSession() const { return s; }
|
||||
inline bool useTemporaryFolder() const { return !defaultTempPath.isEmpty(); }
|
||||
inline QString getDefaultSavePath() const { return defaultSavePath; }
|
||||
inline ScanFoldersModel* getScanFoldersModel() const { return m_scanFolders; }
|
||||
inline bool isDHTEnabled() const { return DHTEnabled; }
|
||||
inline bool isLSDEnabled() const { return LSDEnabled; }
|
||||
inline bool isPexEnabled() const { return PeXEnabled; }
|
||||
inline bool isQueueingEnabled() const { return queueingEnabled; }
|
||||
quint64 getAlltimeDL() const;
|
||||
quint64 getAlltimeUL() const;
|
||||
void postTorrentUpdate();
|
||||
|
||||
public slots:
|
||||
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false, bool imported = false);
|
||||
QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false, bool fromScanDir=false, const QString &filePath=QString());
|
||||
void loadSessionState();
|
||||
void saveSessionState();
|
||||
void downloadFromUrl(const QString &url, const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
||||
void deleteTorrent(const QString &hash, bool delete_local_files = false);
|
||||
void startUpTorrents();
|
||||
void recheckTorrent(const QString &hash);
|
||||
void useAlternativeSpeedsLimit(bool alternative);
|
||||
qlonglong getETA(const QString& hash, const libtorrent::torrent_status &status) const;
|
||||
/* Needed by Web UI */
|
||||
void pauseAllTorrents();
|
||||
void pauseTorrent(const QString &hash);
|
||||
void resumeTorrent(const QString &hash, const bool force = false);
|
||||
void resumeAllTorrents();
|
||||
/* End Web UI */
|
||||
void preAllocateAllFiles(bool b);
|
||||
void saveFastResumeData();
|
||||
void enableIPFilter(const QString &filter_path, bool force=false);
|
||||
void disableIPFilter();
|
||||
void setQueueingEnabled(bool enable);
|
||||
void handleDownloadFailure(QString url, QString reason);
|
||||
void handleMagnetRedirect(const QString &url_new, const QString &url_old);
|
||||
#ifndef DISABLE_GUI
|
||||
void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(),
|
||||
const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>(),
|
||||
const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL);
|
||||
#else
|
||||
void downloadUrlAndSkipDialog(QString url, QString save_path=QString(), QString label=QString(),
|
||||
const QList<QNetworkCookie>& cookies = QList<QNetworkCookie>());
|
||||
#endif
|
||||
// Session configuration - Setters
|
||||
void setListeningPort(int port);
|
||||
void setMaxConnectionsPerTorrent(int max);
|
||||
void setMaxUploadsPerTorrent(int max);
|
||||
void setDownloadRateLimit(long rate);
|
||||
void setUploadRateLimit(long rate);
|
||||
void setGlobalMaxRatio(qreal ratio);
|
||||
qreal getGlobalMaxRatio() const { return global_ratio_limit; }
|
||||
void setMaxRatioPerTorrent(const QString &hash, qreal ratio);
|
||||
qreal getMaxRatioPerTorrent(const QString &hash, bool *usesGlobalRatio) const;
|
||||
void removeRatioPerTorrent(const QString &hash);
|
||||
void setDefaultSavePath(const QString &savepath);
|
||||
void setDefaultTempPath(const QString &temppath);
|
||||
void setAppendLabelToSavePath(bool append);
|
||||
void appendLabelToTorrentSavePath(const QTorrentHandle &h);
|
||||
void changeLabelInTorrentSavePath(const QTorrentHandle &h, QString old_label, QString new_label);
|
||||
void appendqBextensionToTorrent(const QTorrentHandle &h, bool append);
|
||||
void setAppendqBExtension(bool append);
|
||||
void setDownloadLimit(QString hash, long val);
|
||||
void setUploadLimit(QString hash, long val);
|
||||
void enableUPnP(bool b);
|
||||
void enableLSD(bool b);
|
||||
void enableDHT(bool b);
|
||||
void processDownloadedFile(QString, QString);
|
||||
#ifndef DISABLE_GUI
|
||||
void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(),
|
||||
const RssDownloadRule::AddPausedState &aps = RssDownloadRule::USE_GLOBAL, const QString &uri_old = QString());
|
||||
#else
|
||||
void addMagnetSkipAddDlg(const QString& uri, const QString& save_path = QString(), const QString& label = QString(), const QString &uri_old = QString());
|
||||
#endif
|
||||
void addMagnetInteractive(const QString& uri);
|
||||
void downloadFromURLList(const QStringList& urls);
|
||||
void banIP(QString ip);
|
||||
void recursiveTorrentDownload(const QTorrentHandle &h);
|
||||
void unhideMagnet(const QString &hash);
|
||||
void addTrackersAndUrlSeeds(const QString &hash, const QStringList &trackers, const QStringList& urlSeeds);
|
||||
|
||||
private:
|
||||
void applyEncryptionSettings(libtorrent::pe_settings se);
|
||||
void setProxySettings(libtorrent::proxy_settings proxySettings);
|
||||
void setSessionSettings(const libtorrent::session_settings &sessionSettings);
|
||||
QString getSavePath(const QString &hash, bool fromScanDir = false, QString filePath = QString::null, bool imported = false);
|
||||
bool loadFastResumeData(const QString &hash, std::vector<char> &buf);
|
||||
void loadTorrentSettings(QTorrentHandle &h);
|
||||
void loadTorrentTempData(QTorrentHandle &h, QString savePath, bool magnet);
|
||||
void initializeAddTorrentParams(const QString &hash, libtorrent::add_torrent_params &p);
|
||||
void updateRatioTimer();
|
||||
void recoverPersistentData(const QString &hash, const std::vector<char> &buf);
|
||||
void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data);
|
||||
void handleAlert(libtorrent::alert* a);
|
||||
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert* p);
|
||||
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert* p);
|
||||
void handleFileRenamedAlert(libtorrent::file_renamed_alert* p);
|
||||
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert* p);
|
||||
void handleStorageMovedAlert(libtorrent::storage_moved_alert* p);
|
||||
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert* p);
|
||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert* p);
|
||||
void handleFileErrorAlert(libtorrent::file_error_alert* p);
|
||||
void handleFileCompletedAlert(libtorrent::file_completed_alert* p);
|
||||
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert* p);
|
||||
void handleTrackerErrorAlert(libtorrent::tracker_error_alert* p);
|
||||
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert* p);
|
||||
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert* p);
|
||||
void handlePortmapWarningAlert(libtorrent::portmap_error_alert* p);
|
||||
void handlePortmapAlert(libtorrent::portmap_alert* p);
|
||||
void handlePeerBlockedAlert(libtorrent::peer_blocked_alert* p);
|
||||
void handlePeerBanAlert(libtorrent::peer_ban_alert* p);
|
||||
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert* p);
|
||||
void handleUrlSeedAlert(libtorrent::url_seed_alert* p);
|
||||
void handleListenSucceededAlert(libtorrent::listen_succeeded_alert *p);
|
||||
void handleListenFailedAlert(libtorrent::listen_failed_alert *p);
|
||||
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert* p);
|
||||
void handleExternalIPAlert(libtorrent::external_ip_alert *p);
|
||||
void handleStateUpdateAlert(libtorrent::state_update_alert *p);
|
||||
void handleStatsAlert(libtorrent::stats_alert *p);
|
||||
|
||||
private slots:
|
||||
void addTorrentsFromScanFolder(QStringList&);
|
||||
void readAlerts();
|
||||
void processBigRatios();
|
||||
void exportTorrentFiles(QString path);
|
||||
void saveTempFastResumeData();
|
||||
void sendNotificationEmail(const QTorrentHandle &h);
|
||||
void autoRunExternalProgram(const QTorrentHandle &h);
|
||||
void mergeTorrents(const QTorrentHandle &h, const boost::intrusive_ptr<libtorrent::torrent_info> t);
|
||||
void mergeTorrents(const QTorrentHandle &h, const QString &magnet_uri);
|
||||
void mergeTorrents_impl(const QTorrentHandle &h, const QStringList &trackers, const QStringList& urlSeeds);
|
||||
void exportTorrentFile(const QTorrentHandle &h, TorrentExportFolder folder = RegularTorrentExportFolder);
|
||||
void handleIPFilterParsed(int ruleCount);
|
||||
void handleIPFilterError();
|
||||
void configureSession();
|
||||
|
||||
signals:
|
||||
void addedTorrent(const QTorrentHandle& h);
|
||||
void torrentAboutToBeRemoved(const QTorrentHandle &h);
|
||||
void pausedTorrent(const QTorrentHandle& h);
|
||||
void resumedTorrent(const QTorrentHandle& h);
|
||||
void finishedTorrent(const QTorrentHandle& h);
|
||||
void fullDiskError(const QTorrentHandle& h, QString msg);
|
||||
void trackerSuccess(const QString &hash, const QString &tracker);
|
||||
void trackerError(const QString &hash, const QString &tracker);
|
||||
void trackerWarning(const QString &hash, const QString &tracker);
|
||||
void trackerAuthenticationRequired(const QTorrentHandle& h);
|
||||
void newDownloadedTorrent(QString path, QString url);
|
||||
void newDownloadedTorrentFromRss(QString url);
|
||||
void newMagnetLink(const QString& link);
|
||||
void updateFileSize(const QString &hash);
|
||||
void downloadFromUrlFailure(QString url, QString reason);
|
||||
void torrentFinishedChecking(const QTorrentHandle& h);
|
||||
void metadataReceived(const QTorrentHandle &h);
|
||||
void savePathChanged(const QTorrentHandle &h);
|
||||
void alternativeSpeedsModeChanged(bool alternative);
|
||||
void recursiveTorrentDownloadPossible(const QTorrentHandle &h);
|
||||
void ipFilterParsed(bool error, int ruleCount);
|
||||
void metadataReceivedHidden(const QTorrentHandle &h);
|
||||
void stateUpdate(const std::vector<libtorrent::torrent_status> &statuses);
|
||||
void statsReceived(const libtorrent::stats_alert&);
|
||||
void trackersAdded(const QStringList &trackers, const QString &hash);
|
||||
void trackerlessChange(bool trackerless, const QString &hash);
|
||||
void reloadTrackersAndUrlSeeds(const QTorrentHandle &h);
|
||||
|
||||
private:
|
||||
// Bittorrent
|
||||
libtorrent::session *s;
|
||||
QPointer<BandwidthScheduler> bd_scheduler;
|
||||
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl)
|
||||
#ifndef DISABLE_GUI
|
||||
QMap<QUrl, RssDownloadRule::AddPausedState> addpaused_fromurl;
|
||||
#endif
|
||||
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
||||
QHash<QString, QString> savePathsToRemove;
|
||||
QStringList torrentsToPausedAfterChecking;
|
||||
QTimer resumeDataTimer;
|
||||
// Ratio
|
||||
QPointer<QTimer> BigRatioTimer;
|
||||
// HTTP
|
||||
DownloadThread* downloader;
|
||||
// File System
|
||||
ScanFoldersModel *m_scanFolders;
|
||||
// Settings
|
||||
bool preAllocateAll;
|
||||
qreal global_ratio_limit;
|
||||
int high_ratio_action;
|
||||
bool LSDEnabled;
|
||||
bool DHTEnabled;
|
||||
bool PeXEnabled;
|
||||
bool queueingEnabled;
|
||||
bool appendLabelToSavePath;
|
||||
bool m_torrentExportEnabled;
|
||||
bool m_finishedTorrentExportEnabled;
|
||||
bool appendqBExtension;
|
||||
QString defaultSavePath;
|
||||
QString defaultTempPath;
|
||||
// IP filtering
|
||||
QPointer<FilterParserThread> filterParser;
|
||||
QString filterPath;
|
||||
QList<QUrl> url_skippingDlg;
|
||||
// GeoIP
|
||||
#ifndef DISABLE_GUI
|
||||
bool geoipDBLoaded;
|
||||
bool resolve_countries;
|
||||
#endif
|
||||
// Tracker
|
||||
QPointer<QTracker> m_tracker;
|
||||
TorrentSpeedMonitor *m_speedMonitor;
|
||||
shutDownAction m_shutdownAct;
|
||||
// Port forwarding
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
libtorrent::upnp *m_upnp;
|
||||
libtorrent::natpmp *m_natpmp;
|
||||
#endif
|
||||
QAlertDispatcher* m_alertDispatcher;
|
||||
TorrentStatistics* m_torrentStatistics;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,25 +0,0 @@
|
||||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/qbtsession.h \
|
||||
$$PWD/qtorrenthandle.h \
|
||||
$$PWD/bandwidthscheduler.h \
|
||||
$$PWD/trackerinfos.h \
|
||||
$$PWD/torrentspeedmonitor.h \
|
||||
$$PWD/filterparserthread.h \
|
||||
$$PWD/alertdispatcher.h \
|
||||
$$PWD/torrentstatistics.h
|
||||
|
||||
SOURCES += $$PWD/qbtsession.cpp \
|
||||
$$PWD/qtorrenthandle.cpp \
|
||||
$$PWD/torrentspeedmonitor.cpp \
|
||||
$$PWD/alertdispatcher.cpp \
|
||||
$$PWD/torrentstatistics.cpp \
|
||||
$$PWD/filterparserthread.cpp
|
||||
|
||||
!contains(DEFINES, DISABLE_GUI) {
|
||||
HEADERS += $$PWD/torrentmodel.h \
|
||||
$$PWD/shutdownconfirm.h
|
||||
|
||||
SOURCES += $$PWD/torrentmodel.cpp \
|
||||
$$PWD/shutdownconfirm.cpp
|
||||
}
|
||||
@@ -1,857 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QByteArray>
|
||||
#include <math.h>
|
||||
#include "fs_utils.h"
|
||||
#include "misc.h"
|
||||
#include "preferences.h"
|
||||
#include "qtorrenthandle.h"
|
||||
#include "torrentpersistentdata.h"
|
||||
#include "qbtsession.h"
|
||||
#include <libtorrent/version.hpp>
|
||||
#include <libtorrent/magnet_uri.hpp>
|
||||
#include <libtorrent/torrent_info.hpp>
|
||||
#include <libtorrent/bencode.hpp>
|
||||
#include <libtorrent/entry.hpp>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace libtorrent;
|
||||
using namespace std;
|
||||
|
||||
static QPair<int, int> get_file_extremity_pieces(const torrent_info& t, int file_index)
|
||||
{
|
||||
const int num_pieces = t.num_pieces();
|
||||
const int piece_size = t.piece_length();
|
||||
const file_entry& file = t.file_at(file_index);
|
||||
|
||||
// Determine the first and last piece of the file
|
||||
int first_piece = floor((file.offset + 1) / (float) piece_size);
|
||||
Q_ASSERT(first_piece >= 0 && first_piece < num_pieces);
|
||||
|
||||
int num_pieces_in_file = ceil(file.size / (float) piece_size);
|
||||
int last_piece = first_piece + num_pieces_in_file - 1;
|
||||
Q_ASSERT(last_piece >= 0 && last_piece < num_pieces);
|
||||
|
||||
return qMakePair(first_piece, last_piece);
|
||||
}
|
||||
|
||||
QTorrentHandle::QTorrentHandle(const torrent_handle& h): torrent_handle(h)
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
|
||||
QString QTorrentHandle::hash() const
|
||||
{
|
||||
return misc::toQString(torrent_handle::info_hash());
|
||||
}
|
||||
|
||||
QString QTorrentHandle::name() const
|
||||
{
|
||||
QString name = TorrentPersistentData::instance()->getName(hash());
|
||||
if (name.isEmpty()) {
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
name = misc::toQStringU(torrent_handle::name());
|
||||
#else
|
||||
name = misc::toQStringU(status(query_name).name);
|
||||
#endif
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
QString QTorrentHandle::creation_date() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
boost::optional<time_t> t = torrent_handle::get_torrent_info().creation_date();
|
||||
#else
|
||||
boost::optional<time_t> t = torrent_handle::torrent_file()->creation_date();
|
||||
#endif
|
||||
return t ? misc::toQString(*t) : "";
|
||||
}
|
||||
|
||||
qlonglong QTorrentHandle::creation_date_unix() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
boost::optional<time_t> t = torrent_handle::get_torrent_info().creation_date();
|
||||
#else
|
||||
boost::optional<time_t> t = torrent_handle::torrent_file()->creation_date();
|
||||
#endif
|
||||
return t ? *t : -1;
|
||||
}
|
||||
|
||||
QString QTorrentHandle::current_tracker() const
|
||||
{
|
||||
return misc::toQString(status(0x0).current_tracker);
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_paused() const
|
||||
{
|
||||
return is_paused(status(0x0));
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_queued() const
|
||||
{
|
||||
return is_queued(status(0x0));
|
||||
}
|
||||
|
||||
size_type QTorrentHandle::total_size() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return torrent_handle::get_torrent_info().total_size();
|
||||
#else
|
||||
return torrent_handle::torrent_file()->total_size();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_type QTorrentHandle::piece_length() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return torrent_handle::get_torrent_info().piece_length();
|
||||
#else
|
||||
return torrent_handle::torrent_file()->piece_length();
|
||||
#endif
|
||||
}
|
||||
|
||||
int QTorrentHandle::num_pieces() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return torrent_handle::get_torrent_info().num_pieces();
|
||||
#else
|
||||
return torrent_handle::torrent_file()->num_pieces();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QTorrentHandle::first_last_piece_first() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
torrent_info const* t = &get_torrent_info();
|
||||
#else
|
||||
boost::intrusive_ptr<torrent_info const> t = torrent_file();
|
||||
#endif
|
||||
|
||||
// Get int first media file
|
||||
int index = 0;
|
||||
for (index = 0; index < t->num_files(); ++index) {
|
||||
QString path = misc::toQStringU(t->file_at(index).path);
|
||||
const QString ext = fsutils::fileExtension(path);
|
||||
if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (index >= t->num_files()) // No media file
|
||||
return false;
|
||||
|
||||
QPair<int, int> extremities = get_file_extremity_pieces(*t, index);
|
||||
|
||||
return (torrent_handle::piece_priority(extremities.first) == 7)
|
||||
&& (torrent_handle::piece_priority(extremities.second) == 7);
|
||||
}
|
||||
|
||||
QString QTorrentHandle::save_path() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path()));
|
||||
#else
|
||||
return fsutils::fromNativePath(misc::toQStringU(status(torrent_handle::query_save_path).save_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::save_path_parsed() const
|
||||
{
|
||||
QString p;
|
||||
if (has_metadata() && num_files() == 1) {
|
||||
p = firstFileSavePath();
|
||||
}
|
||||
else {
|
||||
p = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash()));
|
||||
if (p.isEmpty())
|
||||
p = save_path();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
QStringList QTorrentHandle::url_seeds() const
|
||||
{
|
||||
QStringList res;
|
||||
try {
|
||||
const std::set<std::string> existing_seeds = torrent_handle::url_seeds();
|
||||
|
||||
std::set<std::string>::const_iterator it = existing_seeds.begin();
|
||||
std::set<std::string>::const_iterator itend = existing_seeds.end();
|
||||
for (; it != itend; ++it) {
|
||||
qDebug("URL Seed: %s", it->c_str());
|
||||
res << misc::toQString(*it);
|
||||
}
|
||||
} catch(std::exception &e) {
|
||||
std::cout << "ERROR: Failed to convert the URL seed" << std::endl;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// get the size of the torrent without the filtered files
|
||||
size_type QTorrentHandle::actual_size() const
|
||||
{
|
||||
return status(query_accurate_download_counters).total_wanted;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::has_filtered_pieces() const
|
||||
{
|
||||
const std::vector<int> piece_priorities = torrent_handle::piece_priorities();
|
||||
foreach (const int priority, piece_priorities)
|
||||
if (priority == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int QTorrentHandle::num_files() const
|
||||
{
|
||||
if (!has_metadata())
|
||||
return -1;
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return torrent_handle::get_torrent_info().num_files();
|
||||
#else
|
||||
return torrent_handle::torrent_file()->num_files();
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::filename_at(unsigned int index) const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files());
|
||||
#else
|
||||
Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files());
|
||||
#endif
|
||||
return fsutils::fileName(filepath_at(index));
|
||||
}
|
||||
|
||||
size_type QTorrentHandle::filesize_at(unsigned int index) const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
Q_ASSERT(index < (unsigned int)torrent_handle::get_torrent_info().num_files());
|
||||
return torrent_handle::get_torrent_info().files().file_size(index);
|
||||
#else
|
||||
Q_ASSERT(index < (unsigned int)torrent_handle::torrent_file()->num_files());
|
||||
return torrent_handle::torrent_file()->files().file_size(index);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::filepath_at(unsigned int index) const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return filepath_at(torrent_handle::get_torrent_info(), index);
|
||||
#else
|
||||
return filepath_at(*torrent_handle::torrent_file(), index);
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::orig_filepath_at(unsigned int index) const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::get_torrent_info().orig_files().file_path(index)));
|
||||
#else
|
||||
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::torrent_file()->orig_files().file_path(index)));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
torrent_status::state_t QTorrentHandle::state() const
|
||||
{
|
||||
return status(0x0).state;
|
||||
}
|
||||
|
||||
QString QTorrentHandle::creator() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return misc::toQStringU(torrent_handle::get_torrent_info().creator());
|
||||
#else
|
||||
return misc::toQStringU(torrent_handle::torrent_file()->creator());
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::comment() const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return misc::toQStringU(torrent_handle::get_torrent_info().comment());
|
||||
#else
|
||||
return misc::toQStringU(torrent_handle::torrent_file()->comment());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_checking() const
|
||||
{
|
||||
return is_checking(status(0x0));
|
||||
}
|
||||
|
||||
// Return a list of absolute paths corresponding
|
||||
// to all files in a torrent
|
||||
QStringList QTorrentHandle::absolute_files_path() const
|
||||
{
|
||||
QDir saveDir(save_path());
|
||||
QStringList res;
|
||||
for (int i = 0; i<num_files(); ++i)
|
||||
res << fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i)));
|
||||
return res;
|
||||
}
|
||||
|
||||
QStringList QTorrentHandle::absolute_files_path_uneeded() const
|
||||
{
|
||||
QDir saveDir(save_path());
|
||||
QStringList res;
|
||||
std::vector<int> fp = torrent_handle::file_priorities();
|
||||
for (uint i = 0; i < fp.size(); ++i) {
|
||||
if (fp[i] == 0) {
|
||||
const QString file_path = fsutils::expandPathAbs(saveDir.absoluteFilePath(filepath_at(i)));
|
||||
if (file_path.contains(".unwanted"))
|
||||
res << file_path;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::has_missing_files() const
|
||||
{
|
||||
const QStringList paths = absolute_files_path();
|
||||
foreach (const QString &path, paths)
|
||||
if (!QFile::exists(path)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int QTorrentHandle::queue_position() const
|
||||
{
|
||||
return queue_position(status(0x0));
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_seed() const
|
||||
{
|
||||
// Affected by bug http://code.rasterbar.com/libtorrent/ticket/402
|
||||
//return torrent_handle::is_seed();
|
||||
// May suffer from approximation problems
|
||||
//return (progress() == 1.);
|
||||
// This looks safe
|
||||
return is_seed(status(0x0));
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_sequential_download() const
|
||||
{
|
||||
return status(0x0).sequential_download;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::priv() const
|
||||
{
|
||||
if (!has_metadata())
|
||||
return false;
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
return torrent_handle::get_torrent_info().priv();
|
||||
#else
|
||||
return torrent_handle::torrent_file()->priv();
|
||||
#endif
|
||||
}
|
||||
|
||||
QString QTorrentHandle::firstFileSavePath() const
|
||||
{
|
||||
Q_ASSERT(has_metadata());
|
||||
QString fsave_path = fsutils::fromNativePath(TorrentPersistentData::instance()->getSavePath(hash()));
|
||||
if (fsave_path.isEmpty())
|
||||
fsave_path = save_path();
|
||||
if (!fsave_path.endsWith("/"))
|
||||
fsave_path += "/";
|
||||
fsave_path += filepath_at(0);
|
||||
// Remove .!qB extension
|
||||
if (fsave_path.endsWith(".!qB", Qt::CaseInsensitive))
|
||||
fsave_path.chop(4);
|
||||
return fsave_path;
|
||||
}
|
||||
|
||||
QString QTorrentHandle::root_path() const
|
||||
{
|
||||
if (num_files() < 2)
|
||||
return save_path();
|
||||
QString first_filepath = filepath_at(0);
|
||||
const int slashIndex = first_filepath.indexOf("/");
|
||||
if (slashIndex >= 0)
|
||||
return QDir(save_path()).absoluteFilePath(first_filepath.left(slashIndex));
|
||||
return save_path();
|
||||
}
|
||||
|
||||
bool QTorrentHandle::has_error() const
|
||||
{
|
||||
return has_error(status(0x0));
|
||||
}
|
||||
|
||||
QString QTorrentHandle::error() const
|
||||
{
|
||||
return misc::toQString(status(0x0).error);
|
||||
}
|
||||
|
||||
void QTorrentHandle::downloading_pieces(bitfield &bf) const
|
||||
{
|
||||
std::vector<partial_piece_info> queue;
|
||||
torrent_handle::get_download_queue(queue);
|
||||
|
||||
std::vector<partial_piece_info>::const_iterator it = queue.begin();
|
||||
std::vector<partial_piece_info>::const_iterator itend = queue.end();
|
||||
for (; it!= itend; ++it)
|
||||
bf.set_bit(it->piece_index);
|
||||
return;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::has_metadata() const
|
||||
{
|
||||
return status(0x0).has_metadata;
|
||||
}
|
||||
|
||||
void QTorrentHandle::file_progress(std::vector<size_type>& fp) const
|
||||
{
|
||||
torrent_handle::file_progress(fp, torrent_handle::piece_granularity);
|
||||
}
|
||||
|
||||
QTorrentState QTorrentHandle::torrentState() const
|
||||
{
|
||||
QTorrentState state = QTorrentState::Unknown;
|
||||
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
||||
|
||||
if (is_paused(s)) {
|
||||
if (has_error(s))
|
||||
state = QTorrentState::Error;
|
||||
else
|
||||
state = is_seed(s) ? QTorrentState::PausedUploading : QTorrentState::PausedDownloading;
|
||||
}
|
||||
else {
|
||||
if (QBtSession::instance()->isQueueingEnabled() && is_queued(s)) {
|
||||
state = is_seed(s) ? QTorrentState::QueuedUploading : QTorrentState::QueuedDownloading;
|
||||
}
|
||||
else {
|
||||
switch (s.state) {
|
||||
case torrent_status::finished:
|
||||
case torrent_status::seeding:
|
||||
state = s.upload_payload_rate > 0 ? QTorrentState::Uploading : QTorrentState::StalledUploading;
|
||||
break;
|
||||
case torrent_status::allocating:
|
||||
case torrent_status::checking_files:
|
||||
case torrent_status::queued_for_checking:
|
||||
case torrent_status::checking_resume_data:
|
||||
state = is_seed(s) ? QTorrentState::CheckingUploading : QTorrentState::CheckingDownloading;
|
||||
break;
|
||||
case torrent_status::downloading:
|
||||
case torrent_status::downloading_metadata:
|
||||
state = s.download_payload_rate > 0 ? QTorrentState::Downloading : QTorrentState::StalledDownloading;
|
||||
break;
|
||||
default:
|
||||
qWarning("Unrecognized torrent status, should not happen!!! status was %d", this->state());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
qulonglong QTorrentHandle::eta() const
|
||||
{
|
||||
libtorrent::torrent_status s = status(torrent_handle::query_accurate_download_counters);
|
||||
return QBtSession::instance()->getETA(hash(), s);
|
||||
}
|
||||
|
||||
void QTorrentHandle::toggleSequentialDownload()
|
||||
{
|
||||
if (is_valid() && has_metadata()) {
|
||||
bool was_sequential = is_sequential_download();
|
||||
set_sequential_download(!was_sequential);
|
||||
if (!was_sequential)
|
||||
prioritize_first_last_piece(true);
|
||||
}
|
||||
}
|
||||
|
||||
void QTorrentHandle::toggleFirstLastPiecePrio()
|
||||
{
|
||||
if (is_valid() && has_metadata())
|
||||
prioritize_first_last_piece(!first_last_piece_first());
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_forced() const
|
||||
{
|
||||
return is_forced(status(0x0));
|
||||
}
|
||||
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
|
||||
void QTorrentHandle::pause() const
|
||||
{
|
||||
torrent_handle::auto_managed(false);
|
||||
torrent_handle::pause();
|
||||
if (!TorrentPersistentData::instance()->getHasMissingFiles(this->hash()))
|
||||
torrent_handle::save_resume_data();
|
||||
}
|
||||
|
||||
void QTorrentHandle::resume(const bool force) const
|
||||
{
|
||||
if (has_error())
|
||||
torrent_handle::clear_error();
|
||||
torrent_handle::set_upload_mode(false);
|
||||
|
||||
const QString torrent_hash = hash();
|
||||
TorrentPersistentData* const TorPersistent = TorrentPersistentData::instance();
|
||||
bool has_persistant_error = TorPersistent->hasError(torrent_hash);
|
||||
TorPersistent->setErrorState(torrent_hash, false);
|
||||
bool temp_path_enabled = Preferences::instance()->isTempPathEnabled();
|
||||
TorPersistent->setHasMissingFiles(torrent_hash, false);
|
||||
if (has_persistant_error && temp_path_enabled) {
|
||||
// Torrent was supposed to be seeding, checking again in final destination
|
||||
qDebug("Resuming a torrent with error...");
|
||||
const QString final_save_path = TorPersistent->getSavePath(torrent_hash);
|
||||
qDebug("Torrent final path is: %s", qPrintable(final_save_path));
|
||||
if (!final_save_path.isEmpty())
|
||||
move_storage(final_save_path);
|
||||
}
|
||||
torrent_handle::auto_managed(!force);
|
||||
torrent_handle::resume();
|
||||
if (has_persistant_error && temp_path_enabled)
|
||||
// Force recheck
|
||||
torrent_handle::force_recheck();
|
||||
}
|
||||
|
||||
void QTorrentHandle::remove_url_seed(const QString& seed) const
|
||||
{
|
||||
torrent_handle::remove_url_seed(seed.toStdString());
|
||||
}
|
||||
|
||||
void QTorrentHandle::add_url_seed(const QString& seed) const
|
||||
{
|
||||
const std::string str_seed = seed.toStdString();
|
||||
qDebug("calling torrent_handle::add_url_seed(%s)", str_seed.c_str());
|
||||
torrent_handle::add_url_seed(str_seed);
|
||||
}
|
||||
|
||||
void QTorrentHandle::set_tracker_login(const QString& username, const QString& password) const
|
||||
{
|
||||
torrent_handle::set_tracker_login(std::string(username.toLocal8Bit().constData()), std::string(password.toLocal8Bit().constData()));
|
||||
}
|
||||
|
||||
void QTorrentHandle::move_storage(const QString& new_path) const
|
||||
{
|
||||
QString hashstr = hash();
|
||||
|
||||
if (TorrentTempData::isMoveInProgress(hashstr)) {
|
||||
qDebug("enqueue move storage to %s", qPrintable(new_path));
|
||||
TorrentTempData::enqueueMove(hashstr, new_path);
|
||||
}
|
||||
else {
|
||||
QString old_path = save_path();
|
||||
|
||||
qDebug("move storage: %s to %s", qPrintable(old_path), qPrintable(new_path));
|
||||
|
||||
if (QDir(old_path) == QDir(new_path))
|
||||
return;
|
||||
|
||||
TorrentTempData::startMove(hashstr, old_path, new_path);
|
||||
|
||||
// Create destination directory if necessary
|
||||
// or move_storage() will fail...
|
||||
QDir().mkpath(new_path);
|
||||
// Actually move the storage
|
||||
torrent_handle::move_storage(fsutils::toNativePath(new_path).toUtf8().constData());
|
||||
}
|
||||
}
|
||||
|
||||
bool QTorrentHandle::save_torrent_file(const QString& path) const
|
||||
{
|
||||
if (!has_metadata()) return false;
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
torrent_info const* t = &get_torrent_info();
|
||||
#else
|
||||
boost::intrusive_ptr<torrent_info const> t = torrent_file();
|
||||
#endif
|
||||
|
||||
entry meta = bdecode(t->metadata().get(),
|
||||
t->metadata().get() + t->metadata_size());
|
||||
entry torrent_entry(entry::dictionary_t);
|
||||
torrent_entry["info"] = meta;
|
||||
if (!torrent_handle::trackers().empty())
|
||||
torrent_entry["announce"] = torrent_handle::trackers().front().url;
|
||||
|
||||
vector<char> out;
|
||||
bencode(back_inserter(out), torrent_entry);
|
||||
QFile torrent_file(path);
|
||||
if (!out.empty() && torrent_file.open(QIODevice::WriteOnly)) {
|
||||
torrent_file.write(&out[0], out.size());
|
||||
torrent_file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QTorrentHandle::file_priority(int index, int priority) const
|
||||
{
|
||||
vector<int> priorities = torrent_handle::file_priorities();
|
||||
if (priorities[index] != priority) {
|
||||
priorities[index] = priority;
|
||||
prioritize_files(priorities);
|
||||
}
|
||||
}
|
||||
|
||||
void QTorrentHandle::prioritize_files(const vector<int> &files) const
|
||||
{
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
torrent_info const& info = torrent_handle::get_torrent_info();
|
||||
#else
|
||||
boost::intrusive_ptr<torrent_info const> info_ptr = torrent_handle::torrent_file();
|
||||
torrent_info const& info = *info_ptr;
|
||||
#endif
|
||||
if ((int)files.size() != info.num_files()) return;
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
bool was_seed = is_seed();
|
||||
qDebug() << Q_FUNC_INFO << "Changing files priorities...";
|
||||
torrent_handle::prioritize_files(files);
|
||||
qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely...";
|
||||
|
||||
QString spath = save_path();
|
||||
|
||||
for (uint i = 0; i < files.size(); ++i) {
|
||||
QString filepath = filepath_at(info, i);
|
||||
// Move unwanted files to a .unwanted subfolder
|
||||
if (files[i] == 0) {
|
||||
QString old_abspath = QDir(spath).absoluteFilePath(filepath);
|
||||
QString parent_abspath = fsutils::branchPath(old_abspath);
|
||||
// Make sure the file does not already exists
|
||||
if (QDir(parent_abspath).dirName() != ".unwanted") {
|
||||
QString unwanted_abspath = parent_abspath + "/.unwanted";
|
||||
QString new_abspath = unwanted_abspath + "/" + fsutils::fileName(filepath);
|
||||
qDebug() << "Unwanted path is" << unwanted_abspath;
|
||||
if (QFile::exists(new_abspath)) {
|
||||
qWarning() << "File" << new_abspath << "already exists at destination.";
|
||||
continue;
|
||||
}
|
||||
bool created = QDir().mkpath(unwanted_abspath);
|
||||
#ifdef Q_OS_WIN
|
||||
qDebug() << "unwanted folder was created:" << created;
|
||||
if (created) {
|
||||
// Hide the folder on Windows
|
||||
qDebug() << "Hiding folder (Windows)";
|
||||
wstring win_path = fsutils::toNativePath(unwanted_abspath).toStdWString();
|
||||
DWORD dwAttrs = GetFileAttributesW(win_path.c_str());
|
||||
bool ret = SetFileAttributesW(win_path.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
|
||||
Q_ASSERT(ret != 0); Q_UNUSED(ret);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(created);
|
||||
#endif
|
||||
QString parent_path = fsutils::branchPath(filepath);
|
||||
if (!parent_path.isEmpty() && !parent_path.endsWith("/"))
|
||||
parent_path += "/";
|
||||
rename_file(i, parent_path + ".unwanted/" + fsutils::fileName(filepath));
|
||||
}
|
||||
}
|
||||
// Move wanted files back to their original folder
|
||||
if (files[i] > 0) {
|
||||
QString parent_relpath = fsutils::branchPath(filepath);
|
||||
if (QDir(parent_relpath).dirName() == ".unwanted") {
|
||||
QString old_name = fsutils::fileName(filepath);
|
||||
QString new_relpath = fsutils::branchPath(parent_relpath);
|
||||
if (new_relpath.isEmpty())
|
||||
rename_file(i, old_name);
|
||||
else
|
||||
rename_file(i, QDir(new_relpath).filePath(old_name));
|
||||
// Remove .unwanted directory if empty
|
||||
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + new_relpath).absoluteFilePath(".unwanted");
|
||||
QDir(spath + "/" + new_relpath).rmdir(".unwanted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (was_seed && !is_seed()) {
|
||||
qDebug() << "Torrent is no longer SEEDING";
|
||||
// Save seed status
|
||||
TorrentPersistentData::instance()->saveSeedStatus(*this);
|
||||
// Move to temp folder if necessary
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
if (pref->isTempPathEnabled()) {
|
||||
QString tmp_path = pref->getTempPath();
|
||||
qDebug() << "tmp folder is enabled, move torrent to " << tmp_path << " from " << spath;
|
||||
move_storage(tmp_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QTorrentHandle::prioritize_first_last_piece(int file_index, bool b) const
|
||||
{
|
||||
// Determine the priority to set
|
||||
int prio = b ? 7 : torrent_handle::file_priority(file_index);
|
||||
|
||||
#if LIBTORRENT_VERSION_NUM < 10000
|
||||
torrent_info const* tf = &get_torrent_info();
|
||||
#else
|
||||
boost::intrusive_ptr<torrent_info const> tf = torrent_file();
|
||||
#endif
|
||||
|
||||
QPair<int, int> extremities = get_file_extremity_pieces(*tf, file_index);
|
||||
piece_priority(extremities.first, prio);
|
||||
piece_priority(extremities.second, prio);
|
||||
}
|
||||
|
||||
void QTorrentHandle::prioritize_first_last_piece(bool b) const
|
||||
{
|
||||
if (!has_metadata()) return;
|
||||
// Download first and last pieces first for all media files in the torrent
|
||||
const int nbfiles = num_files();
|
||||
for (int index = 0; index < nbfiles; ++index) {
|
||||
const QString path = filepath_at(index);
|
||||
const QString ext = fsutils::fileExtension(path);
|
||||
if (misc::isPreviewable(ext) && torrent_handle::file_priority(index) > 0) {
|
||||
qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first";
|
||||
prioritize_first_last_piece(index, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QTorrentHandle::rename_file(int index, const QString& name) const
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << index << name;
|
||||
torrent_handle::rename_file(index, std::string(fsutils::toNativePath(name).toUtf8().constData()));
|
||||
}
|
||||
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
|
||||
bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const
|
||||
{
|
||||
return info_hash() == new_h.info_hash();
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return status.paused && !status.auto_managed;
|
||||
}
|
||||
|
||||
int QTorrentHandle::queue_position(const libtorrent::torrent_status &status)
|
||||
{
|
||||
if (status.queue_position < 0)
|
||||
return -1;
|
||||
return status.queue_position + 1;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return status.paused && status.auto_managed;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return status.state == torrent_status::finished
|
||||
|| status.state == torrent_status::seeding;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return status.state == torrent_status::checking_files
|
||||
|| status.state == torrent_status::checking_resume_data;
|
||||
}
|
||||
|
||||
bool QTorrentHandle::has_error(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return status.paused && !status.error.empty();
|
||||
}
|
||||
|
||||
float QTorrentHandle::progress(const libtorrent::torrent_status &status)
|
||||
{
|
||||
if (!status.total_wanted)
|
||||
return 0.;
|
||||
if (status.total_wanted_done == status.total_wanted)
|
||||
return 1.;
|
||||
float progress = (float) status.total_wanted_done / (float) status.total_wanted;
|
||||
Q_ASSERT(progress >= 0.f && progress <= 1.f);
|
||||
return progress;
|
||||
}
|
||||
|
||||
QString QTorrentHandle::filepath_at(const libtorrent::torrent_info &info, unsigned int index)
|
||||
{
|
||||
return fsutils::fromNativePath(misc::toQStringU(info.files().file_path(index)));
|
||||
|
||||
}
|
||||
|
||||
bool QTorrentHandle::is_forced(const libtorrent::torrent_status &status)
|
||||
{
|
||||
return !status.paused && !status.auto_managed;
|
||||
}
|
||||
|
||||
|
||||
QTorrentState::QTorrentState(int value)
|
||||
: m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
QString QTorrentState::toString() const
|
||||
{
|
||||
switch (m_value) {
|
||||
case Error:
|
||||
return "error";
|
||||
case Uploading:
|
||||
return "uploading";
|
||||
case PausedUploading:
|
||||
return "pausedUP";
|
||||
case QueuedUploading:
|
||||
return "queuedUP";
|
||||
case StalledUploading:
|
||||
return "stalledUP";
|
||||
case CheckingUploading:
|
||||
return "checkingUP";
|
||||
case Downloading:
|
||||
return "downloading";
|
||||
case PausedDownloading:
|
||||
return "pausedDL";
|
||||
case QueuedDownloading:
|
||||
return "queuedDL";
|
||||
case StalledDownloading:
|
||||
return "stalledDL";
|
||||
case CheckingDownloading:
|
||||
return "checkingDL";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
QTorrentState::operator int() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt4 and libtorrent.
|
||||
* Copyright (C) 2006 Christophe Dumez
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*
|
||||
* Contact : chris@qbittorrent.org
|
||||
*/
|
||||
|
||||
#ifndef QTORRENTHANDLE_H
|
||||
#define QTORRENTHANDLE_H
|
||||
|
||||
#include <libtorrent/torrent_handle.hpp>
|
||||
|
||||
#include <QString>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStringList;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class QTorrentState
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
Unknown = -1,
|
||||
|
||||
Error,
|
||||
|
||||
Uploading,
|
||||
PausedUploading,
|
||||
QueuedUploading,
|
||||
StalledUploading,
|
||||
CheckingUploading,
|
||||
|
||||
Downloading,
|
||||
PausedDownloading,
|
||||
QueuedDownloading,
|
||||
StalledDownloading,
|
||||
CheckingDownloading
|
||||
};
|
||||
|
||||
QTorrentState(int value);
|
||||
|
||||
operator int() const;
|
||||
QString toString() const;
|
||||
|
||||
private:
|
||||
int m_value;
|
||||
};
|
||||
|
||||
// A wrapper for torrent_handle in libtorrent
|
||||
// to interact well with Qt types
|
||||
class QTorrentHandle: public libtorrent::torrent_handle
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
|
||||
QTorrentHandle() {}
|
||||
explicit QTorrentHandle(const libtorrent::torrent_handle& h);
|
||||
|
||||
//
|
||||
// Getters
|
||||
//
|
||||
QString hash() const;
|
||||
QString name() const;
|
||||
QString current_tracker() const;
|
||||
bool is_paused() const;
|
||||
bool has_filtered_pieces() const;
|
||||
libtorrent::size_type total_size() const;
|
||||
libtorrent::size_type piece_length() const;
|
||||
int num_pieces() const;
|
||||
QString save_path() const;
|
||||
QString save_path_parsed() const;
|
||||
QStringList url_seeds() const;
|
||||
libtorrent::size_type actual_size() const;
|
||||
int num_files() const;
|
||||
int queue_position() const;
|
||||
bool is_queued() const;
|
||||
QString filename_at(unsigned int index) const;
|
||||
libtorrent::size_type filesize_at(unsigned int index) const;
|
||||
QString filepath_at(unsigned int index) const;
|
||||
QString orig_filepath_at(unsigned int index) const;
|
||||
libtorrent::torrent_status::state_t state() const;
|
||||
QString creator() const;
|
||||
QString comment() const;
|
||||
QStringList absolute_files_path() const;
|
||||
QStringList absolute_files_path_uneeded() const;
|
||||
bool has_missing_files() const;
|
||||
bool is_seed() const;
|
||||
bool is_checking() const;
|
||||
bool is_sequential_download() const;
|
||||
QString creation_date() const;
|
||||
qlonglong creation_date_unix() const;
|
||||
bool priv() const;
|
||||
bool first_last_piece_first() const;
|
||||
QString root_path() const;
|
||||
QString firstFileSavePath() const;
|
||||
bool has_error() const;
|
||||
QString error() const;
|
||||
void downloading_pieces(libtorrent::bitfield& bf) const;
|
||||
bool has_metadata() const;
|
||||
void file_progress(std::vector<libtorrent::size_type>& fp) const;
|
||||
QTorrentState torrentState() const;
|
||||
qulonglong eta() const;
|
||||
void toggleSequentialDownload();
|
||||
void toggleFirstLastPiecePrio();
|
||||
bool is_forced() const;
|
||||
|
||||
//
|
||||
// Setters
|
||||
//
|
||||
void pause() const;
|
||||
void resume(const bool force = false) const;
|
||||
void remove_url_seed(const QString& seed) const;
|
||||
void add_url_seed(const QString& seed) const;
|
||||
void set_tracker_login(const QString& username, const QString& password) const;
|
||||
void move_storage(const QString& path) const;
|
||||
void prioritize_first_last_piece(bool b) const;
|
||||
void rename_file(int index, const QString& name) const;
|
||||
bool save_torrent_file(const QString& path) const;
|
||||
void prioritize_files(const std::vector<int>& files) const;
|
||||
void file_priority(int index, int priority) const;
|
||||
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
bool operator ==(const QTorrentHandle& new_h) const;
|
||||
|
||||
static bool is_paused(const libtorrent::torrent_status &status);
|
||||
static int queue_position(const libtorrent::torrent_status &status);
|
||||
static bool is_queued(const libtorrent::torrent_status &status);
|
||||
static bool is_seed(const libtorrent::torrent_status &status);
|
||||
static bool is_checking(const libtorrent::torrent_status &status);
|
||||
static bool has_error(const libtorrent::torrent_status &status);
|
||||
static float progress(const libtorrent::torrent_status &status);
|
||||
static QString filepath_at(const libtorrent::torrent_info &info, unsigned int index);
|
||||
static bool is_forced(const libtorrent::torrent_status &status);
|
||||
|
||||
private:
|
||||
void prioritize_first_last_piece(int file_index, bool b) const;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user