mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-19 23:17:21 -06:00
Compare commits
847 Commits
v4_0_x
...
release-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a574c4a70a | ||
|
|
1e367f818d | ||
|
|
00599c8f02 | ||
|
|
332a836746 | ||
|
|
a1992acc16 | ||
|
|
c3f002a544 | ||
|
|
c28cbe0a74 | ||
|
|
435daaceed | ||
|
|
e29ab0087b | ||
|
|
aadd5a3312 | ||
|
|
7e354ffad3 | ||
|
|
ee6a071fb6 | ||
|
|
bc8b838953 | ||
|
|
5251d93b3d | ||
|
|
84f0dbecfe | ||
|
|
bba0c8b2cc | ||
|
|
2f90be8bd2 | ||
|
|
cb6b6296aa | ||
|
|
9d25fdce2a | ||
|
|
12b2732f1a | ||
|
|
8c9ece73ee | ||
|
|
a7db786387 | ||
|
|
e5bf65c9bd | ||
|
|
900e7d3a14 | ||
|
|
f1ff74a926 | ||
|
|
30bc4b837e | ||
|
|
050a4f8b23 | ||
|
|
487103d58f | ||
|
|
eeea69d4c1 | ||
|
|
00360ad418 | ||
|
|
a733253ae5 | ||
|
|
9788ee042b | ||
|
|
e9c9ea3bba | ||
|
|
312dfb989d | ||
|
|
75deafe5b1 | ||
|
|
4ca257a389 | ||
|
|
03375a78f2 | ||
|
|
423c7066d7 | ||
|
|
5cd5cc71a8 | ||
|
|
45d4d22055 | ||
|
|
916a92aa0d | ||
|
|
d1ebbcb35d | ||
|
|
2743d998a8 | ||
|
|
dbbfbaff9f | ||
|
|
0be8439cf6 | ||
|
|
66982c5524 | ||
|
|
85af8547f7 | ||
|
|
e26977ab2c | ||
|
|
ec1cc783a6 | ||
|
|
03b00ec045 | ||
|
|
5e90156e9e | ||
|
|
052206efa1 | ||
|
|
305d73180b | ||
|
|
80000bf0fd | ||
|
|
06ebe756e8 | ||
|
|
5fa3d9f19c | ||
|
|
5b4c6d3665 | ||
|
|
77bd0f17d1 | ||
|
|
03a702cfbd | ||
|
|
a932cd2ec1 | ||
|
|
8e5743380a | ||
|
|
8001eb0368 | ||
|
|
f214dc88fc | ||
|
|
5cff5ab135 | ||
|
|
82ba154b64 | ||
|
|
4ea44bbd2b | ||
|
|
a5e68a8725 | ||
|
|
70291014d1 | ||
|
|
1aabcfc30c | ||
|
|
aba80e2b1c | ||
|
|
be683fbcd3 | ||
|
|
2bcf09cfa5 | ||
|
|
697325af63 | ||
|
|
c21bd77be5 | ||
|
|
d5430adaaa | ||
|
|
9e99a0d3f5 | ||
|
|
d088ab6f43 | ||
|
|
820d510c12 | ||
|
|
676847fcd0 | ||
|
|
0204630ee6 | ||
|
|
b515c7eda4 | ||
|
|
73fcecac76 | ||
|
|
a7b82ebcb5 | ||
|
|
f8598b010d | ||
|
|
93779bcc4b | ||
|
|
938f5b9dd9 | ||
|
|
3b4d9f49d5 | ||
|
|
171c93af50 | ||
|
|
6f81e40106 | ||
|
|
e19b5cb2ce | ||
|
|
2c69faca58 | ||
|
|
9272151d0a | ||
|
|
d45ebf5a43 | ||
|
|
8074be7644 | ||
|
|
c99ac99a99 | ||
|
|
976e2450ec | ||
|
|
7e4db8fafd | ||
|
|
115a409d92 | ||
|
|
c203ab3d16 | ||
|
|
5dff96496d | ||
|
|
f813935011 | ||
|
|
2be719449f | ||
|
|
2094c870d5 | ||
|
|
4fe93ae8b8 | ||
|
|
fff1103cf4 | ||
|
|
8cede43a45 | ||
|
|
9b1fa3a5af | ||
|
|
409e73c074 | ||
|
|
c893729d62 | ||
|
|
945466968c | ||
|
|
54f080b755 | ||
|
|
bfad14d552 | ||
|
|
2972e1596d | ||
|
|
987d2aae88 | ||
|
|
4707d34fad | ||
|
|
2ffc09d097 | ||
|
|
afa8d6bb8f | ||
|
|
a37ead98e8 | ||
|
|
c73cd8d618 | ||
|
|
800a3aa61e | ||
|
|
ebd815be75 | ||
|
|
ef669acf89 | ||
|
|
ac6426eab1 | ||
|
|
b107b745f2 | ||
|
|
3d851a448f | ||
|
|
ce133f01aa | ||
|
|
492d378537 | ||
|
|
7ece484423 | ||
|
|
be5ad63e21 | ||
|
|
bdac8f8db8 | ||
|
|
bb893e70c5 | ||
|
|
57ec9db532 | ||
|
|
0287481001 | ||
|
|
0167496ecb | ||
|
|
d86cf193a0 | ||
|
|
246cad1108 | ||
|
|
23bf86a8a8 | ||
|
|
6ce4c885b9 | ||
|
|
faf84e483a | ||
|
|
576004c840 | ||
|
|
c93b05c293 | ||
|
|
55c3813fac | ||
|
|
725c6857be | ||
|
|
86767c9ab4 | ||
|
|
46aa631d2b | ||
|
|
7c61a937c9 | ||
|
|
b8d65dcc45 | ||
|
|
b9ab83eaf2 | ||
|
|
8b7b563992 | ||
|
|
b813a878d7 | ||
|
|
54e486c389 | ||
|
|
12d0a3acc1 | ||
|
|
6ad2a13386 | ||
|
|
2a9c401db9 | ||
|
|
ea5a29018f | ||
|
|
c8d0a715e8 | ||
|
|
2cfc6514ab | ||
|
|
1d78bc7206 | ||
|
|
e5577e43f8 | ||
|
|
17c0463906 | ||
|
|
4168772904 | ||
|
|
44f2186749 | ||
|
|
0c918bcc3a | ||
|
|
0a8925dc75 | ||
|
|
a446597597 | ||
|
|
54354a2732 | ||
|
|
d94b8f08ab | ||
|
|
0d8189efeb | ||
|
|
00c886e426 | ||
|
|
551fc35439 | ||
|
|
9ff17c8d9d | ||
|
|
ec37732e99 | ||
|
|
8a414f32a8 | ||
|
|
bac06acb49 | ||
|
|
ae1e3c2a81 | ||
|
|
67940eb0f9 | ||
|
|
007aa8480e | ||
|
|
dedec10c58 | ||
|
|
75219e21be | ||
|
|
10f5964f8e | ||
|
|
a4a64d51c0 | ||
|
|
1014313d88 | ||
|
|
e486bb4c29 | ||
|
|
5c3d9ffb46 | ||
|
|
2e474fd8db | ||
|
|
b2b110ae1f | ||
|
|
68a34e0738 | ||
|
|
38fa575958 | ||
|
|
6cfeefe054 | ||
|
|
8007971a53 | ||
|
|
d66bd30fae | ||
|
|
3fa59b1b12 | ||
|
|
20e7aff393 | ||
|
|
4b7ce87f57 | ||
|
|
2075533468 | ||
|
|
a4ad5c8d11 | ||
|
|
35f2f56757 | ||
|
|
e6f4aa6a2f | ||
|
|
92fc62bb0d | ||
|
|
44b57a59f5 | ||
|
|
97b8e02bf5 | ||
|
|
5df42420cb | ||
|
|
0ede11a1b7 | ||
|
|
7d9c282db9 | ||
|
|
bc0e0813a4 | ||
|
|
f3aebb3001 | ||
|
|
800f966df9 | ||
|
|
e33df4dd8c | ||
|
|
96d9d810fd | ||
|
|
8707a1bc86 | ||
|
|
0c988a5fd4 | ||
|
|
b396ca771d | ||
|
|
a37dfcf961 | ||
|
|
31989740cd | ||
|
|
501191289b | ||
|
|
8971e92d78 | ||
|
|
0c96e79d0d | ||
|
|
0704c0f5e6 | ||
|
|
9cb190ebe7 | ||
|
|
667f84995c | ||
|
|
7a93fae6e4 | ||
|
|
0d6deca15c | ||
|
|
f54d7d46f2 | ||
|
|
8cf00ba5e1 | ||
|
|
ecc9c6bbd9 | ||
|
|
e11199f988 | ||
|
|
e9ed621178 | ||
|
|
400743fc64 | ||
|
|
ea8c57be23 | ||
|
|
591cde53cf | ||
|
|
90bb67c654 | ||
|
|
8f7fda492c | ||
|
|
f92d85c536 | ||
|
|
55c98d8706 | ||
|
|
6e94b03c13 | ||
|
|
e3a82d4614 | ||
|
|
c61f641613 | ||
|
|
b64a51337e | ||
|
|
19dcf7851b | ||
|
|
fb346e15e8 | ||
|
|
827d8c0dad | ||
|
|
beef041e71 | ||
|
|
8b575484e6 | ||
|
|
d4b77a6541 | ||
|
|
4ba6334506 | ||
|
|
b78899cb9a | ||
|
|
0dec7b9f4f | ||
|
|
81a4f3ced0 | ||
|
|
b155b8609f | ||
|
|
970ad7cf28 | ||
|
|
c4625f50a8 | ||
|
|
001bd38557 | ||
|
|
7aae915a68 | ||
|
|
5f1ac96f66 | ||
|
|
68c2d81072 | ||
|
|
4dea03fc74 | ||
|
|
a70942ed53 | ||
|
|
7e8c72f424 | ||
|
|
e538eae726 | ||
|
|
898239a42e | ||
|
|
1055cc0f45 | ||
|
|
9f36b54b04 | ||
|
|
1f42ab8c4f | ||
|
|
3f0fbc0810 | ||
|
|
7e4b62c68d | ||
|
|
784b154228 | ||
|
|
ddba79ef3d | ||
|
|
7712d0ada0 | ||
|
|
38837db8de | ||
|
|
d997d887e5 | ||
|
|
341a971cd3 | ||
|
|
fa380c8b2c | ||
|
|
758c70283f | ||
|
|
747c70a58e | ||
|
|
6ca2e42bca | ||
|
|
cb505a07fe | ||
|
|
5819b6dd39 | ||
|
|
644e7d0450 | ||
|
|
b0e3d77975 | ||
|
|
643a209812 | ||
|
|
c3311e83a0 | ||
|
|
3301e9cb69 | ||
|
|
cf2c0bd47e | ||
|
|
df0c5c41c5 | ||
|
|
59e6757242 | ||
|
|
6bbcf9b3d6 | ||
|
|
208d21ff73 | ||
|
|
2df71dbde5 | ||
|
|
9e4f246c89 | ||
|
|
e4fc8e4156 | ||
|
|
883d4d81d5 | ||
|
|
041b86981f | ||
|
|
470e993c2d | ||
|
|
cbf10ebb7f | ||
|
|
89f06274bb | ||
|
|
c07cd440cd | ||
|
|
e90be67fec | ||
|
|
37ea01bd44 | ||
|
|
ccc91e2e52 | ||
|
|
d31954761f | ||
|
|
aa122b062e | ||
|
|
65921eaf7b | ||
|
|
bfbd978d3f | ||
|
|
1876dbd523 | ||
|
|
ea7eb7c0ec | ||
|
|
19c733ce95 | ||
|
|
2ba5dd1344 | ||
|
|
61eddfce90 | ||
|
|
0840556f77 | ||
|
|
f564e8a1c4 | ||
|
|
315d9835b2 | ||
|
|
ed03456999 | ||
|
|
e1a14007f8 | ||
|
|
a15cfae03a | ||
|
|
c2a195360a | ||
|
|
a4f99995bb | ||
|
|
16616f4e0e | ||
|
|
a5294e1e08 | ||
|
|
a8dbff06a3 | ||
|
|
d9cd5d4680 | ||
|
|
ac42ccb5e4 | ||
|
|
68ca95faf0 | ||
|
|
8d285c66aa | ||
|
|
1f28122428 | ||
|
|
6ec179e0f7 | ||
|
|
423511765a | ||
|
|
c061d67b14 | ||
|
|
4863078744 | ||
|
|
88881de8c6 | ||
|
|
690c439ae6 | ||
|
|
e78b5202bc | ||
|
|
c9be1d0a19 | ||
|
|
3348eef423 | ||
|
|
931af6c97e | ||
|
|
cea2948593 | ||
|
|
a468c84b30 | ||
|
|
7278625f53 | ||
|
|
7b3e692f68 | ||
|
|
fdb0d97b28 | ||
|
|
08a2439f46 | ||
|
|
6767487e40 | ||
|
|
141a2d2856 | ||
|
|
2eea57e609 | ||
|
|
0b185e347b | ||
|
|
1e1db31ab5 | ||
|
|
7233274110 | ||
|
|
d4e2b21534 | ||
|
|
4f206f987a | ||
|
|
dfddda57b9 | ||
|
|
0cacd71503 | ||
|
|
fdf3ebbb6c | ||
|
|
a3d9e457a0 | ||
|
|
ffb5c0635e | ||
|
|
b4f23d8154 | ||
|
|
ed310c2dac | ||
|
|
50167d40d7 | ||
|
|
5261d4375f | ||
|
|
0b56cd5fa0 | ||
|
|
e22946ef61 | ||
|
|
0457fd260e | ||
|
|
c60b7b213e | ||
|
|
c759c4301e | ||
|
|
307736e263 | ||
|
|
7aa5bc4bc1 | ||
|
|
c47e4efade | ||
|
|
b0781e820c | ||
|
|
1aca3b0adc | ||
|
|
20c4b0ee0c | ||
|
|
3df4a7ac2e | ||
|
|
efdeb81af1 | ||
|
|
34295a78f2 | ||
|
|
57163612ff | ||
|
|
1a9b009951 | ||
|
|
9406b4b226 | ||
|
|
38dc6ab8cf | ||
|
|
55c3db339b | ||
|
|
34456a7459 | ||
|
|
02b029abbe | ||
|
|
b26eb3d146 | ||
|
|
7cb6100c9c | ||
|
|
ef06485c45 | ||
|
|
1c2d9c1fe4 | ||
|
|
6f7f7d87c6 | ||
|
|
817aadd52d | ||
|
|
058bad0af3 | ||
|
|
0e421ae415 | ||
|
|
2227c3afc1 | ||
|
|
050af8a793 | ||
|
|
6207855f3b | ||
|
|
a65a70ab48 | ||
|
|
4110bb874f | ||
|
|
5c74f374af | ||
|
|
a8420a43f9 | ||
|
|
279bce2014 | ||
|
|
f3d370870d | ||
|
|
0e30659c26 | ||
|
|
f34dfca5e6 | ||
|
|
cec68c3fd7 | ||
|
|
5c2f698cb4 | ||
|
|
f5fc2d52b8 | ||
|
|
edef85fa3e | ||
|
|
6244ad5fa8 | ||
|
|
aba23f67a0 | ||
|
|
1a913c502b | ||
|
|
078982ada6 | ||
|
|
3035184d25 | ||
|
|
fccdc7ebdc | ||
|
|
e0c74c2d39 | ||
|
|
288f372914 | ||
|
|
ba331436fa | ||
|
|
afd4069eb4 | ||
|
|
387f9b1230 | ||
|
|
2114817890 | ||
|
|
dac2ab5409 | ||
|
|
b87ca60c5a | ||
|
|
6d44075662 | ||
|
|
69af205094 | ||
|
|
8a2e3f5d93 | ||
|
|
3f79726ab9 | ||
|
|
d1c4b1599a | ||
|
|
a0842a1e68 | ||
|
|
29042e4841 | ||
|
|
3e2cfb5136 | ||
|
|
0d55599e02 | ||
|
|
ea1b0b26b1 | ||
|
|
176b2eb18b | ||
|
|
98a1c111b9 | ||
|
|
47048d8410 | ||
|
|
882fa7ecd4 | ||
|
|
d07ece53e6 | ||
|
|
afd2f6ba14 | ||
|
|
226b0d4194 | ||
|
|
44e4b5d238 | ||
|
|
c59d2575c8 | ||
|
|
f50b4724a6 | ||
|
|
48cbccff1e | ||
|
|
e31ec20ec4 | ||
|
|
b9ec216aa5 | ||
|
|
2ac3004762 | ||
|
|
543cbc6d1c | ||
|
|
27d8dbf13b | ||
|
|
e487b31877 | ||
|
|
2845a791d0 | ||
|
|
024df8c53f | ||
|
|
12d97475da | ||
|
|
5154d431f6 | ||
|
|
0fc1ad664f | ||
|
|
bb683bd393 | ||
|
|
69df8174b9 | ||
|
|
56ccf28000 | ||
|
|
0a45fc9ffe | ||
|
|
a56262401b | ||
|
|
ebea0d91af | ||
|
|
d6cea2f76d | ||
|
|
4386fd9f89 | ||
|
|
1c18edac76 | ||
|
|
247b93e8ec | ||
|
|
6470f9acb9 | ||
|
|
65e0aa6b82 | ||
|
|
53cc8e1115 | ||
|
|
72b8ec8f3b | ||
|
|
29e24232ad | ||
|
|
6d0cceca83 | ||
|
|
8e9b928b61 | ||
|
|
df81e84fc2 | ||
|
|
e6a1b72354 | ||
|
|
e48b97466c | ||
|
|
84b9929025 | ||
|
|
5c50dba9e1 | ||
|
|
55b9b8fc91 | ||
|
|
fb698896c9 | ||
|
|
95c32221a2 | ||
|
|
230fc8e11e | ||
|
|
9b919b6c34 | ||
|
|
49d3b7bf30 | ||
|
|
1d778676cd | ||
|
|
e887b3106f | ||
|
|
be5600dae2 | ||
|
|
686fb701bf | ||
|
|
a73c4d57b1 | ||
|
|
e6ce1dd0b0 | ||
|
|
25ab660c14 | ||
|
|
38d3eea6ee | ||
|
|
13f69a2245 | ||
|
|
ec337a8a84 | ||
|
|
267362a7a0 | ||
|
|
11f26bfefd | ||
|
|
87c3f5163e | ||
|
|
22bdf98617 | ||
|
|
e5001e8f40 | ||
|
|
ddd6c82dd7 | ||
|
|
155dc49c5a | ||
|
|
c2c0dd2717 | ||
|
|
27cf98a962 | ||
|
|
aaaa67050c | ||
|
|
52ae118e3c | ||
|
|
cb929f7e59 | ||
|
|
94b64884f8 | ||
|
|
8a590c7472 | ||
|
|
b11c33b2d9 | ||
|
|
eb201003ee | ||
|
|
cf9bad03aa | ||
|
|
c5127d2eec | ||
|
|
0a35adbdba | ||
|
|
38d68d9d97 | ||
|
|
8494c6c5d4 | ||
|
|
cbfdb7df56 | ||
|
|
ea44923cce | ||
|
|
e75f8603b0 | ||
|
|
5437974b85 | ||
|
|
99abc21dab | ||
|
|
5156399c68 | ||
|
|
c8a5ef8c5d | ||
|
|
5b52835377 | ||
|
|
b8277614ec | ||
|
|
fb6bdbefd5 | ||
|
|
53c33317f9 | ||
|
|
d534973650 | ||
|
|
ed154d35ba | ||
|
|
ff7e5da6de | ||
|
|
d7fa5b6b6b | ||
|
|
d57bd62add | ||
|
|
0f6dff6315 | ||
|
|
97b4e4a3d3 | ||
|
|
84c84160fd | ||
|
|
60c9926834 | ||
|
|
fcde1ba09c | ||
|
|
8dcf326576 | ||
|
|
22b0ebb5a8 | ||
|
|
c5d99a12f3 | ||
|
|
240f5b6718 | ||
|
|
d184bd8c82 | ||
|
|
98a2dedb32 | ||
|
|
c405cb2f1c | ||
|
|
5ea100352e | ||
|
|
86c5a8df75 | ||
|
|
a05562cdc5 | ||
|
|
ee37f1db3d | ||
|
|
c9720cad81 | ||
|
|
2124ef261a | ||
|
|
aae6030064 | ||
|
|
b234fcec9c | ||
|
|
1a45145c1e | ||
|
|
c1a282aa7b | ||
|
|
e23c5614b6 | ||
|
|
f457937980 | ||
|
|
eac8838dc2 | ||
|
|
74d281526b | ||
|
|
7a8128eec4 | ||
|
|
a55be5e58f | ||
|
|
4c317f1e4a | ||
|
|
48c46dc79a | ||
|
|
b9339bb727 | ||
|
|
e1f8e6aa6f | ||
|
|
76c95d8c6c | ||
|
|
a7da714b13 | ||
|
|
916cfcdb03 | ||
|
|
b8fc415870 | ||
|
|
428cb9c986 | ||
|
|
99341409f4 | ||
|
|
f07333acc9 | ||
|
|
342f5f1a09 | ||
|
|
d74efd839f | ||
|
|
af898e9117 | ||
|
|
c0e48627ae | ||
|
|
534ed91d04 | ||
|
|
c58d0af88f | ||
|
|
ca5c1e8a15 | ||
|
|
4e96a1065e | ||
|
|
c9a8d19e6c | ||
|
|
81f5fdda15 | ||
|
|
a38730eb9e | ||
|
|
1a9e650936 | ||
|
|
ae5177b20f | ||
|
|
27090f0cfb | ||
|
|
433b5a46cf | ||
|
|
034d71dce3 | ||
|
|
c3de2310c5 | ||
|
|
b9478e0f00 | ||
|
|
94aeac992c | ||
|
|
4927d26877 | ||
|
|
ae1634623e | ||
|
|
296c7d3c13 | ||
|
|
22add258ae | ||
|
|
77f66be493 | ||
|
|
f41cb8089d | ||
|
|
32a686a037 | ||
|
|
236abcf3f1 | ||
|
|
325f9f629d | ||
|
|
88a771c9ff | ||
|
|
b960e41cfa | ||
|
|
9f461d0fe6 | ||
|
|
9db1f3baca | ||
|
|
af5a02d971 | ||
|
|
71bb6538db | ||
|
|
06ecc45d13 | ||
|
|
31607c4d18 | ||
|
|
84bb214642 | ||
|
|
d821bdc9f3 | ||
|
|
63461e0895 | ||
|
|
08755a211b | ||
|
|
a3c9c1c386 | ||
|
|
0de2f1776e | ||
|
|
da0db92b9f | ||
|
|
95bf63330e | ||
|
|
04cec39277 | ||
|
|
7b95785954 | ||
|
|
7d0342e366 | ||
|
|
21bc08d643 | ||
|
|
ffa6f7ea34 | ||
|
|
6b0449dde7 | ||
|
|
aa4107478e | ||
|
|
9f8e7917a8 | ||
|
|
1b652c882e | ||
|
|
601234e492 | ||
|
|
6c804edadf | ||
|
|
3a5c0d9818 | ||
|
|
2194a05e60 | ||
|
|
a89c1a8d9a | ||
|
|
86215f8483 | ||
|
|
a1b01c1b40 | ||
|
|
09cb95d3da | ||
|
|
48d7bdee0e | ||
|
|
5157fc201c | ||
|
|
492ea5bceb | ||
|
|
e8cf351b6a | ||
|
|
fc0628d35b | ||
|
|
1f7391737e | ||
|
|
acdb7a27dc | ||
|
|
1fed324f91 | ||
|
|
e4771ba508 | ||
|
|
6a36a3b1d9 | ||
|
|
2956b28a32 | ||
|
|
50471ec8c8 | ||
|
|
3aa9ff5fba | ||
|
|
b6be5afb89 | ||
|
|
157b809e21 | ||
|
|
f74e2b0130 | ||
|
|
c887a162ba | ||
|
|
52df71ad3a | ||
|
|
d6ddc90199 | ||
|
|
9600e7f5ad | ||
|
|
f3603d05e7 | ||
|
|
8cd243b06b | ||
|
|
1da3437a4a | ||
|
|
c72fb92318 | ||
|
|
954448863e | ||
|
|
eec6009398 | ||
|
|
ce362f0e5e | ||
|
|
4940a77d12 | ||
|
|
4e77e01a67 | ||
|
|
5acdcb6c09 | ||
|
|
8400d855de | ||
|
|
bd07cb91bb | ||
|
|
d7e0a9b1ad | ||
|
|
dc600d47ec | ||
|
|
0d3952eef5 | ||
|
|
30a4ce40b9 | ||
|
|
9d0ab0ae5f | ||
|
|
e78d8b9fcc | ||
|
|
f350977cb4 | ||
|
|
7a652c0a8c | ||
|
|
a652baadb5 | ||
|
|
66b86888fc | ||
|
|
e07501252c | ||
|
|
2977ba9ec2 | ||
|
|
db35bb54e1 | ||
|
|
19bb6f5fe0 | ||
|
|
aab752dd2e | ||
|
|
8c75fdcb4c | ||
|
|
df03b69151 | ||
|
|
aca7a851bb | ||
|
|
e9caefdfce | ||
|
|
16ac20b594 | ||
|
|
a3e071b734 | ||
|
|
5d3b9d2f22 | ||
|
|
27a7e86599 | ||
|
|
d93d0ab19e | ||
|
|
776627eee9 | ||
|
|
5185eeb7ef | ||
|
|
083bc71315 | ||
|
|
b1b931922e | ||
|
|
0717f0d60c | ||
|
|
525fdd6c2b | ||
|
|
f27e75e8fa | ||
|
|
0ad5290389 | ||
|
|
7895afb8e9 | ||
|
|
2626378dfd | ||
|
|
1378245a63 | ||
|
|
05c92e652c | ||
|
|
6c0dd81528 | ||
|
|
bd1d775ca9 | ||
|
|
b3207b8144 | ||
|
|
4c8dd0ca9d | ||
|
|
61d9fcd753 | ||
|
|
c5607f07c8 | ||
|
|
49802be7d4 | ||
|
|
3d932b1cce | ||
|
|
66a6674cbc | ||
|
|
2d12c126eb | ||
|
|
3ec992474d | ||
|
|
15babe97ea | ||
|
|
308fd3dcac | ||
|
|
e8250104c8 | ||
|
|
796cd674d7 | ||
|
|
1fe9272a8f | ||
|
|
c285de87d4 | ||
|
|
058062d586 | ||
|
|
3b4fd35dde | ||
|
|
2a6bd5de35 | ||
|
|
64e539fcfb | ||
|
|
de6ca29dcd | ||
|
|
ef63202be2 | ||
|
|
62493c672d | ||
|
|
8c58a69be6 | ||
|
|
2eb3ec1a6e | ||
|
|
e3da17caa8 | ||
|
|
c7e35a1801 | ||
|
|
83c6b22d30 | ||
|
|
fc5d49bf9a | ||
|
|
473ed45b3f | ||
|
|
c5ddbcfb5b | ||
|
|
72b0ba36ae | ||
|
|
b107c0671d | ||
|
|
b9e233601d | ||
|
|
8f16388915 | ||
|
|
d143929454 | ||
|
|
45a0c5558a | ||
|
|
b6596f9957 | ||
|
|
7059b947e8 | ||
|
|
4eafe7a2c8 | ||
|
|
d165da9c6c | ||
|
|
a341b793e4 | ||
|
|
a1d46a278b | ||
|
|
c6cf3a98a4 | ||
|
|
56887efdf5 | ||
|
|
4ec193042f | ||
|
|
6b33db3ae3 | ||
|
|
855772ae5a | ||
|
|
467ba380b2 | ||
|
|
93ed8e98c1 | ||
|
|
5c18db240c | ||
|
|
915ec81f83 | ||
|
|
8c545d2947 | ||
|
|
cbfbae1409 | ||
|
|
728b7dec3e | ||
|
|
7b5730723d | ||
|
|
71844e13af | ||
|
|
c6dfdf9135 | ||
|
|
4cd3a37b4a | ||
|
|
5efb4b522f | ||
|
|
64d5e329c0 | ||
|
|
ee9caa4305 | ||
|
|
39b56ac598 | ||
|
|
551ffb88ab | ||
|
|
698bd431e6 | ||
|
|
ccd3b305c2 | ||
|
|
b47c90f0e0 | ||
|
|
1afd5f70dd | ||
|
|
4cb304def8 | ||
|
|
f2af308e6d | ||
|
|
a6868e2b25 | ||
|
|
7a96ddccc6 | ||
|
|
bda5be8e4c | ||
|
|
93f5d4058d | ||
|
|
98dd2be673 | ||
|
|
ea7c1ace2a | ||
|
|
2da2054ccf | ||
|
|
3f5fa0025d | ||
|
|
f4be165767 | ||
|
|
55a1628a5e | ||
|
|
6c32f6beff | ||
|
|
21ec8fe53f | ||
|
|
4185209036 | ||
|
|
6150e0c56b | ||
|
|
42b811e578 | ||
|
|
ef1597e45b | ||
|
|
1e677c6728 | ||
|
|
9ef1e32327 | ||
|
|
173f6fd75d | ||
|
|
bbe7821483 | ||
|
|
65ca490b64 | ||
|
|
907f222a73 | ||
|
|
b649d61e8b | ||
|
|
ea793368a9 | ||
|
|
3331526865 | ||
|
|
cccb22f0e3 | ||
|
|
4f0d9605fe | ||
|
|
19fcd3dad6 | ||
|
|
eb8ff91c5d | ||
|
|
f98edcab5c | ||
|
|
e5ea3bd707 | ||
|
|
c87c1d2359 | ||
|
|
7bb9c69c08 | ||
|
|
25eb2edb86 | ||
|
|
c2a8970109 | ||
|
|
59c14801cc | ||
|
|
ea749bb052 | ||
|
|
1cb69c9b43 | ||
|
|
89d8a2fe72 | ||
|
|
4846b0ec28 | ||
|
|
771033a449 | ||
|
|
16d7301046 | ||
|
|
e2893fe68f | ||
|
|
8d913d77dd | ||
|
|
0522db3f19 | ||
|
|
50cea9d912 | ||
|
|
cff6a64e9f | ||
|
|
219a6b3ad1 | ||
|
|
798c230634 | ||
|
|
3801150a7a | ||
|
|
91b2b14ba5 | ||
|
|
1b8cda7924 | ||
|
|
31a4309077 | ||
|
|
db3fd24cea | ||
|
|
62b956946f | ||
|
|
04552f7921 | ||
|
|
9417105990 | ||
|
|
593c24e5bd | ||
|
|
7380bfa9e4 | ||
|
|
6f0d16bca5 | ||
|
|
78dafc9cbe | ||
|
|
2da5800665 | ||
|
|
6270433237 | ||
|
|
8214d25f9f | ||
|
|
df7565c9e2 | ||
|
|
d48218bfe9 | ||
|
|
db653cbdac | ||
|
|
766f70c81a | ||
|
|
3e9311b096 | ||
|
|
1bfb8d35cd | ||
|
|
07a85a1018 | ||
|
|
145641ac41 | ||
|
|
08aa827366 | ||
|
|
4d8a939291 | ||
|
|
e1f514042f | ||
|
|
7d78457fe0 | ||
|
|
f067b8b37c | ||
|
|
6043584305 | ||
|
|
bb02ccd574 | ||
|
|
64b250903f | ||
|
|
c5e73219bf | ||
|
|
5f47d3b021 | ||
|
|
d22b626a02 | ||
|
|
af9c0cca23 | ||
|
|
a641c0bb2f | ||
|
|
6c3ab99e3c |
@@ -45,8 +45,7 @@ before_build:
|
|||||||
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
|
- CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
|
||||||
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
- SET PATH=%PATH%;c:\qbt\qt5_32\bin;%CACHE_DIR%\jom;
|
||||||
# setup project
|
# setup project
|
||||||
- COPY /Y "%CACHE_DIR%\winconf.pri" "%REPO_DIR%"
|
- COPY /Y "%CACHE_DIR%\conf.pri" "%REPO_DIR%"
|
||||||
- COPY /Y "%CACHE_DIR%\winconf-msvc.pri" "%REPO_DIR%"
|
|
||||||
# workarounds
|
# workarounds
|
||||||
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
- MKLINK /J "c:\qbt\base" "%CACHE_DIR%\base"
|
||||||
|
|
||||||
|
|||||||
17
.travis.yml
17
.travis.yml
@@ -134,15 +134,21 @@ install:
|
|||||||
cp "version" $HOME/hombebrew_cache
|
cp "version" $HOME/hombebrew_cache
|
||||||
cd "$HOME/hombebrew_cache"
|
cd "$HOME/hombebrew_cache"
|
||||||
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
|
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar.rb
|
||||||
wget https://builds.shiki.hu/homebrew/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz
|
wget https://builds.shiki.hu/homebrew/5146d2df7e48f321511273fb9829ebdc3a6dc519f9ef36a344a343ae524c3ae6--libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Copy custom libtorrent bottle to homebrew's cache so it can find and install it
|
# Copy custom libtorrent bottle to homebrew's download cache so it can find and install it
|
||||||
# Also install our custom libtorrent formula by passing the local path to it
|
# Also install our custom libtorrent formula by passing the local path to it
|
||||||
# These 2 files are restored from Travis' cache.
|
# These 2 files are restored from Travis' cache.
|
||||||
cp "$HOME/hombebrew_cache/libtorrent-rasterbar-1.1.6+git20180101.b45acf28a5+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)"
|
cp "$HOME/hombebrew_cache/5146d2df7e48f321511273fb9829ebdc3a6dc519f9ef36a344a343ae524c3ae6--libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz" "$(brew --cache)/downloads"
|
||||||
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
|
brew install "$HOME/hombebrew_cache/libtorrent-rasterbar.rb"
|
||||||
|
|
||||||
|
# NOTE about the bottle name
|
||||||
|
# The part before the "--" characters is a sha256 hash of the string
|
||||||
|
# of the URL homebrew itself would use to download the bottle.
|
||||||
|
# In this case the URL is the following:
|
||||||
|
# http://127.0.0.1/libtorrent-rasterbar-1.1.9+git20180812.0bcf6cef23+patched-configure.el_capitan.bottle.tar.gz
|
||||||
|
|
||||||
if [ "$build_system" = "cmake" ]; then
|
if [ "$build_system" = "cmake" ]; then
|
||||||
brew outdated cmake || brew upgrade cmake
|
brew outdated cmake || brew upgrade cmake
|
||||||
brew install ninja
|
brew install ninja
|
||||||
@@ -166,7 +172,10 @@ script:
|
|||||||
if [ "$build_system" = "cmake" ]; then
|
if [ "$build_system" = "cmake" ]; then
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DGUI=${gui} -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
|
if [ "$gui" = "false" ]; then
|
||||||
|
DISABLE_GUI_OPTION="-DCMAKE_DISABLE_FIND_PACKAGE_Qt5Widgets=ON"
|
||||||
|
fi
|
||||||
|
cmake $DISABLE_GUI_OPTION -DCMAKE_INSTALL_PREFIX="$qbt_path" "$MY_CMAKE_OPENSSL_HINT" \
|
||||||
-G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
|
-G "Ninja" -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=TRUE ..
|
||||||
BUILD_TOOL="ninja"
|
BUILD_TOOL="ninja"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ mode = developer
|
|||||||
|
|
||||||
|
|
||||||
[qbittorrent.qbittorrentdesktop_master]
|
[qbittorrent.qbittorrentdesktop_master]
|
||||||
source_file = src/icons/qBittorrent.desktop
|
source_file = dist/unix/qbittorrent.desktop
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = DESKTOP
|
type = DESKTOP
|
||||||
minimum_perc = 23
|
minimum_perc = 23
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
cmake_policy(VERSION 3.5)
|
|
||||||
|
message(WARNING "No official support for cmake build system. If it is broken, please submit patches!")
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
||||||
include(FunctionReadVersion)
|
include(FunctionReadVersion)
|
||||||
@@ -25,32 +26,29 @@ add_definitions(-DQBT_VERSION_BUILD=${VER_BUILD})
|
|||||||
add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
|
add_definitions(-DQBT_VERSION="v${PROJECT_VERSION}")
|
||||||
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
|
add_definitions(-DQBT_VERSION_2="${PROJECT_VERSION}")
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
include(GNUInstallDirs)
|
||||||
include(GNUInstallDirs)
|
include(FeatureSummary)
|
||||||
endif (UNIX AND NOT APPLE)
|
|
||||||
|
# version requirements
|
||||||
|
set(requiredBoostVersion 1.35)
|
||||||
|
set(requiredQtVersion 5.5.1)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
include(winconf)
|
include(winconf)
|
||||||
endif(WIN32)
|
endif(WIN32)
|
||||||
|
|
||||||
# we need options here, because they are used not only in "src" subdir, but in the "dist" dir too
|
|
||||||
include(CMakeDependentOption)
|
|
||||||
|
|
||||||
option(SYSTEM_QTSINGLEAPPLICATION
|
# we need options here, at the top level, because they are used not only in "src" subdir, but in the "dist" dir too
|
||||||
"Use the system qtsingleapplication library or shipped one otherwise")
|
include(CompileFeature)
|
||||||
|
|
||||||
option(GUI "Allows to disable GUI for headless running. Disables QtDBus and the GeoIP Database" ON)
|
|
||||||
|
|
||||||
option(WEBUI "Allows to disable the WebUI." ON)
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
option(STACKTRACE_WIN "")
|
|
||||||
else (WIN32)
|
|
||||||
cmake_dependent_option(SYSTEMD "Install the systemd service file (headless only)" OFF
|
|
||||||
"NOT GUI" OFF)
|
|
||||||
cmake_dependent_option(DBUS "Enable use of QtDBus (GUI only)" ON "GUI" OFF)
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
|
optional_compile_definitions(COUNTRIES_RESOLUTION FEATURE DESCRIPTION "Enable resolving peers IP addresses to countries"
|
||||||
|
DEFAULT ON DISABLED DISABLE_COUNTRIES_RESOLUTION)
|
||||||
|
optional_compile_definitions(STACKTRACE FEATURE DESCRIPTION "Enable stacktraces"
|
||||||
|
DEFAULT ON ENABLED STACKTRACE)
|
||||||
|
optional_compile_definitions(WEBUI FEATURE DESCRIPTION "Enables built-in HTTP server for headless use"
|
||||||
|
DEFAULT ON DISABLED DISABLE_WEBUI)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(dist)
|
add_subdirectory(dist)
|
||||||
|
|
||||||
|
feature_summary(DESCRIPTION "\nConfiguration results:" WHAT ALL)
|
||||||
|
|||||||
@@ -1,10 +1,38 @@
|
|||||||
All new code must follow the following coding guidelines.
|
All new code **must** follow the following coding guidelines.
|
||||||
If you make changes in a file that still uses another coding style, make sure that you follow these guidelines for your changes instead.
|
If you make changes in a file that still uses another coding style, make sure that you follow these guidelines for your changes.
|
||||||
|
For programming languages other than C++ (e.g. JavaScript) used in this repository and submodules, unless otherwise specified, coding guidelines listed here applies as much as possible.
|
||||||
|
|
||||||
**Note 1:** I will not take your head if you forget and use another style. However, most probably the request will be delayed until you fix your coding style.
|
**Note 1:** I will not take your head if you forget and use another style. However, most probably the request will be delayed until you fix your coding style.
|
||||||
**Note 2:** You can use the `uncrustify` program/tool to clean up any source file. Use it with the `uncrustify.cfg` configuration file found in the root folder.
|
**Note 2:** You can use the `uncrustify` program/tool to clean up any source file. Use it with the `uncrustify.cfg` configuration file found in the root folder.
|
||||||
**Note 3:** There is also a style for QtCreator but it doesn't cover all cases. In QtCreator `Tools->Options...->C++->Code Style->Import...` and choose the `codingStyleQtCreator.xml` file found in the root folder.
|
**Note 3:** There is also a style for QtCreator but it doesn't cover all cases. In QtCreator `Tools->Options...->C++->Code Style->Import...` and choose the `codingStyleQtCreator.xml` file found in the root folder.
|
||||||
|
|
||||||
### 1. Curly braces ###
|
### Table Of Contents
|
||||||
|
|
||||||
|
* [1. New lines & curly braces](#1-new-lines--curly-braces)
|
||||||
|
* [a. Function blocks, class/struct definitions, namespaces](#a-function-blocks-classstruct-definitions-namespaces)
|
||||||
|
* [b. Other code blocks](#b-other-code-blocks)
|
||||||
|
* [c. Blocks in switch's case labels](#c-blocks-in-switchs-case-labels)
|
||||||
|
* [d. If-else statements](#d-if-else-statements)
|
||||||
|
* [e. Single statement if blocks](#e-single-statement-if-blocks)
|
||||||
|
* [f. Acceptable conditions to omit braces](#f-acceptable-conditions-to-omit-braces)
|
||||||
|
* [g. Brace enclosed initializers](#g-brace-enclosed-initializers)
|
||||||
|
* [2. Indentation](#2-indentation)
|
||||||
|
* [3. File encoding and line endings](#3-file-encoding-and-line-endings)
|
||||||
|
* [4. Initialization lists](#4-initialization-lists)
|
||||||
|
* [5. Enums](#5-enums)
|
||||||
|
* [6. Names](#6-names)
|
||||||
|
* [a. Type names and namespaces](#a-type-names-and-namespaces)
|
||||||
|
* [b. Variable names](#b-variable-names)
|
||||||
|
* [c. Private member variable names](#c-private-member-variable-names)
|
||||||
|
* [7. Header inclusion order](#7-header-inclusion-order)
|
||||||
|
* [8. Include guard](#8-include-guard)
|
||||||
|
* [9. Misc](#9-misc)
|
||||||
|
* [10. Git commit message](#10-git-commit-message)
|
||||||
|
* [11. Not covered above](#11-not-covered-above)
|
||||||
|
---
|
||||||
|
|
||||||
|
### 1. New lines & curly braces ###
|
||||||
|
|
||||||
#### a. Function blocks, class/struct definitions, namespaces ####
|
#### a. Function blocks, class/struct definitions, namespaces ####
|
||||||
```c++
|
```c++
|
||||||
int myFunction(int a)
|
int myFunction(int a)
|
||||||
@@ -89,18 +117,8 @@ default:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### d. Brace enclosed initializers ####
|
#### d. If-else statements ####
|
||||||
Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.<br/>
|
The `else if`/`else` must be on their own lines:
|
||||||
But you must insert a space between the variable name and initializer.
|
|
||||||
```c++
|
|
||||||
Class obj {}; // empty
|
|
||||||
Class obj {expr};
|
|
||||||
Class obj {expr1, /*...,*/ exprN};
|
|
||||||
QVariantMap map {{"key1", 5}, {"key2", 10}};
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. If blocks ###
|
|
||||||
#### a. Multiple tests ####
|
|
||||||
```c++
|
```c++
|
||||||
if (condition) {
|
if (condition) {
|
||||||
// code
|
// code
|
||||||
@@ -112,40 +130,71 @@ else {
|
|||||||
// code
|
// code
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
The `else if`/`else` must be on their own lines.
|
|
||||||
|
|
||||||
#### b. Single statement if blocks ####
|
#### e. Single statement if blocks ####
|
||||||
**Most** single statement if blocks should look like this:
|
Most single statement if blocks should look like this:
|
||||||
```c++
|
```c++
|
||||||
if (condition)
|
if (condition)
|
||||||
a = a + b;
|
a = a + b;
|
||||||
```
|
```
|
||||||
|
|
||||||
One acceptable exception to this **can be** `return`, `break` or `continue` statements, provided that the test condition isn't very long. However you can choose to use the first rule instead.
|
One acceptable exception to this can be `return`, `break` or `continue` statements,
|
||||||
|
provided that the test condition isn't very long and its body statement occupies only one line.
|
||||||
|
However you can still choose to use the first rule.
|
||||||
```c++
|
```c++
|
||||||
a = myFunction();
|
if (a > 0) return;
|
||||||
b = a * 1500;
|
|
||||||
|
|
||||||
if (b > 0) return;
|
while (p) {
|
||||||
c = 100 / b;
|
// ...
|
||||||
|
if (!b) continue;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### c. Using curly braces for single statement if blocks ####
|
#### f. Acceptable conditions to omit braces ####
|
||||||
|
When the conditional statement in `if`/`else` has only one line and its body occupy only one line,
|
||||||
|
this also applies to loops statements.
|
||||||
|
Notice that for a series of `if - else` branches, if one branch needs braces then all branches must add braces.
|
||||||
|
```c++
|
||||||
|
if (a < b) // conditional statement
|
||||||
|
do(a); // body
|
||||||
|
|
||||||
However, there are cases where curly braces for single statement if blocks **should** be used.
|
if (a < b)
|
||||||
* If some branch needs braces then all others should use them. Unless you have multiple `else if` in a row and the one needing the braces is only for a very small sub-block of code.
|
do(a);
|
||||||
* Another exception would be when we have nested if blocks or generally multiple levels of code that affect code readability.
|
else if (a > b)
|
||||||
|
do(b);
|
||||||
|
else
|
||||||
|
do(c);
|
||||||
|
|
||||||
Generally it will depend on the particular piece of code and would be determined on how readable that piece of code is. **If in doubt** always use braces if one of the above exceptions applies.
|
if (a < b) {
|
||||||
|
do(a);
|
||||||
|
}
|
||||||
|
else if (a > b) { // curly braces required here, then all branches should also add them
|
||||||
|
do(b);
|
||||||
|
do(d);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
do(c);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### 3. Indentation ###
|
#### g. Brace enclosed initializers ####
|
||||||
|
Unlike single-line functions, you must not insert spaces between the brackets and concluded expressions.<br/>
|
||||||
|
But you must insert a space between the variable name and initializer.
|
||||||
|
```c++
|
||||||
|
Class obj {}; // empty
|
||||||
|
Class obj {expr};
|
||||||
|
Class obj {expr1, /*...,*/ exprN};
|
||||||
|
QVariantMap map {{"key1", 5}, {"key2", 10}};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Indentation ###
|
||||||
4 spaces.
|
4 spaces.
|
||||||
|
|
||||||
### 4. File encoding and line endings. ###
|
### 3. File encoding and line endings ###
|
||||||
|
|
||||||
UTF-8 and Unix-like line ending (LF). Unless some platform specific 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. ###
|
### 4. Initialization lists ###
|
||||||
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.
|
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++
|
```c++
|
||||||
myClass::myClass(int a, int b, int c, int d)
|
myClass::myClass(int a, int b, int c, int d)
|
||||||
@@ -158,7 +207,7 @@ myClass::myClass(int a, int b, int c, int d)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6. Enums. ###
|
### 5. Enums ###
|
||||||
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
|
Enums should be vertical. This will allow for more easily readable diffs. The members should be indented.
|
||||||
```c++
|
```c++
|
||||||
enum Days
|
enum Days
|
||||||
@@ -173,7 +222,7 @@ enum Days
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7. Names. ###
|
### 6. Names ###
|
||||||
All names should be camelCased.
|
All names should be camelCased.
|
||||||
|
|
||||||
#### a. Type names and namespaces ####
|
#### a. Type names and namespaces ####
|
||||||
@@ -207,40 +256,73 @@ class MyClass
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 8. Header inclusion order. ###
|
### 7. Header inclusion order ###
|
||||||
The headers should be placed in the following order:
|
The headers should be placed in the following group order:
|
||||||
1. Module header (in .cpp)
|
1. Module header (in .cpp)
|
||||||
2. System/Qt/Boost etc. headers (splitted in subcategories if you have many).
|
2. C++ Standard Library headers
|
||||||
3. Application headers, starting from *Base* headers.
|
3. System headers
|
||||||
|
4. Boost library headers
|
||||||
|
5. Libtorrent headers
|
||||||
|
6. Qt headers
|
||||||
|
7. qBittorrent's own headers, starting from the *base* headers.
|
||||||
|
|
||||||
|
The headers should be ordered alphabetically within each group.
|
||||||
|
If there are conditionals for the same header group, then put them at the bottom of the respective group.
|
||||||
|
If there are conditionals that contain headers from several different header groups, then put them above the "qBittorrent's own headers" group.
|
||||||
|
|
||||||
|
One exception is the header containing the library version (for example, QtGlobal), this particular header isn't constrained by the aforementioned order.
|
||||||
|
|
||||||
The headers should be ordered alphabetically within each group (subgroup).<br/>
|
|
||||||
<br/>
|
|
||||||
Example:
|
Example:
|
||||||
```c++
|
```c++
|
||||||
// examplewidget.cpp
|
// file: examplewidget.cpp
|
||||||
|
|
||||||
|
// Module header
|
||||||
#include "examplewidget.h"
|
#include "examplewidget.h"
|
||||||
|
|
||||||
#include <cmath>
|
// exceptions, headers containing version number
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
// C++ Standard Library headers
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include <QDateTime>
|
#ifdef Q_OS_WIN // conditional
|
||||||
#include <QList>
|
#include <cmath>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// System headers
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Boost library headers
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
|
|
||||||
|
// Libtorrent headers
|
||||||
|
#include <libtorrent/session.hpp>
|
||||||
|
|
||||||
|
// Qt headers
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
#ifdef Q_OS_MAC // conditional
|
||||||
|
#include <QFont>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// conditional that contains headers from several different header groups
|
||||||
|
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||||
|
#include <memory>
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// qBittorrent's own headers
|
||||||
#include "base/bittorrent/infohash.h"
|
#include "base/bittorrent/infohash.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "anothermodule.h"
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/misc.h"
|
|
||||||
#include "base/utils/string.h"
|
|
||||||
#include "ui_examplewidget.h"
|
#include "ui_examplewidget.h"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 9. Include guard. ###
|
### 8. Include guard ###
|
||||||
`#pragma once` should be used instead of "include guard" in new code:
|
`#pragma once` should be used instead of "include guard" in new code:
|
||||||
```c++
|
```c++
|
||||||
// examplewidget.h
|
// examplewidget.h
|
||||||
@@ -256,7 +338,7 @@ class ExampleWidget : public QWidget
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 10. Misc. ###
|
### 9. Misc ###
|
||||||
|
|
||||||
* Line breaks for long lines with operation:
|
* Line breaks for long lines with operation:
|
||||||
|
|
||||||
@@ -332,5 +414,16 @@ i++, j--; // No
|
|||||||
|
|
||||||
* Method definitions aren't allowed in header files
|
* Method definitions aren't allowed in header files
|
||||||
|
|
||||||
### 10. Not covered above ###
|
### 10. Git commit message ###
|
||||||
If something isn't covered above, just follow the same style the file you are editing has. If that particular detail isn't present in the file you are editing, then use whatever the rest of the project uses.
|
1. Limit the subject line to 50 characters. Subject should contain only the very essence of the changes (you should avoid extra details and internals)
|
||||||
|
2. Separate subject from body with a blank line
|
||||||
|
3. Capitalize the subject line
|
||||||
|
4. Do not end the subject line with a period
|
||||||
|
5. Use the imperative mood in the subject line (it's like you're ordering the program to do something (e.g. "Don't create temporary substrings")
|
||||||
|
6. Wrap the body at 72 characters
|
||||||
|
7. Use the body to explain what and why vs. how
|
||||||
|
8. If commit fixes a reported issue, mention it in the message body (e.g. `Closes #4134.`)
|
||||||
|
|
||||||
|
### 11. Not covered above ###
|
||||||
|
If something isn't covered above, just follow the same style the file you are editing has.
|
||||||
|
*This guide is not exhaustive and the style for a particular piece of code not specified here will be determined by project members on code review.*
|
||||||
|
|||||||
238
CONTRIBUTING.md
238
CONTRIBUTING.md
@@ -1,35 +1,221 @@
|
|||||||
# Filing an issue
|
# How to contribute to qBittorrent
|
||||||
|
|
||||||
### Must read
|
There are three main ways to contribute to the project.
|
||||||
* If you aren't sure, you can ask on the [**forum**](http://forum.qbittorrent.org) or read our [**wiki**](http://wiki.qbittorrent.org) first.
|
Read the respective section to find out more.
|
||||||
* Do a quick **search**. Others might already reported the issue.
|
|
||||||
* Write in **English**!
|
|
||||||
* Provide **version** information: (You can find version numbers at menu `Help -> About -> Libraries`)
|
|
||||||
```
|
|
||||||
qBittorrent:
|
|
||||||
Qt:
|
|
||||||
libtorrent:
|
|
||||||
boost:
|
|
||||||
OS version:
|
|
||||||
```
|
|
||||||
* Provide **steps** to reproduce the problem, it will be easier to pinpoint the fault.
|
|
||||||
* **Screenshots**! A screenshot is worth a thousand words. just upload it. [(How?)](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests)
|
|
||||||
|
|
||||||
### Good to know
|
### Table Of Contents
|
||||||
* **Be patient**. The dev team is small and resource limited. Devs finding their free time, analyzing the problem and fixing the issue, it all takes time. :clock3:
|
|
||||||
* If you can code, why not become a **contributor** by fixing the issue and open a pull request? :wink:
|
|
||||||
* Harsh words or threats won't help your situation. What's worse, your complain will (very likely) to be **ignored**. :fearful:
|
|
||||||
|
|
||||||
|
* **[Bug reporting etiquette](#bug-reporting-etiquette)**
|
||||||
|
|
||||||
|
|
||||||
|
* **[Submitting an issue/bug report](#submitting-an-issuebug-report)**
|
||||||
|
* [What is an actual bug report?](#what-is-an-actual-bug-report)
|
||||||
|
* [Before submitting a bug report](#before-submitting-a-bug-report)
|
||||||
|
* [Steps to ensure a good bug report](#steps-to-ensure-a-good-bug-report)
|
||||||
|
|
||||||
|
|
||||||
|
* **[Suggesting enhancements/feature requests](#suggesting-enhancementsfeature-requests)**
|
||||||
|
* [Before submitting an enhancement/feature request](#before-submitting-an-enhancementfeature-request)
|
||||||
|
* [Steps to ensure a good enhancement/feature suggestion](#steps-to-ensure-a-good-enhancementfeature-suggestion)
|
||||||
|
|
||||||
|
|
||||||
|
* **[Opening a pull request](#opening-a-pull-request)**
|
||||||
|
* [Must read](#must-read)
|
||||||
|
* [Good to know](#good-to-know)
|
||||||
|
|
||||||
|
# Bug reporting etiquette
|
||||||
|
|
||||||
|
* Issues, pull requests, and comments must always be in **English.**
|
||||||
|
|
||||||
|
* This project is supported by volunteers, do not expect "customer support"-style interaction.
|
||||||
|
|
||||||
|
* **Be patient.** The development team is small and resource limited. Developers and contributors take from their free time to analyze the problem and fix the issue. :clock3:
|
||||||
|
|
||||||
|
* Harsh words or threats won't help your situation. What's worse, your complain will (very likely) be **ignored.** :fearful:
|
||||||
|
|
||||||
|
# Submitting an issue/bug report
|
||||||
|
|
||||||
|
This section guides you through submitting an issue/bug report for qBittorrent.
|
||||||
|
|
||||||
|
Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
|
||||||
|
|
||||||
|
Make sure to follow these rules carefully when submitting a bug report. Failure to do so will result in the issue being closed.
|
||||||
|
|
||||||
|
## What is an actual bug report?
|
||||||
|
|
||||||
|
Developers and contributors are not supposed to deal with issues for which little to no investigation to find the actual cause of a purported issue was made by the reporter.
|
||||||
|
|
||||||
|
Positive contributions are those which are reported with efforts to find the actual cause of an issue, or at the very least efforts were made to narrow it as much as possible.
|
||||||
|
|
||||||
|
Requiring people to investigate as much as possible before opening an issue will more than likely avoid burdening the project with invalid issues or issues unrelated to qBittorrent.
|
||||||
|
|
||||||
|
The following are _not_ bug reports. **Check the [wiki][wiki-url], [forum][forum-url] or other places for help and support for issues like these**:
|
||||||
|
|
||||||
|
- Explanation of qBittorrent options (see [wiki][wiki-url]).
|
||||||
|
- Help with WebUI setup.
|
||||||
|
- Help with embedded tracker setup.
|
||||||
|
- Help about BitTorrent in general.
|
||||||
|
- Issues with specific search plugins.
|
||||||
|
- Asking for specific builds of qBittorrent other than the current one. You can install older releases at your own risk or for regression testing purposes. Previous Windows and macOS builds are available [here][builds-url].
|
||||||
|
- If you want older Linux builds, you will have to compile them yourself from the corresponding commits, or ask someone on the [forum][forum-url] to do it for you.
|
||||||
|
- Possibly others. Read on and use common sense.
|
||||||
|
|
||||||
|
The issue tracker is for provable issues only: You will have to make the case that the issue is really with qBittorrent and not something else on your side.
|
||||||
|
|
||||||
|
To make a case means to provide detailed steps so that anybody can reproduce the issue.
|
||||||
|
Be sure to rule out that the issue is not caused by something specific on your side.
|
||||||
|
|
||||||
|
Issue reports for bugs that apparently aren't easily reproducible or that you can't figure out what triggers it even though you tried are OK.
|
||||||
|
|
||||||
|
Any issue opened without effort to provide the required details for developers, contributors or anybody else to reproduce the problem will be closed as invalid.
|
||||||
|
For example:
|
||||||
|
- Crash reports with just a stack trace.
|
||||||
|
- Speculated performance issues that do not come with actual profiling data + analysis supporting the claim.
|
||||||
|
|
||||||
|
## Before submitting a bug report
|
||||||
|
|
||||||
|
- **Do some basic troubleshooting (examples)**:
|
||||||
|
- Restart qBittorrent.
|
||||||
|
- Restart your PC.
|
||||||
|
- Update your OS (e.g. Windows updates).
|
||||||
|
- Update your network card drivers.
|
||||||
|
- Fully reinstall qBittorrent.
|
||||||
|
- etc...
|
||||||
|
- Make sure the problem is not caused by anti-virus or other program messing with your files.
|
||||||
|
- Check if you can reproduce the problem in the latest version of qBittorrent.
|
||||||
|
- **Check [forum][forum-url] and [wiki][wiki-url].** You might be able to find the cause of the problem and fix things yourself.
|
||||||
|
- **Check if the issue exists already in the issue tracker.**
|
||||||
|
- If it does and the issue is still open, add a comment to the existing issue instead of opening a new one.
|
||||||
|
- If you find a Closed issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
|
||||||
|
- If the issue is with the search functionality:
|
||||||
|
- **Make sure you have [`python`][python-url] installed correctly (remember the search functionality requires a working python installation).**
|
||||||
|
- Make sure it is in fact a problem with the search functionality itself, and not a problem with the plugins. If something does not work properly with the search functionality, the first step is to rule out search plugin-related issues.
|
||||||
|
- For search plugin issues, report on the respective search plugin support page, or at [qbittorrent/search-plugins][search-plugins-url].
|
||||||
|
|
||||||
|
## Steps to ensure a good bug report
|
||||||
|
|
||||||
|
**Follow these guidelines** in order to provide as much useful information as possible right away. Not all of them are applicable to all issues, but you are expected to follow most of these steps (use common sense).
|
||||||
|
Otherwise, we've noticed that a lot of your time (and the developers') gets thrown away on exchanging back and forth to get this information.
|
||||||
|
|
||||||
|
* Use a **clear and descriptive title** for the issue to identify the problem.
|
||||||
|
|
||||||
|
* Post only **one specific issue per submission.**
|
||||||
|
|
||||||
|
* **Fill out the issue template properly.**
|
||||||
|
|
||||||
|
- **Make sure you are using qBittorrent on a supported platform.** Do not submit issues which can only be reproduced on beta/unsupported releases of supported operating systems (e.g. Windows 10 Insider, Ubuntu 12.04 LTS, etc).
|
||||||
|
These are unstable/unsupported platforms, and in all likelihood, whatever the issue is, it is not related to qBittorrent.
|
||||||
|
|
||||||
|
* **Specify the OS you're using, its version and architecture.**
|
||||||
|
* Examples: Windows 8.1 32-bit, Linux Mint 17.1 64-bit, Windows 10 Fall creators Update 64-bit, etc.
|
||||||
|
|
||||||
|
|
||||||
|
* **Report only if you run into the issue with an official stable release, a beta release, or with the most recent upstream changes (in this last case specify the specific commit you are on).** (beta testing is encouraged :smile:). We do not provide support for bugs on unofficial Windows builds.
|
||||||
|
|
||||||
|
* **Specify the version of qBittorrent** you are using, as well as its **architecture** (x86 or x64) and its **libraries' versions** (Help -> About -> Libraries).
|
||||||
|
|
||||||
|
* Specify **how you installed**:
|
||||||
|
- Linux: either from the PPA, your distribution's repositories, or compiled from source, or even possibly third-party repositories.
|
||||||
|
- Windows: either from the installer, or compiled from source, or even possibly third-party repositories.
|
||||||
|
- macOS: either from the installer, or compiled from source, or even possibly third-party repositories.
|
||||||
|
|
||||||
|
|
||||||
|
* **Describe the exact steps which reproduce the problem in as many details as possible.**
|
||||||
|
- For example, start by explaining how you started qBittorrent, e.g. was it via the terminal? Desktop icon? Did you start it as root or normal user?
|
||||||
|
- **When listing steps, don't just say what you did, but explain how you did it.**
|
||||||
|
- For example, if you added a torrent for download, did you do so via a `.torrent` file or via a magnet link? If it was with a torrent file did you do so by dragging the torrent file from the file manager to the transfer list, or did you use the "Add Torrent File" in the Top Bar?
|
||||||
|
- Describe the behavior you observed after following the steps and point out what exactly is the problem with that behavior; this is what we'll be looking for after executing the steps.
|
||||||
|
|
||||||
|
|
||||||
|
* **Explain which behavior you expected to see instead** and why.
|
||||||
|
|
||||||
|
* Use **screenshots/animated GIFs to help describe the issue** whenever appropriate [(How?)][attachments-howto-url].
|
||||||
|
|
||||||
|
* If the problem wasn't triggered by a specific action, describe what you were doing before the problem happened.
|
||||||
|
|
||||||
|
* **If you are reporting that qBittorrent crashes**, include the stack trace in the report; include it in a code block, a file attachment, or put it in a gist and provide link to that gist.
|
||||||
|
|
||||||
|
* **For performance-related issues**, include as much profiling data as you can (resource usage graphs, etc).
|
||||||
|
|
||||||
|
* Paste the **qBittorrent log** (or put the contents of the log in a gist and provide a link to the gist). The log can be viewed in the GUI (View -> Log -> tick all boxes). If you can't do that, the file is at:
|
||||||
|
- Linux: `~/.local/share/data/qBittorrent/logs/qBittorrent.log`
|
||||||
|
- Windows: `%LocalAppData%\qBittorrent\logs`
|
||||||
|
- macOS: `~/Library/Application Support/qBittorrent/qBittorrent.log`
|
||||||
|
|
||||||
|
|
||||||
|
* **Do NOT post comments like "+1" or "me too!"** without providing new relevant info on the issue. Using the built-in reactions is OK though. Remember that you can use the "subscribe" button to receive notifications of that report without having to comment first.
|
||||||
|
|
||||||
|
* If there seems to be an **issue with specific torrent files/magnet links**:
|
||||||
|
- Don't post private `.torrent` files/magnet links, or ones that point to copyrighted content. If you are willing, offer to email a link or the `.torrent` file itself to whoever developer is debugging it and requests it.
|
||||||
|
- Make sure you can't reproduce the problem with another client, to rule out the possibility that the issue is with the `.torrent` file/magnet link itself.
|
||||||
|
|
||||||
|
|
||||||
|
* A screenshot, transcription or file upload of any of **qBittorrent's preferences that differ from the defaults.** Please include everything different from the defaults whether or not it seems relevant to your issue.
|
||||||
|
|
||||||
|
* **Attachment rules**:
|
||||||
|
- Short logs and error messages can be pasted as quotes/code whenever small enough; otherwise make a gist with the contents and post the link to the gist.
|
||||||
|
- Avoid linking/attaching impractical file formats such as PDFs/Word documents with images. If you want to post an image, just post the image.
|
||||||
|
|
||||||
|
### Provide more context by answering these questions (if applicable):
|
||||||
|
|
||||||
|
- Can you **reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens (e.g. only happens with extremely large torrents/only happens after qBittorrent is open for more than 2 days/etc...)
|
||||||
|
|
||||||
|
- Did the problem start happening recently (e.g. after updating to a new version of qBittorrent) or was this always a problem?
|
||||||
|
|
||||||
|
- If the problem started happening recently, can you reproduce the problem in an older version of qBittorrent?
|
||||||
|
|
||||||
|
- Are you saving files locally (in a disk in your machine), or are you saving them remotely (e.g. network drives)?
|
||||||
|
|
||||||
|
- Are you using qBittorrent with multiple monitors? If so, can you reproduce the problem when you use a single monitor?
|
||||||
|
|
||||||
|
Good read: [How to Report Bugs Effectively][howto-report-bugs-url]
|
||||||
|
|
||||||
|
# Suggesting enhancements/feature requests
|
||||||
|
|
||||||
|
This section guides you through submitting an enhancement suggestion for qBittorrent, including completely new features and minor improvements to existing functionality.
|
||||||
|
|
||||||
|
Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
|
||||||
|
|
||||||
|
## Before submitting an enhancement/feature request
|
||||||
|
|
||||||
|
* Check the [wiki][wiki-url] and [forum][forum-url] for tips — you might discover that the enhancement is already available.
|
||||||
|
* Most importantly, check if you're using the latest version of qBittorrent and if you can get the desired behavior by changing qBittorrent's settings.
|
||||||
|
* Check in the [releases][releases-url] page or on the [forum][forum-url], see if there's already a alpha/beta version with that enhancement.
|
||||||
|
* Perform a cursory search to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
|
||||||
|
|
||||||
|
## Steps to ensure a good enhancement/feature suggestion
|
||||||
|
|
||||||
|
- Specify which version of qBittorrent you're using.
|
||||||
|
- Specify the name and version of the OS you're using.
|
||||||
|
- Provide a step-by-step description of the suggested enhancement in as many details as possible.
|
||||||
|
- Describe the current behavior and explain which behavior you expected to see instead and why.
|
||||||
|
- Include screenshots and animated GIFs which help you demonstrate the steps or point out the part of qBittorrent which the suggestion is related to.
|
||||||
|
- If this enhancement exists in other BitTorrent clients, list those clients.
|
||||||
|
|
||||||
# Opening a pull request
|
# Opening a pull request
|
||||||
|
|
||||||
### Must read
|
### Must read
|
||||||
* Read our [**coding guidelines**](https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md). There are some scripts to help you: [uncrustify script](https://raw.githubusercontent.com/qbittorrent/qBittorrent/master/uncrustify.cfg), [astyle script](https://gist.github.com/Chocobo1/539cee860d1eef0acfa6), [(related thread)](https://github.com/qbittorrent/qBittorrent/issues/2192).
|
* Read our [**coding guidelines**][coding-guidelines-url]. There are some scripts to help you: [uncrustify script][uncrustify-script-url], [astyle script][astyle-script-url], [(related thread)][coding-guidelines-thread-url].
|
||||||
* Keep the title **short** and provide a **clear** description about what your pull request does.
|
* Keep the title **short** and provide a **clear** description about what your pull request does.
|
||||||
* Provide **screenshots** for UI related changes.
|
* Provide **screenshots** for UI related changes.
|
||||||
* Keep your git commit history **clean** and **precise**. Commits like `xxx fixup` should not appear.
|
* Keep your git commit history **clean** and **precise.** Refer to the section about "Git commit messages" in the [**coding guidelines**][coding-guidelines-url].
|
||||||
* If your commit fix a reported issue (for example #4134), add the following message to the commit `Closes #4134.`. Example [here](https://github.com/qbittorrent/qBittorrent/commit/a74bac20c4e8de9776bf9bb77fdc7526135d1988).
|
* If your commit fixes a reported issue (for example #4134), add the following message to the commit `Closes #4134.`. Example [here][commit-message-fix-issue-example-url].
|
||||||
|
|
||||||
### Good to know
|
### Good to know
|
||||||
* **Search** pull request history! Others might already implemented your idea and is waiting to be merged (or got rejected already). Save your precious time by doing a search first.
|
* **Search** pull request history! Others might have already implemented your idea and it is waiting to be merged (or got rejected already). Save your precious time by doing a search first.
|
||||||
* When resolving merge conflicts, do `git rebase <target_branch_name>`, don't do `git pull`. Then you can start fixing the conflicts. Here is a good explanation: [link](https://www.atlassian.com/git/tutorials/merging-vs-rebasing).
|
* When resolving merge conflicts, do `git rebase <target_branch_name>`, don't do `git pull`. Then you can start fixing the conflicts. Here is a good explanation: [link][merging-vs-rebasing-url].
|
||||||
|
|
||||||
|
[astyle-script-url]: https://gist.github.com/Chocobo1/539cee860d1eef0acfa6
|
||||||
|
[attachments-howto-url]: https://help.github.com/articles/file-attachments-on-issues-and-pull-requests
|
||||||
|
[coding-guidelines-url]: https://github.com/qbittorrent/qBittorrent/blob/master/CODING_GUIDELINES.md
|
||||||
|
[coding-guidelines-thread-url]: https://github.com/qbittorrent/qBittorrent/issues/2192
|
||||||
|
[commit-message-fix-issue-example-url]: https://github.com/qbittorrent/qBittorrent/commit/c07cd440cd46345297debb47cb260f8688975f50
|
||||||
|
[forum-url]: http://forum.qbittorrent.org/
|
||||||
|
[howto-report-bugs-url]: https://www.chiark.greenend.org.uk/~sgtatham/bugs.html
|
||||||
|
[merging-vs-rebasing-url]: https://www.atlassian.com/git/tutorials/merging-vs-rebasing
|
||||||
|
[python-url]: https://www.python.org/
|
||||||
|
[releases-url]: https://github.com/qbittorrent/qBittorrent/releases
|
||||||
|
[search-plugins-url]: https://github.com/qbittorrent/search-plugins
|
||||||
|
[uncrustify-script-url]: https://raw.githubusercontent.com/qbittorrent/qBittorrent/master/uncrustify.cfg
|
||||||
|
[wiki-url]: https://github.com/qbittorrent/qBittorrent/wiki
|
||||||
|
[builds-url]: https://sourceforge.net/projects/qbittorrent/files/
|
||||||
|
|||||||
215
Changelog
215
Changelog
@@ -1,3 +1,176 @@
|
|||||||
|
* Tue Sep 18 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.3
|
||||||
|
- FEATURE: Preselect name without extension when renaming files (thalieht)
|
||||||
|
- FEATURE: Allow setting seq & first/last from context menu without metadata (thalieht)
|
||||||
|
- BUGFIX: Show "N/A" if there is no scrape (thalieht)
|
||||||
|
- BUGFIX: Save option about tracker favicons under correct key (sledgehammer999)
|
||||||
|
- BUGFIX: When file data are unreachable pause torrent and show "Missing Files" status (temporary fix) (sledgehammer999)
|
||||||
|
- BUGFIX: Don't disable DHT when using force proxy (Thomas Piccirello)
|
||||||
|
- BUGFIX: Correctly save torrent queue position/state/priority changes in fastresume (glassez, thalieht, sledgehammer999)
|
||||||
|
- BUGFIX: Fix icon height/width ratio (Chocobo1)
|
||||||
|
- BUGFIX: Fix values sorted wrong in "Last Activity" column (Chocobo1)
|
||||||
|
- BUGFIX: Replace png icons with svg (Chocobo1)
|
||||||
|
- WEBUI: Allow WebUI sidebar filters to be hidden (Thomas Piccirello)
|
||||||
|
- WEBUI: Increase WebUI Options initial height (Thomas Piccirello)
|
||||||
|
- WEBUI: Adjust WebUI Options form alignment (Thomas Piccirello)
|
||||||
|
- WEBUI: Fix WebUI unreachable issue (Chocobo1)
|
||||||
|
- WEBUI: Require torrent category creation to be explicit (Thomas Piccirello)
|
||||||
|
- WEBUI: Include category save path in web api sync data (Thomas Piccirello)
|
||||||
|
- WEBUI: Add save path and editing to WebUI new category dialog (Thomas Piccirello)
|
||||||
|
- WEBUI: Bump Web API version
|
||||||
|
- SEARCH: Refactor in searchjob to always color visited entries (thalieht)
|
||||||
|
- SEARCH: Set "enter" as shortcut to download the selected torrents in search job (thalieht)
|
||||||
|
- SEARCH: Add regex option in the search filter's context menu (thalieht)
|
||||||
|
- LINUX: Fix GUI scaling issue on Linux (Chocobo1)
|
||||||
|
- LINUX: Fix regression that broke installing desktop file (Eli Schwartz)
|
||||||
|
- OPENBSD: Better filesystem support for filewatcher (Elias M. Mariani)
|
||||||
|
|
||||||
|
* Sun Aug 12 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.2
|
||||||
|
- FEATURE: New options for "inhibit sleep" (Lukas Greib)
|
||||||
|
- FEATURE: Add option for regexps in the transferlist search filter's context menu (thalieht)
|
||||||
|
- FEATURE: Add async io threads option to AdvancedSettings (tjjh89017)
|
||||||
|
- FEATURE: Allow save resume interval to be disabled (Chocobo1)
|
||||||
|
- FEATURE: Add checkbox for recursive download dialog (Chocobo1)
|
||||||
|
- FEATURE: Add changelog link in program updater (Chocobo1)
|
||||||
|
- BUGFIX: Avoid allocating large memory when loading a .torrent file (Couchy)
|
||||||
|
- BUGFIX: Notify users on 1st time close/minimize to tray (sledgehammer999)
|
||||||
|
- BUGFIX: Fix I/O error after fetching magnet metadata (Chocobo1)
|
||||||
|
- BUGFIX: Never save resume data for already paused torrents (glassez)
|
||||||
|
- BUGFIX: Make ProgramUpdater upgrade to 64-bit qbt when running on 64-bit Windows (Chocobo1)
|
||||||
|
- BUGFIX: Put temporary files in qbt own temp folder (Chocobo1)
|
||||||
|
- BUGFIX: Avoid potentially setting the wrong piece priorities (Chocobo1)
|
||||||
|
- BUGFIX: Various code refactorings/improvements (Chocobo1, thalieht, glassez)
|
||||||
|
- BUGFIX: Add options "Download in sequential order" and "Download first and last pieces first" in AddNewTorrentDialog (Chocobo1)
|
||||||
|
- BUGFIX: Download favicon using appropriate protocol (glassez)
|
||||||
|
- BUGFIX: Apply proxy settings on DownloadManager creation (glassez)
|
||||||
|
- BUGFIX: Improve torrent initialization (glassez)
|
||||||
|
- BUGFIX: Save resume data on torrent change events (glassez)
|
||||||
|
- BUGFIX: Increase default resume data save interval (Chocobo1)
|
||||||
|
- BUGFIX: Work around crash when procesing recursive download. Closes #9086 (Chocobo1)
|
||||||
|
- BUGFIX: Reduce queries to python version (Chocobo1)
|
||||||
|
- BUGFIX: Disable certain mouse wheel events in Options dialog (Chocobo1)
|
||||||
|
- WEBUI: Send all rechecks in one request (Thomas Piccirello)
|
||||||
|
- WEBUI: Add WebUI Force Reannounce option (Thomas Piccirello)
|
||||||
|
- WEBUI: Create non-existing path in setLocationAction() (Goshik)
|
||||||
|
- WEBUI: Add WebUI support for Mac ⌘ (Command) key (Thomas Piccirello)
|
||||||
|
- WEBUI: Show current save path in 'Set location' window (Goshik)
|
||||||
|
- WEBUI: Fix WebUI cache behavior for css files (Chocobo1)
|
||||||
|
- WEBUI: Send Cache-Control header in WebUI responses (Chocobo1)
|
||||||
|
- WEBUI: Add form-action to CSP (Thomas Piccirello)
|
||||||
|
- WEBUI: Add upgrade-insecure-requests to CSP when HTTPS is enabled (Thomas Piccirello)
|
||||||
|
- WEBUI: Reset WebUI ban counter on login success (Chocobo1)
|
||||||
|
- WEBUI: Add logging messages in WebUI login action (Chocobo1)
|
||||||
|
- WEBUI: Add option to control CSRF protection (Chocobo1)
|
||||||
|
- WEBUI: Add option to control WebUI clickjacking protection (Chocobo1)
|
||||||
|
- RSS: Implement "Sequential downloading" feature. Closes #6835 (glassez)
|
||||||
|
- RSS: Don't use RSS feed URLs as base for file names. Closes #8399 (glassez)
|
||||||
|
- SEARCH: Add a name filter for search results (thalieht)
|
||||||
|
- SEARCH: Fix python version detection (Chocobo1)
|
||||||
|
- SEARCH: Clear python cache conditionally (Chocobo1)
|
||||||
|
- SEARCH: Properly normalize version string before parsing it (hannsen)
|
||||||
|
- WINDOWS: Turn on Control Flow Guard for MSVC builds (Chocobo1)
|
||||||
|
- MACOS: Replace deprecated function IOPMAssertionCreate() on macOS (Chocobo1)
|
||||||
|
- OTHER: Fix CMake build with QtSingleApplication. Fixes #9196 (Eugene Shalygin)
|
||||||
|
|
||||||
|
* Sun May 27 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.1
|
||||||
|
- FEATURE: Add 'Moving' state for torrents being relocated/moved (sledgehammer999)
|
||||||
|
- FEATURE: Show rechecking progress (sledgehammer999)
|
||||||
|
- FEATURE: Add option to remember last used save path (glassez)
|
||||||
|
- FEATURE: Torrent name is also renamed if the content was renamed in the "Add New Torrent" dialog (glassez)
|
||||||
|
- FEATURE: Relax behavior of "Download first and last piece first". It applies to all files and not only to the previewable. (Chocobo1)
|
||||||
|
- BUGFIX: Fix issues with translatable strings (Chocobo1)
|
||||||
|
- BUGFIX: Fix displayed tracker messages (Chocobo1)
|
||||||
|
- BUGFIX: Make settings file recovery more robust (Chocobo1)
|
||||||
|
- BUGFIX: Retry saving settings when operation failed (Chocobo1)
|
||||||
|
- BUGFIX: Log successful torrent move (sledgehammer999)
|
||||||
|
- BUGFIX: Fix deletion of old logs (sledgehammer999)
|
||||||
|
- BUGFIX: Delete non-commited fastresume files (sledgehammer999)
|
||||||
|
- BUGFIX: Don't migrate torrents that have newer fastresumes (sledgehammer999)
|
||||||
|
- BUGFIX: Fix adding multiple torrents at once from WebUI (glassez)
|
||||||
|
- BUGFIX: Improve "Run External Program" behavior. On Windows, a backslash isn't appended to paths from path variables (Chocobo1)
|
||||||
|
- BUGFIX: Suppress multiple I/O errors for the same torrent (sledgehammer999)
|
||||||
|
- BUGFIX: Replace raster qbt logo with vector version (Chocobo1)
|
||||||
|
- WEBUI: Fix wrong API method names (glassez)
|
||||||
|
- WEBUI: Filter torrent info endpoint by hashes (Marcel Petersen)
|
||||||
|
- WEBUI: Fix invalid API calls in WebUI (glassez)
|
||||||
|
- WEBUI: Improve legacy API params handling (glassez)
|
||||||
|
- WEBUI: Fix params handling for some legacy API methods (glassez)
|
||||||
|
- WEBUI: Apply locale changes immediately in WebUI (Chocobo1)
|
||||||
|
- WEBUI: Use 32px icons for favicon (Chocobo1)
|
||||||
|
- WEBUI/RSS: Properly set RSS settings via API (glassez)
|
||||||
|
- RSS: Fix auto-downloading rule when Smart filter with regular Episode filter are used (glassez)
|
||||||
|
- RSS: Make "Ignoring days" to behave like other filters (glassez)
|
||||||
|
- RSS: Place "Use Smart Episode Filter" more correctly (glassez)
|
||||||
|
- RSS: Use RSS feed update time as a fallback (glassez)
|
||||||
|
- COSMETIC: Fix Stats dialog size (sledgehammer999)
|
||||||
|
- MACOS: Fix GUI scaling factor on macOS (Chocobo1)
|
||||||
|
- WINDOWS: Update icons (adem4ik)
|
||||||
|
- LINUX: Fix open destination folder with Nautilus > 3.28 (Evgeny Lensky)
|
||||||
|
- OTHER: Code improvements and refactoring (thalieht, Nick Korotysh, Chocobo1)
|
||||||
|
|
||||||
|
* Sat May 05 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.1.0
|
||||||
|
- FEATURE: Add "Coalesce reads & writes" checkbox in advanced options (Chocobo1)
|
||||||
|
- FEATURE: Smart Filter for RSS (Stephen Dawkins)
|
||||||
|
- FEATURE: Possibility to configure at which speed a torrent is considered slow (thalieht)
|
||||||
|
- FEATURE: When creating a torrent you can choose to preserve the file order (toster, Chocobo1)
|
||||||
|
- FEATURE: A new, redesigned and refactored WebAPI (glassez)
|
||||||
|
- BUGFIX: Redefine CacheStatus.readRatio field. (Chocobo1)
|
||||||
|
- BUGFIX: Clarify some terms in stats dialog (Chocobo1)
|
||||||
|
- BUGFIX: Fix possible crash when using both share limits (thalieht)
|
||||||
|
- BUGFIX: Disable options when `Disable connections not supported by proxies` is enabled (Thomas Piccirello)
|
||||||
|
- BUGFIX: Add link to an explanation of `Disable connections not supported by proxies` (Thomas Piccirello)
|
||||||
|
- BUGFIX: Fix typo in a log message (Andrei Stepanov)
|
||||||
|
- BUGFIX: Fix loading very large torrents. Closes #8449. (Chocobo1)
|
||||||
|
- BUGFIX: Fix reverting backslashes to slashes in run external program. Closes #7800 (Chocobo1)
|
||||||
|
- BUGFIX: Use https for documentation links (Chocobo1)
|
||||||
|
- BUGFIX: Use original scheme when downloading favicons (Chocobo1)
|
||||||
|
- BUGFIX: Parse URL query string at application level (glassez)
|
||||||
|
- BUGFIX: Properly reply to announce request (embedded tracker) (glassez)
|
||||||
|
- BUGFIX: Add `Tags` parameter to "Run External Program" (Chocobo1)
|
||||||
|
- BUGFIX: Fix various typos (Chocobo1)
|
||||||
|
- BUGFIX: Refactor filesystem watcher. Delay before processing new files. (Chocobo1)
|
||||||
|
- BUGFIX: Don't strip empty arguments passed to external program. Closes #8454. (Chocobo1)
|
||||||
|
- BUGFIX: Stop creating Download folder on start (Chocobo1)
|
||||||
|
- BUGFIX: Avoid data corruption when rechecking paused torrents (sledgehammer999)
|
||||||
|
- BUGFIX: Fix crashes due to invalid iterator use (Luís Pereira)
|
||||||
|
- BUGFIX: Fix renaming completed files (Chocobo1)
|
||||||
|
- BUGFIX: Fix path separator in log messages (Chocobo1)
|
||||||
|
- WEBUI: Switch built-in Web UI html to HTML5 (glassez)
|
||||||
|
- WEBUI: WebUI Save user's resized window sizes (Thomas Piccirello)
|
||||||
|
- WEBUI: Make download + upload windows resizable (Thomas Piccirello)
|
||||||
|
- WEBUI: Add option to show/hide webui status bar (Thomas Piccirello)
|
||||||
|
- WEBUI: Add "Use proxy only for torrents" option to webui (Thomas Piccirello)
|
||||||
|
- WEBUI: Various fixes in the html code (Thomas Piccirello)
|
||||||
|
- WEBUI: Don't unselect selected torrents after a few seconds (Thomas Piccirello)
|
||||||
|
- WEBUI: Enable Http/1.1 persistence connection (Chocobo1)
|
||||||
|
- WEBUI: Format Read cache hits as percentage (Thomas Piccirello)
|
||||||
|
- WEBUI: Re-order and rename stats (Thomas Piccirello)
|
||||||
|
- WEBUI: Right align stat values (Thomas Piccirello)
|
||||||
|
- WEBUI: Enable Statistics window to be scrolled and resized (Tom Piccirello)
|
||||||
|
- WEBUI: Save WebUI Statistics window size (Thomas Piccirello)
|
||||||
|
- WEBUI: Make WebUI iframe windows scrollable on iOS (Thomas Piccirello)
|
||||||
|
- WEBUI: Remove unused CSS from WebUI login page (Thomas Piccirello)
|
||||||
|
- WEBUI: Consolidate CSS into style.css (Thomas Piccirello)
|
||||||
|
- WEBUI: Resolve JavaScript errors (Thomas Piccirello)
|
||||||
|
- WEBUI: Fix spacing in login form(Thomas Piccirello)
|
||||||
|
- WEBUI: Update WebUI to be more compliant with HTML5 standard (Chocobo1)
|
||||||
|
- WEBUI: Update clipboard.js to v2.0.0 (Chocobo1)
|
||||||
|
- WEBUI: Remove unused JavaScript library (Chocobo1)
|
||||||
|
- WEBUI: Fix setting preferences via WebAPI (glassez)
|
||||||
|
- WEBUI: Rename property to match its definition (Thomas Piccirello)
|
||||||
|
- WEBUI: Add Limit Share Ratio context menu option (Thomas Piccirello)
|
||||||
|
- RSS: Disable Auto TMM when RSS rule has save path (glassez)
|
||||||
|
- RSS: Process loaded RSS articles in case of error (glassez)
|
||||||
|
- RSS: Resolve (X)HTML entities in RSS content (glassez)
|
||||||
|
- SEARCH: Improve Search handling (glassez)
|
||||||
|
- SEARCH: Calculate supported categories based on selected plugin (Thomas Piccirello)
|
||||||
|
- SEARCH: Fix memory leak (Chocobo1)
|
||||||
|
- COSMETIC: Use spinbox suffix to display rate/time units (thalieht)
|
||||||
|
- COSMETIC: Avoid showing an empty row in AdvancedSettings (Chocobo1)
|
||||||
|
- OTHER: Various code optimizations and fixes (Luís Pereira, Chocobo1)
|
||||||
|
- OTHER: Fix build when using Clang under CMake (Luís Pereira)
|
||||||
|
- OTHER: Allow to disable Stacktrace support (Nick Korotysh)
|
||||||
|
- OTHER: Use RNG provided by OS (Chocobo1)
|
||||||
|
|
||||||
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
|
* Fri Feb 16 2018 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.0.4
|
||||||
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
|
- FEATURE: Add source field in Torrent creator. Closes #7965. (Chocobo1)
|
||||||
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
|
- FEATURE: Torrent creator: raise maximum piece size to 32 MiB (Chocobo1)
|
||||||
@@ -122,7 +295,7 @@
|
|||||||
- BUGFIX: Optimize code for SpeedWidget. (dzmat)
|
- BUGFIX: Optimize code for SpeedWidget. (dzmat)
|
||||||
- BUGFIX: Disable processing events when adding torrents(prevents crashes). Closes #7436. (Chocobo1)
|
- BUGFIX: Disable processing events when adding torrents(prevents crashes). Closes #7436. (Chocobo1)
|
||||||
- BUGFIX: Open links in browser. Closes #7651. (Chocobo1)
|
- BUGFIX: Open links in browser. Closes #7651. (Chocobo1)
|
||||||
- BUGFIX: Change default settings for tracker/tier announces to mimick μTorrent behavior. (sledgehammer999)
|
- BUGFIX: Change default settings for tracker/tier announces to mimic μTorrent behavior. (sledgehammer999)
|
||||||
- BUGFIX: Explicitly set UPnP state on start-up. Closes #7338. (Chocobo1)
|
- BUGFIX: Explicitly set UPnP state on start-up. Closes #7338. (Chocobo1)
|
||||||
- BUGFIX: Include/print caught signal in stackdump (Chocobo1)
|
- BUGFIX: Include/print caught signal in stackdump (Chocobo1)
|
||||||
- COSMETIC: Trackerlist: Set text alignment of columns with numbers to the right (thalieht)
|
- COSMETIC: Trackerlist: Set text alignment of columns with numbers to the right (thalieht)
|
||||||
@@ -253,7 +426,7 @@
|
|||||||
- FEATURE: Use Ctrl+F to search torrents. Closes #5797. (Tim Delaney)
|
- FEATURE: Use Ctrl+F to search torrents. Closes #5797. (Tim Delaney)
|
||||||
- FEATURE: Transferlist: add hotkeys for double click and recheck selected torrents (thalieht)
|
- FEATURE: Transferlist: add hotkeys for double click and recheck selected torrents (thalieht)
|
||||||
- FEATURE: Add hotkey for execution log tab, Trackerlist, Peerlist etc (thalieht)
|
- FEATURE: Add hotkey for execution log tab, Trackerlist, Peerlist etc (thalieht)
|
||||||
- FEATURE: Seperate seeds from peers for DHT, PeX and LSD (thalieht)
|
- FEATURE: Separate seeds from peers for DHT, PeX and LSD (thalieht)
|
||||||
- BUGFIX: Do not remove added files unconditionally. Closes #6248 (Eugene Shalygin)
|
- BUGFIX: Do not remove added files unconditionally. Closes #6248 (Eugene Shalygin)
|
||||||
- BUGFIX: Ignore mouse wheel events in Advanced Settings. Closes #866. (Chocobo1)
|
- BUGFIX: Ignore mouse wheel events in Advanced Settings. Closes #866. (Chocobo1)
|
||||||
- BUGFIX: Add queue repair code. It should fix missing torrents after restarting. (Eugene Shalygin, nxd4)
|
- BUGFIX: Add queue repair code. It should fix missing torrents after restarting. (Eugene Shalygin, nxd4)
|
||||||
@@ -265,7 +438,7 @@
|
|||||||
- BUGFIX: TransferListWidget: keep columns width even if they are hidden on qBittorrent startup (unless something goes wrong) (thalieht)
|
- BUGFIX: TransferListWidget: keep columns width even if they are hidden on qBittorrent startup (unless something goes wrong) (thalieht)
|
||||||
- BUGFIX: fix index overflow for torrents with invalid meta data or empty progress (Falco)
|
- BUGFIX: fix index overflow for torrents with invalid meta data or empty progress (Falco)
|
||||||
- BUGFIX: Immediately update torrent_status after manipulating super seeding mode. Partially fixes #6072. (sledgehammer999)
|
- BUGFIX: Immediately update torrent_status after manipulating super seeding mode. Partially fixes #6072. (sledgehammer999)
|
||||||
- BUGFIX: Use case-insensitive comparsion for torrent content window. Closes #6327. (Chocobo1)
|
- BUGFIX: Use case-insensitive comparison for torrent content window. Closes #6327. (Chocobo1)
|
||||||
- BUGFIX: Fixed sort order for datetime columns with empty values (closes #2988) (Vladimir Sinenko)
|
- BUGFIX: Fixed sort order for datetime columns with empty values (closes #2988) (Vladimir Sinenko)
|
||||||
- BUGFIX: Disable proxy in WebUI HTTP server. Closes #6349. (Eugene Shalygin)
|
- BUGFIX: Disable proxy in WebUI HTTP server. Closes #6349. (Eugene Shalygin)
|
||||||
- COSMETIC: Use a disabled progressbar's palette for unselected files. (sledgehammer999)
|
- COSMETIC: Use a disabled progressbar's palette for unselected files. (sledgehammer999)
|
||||||
@@ -393,12 +566,12 @@
|
|||||||
- FEATURE: Add option to automatically remove .torrent files upon adding (Eugene Shalygin)
|
- FEATURE: Add option to automatically remove .torrent files upon adding (Eugene Shalygin)
|
||||||
- FEATURE: Add option to bind directly to an IP instead of using a network Interface (Sjoerd van der Berg, sledgehammer999)
|
- FEATURE: Add option to bind directly to an IP instead of using a network Interface (Sjoerd van der Berg, sledgehammer999)
|
||||||
- FEATURE: Detailed tooltips on the progress and availability bars in the General button of each torrent. (Eugene Shalygin)
|
- FEATURE: Detailed tooltips on the progress and availability bars in the General button of each torrent. (Eugene Shalygin)
|
||||||
- FEATURE: Let user able to specifiy a filter when choosing an IP filter file (Chocobo1)
|
- FEATURE: Let user able to specify a filter when choosing an IP filter file (Chocobo1)
|
||||||
- FEATURE: Improve usability of "Run External Program". Users can write (platform dependent) shell scripts now. (Chocobo1)
|
- FEATURE: Improve usability of "Run External Program". Users can write (platform dependent) shell scripts now. (Chocobo1)
|
||||||
- PERFORMANCE: Optimize drawing in speed graph (Anton Lashkov, Chocobo1)
|
- PERFORMANCE: Optimize drawing in speed graph (Anton Lashkov, Chocobo1)
|
||||||
- BUGFIX: Fix memory leak. (sledgehammer999)
|
- BUGFIX: Fix memory leak. (sledgehammer999)
|
||||||
- BUGFIX: Fix resizing bug in "add torrent dialog". Closes #5036. (Chocobo1)
|
- BUGFIX: Fix resizing bug in "add torrent dialog". Closes #5036. (Chocobo1)
|
||||||
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all donwloads are done -> exit" option enabled. (glassez, Chocobo1)
|
- BUGFIX: Fix qBittorrent doesn't exit immediately when "all downloads are done -> exit" option enabled. (glassez, Chocobo1)
|
||||||
- BUGFIX: Display the filepath when a torrent fails to load. Closes #100 and #805. (sledgehammer999)
|
- BUGFIX: Display the filepath when a torrent fails to load. Closes #100 and #805. (sledgehammer999)
|
||||||
- BUGFIX: Fix Add tracker dialog empty trackers (ngosang)
|
- BUGFIX: Fix Add tracker dialog empty trackers (ngosang)
|
||||||
- BUGFIX: Fix Add tracker dialog URL download (ngosang)
|
- BUGFIX: Fix Add tracker dialog URL download (ngosang)
|
||||||
@@ -451,7 +624,7 @@
|
|||||||
- OTHER: Enable access to shutdown functions when configured with `--disable-gui` option (Chocobo1)
|
- OTHER: Enable access to shutdown functions when configured with `--disable-gui` option (Chocobo1)
|
||||||
- OTHER: Delete Import Torrent Dialog. Just use the "add new torrent" dialog. (glassez)
|
- OTHER: Delete Import Torrent Dialog. Just use the "add new torrent" dialog. (glassez)
|
||||||
- OTHER: Optimize code for natural sorting (Chocobo1)
|
- OTHER: Optimize code for natural sorting (Chocobo1)
|
||||||
- OTHER: Use new alert dispathing API for libtorrent 1.1.x (glassez)
|
- OTHER: Use new alert dispatching API for libtorrent 1.1.x (glassez)
|
||||||
- OTHER: Fix gcc 6 compilation with qmake. See #5237. (sledgehammer999)
|
- OTHER: Fix gcc 6 compilation with qmake. See #5237. (sledgehammer999)
|
||||||
|
|
||||||
* Tue Mar 29 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.4
|
* Tue Mar 29 2016 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.3.4
|
||||||
@@ -775,7 +948,7 @@
|
|||||||
- SEARCH: Fix thepiratebay. Closes #3012 (ngosang)
|
- SEARCH: Fix thepiratebay. Closes #3012 (ngosang)
|
||||||
- SEARCH: Improve torrentz engine to return more results (ngosang)
|
- SEARCH: Improve torrentz engine to return more results (ngosang)
|
||||||
- SEARCH: Change width of columns in search tab. Closes #764 (ngosang)
|
- SEARCH: Change width of columns in search tab. Closes #764 (ngosang)
|
||||||
- SEARCH: Make strings translatable in seach engine (ngosang)
|
- SEARCH: Make strings translatable in search engine (ngosang)
|
||||||
- SEARCH: Aborting search engine process during closure. Close #2671 (DoumanAsh)
|
- SEARCH: Aborting search engine process during closure. Close #2671 (DoumanAsh)
|
||||||
- SEARCH: Perform searches in parallel (DoumanAsh)
|
- SEARCH: Perform searches in parallel (DoumanAsh)
|
||||||
- SEARCH: Add Demonoid search engine (ngosang)
|
- SEARCH: Add Demonoid search engine (ngosang)
|
||||||
@@ -942,7 +1115,7 @@
|
|||||||
- WEBUI: Removed broken 'Documentation'. Improves fix for #1343 (Benjamin Hutchins)
|
- WEBUI: Removed broken 'Documentation'. Improves fix for #1343 (Benjamin Hutchins)
|
||||||
- WEBUI: Removed essentially useless 'Visit website' iframe and changed it to a regular link. Improves fix for #1343 (Benjamin Hutchins)
|
- WEBUI: Removed essentially useless 'Visit website' iframe and changed it to a regular link. Improves fix for #1343 (Benjamin Hutchins)
|
||||||
- BUGFIX: Fix RSS feed icon. The tmp file gets deleted in the feed destructor. Closes #1639 (sledgehammer999)
|
- BUGFIX: Fix RSS feed icon. The tmp file gets deleted in the feed destructor. Closes #1639 (sledgehammer999)
|
||||||
- BUGFIX: fix issue #1674: AddNewTorrentDialog is shown again and again even if checkbox "dont ask me again" is set (Ivan Sorokin)
|
- BUGFIX: fix issue #1674: AddNewTorrentDialog is shown again and again even if checkbox "don't ask me again" is set (Ivan Sorokin)
|
||||||
- BUGFIX: Don't show availability bar for magnet links (Ivan Sorokin)
|
- BUGFIX: Don't show availability bar for magnet links (Ivan Sorokin)
|
||||||
- BUGFIX: Fix crash when the selected torrent disappears from the transfer list. Closes #1661 (sledgehammer999)
|
- BUGFIX: Fix crash when the selected torrent disappears from the transfer list. Closes #1661 (sledgehammer999)
|
||||||
- BUGFIX: Fix tracker announcing problem(hit-and-run) when many torrents are being active. Closes #1571 (sledgehammer999)
|
- BUGFIX: Fix tracker announcing problem(hit-and-run) when many torrents are being active. Closes #1571 (sledgehammer999)
|
||||||
@@ -1076,7 +1249,7 @@
|
|||||||
- FEATURE: Show external IP in the log. Closes #968. (sledgehammer999)
|
- FEATURE: Show external IP in the log. Closes #968. (sledgehammer999)
|
||||||
- FEATURE: Enable gzip compression in the webui. It should be faster now. (sledgehammer999)
|
- FEATURE: Enable gzip compression in the webui. It should be faster now. (sledgehammer999)
|
||||||
- FEATURE: Torrents show more states(queued for checking, downloading metadata, allocating, checking resume). (sledgehammer999)
|
- FEATURE: Torrents show more states(queued for checking, downloading metadata, allocating, checking resume). (sledgehammer999)
|
||||||
- FEATURE: Reenable "force reannounce" to all trackers. (sledgehammer999)
|
- FEATURE: Re-enable "force reannounce" to all trackers. (sledgehammer999)
|
||||||
- FEATURE: Allow to clear the UI lock password. Closes #973. (sledgehammer999)
|
- FEATURE: Allow to clear the UI lock password. Closes #973. (sledgehammer999)
|
||||||
- FEATURE: New translations: English(Australia) and English(United Kingdom)
|
- FEATURE: New translations: English(Australia) and English(United Kingdom)
|
||||||
- BUGFIX: Expose all available translation in the WebUI. Closes #976. (sledgehammer999)
|
- BUGFIX: Expose all available translation in the WebUI. Closes #976. (sledgehammer999)
|
||||||
@@ -1125,8 +1298,8 @@
|
|||||||
- OTHER: Make peer tab sortable by ip too (Gelmir)
|
- OTHER: Make peer tab sortable by ip too (Gelmir)
|
||||||
- OTHER: Translations moved to Transifex(https://www.transifex.com/projects/p/qbittorrent/)
|
- OTHER: Translations moved to Transifex(https://www.transifex.com/projects/p/qbittorrent/)
|
||||||
- OTHER: New Translation - Vietnamese (Anh Phan)
|
- OTHER: New Translation - Vietnamese (Anh Phan)
|
||||||
- PERFORMANCE: Impove drawing speed of tranferlist when there are many torrents(>100)
|
- PERFORMANCE: Improve drawing speed of tranferlist when there are many torrents(>100)
|
||||||
- PERFORMANCE: Impove drawing speed of peers list when there are many peers
|
- PERFORMANCE: Improve drawing speed of peers list when there are many peers
|
||||||
|
|
||||||
* Mon Jul 29 2013 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.0.11
|
* Mon Jul 29 2013 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v3.0.11
|
||||||
- FEATURE: Allow more fine tuning of upload slots. It should improve speed (sledgehammer999)
|
- FEATURE: Allow more fine tuning of upload slots. It should improve speed (sledgehammer999)
|
||||||
@@ -1166,8 +1339,8 @@
|
|||||||
- LIBTORRENT: SOCKS5 fixes (0.16.10)
|
- LIBTORRENT: SOCKS5 fixes (0.16.10)
|
||||||
- LIBTORRENT: Fix hanging issue on Windows when closing files (0.16.10)
|
- LIBTORRENT: Fix hanging issue on Windows when closing files (0.16.10)
|
||||||
- LIBTORRENT: Cache can now be returned to the OS (0.16.10)
|
- LIBTORRENT: Cache can now be returned to the OS (0.16.10)
|
||||||
- PERFORMANCE: Impove drawing speed of tranferlist when there are many torrents(>100) (sledgehammer999)
|
- PERFORMANCE: Improve drawing speed of tranferlist when there are many torrents(>100) (sledgehammer999)
|
||||||
- PERFORMANCE: Impove drawing speed of peers list when there are many peers (sledgehammer999)
|
- PERFORMANCE: Improve drawing speed of peers list when there are many peers (sledgehammer999)
|
||||||
|
|
||||||
* Sat Mar 16 2013 - Christophe Dumez <chris@qbittorrent.org> - v3.0.9
|
* Sat Mar 16 2013 - Christophe Dumez <chris@qbittorrent.org> - v3.0.9
|
||||||
- BUGFIX: Raise qBittorrent windows when another instance is launched
|
- BUGFIX: Raise qBittorrent windows when another instance is launched
|
||||||
@@ -1191,7 +1364,7 @@
|
|||||||
- BUGFIX: Fix "Couldn't set environment variable..." message on start up (closes #245)
|
- BUGFIX: Fix "Couldn't set environment variable..." message on start up (closes #245)
|
||||||
- BUGFIX: Use right path separator in torrent addition dialog on Windows
|
- BUGFIX: Use right path separator in torrent addition dialog on Windows
|
||||||
- BUGFIX: Fix "Set as default save path" setting (closes #254)
|
- BUGFIX: Fix "Set as default save path" setting (closes #254)
|
||||||
- BUGFIX: Reenable disk cache on Windows since the memory issue seems to be gone
|
- BUGFIX: Re-enable disk cache on Windows since the memory issue seems to be gone
|
||||||
- BUGFIX: Fixed several search engine plugins and removed the dead ones
|
- BUGFIX: Fixed several search engine plugins and removed the dead ones
|
||||||
- BUGFIX: Use https links in search plugins when possible
|
- BUGFIX: Use https links in search plugins when possible
|
||||||
- BUGFIX: Bump Mootools to v1.4.5 (Web UI)
|
- BUGFIX: Bump Mootools to v1.4.5 (Web UI)
|
||||||
@@ -1327,7 +1500,7 @@
|
|||||||
- I18N: Add Georgian translation
|
- I18N: Add Georgian translation
|
||||||
|
|
||||||
* Sat Oct 29 2011 - Christophe Dumez <chris@qbittorrent.org> - v2.9.2
|
* Sat Oct 29 2011 - Christophe Dumez <chris@qbittorrent.org> - v2.9.2
|
||||||
- BUGFIX: Fix mimimum dimensions for torrent addition dialog
|
- BUGFIX: Fix minimum dimensions for torrent addition dialog
|
||||||
- BUGFIX: Remove dependency on boost-datetime
|
- BUGFIX: Remove dependency on boost-datetime
|
||||||
- BUGFIX: Remove dependency on boost-filesystem (libtorrent v0.16.x)
|
- BUGFIX: Remove dependency on boost-filesystem (libtorrent v0.16.x)
|
||||||
|
|
||||||
@@ -1538,7 +1711,7 @@
|
|||||||
- BUGFIX: Update RSS feed as soon as feed downloader is enabled
|
- BUGFIX: Update RSS feed as soon as feed downloader is enabled
|
||||||
- BUGFIX: RSS Feed downloader ignores articles above maximum number of articles
|
- BUGFIX: RSS Feed downloader ignores articles above maximum number of articles
|
||||||
- BUGFIX: Fix possible bug when deleting a RSS folder
|
- BUGFIX: Fix possible bug when deleting a RSS folder
|
||||||
- BUGFIX: Remove persistant data when a RSS feed is deleted
|
- BUGFIX: Remove persistent data when a RSS feed is deleted
|
||||||
- BUGFIX: RSS filters are now alphabetically sorted
|
- BUGFIX: RSS filters are now alphabetically sorted
|
||||||
- BUGFIX: Fix crash when renaming currently displayed RSS filter
|
- BUGFIX: Fix crash when renaming currently displayed RSS filter
|
||||||
- BUGFIX: Remove overwriting confirmation when exporting RSS filters since Qt takes care of it
|
- BUGFIX: Remove overwriting confirmation when exporting RSS filters since Qt takes care of it
|
||||||
@@ -1574,7 +1747,7 @@
|
|||||||
- BUGFIX: Use the save path set in program preferences as a default in torrent addition dialog
|
- BUGFIX: Use the save path set in program preferences as a default in torrent addition dialog
|
||||||
|
|
||||||
* Fri Dec 18 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.2
|
* Fri Dec 18 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.2
|
||||||
- BUGFIX: Fix .qbittorrent folder not being created (critical bug introduced in v2.0.1 that makes qBittorrent unusuable for new users)
|
- BUGFIX: Fix .qbittorrent folder not being created (critical bug introduced in v2.0.1 that makes qBittorrent unusable for new users)
|
||||||
- BUGFIX: Fix RSS Feed downloader for some feeds
|
- BUGFIX: Fix RSS Feed downloader for some feeds
|
||||||
- BUGFIX: Do not use home folder as a fallback when the save path is not accessible
|
- BUGFIX: Do not use home folder as a fallback when the save path is not accessible
|
||||||
- BUGFIX: Fix Mininova, ThePirateBay search engine plugins
|
- BUGFIX: Fix Mininova, ThePirateBay search engine plugins
|
||||||
@@ -1664,7 +1837,7 @@
|
|||||||
- BUGFIX: Fix trackers addition to torrents (bug introduced in v1.5.4)
|
- BUGFIX: Fix trackers addition to torrents (bug introduced in v1.5.4)
|
||||||
- BUGFIX: Suppress compilation warning regarding sortNewsList() not being used
|
- BUGFIX: Suppress compilation warning regarding sortNewsList() not being used
|
||||||
- BUGFIX: Make sure scan folder is different than qBittorrent backup directory to avoid torrents deletion
|
- BUGFIX: Make sure scan folder is different than qBittorrent backup directory to avoid torrents deletion
|
||||||
- BUGFIX: Added safety mecanism which adds the torrents back to the list in case qbittorrent-resume.conf gets deleted or corrupted.
|
- BUGFIX: Added safety mechanism which adds the torrents back to the list in case qbittorrent-resume.conf gets deleted or corrupted.
|
||||||
|
|
||||||
* Sun Oct 25 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.4
|
* Sun Oct 25 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.4
|
||||||
- BUGFIX: Updated man page
|
- BUGFIX: Updated man page
|
||||||
@@ -1731,7 +1904,7 @@
|
|||||||
- FEATURE: Added right click menu in search engine to clear completion history
|
- FEATURE: Added right click menu in search engine to clear completion history
|
||||||
- FEATURE: Allow to set a different port for DHT (UDP) than the one used for Bittorrent
|
- FEATURE: Allow to set a different port for DHT (UDP) than the one used for Bittorrent
|
||||||
- FEATURE: Updated spoofing code to avoid trackers ban
|
- FEATURE: Updated spoofing code to avoid trackers ban
|
||||||
- BUGFIX: Provide more helpful explanation when an I/O error occured
|
- BUGFIX: Provide more helpful explanation when an I/O error occurred
|
||||||
- BUGFIX: Stop enforcing UTF-8 and use system locale instead
|
- BUGFIX: Stop enforcing UTF-8 and use system locale instead
|
||||||
- COSMETIC: Redesigned program preferences
|
- COSMETIC: Redesigned program preferences
|
||||||
- COSMETIC: Updated icons set
|
- COSMETIC: Updated icons set
|
||||||
@@ -1749,7 +1922,7 @@
|
|||||||
- BUGFIX: Suppressed QLayout: Attempting to add QLayout "" to properties "properties" warning message when opening a properties dialog
|
- BUGFIX: Suppressed QLayout: Attempting to add QLayout "" to properties "properties" warning message when opening a properties dialog
|
||||||
- BUGFIX: Fixed a little bug in search engine plugins helper file
|
- BUGFIX: Fixed a little bug in search engine plugins helper file
|
||||||
- BUGFIX: Fixed compilation problems with Qt 4.3
|
- BUGFIX: Fixed compilation problems with Qt 4.3
|
||||||
- BUGFIX: Percentages no longer disapear with default cleanlooks style
|
- BUGFIX: Percentages no longer disappear with default cleanlooks style
|
||||||
- BUGFIX: Cleanly fixed popup menus position in lists (no more workarounds)
|
- BUGFIX: Cleanly fixed popup menus position in lists (no more workarounds)
|
||||||
- BUGFIX: Fixed memory leak in search engine
|
- BUGFIX: Fixed memory leak in search engine
|
||||||
- BUGFIX: Torrents with an infinite ratio are no longer affected by ratio_limit set in program preferences
|
- BUGFIX: Torrents with an infinite ratio are no longer affected by ratio_limit set in program preferences
|
||||||
@@ -1994,7 +2167,7 @@
|
|||||||
- FEATURE: Number of complete/incomplete sources are now displayed in download list for each torrent
|
- FEATURE: Number of complete/incomplete sources are now displayed in download list for each torrent
|
||||||
- FEATURE: Implemented close to systray
|
- FEATURE: Implemented close to systray
|
||||||
- FEATURE: Added Autocompletion to search engine
|
- FEATURE: Added Autocompletion to search engine
|
||||||
- FEATURE: Splitted BT & GUI parts (huge code rewriting & optimization)
|
- FEATURE: Split BT & GUI parts (huge code rewriting & optimization)
|
||||||
- FEATURE: New parameters for configure file to point to custom locations for libtorrent/libcurl
|
- FEATURE: New parameters for configure file to point to custom locations for libtorrent/libcurl
|
||||||
- FEATURE: Update application style according to the system (WindowsXP, MacOS, X11)
|
- FEATURE: Update application style according to the system (WindowsXP, MacOS, X11)
|
||||||
- BUGFIX: Two torrents can now have the same name although they are different (use their hash)
|
- BUGFIX: Two torrents can now have the same name although they are different (use their hash)
|
||||||
|
|||||||
49
INSTALL
49
INSTALL
@@ -1,54 +1,49 @@
|
|||||||
qBittorrent - A BitTorrent client in C++ / Qt4
|
qBittorrent - A BitTorrent client in C++ / Qt
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
1) Compile and install qBittorrent with Qt4 Graphical Interface
|
1) Compile and install qBittorrent with Qt graphical interface
|
||||||
|
|
||||||
$ ./configure
|
$ ./configure
|
||||||
$ make && make install
|
$ make && make install
|
||||||
$ qbittorrent
|
$ qbittorrent
|
||||||
|
|
||||||
will install and execute qBittorrent hopefully without any problems.
|
will install and execute qBittorrent.
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
- Qt >= 4.6.0 (libqtgui, libqtcore, libqtnetwork, libqtxml, libqtdbus/optional)
|
- Qt >= 5.5.1
|
||||||
|
|
||||||
- pkg-config executable
|
- pkg-config
|
||||||
|
|
||||||
- libtorrent-rasterbar by Arvid Norberg (>= 1.0.6)
|
- libtorrent-rasterbar >= 1.0.6 (by Arvid Norberg)
|
||||||
-> http://www.libtorrent.net
|
* https://www.libtorrent.org/
|
||||||
Be careful: another library (the one used by rTorrent) uses a similar name.
|
* Be careful: another library (the one used by rTorrent) uses a similar name
|
||||||
|
|
||||||
- libboost >= 1.35.x (libboost-system)
|
- Boost >= 1.35
|
||||||
|
|
||||||
- python >= 2.3 (needed by search engine)
|
- Python >= 2.7.9 / 3.3.0 (optional, runtime only)
|
||||||
* Run time only dependency
|
* Required by the internal search engine
|
||||||
|
|
||||||
- geoip-database (optional)
|
2) Compile and install qBittorrent without Qt graphical interface
|
||||||
* If qBittorrent cannot find this database, it will try to resolve countries using the Internet but it will be a lot slower.
|
|
||||||
* Run time only dependency
|
|
||||||
|
|
||||||
2) Compile and install qBittorrent without Qt4 Graphical interface
|
|
||||||
|
|
||||||
$ ./configure --disable-gui
|
$ ./configure --disable-gui
|
||||||
$ make && make install
|
$ make && make install
|
||||||
$ qbittorrent
|
$ qbittorrent-nox
|
||||||
|
|
||||||
will install and execute qBittorrent hopefully without any problems.
|
will install and execute qBittorrent.
|
||||||
|
|
||||||
Dependencies:
|
Dependencies:
|
||||||
- Qt >= 4.4.0 (libqt-devel, libqtcore, libqtnetwork)
|
- Qt >= 5.5.1
|
||||||
|
|
||||||
- pkg-config executable
|
- pkg-config
|
||||||
|
|
||||||
- libtorrent-rasterbar by Arvid Norberg (>= v1.0.6)
|
- libtorrent-rasterbar >= 1.0.6 (by Arvid Norberg)
|
||||||
-> http://www.libtorrent.net
|
* https://www.libtorrent.org/
|
||||||
Be careful: another library (the one used by rTorrent) uses a similar name.
|
* Be careful: another library (the one used by rTorrent) uses a similar name
|
||||||
|
|
||||||
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
|
|
||||||
|
|
||||||
|
- Boost >= 1.35
|
||||||
|
|
||||||
DOCUMENTATION:
|
DOCUMENTATION:
|
||||||
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
Please note that there is a "Compilation" section at http://wiki.qbittorrent.org.
|
||||||
|
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
Christophe Dumez <chris@qbittorrent.org>
|
sledgehammer999 <sledgehammer999@qbittorrent.org>
|
||||||
|
|||||||
22
cmake/Modules/CompileFeature.cmake
Normal file
22
cmake/Modules/CompileFeature.cmake
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Helper function for coupling add_feature_info(), option(), and add_definitions()
|
||||||
|
|
||||||
|
function(optional_compile_definitions _name)
|
||||||
|
set(options FEATURE)
|
||||||
|
set(oneValueArgs DESCRIPTION DEFAULT)
|
||||||
|
set(multiValueArgs ENABLED DISABLED)
|
||||||
|
cmake_parse_arguments(OCD "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
option(${_name} "${OCD_DESCRIPTION}" ${OCD_DEFAULT})
|
||||||
|
if (${${_name}})
|
||||||
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_ENABLED})
|
||||||
|
else()
|
||||||
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY COMPILE_DEFINITIONS ${OCD_DISABLED})
|
||||||
|
endif()
|
||||||
|
if(${OCD_FEATURE})
|
||||||
|
add_feature_info(${_name} ${_name} "${OCD_DESCRIPTION}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
macro(feature_option _name _description _default)
|
||||||
|
option(${_name} "${_description}" ${_default})
|
||||||
|
add_feature_info(${_name} ${_name} "${_description}")
|
||||||
|
endmacro()
|
||||||
@@ -99,6 +99,7 @@ list(FIND LibtorrentRasterbar_DEFINITIONS -DTORRENT_USE_OPENSSL LibtorrentRaster
|
|||||||
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
|
if(LibtorrentRasterbar_ENCRYPTION_INDEX GREATER -1)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
|
set(LibtorrentRasterbar_LIBRARIES ${LibtorrentRasterbar_LIBRARIES} OpenSSL::SSL OpenSSL::Crypto)
|
||||||
|
list(APPEND LibtorrentRasterbar_INCLUDE_DIRS "${OPENSSL_INCLUDE_DIR}")
|
||||||
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
|
set(LibtorrentRasterbar_OPENSSL_ENABLED ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@@ -113,10 +114,10 @@ mark_as_advanced(LibtorrentRasterbar_INCLUDE_DIR LibtorrentRasterbar_LIBRARY
|
|||||||
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
|
LibtorrentRasterbar_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES
|
||||||
LibtorrentRasterbar_ENCRYPTION_INDEX)
|
LibtorrentRasterbar_ENCRYPTION_INDEX)
|
||||||
|
|
||||||
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::LibTorrent)
|
if (LibtorrentRasterbar_FOUND AND NOT TARGET LibtorrentRasterbar::torrent-rasterbar)
|
||||||
add_library(LibtorrentRasterbar::LibTorrent UNKNOWN IMPORTED)
|
add_library(LibtorrentRasterbar::torrent-rasterbar UNKNOWN IMPORTED)
|
||||||
|
|
||||||
set_target_properties(LibtorrentRasterbar::LibTorrent PROPERTIES
|
set_target_properties(LibtorrentRasterbar::torrent-rasterbar PROPERTIES
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
|
IMPORTED_LOCATION "${LibtorrentRasterbar_LIBRARY}"
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
|
INTERFACE_INCLUDE_DIRECTORIES "${LibtorrentRasterbar_INCLUDE_DIRS}"
|
||||||
|
|||||||
@@ -1,94 +1,79 @@
|
|||||||
# - Try to find the QtSingleApplication includes and library
|
# - Try to find the QtSingleApplication includes and library
|
||||||
# which defines
|
# which defines
|
||||||
#
|
#
|
||||||
# QTSINGLEAPPLICATION_FOUND - system has QtSingleApplication
|
# QtSingleApplication_FOUND - system has QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_INCLUDE_DIR - where to find header QtSingleApplication
|
# QtSingleApplication_INCLUDE_DIR - where to find header QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_LIBRARIES - the libraries to link against to use QtSingleApplication
|
# QtSingleApplication_LIBRARIES - the libraries to link against to use QtSingleApplication
|
||||||
# QTSINGLEAPPLICATION_LIBRARY - where to find the QtSingleApplication library (not for general use)
|
# QtSingleApplication_LIBRARY - where to find the QtSingleApplication library (not for general use)
|
||||||
|
|
||||||
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
|
# copyright (c) 2013 TI_Eugene ti.eugene@gmail.com
|
||||||
#
|
#
|
||||||
# Redistribution and use is allowed according to the terms of the FreeBSD license.
|
# Redistribution and use is allowed according to the terms of the FreeBSD license.
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_FOUND FALSE)
|
SET(QtSingleApplication_FOUND FALSE)
|
||||||
|
|
||||||
IF(QT4_FOUND)
|
if (Qt5Widgets_FOUND)
|
||||||
message(STATUS "Looking for Qt4 single application library")
|
set(_includeFileName qtsingleapplication.h)
|
||||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication
|
else()
|
||||||
# standard locations
|
set(_includeFileName qtsinglecoreapplication.h)
|
||||||
/usr/include
|
endif()
|
||||||
/usr/include/QtSolutions
|
|
||||||
# qt4 location except mac's frameworks
|
|
||||||
"${QT_INCLUDE_DIR}/QtSolutions"
|
|
||||||
# mac's frameworks
|
|
||||||
${FRAMEWORK_INCLUDE_DIR}/QtSolutions
|
|
||||||
)
|
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
|
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
|
||||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
FIND_PATH(QtSingleApplication_INCLUDE_DIR ${_includeFileName} ${TOP_INCLUDE_PATH}/QtSolutions)
|
||||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
|
||||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
|
||||||
PATHS ${QT_LIBRARY_DIR}
|
|
||||||
)
|
|
||||||
ELSEIF(Qt5Core_FOUND)
|
|
||||||
message(STATUS "Looking for Qt5 single application library")
|
|
||||||
FOREACH(TOP_INCLUDE_PATH in ${Qt5Core_INCLUDE_DIRS} ${FRAMEWORK_INCLUDE_DIR})
|
|
||||||
FIND_PATH(QTSINGLEAPPLICATION_INCLUDE_DIR QtSingleApplication ${TOP_INCLUDE_PATH}/QtSolutions)
|
|
||||||
|
|
||||||
IF(QTSINGLEAPPLICATION_INCLUDE_DIR)
|
IF(QtSingleApplication_INCLUDE_DIR)
|
||||||
BREAK()
|
BREAK()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDFOREACH()
|
ENDFOREACH()
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_NAMES ${QTSINGLEAPPLICATION_NAMES}
|
SET(QtSingleApplication_NAMES ${QtSingleApplication_NAMES}
|
||||||
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
|
Qt5Solutions_SingleApplication-2.6 libQt5Solutions_SingleApplication-2.6
|
||||||
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
QtSolutions_SingleApplication-2.6 libQtSolutions_SingleApplication-2.6)
|
||||||
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
|
GET_TARGET_PROPERTY(_QT5_CORELIBRARY Qt5::Core LOCATION)
|
||||||
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
|
GET_FILENAME_COMPONENT(_QT5_CORELIBRARYPATH ${_QT5_CORELIBRARY} PATH)
|
||||||
|
|
||||||
FIND_LIBRARY(QTSINGLEAPPLICATION_LIBRARY
|
FIND_LIBRARY(QtSingleApplication_LIBRARY
|
||||||
NAMES ${QTSINGLEAPPLICATION_NAMES}
|
NAMES ${QtSingleApplication_NAMES}
|
||||||
PATHS ${_QT5_CORELIBRARYPATH}
|
PATHS ${_QT5_CORELIBRARYPATH}
|
||||||
)
|
)
|
||||||
ENDIF()
|
|
||||||
|
|
||||||
IF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
IF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
|
||||||
|
|
||||||
SET(QTSINGLEAPPLICATION_LIBRARIES ${QTSINGLEAPPLICATION_LIBRARY})
|
SET(QtSingleApplication_LIBRARIES ${QtSingleApplication_LIBRARY})
|
||||||
SET(QTSINGLEAPPLICATION_FOUND TRUE)
|
SET(QtSingleApplication_FOUND TRUE)
|
||||||
|
|
||||||
IF (CYGWIN)
|
IF (CYGWIN)
|
||||||
IF(BUILD_SHARED_LIBS)
|
IF(BUILD_SHARED_LIBS)
|
||||||
# No need to define QTSINGLEAPPLICATION_USE_DLL here, because it's default for Cygwin.
|
# No need to define QtSingleApplication_USE_DLL here, because it's default for Cygwin.
|
||||||
ELSE(BUILD_SHARED_LIBS)
|
ELSE(BUILD_SHARED_LIBS)
|
||||||
SET (QTSINGLEAPPLICATION_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
|
SET (QtSingleApplication_DEFINITIONS -DQTSINGLEAPPLICATION_STATIC)
|
||||||
ENDIF(BUILD_SHARED_LIBS)
|
ENDIF(BUILD_SHARED_LIBS)
|
||||||
ENDIF (CYGWIN)
|
ENDIF (CYGWIN)
|
||||||
|
|
||||||
ENDIF (QTSINGLEAPPLICATION_LIBRARY AND QTSINGLEAPPLICATION_INCLUDE_DIR)
|
ENDIF (QtSingleApplication_LIBRARY AND QtSingleApplication_INCLUDE_DIR)
|
||||||
|
|
||||||
IF (QTSINGLEAPPLICATION_FOUND)
|
IF (QtSingleApplication_FOUND)
|
||||||
IF (NOT QtSingleApplication_FIND_QUIETLY)
|
IF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||||
MESSAGE(STATUS "Found QtSingleApplication: ${QTSINGLEAPPLICATION_LIBRARY}")
|
MESSAGE(STATUS "Found QtSingleApplication: ${QtSingleApplication_LIBRARY}")
|
||||||
MESSAGE(STATUS " includes: ${QTSINGLEAPPLICATION_INCLUDE_DIR}")
|
MESSAGE(STATUS " includes: ${QtSingleApplication_INCLUDE_DIR}")
|
||||||
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
|
ENDIF (NOT QtSingleApplication_FIND_QUIETLY)
|
||||||
ELSE (QTSINGLEAPPLICATION_FOUND)
|
if(NOT TARGET QtSingleApplication::QtSingleApplication)
|
||||||
|
add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
|
||||||
|
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${QtSingleApplication_INCLUDE_DIR}"
|
||||||
|
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QtSingleApplication_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
if(EXISTS "${QtSingleApplication_LIBRARY}")
|
||||||
|
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
|
IMPORTED_LOCATION "${QtSingleApplication_LIBRARY}")
|
||||||
|
endif()
|
||||||
|
endif(NOT TARGET QtSingleApplication::QtSingleApplication)
|
||||||
|
|
||||||
|
ELSE (QtSingleApplication_FOUND)
|
||||||
IF (QtSingleApplication_FIND_REQUIRED)
|
IF (QtSingleApplication_FIND_REQUIRED)
|
||||||
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
|
MESSAGE(FATAL_ERROR "Could not find QtSingleApplication library")
|
||||||
ENDIF (QtSingleApplication_FIND_REQUIRED)
|
ENDIF (QtSingleApplication_FIND_REQUIRED)
|
||||||
ENDIF (QTSINGLEAPPLICATION_FOUND)
|
ENDIF (QtSingleApplication_FOUND)
|
||||||
|
|
||||||
MARK_AS_ADVANCED(QTSINGLEAPPLICATION_INCLUDE_DIR QTSINGLEAPPLICATION_LIBRARY)
|
MARK_AS_ADVANCED(QtSingleApplication_INCLUDE_DIR QtSingleApplication_LIBRARY)
|
||||||
|
|
||||||
if(NOT TARGET QtSingleApplication::QtSingleApplication)
|
|
||||||
add_library(QtSingleApplication::QtSingleApplication UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
|
|
||||||
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${QTSINGLEAPPLICATION_INCLUDE_DIR}"
|
|
||||||
)
|
|
||||||
if(EXISTS "${QTSINGLEAPPLICATION_LIBRARY}")
|
|
||||||
set_target_properties(QtSingleApplication::QtSingleApplication PROPERTIES
|
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
|
||||||
IMPORTED_LOCATION "${QTSINGLEAPPLICATION_LIBRARY}")
|
|
||||||
endif()
|
|
||||||
endif(NOT TARGET QtSingleApplication::QtSingleApplication)
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# - macro similar to target_link_libraries, which links Qt components
|
# - macro similar to target_link_libraries, which links Qt components
|
||||||
# names of the components are pased in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
|
# names of the components are passed in Qt4/Qt5 agnostic way (Core, DBus, Xml...)
|
||||||
# and the macro links Qt4 ones if QT4_FOUND is set or Qt5 ones if not
|
# and the macro links Qt4 ones if QT4_FOUND is set or Qt5 ones if not
|
||||||
|
|
||||||
macro (target_link_qt_components target)
|
macro (target_link_qt_components target)
|
||||||
|
|||||||
@@ -11,18 +11,17 @@ macro(qbt_set_compiler_options)
|
|||||||
#-Wshadow -Wconversion ?
|
#-Wshadow -Wconversion ?
|
||||||
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
|
set(_GCC_COMMON_C_AND_CXX_FLAGS "-Wall -Wextra"
|
||||||
"-Wfloat-equal -Wcast-qual -Wcast-align"
|
"-Wfloat-equal -Wcast-qual -Wcast-align"
|
||||||
"-Wsign-conversion -Winvalid-pch -Werror=return-type -Wno-long-long"
|
"-Wsign-conversion -Winvalid-pch -Wno-long-long"
|
||||||
# -fstack-protector-all
|
#"-fstack-protector-all"
|
||||||
"-Werror -Wno-error=deprecated-declarations"
|
#"-Werror -Wno-error=deprecated-declarations"
|
||||||
)
|
)
|
||||||
set (_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
|
set(_GCC_COMMON_CXX_FLAGS "-fexceptions -frtti"
|
||||||
"-Woverloaded-virtual -Wold-style-cast -Wstrict-null-sentinel"
|
"-Woverloaded-virtual -Wold-style-cast"
|
||||||
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
|
"-Wnon-virtual-dtor -Wfloat-equal -Wcast-qual -Wcast-align"
|
||||||
"-Werror=overloaded-virtual"
|
#"-Weffc++"
|
||||||
# "-Weffc++"
|
#"-Werror -Wno-error=cpp"
|
||||||
"-Werror -Wno-error=cpp"
|
|
||||||
# we should modify code to make these ones obsolete
|
# we should modify code to make these ones obsolete
|
||||||
"-Wno-error=sign-conversion -Wno-error=float-equal"
|
#"-Wno-error=sign-conversion -Wno-error=float-equal"
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
||||||
@@ -54,6 +53,20 @@ macro(qbt_set_compiler_options)
|
|||||||
endif(${GLIBC_VERSION})
|
endif(${GLIBC_VERSION})
|
||||||
endif (CMAKE_SYSTEM_NAME MATCHES Linux)
|
endif (CMAKE_SYSTEM_NAME MATCHES Linux)
|
||||||
|
|
||||||
|
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
# Clang 5.0 still doesn't support -Wstrict-null-sentinel
|
||||||
|
check_cxx_compiler_flag(-Wstrict-null-sentinel _STRICT_NULL_SENTINEL_IS_SUPPORTED)
|
||||||
|
if (_STRICT_NULL_SENTINEL_IS_SUPPORTED)
|
||||||
|
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wstrict-null-sentinel")
|
||||||
|
endif (_STRICT_NULL_SENTINEL_IS_SUPPORTED)
|
||||||
|
|
||||||
|
# Code should be improved to render this not needed
|
||||||
|
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wno-error=unused-function -Wno-error=inconsistent-missing-override")
|
||||||
|
else ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
# GCC supports it
|
||||||
|
list(APPEND _GCC_COMMON_CXX_FLAGS "-Wstrict-null-sentinel")
|
||||||
|
endif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
|
|
||||||
string(REPLACE ";" " " _GCC_COMMON_C_AND_CXX_FLAGS_STRING "${_GCC_COMMON_C_AND_CXX_FLAGS}")
|
string(REPLACE ";" " " _GCC_COMMON_C_AND_CXX_FLAGS_STRING "${_GCC_COMMON_C_AND_CXX_FLAGS}")
|
||||||
string(REPLACE ";" " " _GCC_COMMON_CXX_FLAGS_STRING "${_GCC_COMMON_CXX_FLAGS}")
|
string(REPLACE ";" " " _GCC_COMMON_CXX_FLAGS_STRING "${_GCC_COMMON_CXX_FLAGS}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
# a helper function which appends source to the main qBt target
|
# a helper function which appends source to the target
|
||||||
# sources file names are relative to the the ${qBittorrent_SOURCE_DIR}
|
# sources file names are relative to the the target source dir
|
||||||
|
|
||||||
function (qbt_target_sources)
|
function (qbt_target_sources _target _scope)
|
||||||
set (_sources_rel "")
|
get_target_property(targetSourceDir ${_target} SOURCE_DIR)
|
||||||
foreach (_source IN ITEMS ${ARGN})
|
set(sourcesRelative "")
|
||||||
if (IS_ABSOLUTE "${_source}")
|
foreach(source IN ITEMS ${ARGN})
|
||||||
set(source_abs "${_source}")
|
if(IS_ABSOLUTE "${source}")
|
||||||
|
set(sourceAbsolutePath "${source}")
|
||||||
else()
|
else()
|
||||||
get_filename_component(_source_abs "${_source}" ABSOLUTE)
|
get_filename_component(sourceAbsolutePath "${source}" ABSOLUTE)
|
||||||
endif()
|
endif()
|
||||||
file (RELATIVE_PATH _source_rel "${qbt_executable_SOURCE_DIR}" "${_source_abs}")
|
file(RELATIVE_PATH sourceRelativePath "${targetSourceDir}" "${sourceAbsolutePath}")
|
||||||
list (APPEND _sources_rel "${_source_rel}")
|
list(APPEND sourcesRelative "${sourceRelativePath}")
|
||||||
endforeach()
|
endforeach()
|
||||||
target_sources (qBittorrent PRIVATE "${_sources_rel}")
|
target_sources(${_target} ${_scope} "${sourcesRelative}")
|
||||||
endfunction (qbt_target_sources)
|
endfunction(qbt_target_sources)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
if (STACKTRACE_WIN)
|
if (STACKTRACE)
|
||||||
if ("${WINXXBITS}" NOT STREQUAL "Win64")
|
if ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||||
add_compile_options(-fno-omit-frame-pointer)
|
add_compile_options(-fno-omit-frame-pointer)
|
||||||
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
|
endif ("${WINXXBITS}" NOT STREQUAL "Win64")
|
||||||
link_libraries(libdbghelp -Wl,--export-all-symbols)
|
link_libraries(libdbghelp -Wl,--export-all-symbols)
|
||||||
endif (STACKTRACE_WIN)
|
endif (STACKTRACE)
|
||||||
|
|
||||||
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
|
if (("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") OR ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo"))
|
||||||
link_libraries(-Wl,--dynamicbase)
|
link_libraries(-Wl,--dynamicbase)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
if (STACKTRACE_WIN)
|
if (STACKTRACE)
|
||||||
if ("${WINXXBITS}" STREQUAL "Win64")
|
if ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
add_compile_options(-Zi)
|
add_compile_options(-Zi)
|
||||||
else ("${WINXXBITS}" STREQUAL "Win64")
|
else ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
@@ -6,7 +6,7 @@ if (STACKTRACE_WIN)
|
|||||||
add_compile_options(-Oy-)
|
add_compile_options(-Oy-)
|
||||||
endif ("${WINXXBITS}" STREQUAL "Win64")
|
endif ("${WINXXBITS}" STREQUAL "Win64")
|
||||||
link_libraries(dbghelp.lib)
|
link_libraries(dbghelp.lib)
|
||||||
endif (STACKTRACE_WIN)
|
endif (STACKTRACE)
|
||||||
|
|
||||||
# Enable Wide characters
|
# Enable Wide characters
|
||||||
add_definitions(-DTORRENT_USE_WPATH)
|
add_definitions(-DTORRENT_USE_WPATH)
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
|
|||||||
#DEFINES += BOOST_ASIO_SEPARATE_COMPILATION
|
#DEFINES += BOOST_ASIO_SEPARATE_COMPILATION
|
||||||
# Enable if building against libtorrent 1.0.x (RC_1_0) (dynamic linking)
|
# Enable if building against libtorrent 1.0.x (RC_1_0) (dynamic linking)
|
||||||
#DEFINES += BOOST_ASIO_DYN_LINK
|
#DEFINES += BOOST_ASIO_DYN_LINK
|
||||||
|
# Enable if encountered build error with boost version <= 1.59
|
||||||
|
#DEFINES += BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
|
||||||
# Enable if building against libtorrent 1.1.x (RC_1_1)
|
# Enable if building against libtorrent 1.1.x (RC_1_1)
|
||||||
# built with this flag defined
|
# built with this flag defined
|
||||||
@@ -58,4 +60,10 @@ DEFINES += BOOST_USE_WINAPI_VERSION=0x0501
|
|||||||
#DEFINES += TORRENT_LINKING_SHARED
|
#DEFINES += TORRENT_LINKING_SHARED
|
||||||
|
|
||||||
# Enable stack trace support
|
# Enable stack trace support
|
||||||
CONFIG += strace_win
|
CONFIG += stacktrace
|
||||||
|
|
||||||
|
win32-msvc* {
|
||||||
|
QMAKE_CXXFLAGS += "/guard:cf"
|
||||||
|
QMAKE_LFLAGS += "/guard:cf"
|
||||||
|
QMAKE_LFLAGS_RELEASE += "/OPT:REF /OPT:ICF"
|
||||||
|
}
|
||||||
|
|||||||
102
configure
vendored
102
configure
vendored
@@ -1,6 +1,6 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.69 for qbittorrent v4.0.4.
|
# Generated by GNU Autoconf 2.69 for qbittorrent v4.1.3.
|
||||||
#
|
#
|
||||||
# Report bugs to <bugs.qbittorrent.org>.
|
# Report bugs to <bugs.qbittorrent.org>.
|
||||||
#
|
#
|
||||||
@@ -580,8 +580,8 @@ MAKEFLAGS=
|
|||||||
# Identity of this package.
|
# Identity of this package.
|
||||||
PACKAGE_NAME='qbittorrent'
|
PACKAGE_NAME='qbittorrent'
|
||||||
PACKAGE_TARNAME='qbittorrent'
|
PACKAGE_TARNAME='qbittorrent'
|
||||||
PACKAGE_VERSION='v4.0.4'
|
PACKAGE_VERSION='v4.1.3'
|
||||||
PACKAGE_STRING='qbittorrent v4.0.4'
|
PACKAGE_STRING='qbittorrent v4.1.3'
|
||||||
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
|
||||||
PACKAGE_URL='https://www.qbittorrent.org/'
|
PACKAGE_URL='https://www.qbittorrent.org/'
|
||||||
|
|
||||||
@@ -717,6 +717,7 @@ enable_dependency_tracking
|
|||||||
enable_silent_rules
|
enable_silent_rules
|
||||||
with_qtsingleapplication
|
with_qtsingleapplication
|
||||||
enable_debug
|
enable_debug
|
||||||
|
enable_stacktrace
|
||||||
enable_gui
|
enable_gui
|
||||||
enable_systemd
|
enable_systemd
|
||||||
enable_webui
|
enable_webui
|
||||||
@@ -1296,7 +1297,7 @@ if test "$ac_init_help" = "long"; then
|
|||||||
# Omit some internal or obsolete options to make the list less imposing.
|
# Omit some internal or obsolete options to make the list less imposing.
|
||||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||||
cat <<_ACEOF
|
cat <<_ACEOF
|
||||||
\`configure' configures qbittorrent v4.0.4 to adapt to many kinds of systems.
|
\`configure' configures qbittorrent v4.1.3 to adapt to many kinds of systems.
|
||||||
|
|
||||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||||
|
|
||||||
@@ -1367,7 +1368,7 @@ fi
|
|||||||
|
|
||||||
if test -n "$ac_init_help"; then
|
if test -n "$ac_init_help"; then
|
||||||
case $ac_init_help in
|
case $ac_init_help in
|
||||||
short | recursive ) echo "Configuration of qbittorrent v4.0.4:";;
|
short | recursive ) echo "Configuration of qbittorrent v4.1.3:";;
|
||||||
esac
|
esac
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
|
|
||||||
@@ -1382,6 +1383,7 @@ Optional Features:
|
|||||||
--enable-silent-rules less verbose build output (undo: "make V=1")
|
--enable-silent-rules less verbose build output (undo: "make V=1")
|
||||||
--disable-silent-rules verbose build output (undo: "make V=0")
|
--disable-silent-rules verbose build output (undo: "make V=0")
|
||||||
--enable-debug Enable debug build
|
--enable-debug Enable debug build
|
||||||
|
--enable-stacktrace Enable stacktrace feature (default=auto)
|
||||||
--disable-gui Disable the GUI for headless running. Disables
|
--disable-gui Disable the GUI for headless running. Disables
|
||||||
QtDBus and the GeoIP Database.
|
QtDBus and the GeoIP Database.
|
||||||
--enable-systemd Install the systemd service file (headless only).
|
--enable-systemd Install the systemd service file (headless only).
|
||||||
@@ -1501,7 +1503,7 @@ fi
|
|||||||
test -n "$ac_init_help" && exit $ac_status
|
test -n "$ac_init_help" && exit $ac_status
|
||||||
if $ac_init_version; then
|
if $ac_init_version; then
|
||||||
cat <<\_ACEOF
|
cat <<\_ACEOF
|
||||||
qbittorrent configure v4.0.4
|
qbittorrent configure v4.1.3
|
||||||
generated by GNU Autoconf 2.69
|
generated by GNU Autoconf 2.69
|
||||||
|
|
||||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||||
@@ -1640,7 +1642,7 @@ cat >config.log <<_ACEOF
|
|||||||
This file contains any messages produced by compilers while
|
This file contains any messages produced by compilers while
|
||||||
running configure, to aid debugging if configure makes a mistake.
|
running configure, to aid debugging if configure makes a mistake.
|
||||||
|
|
||||||
It was created by qbittorrent $as_me v4.0.4, which was
|
It was created by qbittorrent $as_me v4.1.3, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
$ $0 $@
|
$ $0 $@
|
||||||
@@ -3818,7 +3820,7 @@ fi
|
|||||||
|
|
||||||
# Define the identity of the package.
|
# Define the identity of the package.
|
||||||
PACKAGE='qbittorrent'
|
PACKAGE='qbittorrent'
|
||||||
VERSION='v4.0.4'
|
VERSION='v4.1.3'
|
||||||
|
|
||||||
|
|
||||||
cat >>confdefs.h <<_ACEOF
|
cat >>confdefs.h <<_ACEOF
|
||||||
@@ -4189,6 +4191,14 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Check whether --enable-stacktrace was given.
|
||||||
|
if test "${enable_stacktrace+set}" = set; then :
|
||||||
|
enableval=$enable_stacktrace;
|
||||||
|
else
|
||||||
|
enable_stacktrace=auto
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Check whether --enable-gui was given.
|
# Check whether --enable-gui was given.
|
||||||
if test "${enable_gui+set}" = set; then :
|
if test "${enable_gui+set}" = set; then :
|
||||||
enableval=$enable_gui;
|
enableval=$enable_gui;
|
||||||
@@ -4389,6 +4399,39 @@ $as_echo "$enable_debug" >&6; }
|
|||||||
as_fn_error $? "Unknown option \"$enable_debug\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
as_fn_error $? "Unknown option \"$enable_debug\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the stacktrace feature" >&5
|
||||||
|
$as_echo_n "checking whether to enable the stacktrace feature... " >&6; }
|
||||||
|
|
||||||
|
case "x$enable_stacktrace" in #(
|
||||||
|
"xno") :
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||||
|
$as_echo "no" >&6; }
|
||||||
|
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace" ;; #(
|
||||||
|
"xyes") :
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
|
$as_echo "yes" >&6; }
|
||||||
|
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace" ;; #(
|
||||||
|
"xauto") :
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <execinfo.h>
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||||
|
$as_echo "yes" >&6; }
|
||||||
|
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"
|
||||||
|
else
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||||
|
$as_echo "no" >&6; }
|
||||||
|
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ;; #(
|
||||||
|
*) :
|
||||||
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_stacktrace" >&5
|
||||||
|
$as_echo "$enable_stacktrace" >&6; }
|
||||||
|
as_fn_error $? "Unknown option \"$enable_stacktrace\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the GUI" >&5
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable the GUI" >&5
|
||||||
$as_echo_n "checking whether to enable the GUI... " >&6; }
|
$as_echo_n "checking whether to enable the GUI... " >&6; }
|
||||||
case "x$enable_gui" in #(
|
case "x$enable_gui" in #(
|
||||||
@@ -4636,7 +4679,6 @@ esac
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check whether --with-boost was given.
|
# Check whether --with-boost was given.
|
||||||
if test "${with_boost+set}" = set; then :
|
if test "${with_boost+set}" = set; then :
|
||||||
withval=$with_boost;
|
withval=$with_boost;
|
||||||
@@ -4972,6 +5014,40 @@ fi
|
|||||||
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
||||||
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
||||||
|
|
||||||
|
# add workaround for problematic boost version
|
||||||
|
ac_ext=cpp
|
||||||
|
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||||
|
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||||
|
|
||||||
|
# taken from ax_boost_base.m4
|
||||||
|
|
||||||
|
|
||||||
|
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||||
|
/* end confdefs.h. */
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
int
|
||||||
|
main ()
|
||||||
|
{
|
||||||
|
(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < (106000))]));
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ACEOF
|
||||||
|
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||||
|
|
||||||
|
else
|
||||||
|
QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"
|
||||||
|
fi
|
||||||
|
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||||
|
ac_ext=cpp
|
||||||
|
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||||
|
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||||
|
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||||
|
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Check whether --with-boost-system was given.
|
# Check whether --with-boost-system was given.
|
||||||
@@ -6098,7 +6174,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by qbittorrent $as_me v4.0.4, which was
|
This file was extended by qbittorrent $as_me v4.1.3, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
@@ -6156,7 +6232,7 @@ _ACEOF
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
qbittorrent config.status v4.0.4
|
qbittorrent config.status v4.1.3
|
||||||
configured by $0, generated by GNU Autoconf 2.69,
|
configured by $0, generated by GNU Autoconf 2.69,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
@@ -7413,7 +7489,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
|||||||
# report actual input values of CONFIG_FILES etc. instead of their
|
# report actual input values of CONFIG_FILES etc. instead of their
|
||||||
# values after options handling.
|
# values after options handling.
|
||||||
ac_log="
|
ac_log="
|
||||||
This file was extended by qbittorrent $as_me v4.0.4, which was
|
This file was extended by qbittorrent $as_me v4.1.3, which was
|
||||||
generated by GNU Autoconf 2.69. Invocation command line was
|
generated by GNU Autoconf 2.69. Invocation command line was
|
||||||
|
|
||||||
CONFIG_FILES = $CONFIG_FILES
|
CONFIG_FILES = $CONFIG_FILES
|
||||||
@@ -7471,7 +7547,7 @@ _ACEOF
|
|||||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||||
ac_cs_version="\\
|
ac_cs_version="\\
|
||||||
qbittorrent config.status v4.0.4
|
qbittorrent config.status v4.1.3
|
||||||
configured by $0, generated by GNU Autoconf 2.69,
|
configured by $0, generated by GNU Autoconf 2.69,
|
||||||
with options \\"\$ac_cs_config\\"
|
with options \\"\$ac_cs_config\\"
|
||||||
|
|
||||||
|
|||||||
36
configure.ac
36
configure.ac
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([qbittorrent], [v4.0.4], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
AC_INIT([qbittorrent], [v4.1.3], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@@ -24,6 +24,12 @@ AC_ARG_ENABLE(debug,
|
|||||||
[],
|
[],
|
||||||
[enable_debug=no])
|
[enable_debug=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(stacktrace,
|
||||||
|
[AS_HELP_STRING([--enable-stacktrace],
|
||||||
|
[Enable stacktrace feature (default=auto)])],
|
||||||
|
[],
|
||||||
|
[enable_stacktrace=auto])
|
||||||
|
|
||||||
AC_ARG_ENABLE(gui,
|
AC_ARG_ENABLE(gui,
|
||||||
[AS_HELP_STRING([--disable-gui],
|
[AS_HELP_STRING([--disable-gui],
|
||||||
[Disable the GUI for headless running. Disables QtDBus and the GeoIP Database.])],
|
[Disable the GUI for headless running. Disables QtDBus and the GeoIP Database.])],
|
||||||
@@ -80,6 +86,23 @@ AS_CASE(["x$enable_debug"],
|
|||||||
[AC_MSG_RESULT([$enable_debug])
|
[AC_MSG_RESULT([$enable_debug])
|
||||||
AC_MSG_ERROR([Unknown option "$enable_debug". Use either "yes" or "no".])])
|
AC_MSG_ERROR([Unknown option "$enable_debug". Use either "yes" or "no".])])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether to enable the stacktrace feature])
|
||||||
|
AS_CASE(["x$enable_stacktrace"],
|
||||||
|
["xno"],
|
||||||
|
[AC_MSG_RESULT([no])
|
||||||
|
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"],
|
||||||
|
["xyes"],
|
||||||
|
[AC_MSG_RESULT([yes])
|
||||||
|
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"],
|
||||||
|
["xauto"],
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include <execinfo.h>]])],
|
||||||
|
[AC_MSG_RESULT([yes])
|
||||||
|
QBT_ADD_CONFIG="$QBT_ADD_CONFIG stacktrace"],
|
||||||
|
[AC_MSG_RESULT([no])
|
||||||
|
QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG stacktrace"])],
|
||||||
|
[AC_MSG_RESULT([$enable_stacktrace])
|
||||||
|
AC_MSG_ERROR([Unknown option "$enable_stacktrace". Use either "yes" or "no".])])
|
||||||
|
|
||||||
AC_MSG_CHECKING([whether to enable the GUI])
|
AC_MSG_CHECKING([whether to enable the GUI])
|
||||||
AS_CASE(["x$enable_gui"],
|
AS_CASE(["x$enable_gui"],
|
||||||
["xyes"],
|
["xyes"],
|
||||||
@@ -145,6 +168,17 @@ AX_BOOST_BASE([1.35],
|
|||||||
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
CPPFLAGS="$BOOST_CPPFLAGS $CPPFLAGS"
|
||||||
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
LDFLAGS="$BOOST_LDFLAGS $LDFLAGS"
|
||||||
|
|
||||||
|
# add workaround for problematic boost version
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
|
# taken from ax_boost_base.m4
|
||||||
|
m4_define([DETECT_BOOST_VERSION_PROGRAM],
|
||||||
|
[AC_LANG_PROGRAM([[#include <boost/version.hpp>]],
|
||||||
|
[[(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));]])])
|
||||||
|
|
||||||
|
AC_COMPILE_IFELSE([DETECT_BOOST_VERSION_PROGRAM(106000)], [],
|
||||||
|
[QBT_ADD_DEFINES="$QBT_ADD_DEFINES BOOST_NO_CXX11_RVALUE_REFERENCES"])
|
||||||
|
AC_LANG_POP([C++])
|
||||||
|
|
||||||
AX_BOOST_SYSTEM()
|
AX_BOOST_SYSTEM()
|
||||||
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
|
AC_MSG_NOTICE([Boost.System LIB: "$BOOST_SYSTEM_LIB"])
|
||||||
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
LIBS="$BOOST_SYSTEM_LIB $LIBS"
|
||||||
|
|||||||
2
dist/CMakeLists.txt
vendored
2
dist/CMakeLists.txt
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
find_package(Qt5Widgets ${requiredQtVersion}) # to conditionally install desktop-related files
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
add_subdirectory(mac)
|
add_subdirectory(mac)
|
||||||
else (APPLE)
|
else (APPLE)
|
||||||
|
|||||||
2
dist/mac/Info.plist
vendored
2
dist/mac/Info.plist
vendored
@@ -45,7 +45,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>4.0.4</string>
|
<string>4.1.3</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>qBit</string>
|
<string>qBit</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
|||||||
30
dist/unix/CMakeLists.txt
vendored
30
dist/unix/CMakeLists.txt
vendored
@@ -1,31 +1,37 @@
|
|||||||
if (SYSTEMD)
|
if (NOT Qt5Widgets_FOUND)
|
||||||
find_package(Systemd)
|
feature_option(SYSTEMD "Install systemd service file (headless only)" OFF)
|
||||||
if (SYSTEMD_FOUND)
|
if (SYSTEMD)
|
||||||
|
if (NOT Systemd_SERVICES_INSTALL_DIR)
|
||||||
|
find_package(Systemd)
|
||||||
|
if (NOT Systemd_FOUND)
|
||||||
|
message(FATAL_ERROR "Could not locate systemd services install dir."
|
||||||
|
" Either pass -DSystemd_SERVICES_INSTALL_DIR=/path/to/systemd/services option or install systemd pkg-config")
|
||||||
|
endif(NOT Systemd_FOUND)
|
||||||
|
endif(NOT Systemd_SERVICES_INSTALL_DIR)
|
||||||
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
|
set(EXPAND_BINDIR ${CMAKE_INSTALL_FULL_BINDIR})
|
||||||
configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY)
|
configure_file(systemd/qbittorrent-nox@.service.in ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service @ONLY)
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qbittorrent-nox@.service
|
||||||
DESTINATION ${SYSTEMD_SERVICES_INSTALL_DIR}
|
DESTINATION ${Systemd_SERVICES_INSTALL_DIR}
|
||||||
COMPONENT data)
|
COMPONENT data)
|
||||||
endif(SYSTEMD_FOUND)
|
endif(SYSTEMD)
|
||||||
endif(SYSTEMD)
|
endif()
|
||||||
|
|
||||||
|
if (Qt5Widgets_FOUND)
|
||||||
if (GUI)
|
|
||||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
|
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent.1)
|
||||||
else (GUI)
|
else (Qt5Widgets_FOUND)
|
||||||
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
|
list(APPEND MAN_FILES ${qBittorrent_SOURCE_DIR}/doc/qbittorrent-nox.1)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
install(FILES ${MAN_FILES}
|
install(FILES ${MAN_FILES}
|
||||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||||
COMPONENT doc)
|
COMPONENT doc)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
install(DIRECTORY menuicons/
|
install(DIRECTORY menuicons/
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor
|
||||||
FILES_MATCHING PATTERN "*.png")
|
FILES_MATCHING PATTERN "*.png")
|
||||||
|
|
||||||
install(FILES ${qBittorrent_SOURCE_DIR}/src/icons/qbittorrent.desktop
|
install(FILES qbittorrent.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications/
|
||||||
COMPONENT data)
|
COMPONENT data)
|
||||||
|
|
||||||
|
|||||||
2
dist/windows/options.nsi
vendored
2
dist/windows/options.nsi
vendored
@@ -27,7 +27,7 @@ XPStyle on
|
|||||||
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
|
||||||
|
|
||||||
; Program specific
|
; Program specific
|
||||||
!define PROG_VERSION "4.0.4"
|
!define PROG_VERSION "4.1.3"
|
||||||
|
|
||||||
!define MUI_FINISHPAGE_RUN
|
!define MUI_FINISHPAGE_RUN
|
||||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ TEMPLATE = subdirs
|
|||||||
SUBDIRS += src
|
SUBDIRS += src
|
||||||
|
|
||||||
include(version.pri)
|
include(version.pri)
|
||||||
include(qm_gen.pri)
|
|
||||||
|
|
||||||
# Make target to create release tarball. Use 'make tarball'
|
# Make target to create release tarball. Use 'make tarball'
|
||||||
tarball.commands += rm -fR ../$${PROJECT_NAME}-$${PROJECT_VERSION}/ &&
|
tarball.commands += rm -fR ../$${PROJECT_NAME}-$${PROJECT_VERSION}/ &&
|
||||||
@@ -17,3 +16,11 @@ tarball.commands += xz -f $${PROJECT_NAME}-$${PROJECT_VERSION}.tar &&
|
|||||||
tarball.commands += rm -fR $${PROJECT_NAME}-$${PROJECT_VERSION}
|
tarball.commands += rm -fR $${PROJECT_NAME}-$${PROJECT_VERSION}
|
||||||
|
|
||||||
QMAKE_EXTRA_TARGETS += tarball
|
QMAKE_EXTRA_TARGETS += tarball
|
||||||
|
|
||||||
|
# Translations included here (at top level) is to avoid regenerating the .qm files
|
||||||
|
# every time when src.pro is processed
|
||||||
|
include(src/lang/lang.pri)
|
||||||
|
|
||||||
|
# For Qt Creator beautifier
|
||||||
|
DISTFILES += \
|
||||||
|
uncrustify.cfg
|
||||||
|
|||||||
21
qm_gen.pri
21
qm_gen.pri
@@ -1,21 +0,0 @@
|
|||||||
TS_IN = $$fromfile(src/src.pro,TRANSLATIONS)
|
|
||||||
TS_IN_NOEXT = $$replace(TS_IN,".ts","")
|
|
||||||
|
|
||||||
isEmpty(QMAKE_LRELEASE) {
|
|
||||||
win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
|
|
||||||
else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
|
|
||||||
unix {
|
|
||||||
equals(QT_MAJOR_VERSION, 5) {
|
|
||||||
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease-qt5 }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message("Building translations")
|
|
||||||
for(L,TS_IN_NOEXT) {
|
|
||||||
message("Processing $${L}")
|
|
||||||
system("$$QMAKE_LRELEASE -silent src/$${L}.ts -qm src/$${L}.qm")
|
|
||||||
!exists("src/$${L}.qm"):error("Building translations failed, cannot continue")
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,45 @@
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||||
set(CMAKE_CXX_STANDARD "11")
|
set(CMAKE_CXX_STANDARD "11")
|
||||||
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
|
|
||||||
|
|
||||||
include(MacroQbtCompilerSettings)
|
include(MacroQbtCompilerSettings)
|
||||||
qbt_set_compiler_options()
|
qbt_set_compiler_options()
|
||||||
|
|
||||||
include(MacroLinkQtComponents)
|
|
||||||
include(QbtTargetSources)
|
include(QbtTargetSources)
|
||||||
|
|
||||||
|
find_package(Boost ${requiredBoostVersion} REQUIRED)
|
||||||
find_package(LibtorrentRasterbar REQUIRED)
|
find_package(LibtorrentRasterbar REQUIRED)
|
||||||
|
|
||||||
# Qt
|
if (Boost_VERSION VERSION_LESS 106000)
|
||||||
list(APPEND QBT_QT_COMPONENTS Core Network Xml)
|
add_definitions(-DBOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||||
if (GUI)
|
endif()
|
||||||
list (APPEND QBT_QT_COMPONENTS Gui Svg Widgets)
|
|
||||||
if (WIN32)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS WinExtras)
|
|
||||||
endif(WIN32)
|
|
||||||
if (APPLE)
|
|
||||||
list (APPEND QBT_GUI_OPTIONAL_LINK_LIBRARIES objc)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS MacExtras)
|
|
||||||
endif (APPLE)
|
|
||||||
endif (GUI)
|
|
||||||
if (DBUS)
|
|
||||||
list (APPEND QBT_QT_COMPONENTS DBus)
|
|
||||||
endif (DBUS)
|
|
||||||
find_package(Qt5 5.5.1 COMPONENTS ${QBT_QT_COMPONENTS} REQUIRED)
|
|
||||||
|
|
||||||
if (GUI AND APPLE)
|
find_package(Qt5 ${requiredQtVersion} REQUIRED COMPONENTS Core Network Xml)
|
||||||
# Fix MOC inability to detect macOS. This seems to only affect cmake.
|
find_package(Qt5Widgets ${requiredQtVersion})
|
||||||
# Relevant issue: https://bugreports.qt.io/browse/QTBUG-58325
|
if (Qt5Widgets_FOUND)
|
||||||
set(CMAKE_AUTOMOC_MOC_OPTIONS ${CMAKE_AUTOMOC_MOC_OPTIONS} -DQ_OS_MAC)
|
find_package(Qt5DBus ${requiredQtVersion})
|
||||||
endif ()
|
else()
|
||||||
|
add_definitions(-DDISABLE_GUI)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_package_properties(Qt5Widgets PROPERTIES
|
||||||
|
DESCRIPTION "Set of components for creating classic desktop-style UIs for the Qt5 framework"
|
||||||
|
PURPOSE "Enables qBittorrent GUI. Unneeded for headless configuration."
|
||||||
|
TYPE OPTIONAL
|
||||||
|
)
|
||||||
|
|
||||||
|
set_package_properties(Qt5DBus PROPERTIES
|
||||||
|
DESCRIPTION "Qt5 module for inter-process communication over the D-Bus protocol"
|
||||||
|
PURPOSE "Enables communication with other system components (e.g. notification service) via D-Bus. "
|
||||||
|
TYPE RECOMMENDED
|
||||||
|
)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC True)
|
set(CMAKE_AUTOMOC True)
|
||||||
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
|
list(APPEND CMAKE_AUTORCC_OPTIONS -compress 9 -threshold 5)
|
||||||
|
if (APPLE)
|
||||||
|
# Workaround CMake bug (autogen does not pass required parameters to moc)
|
||||||
|
# Relevant issue: https://gitlab.kitware.com/cmake/cmake/issues/18041
|
||||||
|
list(APPEND CMAKE_AUTOMOC_MOC_OPTIONS -DQ_OS_MAC -DQ_OS_DARWIN)
|
||||||
|
endif ()
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
@@ -43,56 +48,34 @@ add_definitions(-DQT_NO_CAST_TO_ASCII)
|
|||||||
# Efficient construction for QString & QByteArray (Qt >= 4.8)
|
# Efficient construction for QString & QByteArray (Qt >= 4.8)
|
||||||
add_definitions(-DQT_USE_QSTRINGBUILDER)
|
add_definitions(-DQT_USE_QSTRINGBUILDER)
|
||||||
|
|
||||||
if (NOT GUI)
|
if (CMAKE_BUILD_TYPE MATCHES "Debug")
|
||||||
add_definitions(-DDISABLE_GUI -DDISABLE_COUNTRIES_RESOLUTION)
|
|
||||||
endif (NOT GUI)
|
|
||||||
|
|
||||||
if (NOT WEBUI)
|
|
||||||
add_definitions(-DDISABLE_WEBUI)
|
|
||||||
endif (NOT WEBUI)
|
|
||||||
|
|
||||||
if (STACKTRACE_WIN)
|
|
||||||
add_definitions(-DSTACKTRACE_WIN)
|
|
||||||
endif(STACKTRACE_WIN)
|
|
||||||
# nogui {
|
|
||||||
# TARGET = qbittorrent-nox
|
|
||||||
# } else {
|
|
||||||
# CONFIG(static) {
|
|
||||||
# DEFINES += QBT_STATIC_QT
|
|
||||||
# QTPLUGIN += qico
|
|
||||||
# }
|
|
||||||
# TARGET = qbittorrent
|
|
||||||
# }
|
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
|
||||||
add_compile_options(-Wformat -Wformat-security)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
message(STATUS "Project is built in DEBUG mode.")
|
message(STATUS "Project is built in DEBUG mode.")
|
||||||
else (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
else()
|
||||||
message(STATUS "Project is built in RELEASE mode.")
|
message(STATUS "Project is built in RELEASE mode.")
|
||||||
message(STATUS "Disabling debug output.")
|
message(STATUS "Disabling debug output.")
|
||||||
add_definitions(-DQT_NO_DEBUG_OUTPUT)
|
add_definitions(-DQT_NO_DEBUG_OUTPUT)
|
||||||
endif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
endif()
|
||||||
|
|
||||||
set(QBT_USE_GUI ${GUI})
|
|
||||||
set(QBT_USE_WEBUI ${WEBUI})
|
|
||||||
|
|
||||||
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
if (SYSTEM_QTSINGLEAPPLICATION)
|
find_package(QtSingleApplication)
|
||||||
find_package(QtSingleApplication REQUIRED)
|
set_package_properties(QtSingleApplication PROPERTIES
|
||||||
else (SYSTEM_QTSINGLEAPPLICATION)
|
URL "https://code.qt.io/cgit/qt-solutions/qt-solutions.git/"
|
||||||
|
DESCRIPTION "Qt library to start applications only once per user"
|
||||||
|
TYPE RECOMMENDED
|
||||||
|
PURPOSE "Use the system qtsingleapplication library or shipped one otherwise"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (NOT QtSingleApplication_FOUND)
|
||||||
add_subdirectory(app/qtsingleapplication)
|
add_subdirectory(app/qtsingleapplication)
|
||||||
endif (SYSTEM_QTSINGLEAPPLICATION)
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
add_subdirectory(base)
|
add_subdirectory(base)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
endif (GUI)
|
endif ()
|
||||||
|
|
||||||
if (WEBUI)
|
if (WEBUI)
|
||||||
add_subdirectory(webui)
|
add_subdirectory(webui)
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
project(qbt_executable)
|
add_executable(qBittorrent
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
|
|
||||||
set(QBT_APP_HEADERS
|
|
||||||
application.h
|
application.h
|
||||||
cmdoptions.h
|
cmdoptions.h
|
||||||
filelogger.h
|
filelogger.h
|
||||||
)
|
upgrade.h
|
||||||
|
|
||||||
set(QBT_APP_SOURCES
|
|
||||||
application.cpp
|
application.cpp
|
||||||
cmdoptions.cpp
|
cmdoptions.cpp
|
||||||
filelogger.cpp
|
filelogger.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_include_directories(qBittorrent PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_link_libraries(qBittorrent
|
||||||
|
PRIVATE
|
||||||
|
qbt_base
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(qBittorrent
|
||||||
|
PROPERTIES
|
||||||
|
AUTOUIC True
|
||||||
|
AUTORCC True
|
||||||
|
MACOSX_BUNDLE True
|
||||||
|
)
|
||||||
|
|
||||||
# translations
|
# translations
|
||||||
file(GLOB QBT_TS_FILES ../lang/*.ts)
|
file(GLOB QBT_TS_FILES ../lang/*.ts)
|
||||||
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
get_filename_component(QBT_QM_FILES_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
||||||
@@ -22,9 +30,9 @@ set_source_files_properties(${QBT_TS_FILES} PROPERTIES OUTPUT_LOCATION "${QBT_QM
|
|||||||
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
|
find_package(Qt5 COMPONENTS LinguistTools REQUIRED)
|
||||||
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
qt5_add_translation(QBT_QM_FILES ${QBT_TS_FILES})
|
||||||
|
|
||||||
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang.qrc" ABSOLUTE)
|
get_filename_component(_lang_qrc_src "${CMAKE_CURRENT_SOURCE_DIR}/../lang/lang.qrc" ABSOLUTE)
|
||||||
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang.qrc" ABSOLUTE)
|
get_filename_component(_lang_qrc_dst "${CMAKE_CURRENT_BINARY_DIR}/../lang/lang.qrc" ABSOLUTE)
|
||||||
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../" ABSOLUTE)
|
get_filename_component(_lang_qrc_dst_dir "${CMAKE_CURRENT_BINARY_DIR}/../lang" ABSOLUTE)
|
||||||
|
|
||||||
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
|
message(STATUS "copying ${_lang_qrc_src} -> ${_lang_qrc_dst}")
|
||||||
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
|
file(COPY ${_lang_qrc_src} DESTINATION ${_lang_qrc_dst_dir})
|
||||||
@@ -35,8 +43,8 @@ foreach(qm_file ${QBT_QM_FILES})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
set(QBT_APP_RESOURCES
|
set(QBT_APP_RESOURCES
|
||||||
../icons.qrc
|
../icons/icons.qrc
|
||||||
../searchengine.qrc
|
../searchengine/searchengine.qrc
|
||||||
"${_lang_qrc_dst}"
|
"${_lang_qrc_dst}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,51 +54,41 @@ qt5_add_resources(QBT_APP_RESOURCE_SOURCE ${QBT_APP_RESOURCES})
|
|||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
list (APPEND QBT_APP_SOURCES ../qbittorrent_mingw.rc)
|
target_sources(qBittorrent PRIVATE ../qbittorrent_mingw.rc)
|
||||||
else (MINGW)
|
else (MINGW)
|
||||||
list (APPEND QBT_APP_SOURCES ../qbittorrent.rc)
|
target_sources(qBittorrent PRIVATE ../qbittorrent.rc)
|
||||||
endif (MINGW)
|
endif (MINGW)
|
||||||
list(APPEND QBT_APP_SOURCES ../qbittorrent.exe.manifest)
|
target_sources(qBittorrent PRIVATE ../qbittorrent.exe.manifest)
|
||||||
endif (WIN32)
|
endif (WIN32)
|
||||||
|
|
||||||
if (UNIX)
|
if (STACKTRACE)
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace.h)
|
if (UNIX)
|
||||||
endif (UNIX)
|
target_sources(qBittorrent PRIVATE stacktrace.h)
|
||||||
|
else (UNIX)
|
||||||
|
target_sources(qBittorrent PRIVATE stacktrace_win.h)
|
||||||
|
if (Qt5Widgets_FOUND)
|
||||||
|
target_sources(qBittorrent PRIVATE stacktracedialog.h)
|
||||||
|
endif (Qt5Widgets_FOUND)
|
||||||
|
endif (UNIX)
|
||||||
|
endif (STACKTRACE)
|
||||||
|
|
||||||
if (STACKTRACE_WIN)
|
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace_win.h)
|
|
||||||
if (GUI)
|
|
||||||
list(APPEND QBT_APP_HEADERS stacktrace_win_dlg.h)
|
|
||||||
endif (GUI)
|
|
||||||
endif (STACKTRACE_WIN)
|
|
||||||
|
|
||||||
# usesystemqtsingleapplication {
|
if (Qt5Widgets_FOUND)
|
||||||
# nogui {
|
target_link_libraries(qBittorrent PRIVATE qbt_searchengine qbt_gui)
|
||||||
# CONFIG += qtsinglecoreapplication
|
set_target_properties(qBittorrent
|
||||||
# } else {
|
PROPERTIES
|
||||||
# CONFIG += qtsingleapplication
|
OUTPUT_NAME qbittorrent
|
||||||
# }
|
WIN32_EXECUTABLE True
|
||||||
# } else {
|
|
||||||
# nogui {
|
|
||||||
# include(qtsingleapplication/qtsinglecoreapplication.pri)
|
|
||||||
# } else {
|
|
||||||
# include(qtsingleapplication/qtsingleapplication.pri)
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# upgrade code
|
|
||||||
list(APPEND QBT_APP_HEADERS upgrade.h)
|
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_base)
|
|
||||||
|
|
||||||
if (GUI)
|
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_searchengine qbt_gui)
|
|
||||||
include_directories(../gui
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/../gui
|
|
||||||
)
|
)
|
||||||
endif (GUI)
|
else(Qt5Widgets_FOUND)
|
||||||
|
set_target_properties(qBittorrent
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME qbittorrent-nox
|
||||||
|
)
|
||||||
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
if (WEBUI)
|
if (WEBUI)
|
||||||
list(APPEND QBT_TARGET_LIBRARIES qbt_webui)
|
target_link_libraries(qBittorrent PRIVATE qbt_webui)
|
||||||
endif (WEBUI)
|
endif (WEBUI)
|
||||||
|
|
||||||
# we have to include resources into the bundle
|
# we have to include resources into the bundle
|
||||||
@@ -142,30 +140,11 @@ if (APPLE)
|
|||||||
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
|
PROPERTIES MACOSX_PACKAGE_LOCATION translations)
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
add_executable(qBittorrent ${QBT_APP_HEADERS} ${QBT_APP_SOURCES} ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
|
target_sources(qBittorrent PRIVATE ${QBT_QM_FILES} ${QBT_APP_RESOURCE_SOURCE})
|
||||||
if (GUI)
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
OUTPUT_NAME qbittorrent
|
|
||||||
WIN32_EXECUTABLE True
|
|
||||||
)
|
|
||||||
else (GUI)
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
OUTPUT_NAME qbittorrent-nox
|
|
||||||
)
|
|
||||||
endif (GUI)
|
|
||||||
|
|
||||||
set_target_properties(qBittorrent
|
|
||||||
PROPERTIES
|
|
||||||
AUTOUIC True
|
|
||||||
AUTORCC True
|
|
||||||
MACOSX_BUNDLE True
|
|
||||||
)
|
|
||||||
|
|
||||||
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
|
get_target_property(QBT_EXECUTABLE_NAME qBittorrent OUTPUT_NAME)
|
||||||
|
|
||||||
target_link_libraries(qBittorrent ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
|
target_link_libraries(qBittorrent PRIVATE ${QBT_TARGET_LIBRARIES} QtSingleApplication::QtSingleApplication)
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})
|
set(qbt_BUNDLE_NAME ${QBT_EXECUTABLE_NAME})
|
||||||
@@ -186,6 +165,7 @@ install(TARGETS qBittorrent
|
|||||||
BUNDLE DESTINATION .
|
BUNDLE DESTINATION .
|
||||||
COMPONENT runtime)
|
COMPONENT runtime)
|
||||||
|
|
||||||
if (GUI AND APPLE)
|
if (Qt5Widgets_FOUND AND APPLE)
|
||||||
|
find_package(Qt5Svg REQUIRED)
|
||||||
include(bundle)
|
include(bundle)
|
||||||
endif (GUI AND APPLE)
|
endif (Qt5Widgets_FOUND AND APPLE)
|
||||||
|
|||||||
@@ -25,12 +25,16 @@ SOURCES += \
|
|||||||
$$PWD/filelogger.cpp \
|
$$PWD/filelogger.cpp \
|
||||||
$$PWD/main.cpp
|
$$PWD/main.cpp
|
||||||
|
|
||||||
unix: HEADERS += $$PWD/stacktrace.h
|
stacktrace {
|
||||||
strace_win {
|
unix {
|
||||||
HEADERS += $$PWD/stacktrace_win.h
|
HEADERS += $$PWD/stacktrace.h
|
||||||
!nogui {
|
}
|
||||||
HEADERS += $$PWD/stacktrace_win_dlg.h
|
else {
|
||||||
FORMS += $$PWD/stacktrace_win_dlg.ui
|
HEADERS += $$PWD/stacktrace_win.h
|
||||||
|
!nogui {
|
||||||
|
HEADERS += $$PWD/stacktracedialog.h
|
||||||
|
FORMS += $$PWD/stacktracedialog.ui
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,62 +27,67 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
#include "application.h"
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QLocale>
|
#include <algorithm>
|
||||||
#include <QLibraryInfo>
|
|
||||||
#include <QSysInfo>
|
|
||||||
#include <QProcess>
|
|
||||||
#include <QAtomicInt>
|
#include <QAtomicInt>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QLocale>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QSysInfo>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <memory>
|
||||||
|
#include <Shellapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#include "gui/guiiconprovider.h"
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
#include <windows.h>
|
|
||||||
#include <QSharedMemory>
|
|
||||||
#include <QSessionManager>
|
#include <QSessionManager>
|
||||||
|
#include <QSharedMemory>
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
#include <QFileOpenEvent>
|
#include <QFileOpenEvent>
|
||||||
#include <QFont>
|
|
||||||
#include <QUrl>
|
|
||||||
#endif // Q_OS_MAC
|
#endif // Q_OS_MAC
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "addnewtorrentdialog.h"
|
#include "addnewtorrentdialog.h"
|
||||||
#include "shutdownconfirmdlg.h"
|
#include "gui/guiiconprovider.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "shutdownconfirmdialog.h"
|
||||||
#else // DISABLE_GUI
|
#else // DISABLE_GUI
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
|
#include "base/bittorrent/session.h"
|
||||||
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
|
#include "base/iconprovider.h"
|
||||||
|
#include "base/logger.h"
|
||||||
|
#include "base/net/downloadmanager.h"
|
||||||
|
#include "base/net/geoipmanager.h"
|
||||||
|
#include "base/net/proxyconfigurationmanager.h"
|
||||||
|
#include "base/net/smtp.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
|
#include "base/profile.h"
|
||||||
|
#include "base/rss/rss_autodownloader.h"
|
||||||
|
#include "base/rss/rss_session.h"
|
||||||
|
#include "base/scanfoldersmodel.h"
|
||||||
|
#include "base/settingsstorage.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/utils/misc.h"
|
||||||
|
#include "base/utils/string.h"
|
||||||
|
#include "filelogger.h"
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
#include "webui/webui.h"
|
#include "webui/webui.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
#include "filelogger.h"
|
|
||||||
#include "base/logger.h"
|
|
||||||
#include "base/preferences.h"
|
|
||||||
#include "base/settingsstorage.h"
|
|
||||||
#include "base/profile.h"
|
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/misc.h"
|
|
||||||
#include "base/iconprovider.h"
|
|
||||||
#include "base/scanfoldersmodel.h"
|
|
||||||
#include "base/net/smtp.h"
|
|
||||||
#include "base/net/downloadmanager.h"
|
|
||||||
#include "base/net/geoipmanager.h"
|
|
||||||
#include "base/net/proxyconfigurationmanager.h"
|
|
||||||
#include "base/bittorrent/session.h"
|
|
||||||
#include "base/bittorrent/torrenthandle.h"
|
|
||||||
#include "base/rss/rss_autodownloader.h"
|
|
||||||
#include "base/rss/rss_session.h"
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
#define SETTINGS_KEY(name) "Application/" name
|
#define SETTINGS_KEY(name) "Application/" name
|
||||||
|
|
||||||
// FileLogger properties keys
|
// FileLogger properties keys
|
||||||
#define FILELOGGER_SETTINGS_KEY(name) SETTINGS_KEY("FileLogger/") name
|
#define FILELOGGER_SETTINGS_KEY(name) QStringLiteral(SETTINGS_KEY("FileLogger/") name)
|
||||||
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
|
const QString KEY_FILELOGGER_ENABLED = FILELOGGER_SETTINGS_KEY("Enabled");
|
||||||
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
const QString KEY_FILELOGGER_PATH = FILELOGGER_SETTINGS_KEY("Path");
|
||||||
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
const QString KEY_FILELOGGER_BACKUP = FILELOGGER_SETTINGS_KEY("Backup");
|
||||||
@@ -91,13 +96,13 @@ namespace
|
|||||||
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
const QString KEY_FILELOGGER_AGE = FILELOGGER_SETTINGS_KEY("Age");
|
||||||
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
const QString KEY_FILELOGGER_AGETYPE = FILELOGGER_SETTINGS_KEY("AgeType");
|
||||||
|
|
||||||
//just a shortcut
|
// just a shortcut
|
||||||
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
|
inline SettingsStorage *settings() { return SettingsStorage::instance(); }
|
||||||
|
|
||||||
const QString LOG_FOLDER("logs");
|
const QString LOG_FOLDER = QStringLiteral("logs");
|
||||||
const char PARAMS_SEPARATOR[] = "|";
|
const QChar PARAMS_SEPARATOR = '|';
|
||||||
|
|
||||||
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QLatin1String("profile");
|
const QString DEFAULT_PORTABLE_MODE_PROFILE_DIR = QStringLiteral("profile");
|
||||||
|
|
||||||
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
const int MIN_FILELOG_SIZE = 1024; // 1KiB
|
||||||
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
const int MAX_FILELOG_SIZE = 1000 * 1024 * 1024; // 1000MiB
|
||||||
@@ -140,11 +145,11 @@ Application::Application(const QString &id, int &argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
#if defined(Q_OS_WIN) && !defined(DISABLE_GUI)
|
||||||
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
|
connect(this, &QGuiApplication::commitDataRequest, this, &Application::shutdownCleanup, Qt::DirectConnection);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
connect(this, &Application::messageReceived, this, &Application::processMessage);
|
||||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
connect(this, &QCoreApplication::aboutToQuit, this, &Application::cleanup);
|
||||||
|
|
||||||
if (isFileLoggerEnabled())
|
if (isFileLoggerEnabled())
|
||||||
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
m_fileLogger = new FileLogger(fileLoggerPath(), isFileLoggerBackup(), fileLoggerMaxSize(), isFileLoggerDeleteOld(), fileLoggerAge(), static_cast<FileLogger::FileLogAgeType>(fileLoggerAgeType()));
|
||||||
@@ -250,17 +255,17 @@ void Application::setFileLoggerAge(const int value)
|
|||||||
int Application::fileLoggerAgeType() const
|
int Application::fileLoggerAgeType() const
|
||||||
{
|
{
|
||||||
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
|
int val = settings()->loadValue(KEY_FILELOGGER_AGETYPE, 1).toInt();
|
||||||
return (val < 0 || val > 2) ? 1 : val;
|
return ((val < 0) || (val > 2)) ? 1 : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setFileLoggerAgeType(const int value)
|
void Application::setFileLoggerAgeType(const int value)
|
||||||
{
|
{
|
||||||
settings()->storeValue(KEY_FILELOGGER_AGETYPE, (value < 0 || value > 2) ? 1 : value);
|
settings()->storeValue(KEY_FILELOGGER_AGETYPE, ((value < 0) || (value > 2)) ? 1 : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::processMessage(const QString &message)
|
void Application::processMessage(const QString &message)
|
||||||
{
|
{
|
||||||
QStringList params = message.split(QLatin1String(PARAMS_SEPARATOR), QString::SkipEmptyParts);
|
QStringList params = message.split(PARAMS_SEPARATOR, QString::SkipEmptyParts);
|
||||||
// If Application is not running (i.e., other
|
// If Application is not running (i.e., other
|
||||||
// components are not ready) store params
|
// components are not ready) store params
|
||||||
if (m_running)
|
if (m_running)
|
||||||
@@ -269,38 +274,70 @@ void Application::processMessage(const QString &message)
|
|||||||
m_paramsQueue.append(params);
|
m_paramsQueue.append(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::runExternalProgram(BitTorrent::TorrentHandle *const torrent) const
|
void Application::runExternalProgram(const BitTorrent::TorrentHandle *torrent) const
|
||||||
{
|
{
|
||||||
QString program = Preferences::instance()->getAutoRunProgram();
|
QString program = Preferences::instance()->getAutoRunProgram().trimmed();
|
||||||
program.replace("%N", torrent->name());
|
program.replace("%N", torrent->name());
|
||||||
program.replace("%L", torrent->category());
|
program.replace("%L", torrent->category());
|
||||||
|
|
||||||
|
QStringList tags = torrent->tags().toList();
|
||||||
|
std::sort(tags.begin(), tags.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||||
|
program.replace("%G", tags.join(','));
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
const auto chopPathSep = [](const QString &str) -> QString
|
||||||
|
{
|
||||||
|
if (str.endsWith('\\'))
|
||||||
|
return str.mid(0, (str.length() -1));
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
program.replace("%F", chopPathSep(Utils::Fs::toNativePath(torrent->contentPath())));
|
||||||
|
program.replace("%R", chopPathSep(Utils::Fs::toNativePath(torrent->rootPath())));
|
||||||
|
program.replace("%D", chopPathSep(Utils::Fs::toNativePath(torrent->savePath())));
|
||||||
|
#else
|
||||||
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
|
program.replace("%F", Utils::Fs::toNativePath(torrent->contentPath()));
|
||||||
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
|
program.replace("%R", Utils::Fs::toNativePath(torrent->rootPath()));
|
||||||
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
|
program.replace("%D", Utils::Fs::toNativePath(torrent->savePath()));
|
||||||
|
#endif
|
||||||
program.replace("%C", QString::number(torrent->filesCount()));
|
program.replace("%C", QString::number(torrent->filesCount()));
|
||||||
program.replace("%Z", QString::number(torrent->totalSize()));
|
program.replace("%Z", QString::number(torrent->totalSize()));
|
||||||
program.replace("%T", torrent->currentTracker());
|
program.replace("%T", torrent->currentTracker());
|
||||||
program.replace("%I", torrent->hash());
|
program.replace("%I", torrent->hash());
|
||||||
|
|
||||||
Logger *logger = Logger::instance();
|
Logger *logger = Logger::instance();
|
||||||
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name()).arg(program));
|
logger->addMessage(tr("Torrent: %1, running external program, command: %2").arg(torrent->name(), program));
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_WIN)
|
||||||
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
std::unique_ptr<wchar_t[]> programWchar(new wchar_t[program.length() + 1] {});
|
||||||
|
program.toWCharArray(programWchar.get());
|
||||||
|
|
||||||
|
// Need to split arguments manually because QProcess::startDetached(QString)
|
||||||
|
// will strip off empty parameters.
|
||||||
|
// E.g. `python.exe "1" "" "3"` will become `python.exe "1" "3"`
|
||||||
|
int argCount = 0;
|
||||||
|
LPWSTR *args = ::CommandLineToArgvW(programWchar.get(), &argCount);
|
||||||
|
|
||||||
|
QStringList argList;
|
||||||
|
for (int i = 1; i < argCount; ++i)
|
||||||
|
argList += QString::fromWCharArray(args[i]);
|
||||||
|
|
||||||
|
QProcess::startDetached(QString::fromWCharArray(args[0]), argList);
|
||||||
|
|
||||||
|
::LocalFree(args);
|
||||||
#else
|
#else
|
||||||
QProcess::startDetached(program);
|
QProcess::startDetached(QLatin1String("/bin/sh"), {QLatin1String("-c"), program});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::sendNotificationEmail(const BitTorrent::TorrentHandle *torrent)
|
void Application::sendNotificationEmail(const BitTorrent::TorrentHandle *torrent)
|
||||||
{
|
{
|
||||||
// Prepare mail content
|
// Prepare mail content
|
||||||
const QString content = tr("Torrent name: %1").arg(torrent->name()) + "\n"
|
const QString content = tr("Torrent name: %1").arg(torrent->name()) + '\n'
|
||||||
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + "\n"
|
+ tr("Torrent size: %1").arg(Utils::Misc::friendlyUnit(torrent->wantedSize())) + '\n'
|
||||||
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
|
+ tr("Save path: %1").arg(torrent->savePath()) + "\n\n"
|
||||||
+ tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds")
|
+ 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"
|
.arg(Utils::Misc::userFriendlyDuration(torrent->activeTime())) + "\n\n\n"
|
||||||
+ tr("Thank you for using qBittorrent.") + "\n";
|
+ tr("Thank you for using qBittorrent.") + '\n';
|
||||||
|
|
||||||
// Send the notification email
|
// Send the notification email
|
||||||
const Preferences *pref = Preferences::instance();
|
const Preferences *pref = Preferences::instance();
|
||||||
@@ -351,7 +388,7 @@ void Application::allTorrentsFinished()
|
|||||||
// do nothing & skip confirm
|
// do nothing & skip confirm
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!ShutdownConfirmDlg::askForConfirmation(m_window, action)) return;
|
if (!ShutdownConfirmDialog::askForConfirmation(m_window, action)) return;
|
||||||
}
|
}
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
@@ -372,7 +409,7 @@ void Application::allTorrentsFinished()
|
|||||||
|
|
||||||
bool Application::sendParams(const QStringList ¶ms)
|
bool Application::sendParams(const QStringList ¶ms)
|
||||||
{
|
{
|
||||||
return sendMessage(params.join(QLatin1String(PARAMS_SEPARATOR)));
|
return sendMessage(params.join(PARAMS_SEPARATOR));
|
||||||
}
|
}
|
||||||
|
|
||||||
// As program parameters, we can get paths or urls.
|
// As program parameters, we can get paths or urls.
|
||||||
@@ -401,7 +438,7 @@ void Application::processParams(const QStringList ¶ms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (param.startsWith(QLatin1String("@addPaused="))) {
|
if (param.startsWith(QLatin1String("@addPaused="))) {
|
||||||
torrentParams.addPaused = param.mid(11).toInt() ? TriStateBool::True : TriStateBool::False;
|
torrentParams.addPaused = param.midRef(11).toInt() ? TriStateBool::True : TriStateBool::False;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +463,7 @@ void Application::processParams(const QStringList ¶ms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (param.startsWith(QLatin1String("@skipDialog="))) {
|
if (param.startsWith(QLatin1String("@skipDialog="))) {
|
||||||
skipTorrentDialog = param.mid(12).toInt() ? TriStateBool::True : TriStateBool::False;
|
skipTorrentDialog = param.midRef(12).toInt() ? TriStateBool::True : TriStateBool::False;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +495,8 @@ int Application::exec(const QStringList ¶ms)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
BitTorrent::Session::initInstance();
|
BitTorrent::Session::initInstance();
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(torrentFinished(BitTorrent::TorrentHandle *const)), SLOT(torrentFinished(BitTorrent::TorrentHandle *const)));
|
connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentFinished, this, &Application::torrentFinished);
|
||||||
connect(BitTorrent::Session::instance(), SIGNAL(allTorrentsFinished()), SLOT(allTorrentsFinished()), Qt::QueuedConnection);
|
connect(BitTorrent::Session::instance(), &BitTorrent::Session::allTorrentsFinished, this, &Application::allTorrentsFinished, Qt::QueuedConnection);
|
||||||
|
|
||||||
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
#ifndef DISABLE_COUNTRIES_RESOLUTION
|
||||||
Net::GeoIPManager::initInstance();
|
Net::GeoIPManager::initInstance();
|
||||||
@@ -480,15 +517,18 @@ int Application::exec(const QStringList ¶ms)
|
|||||||
|
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
// Display some information to the user
|
// Display some information to the user
|
||||||
std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl;
|
const QString mesg = QString("\n******** %1 ********\n").arg(tr("Information"))
|
||||||
std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(pref->getWebUiPort()))) << std::endl;
|
+ tr("To control qBittorrent, access the Web UI at %1")
|
||||||
std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername())) << std::endl;
|
.arg(QString("http://localhost:") + QString::number(pref->getWebUiPort())) + '\n'
|
||||||
|
+ tr("The Web UI administrator user name is: %1").arg(pref->getWebUiUsername()) + '\n';
|
||||||
|
printf("%s", qUtf8Printable(mesg));
|
||||||
qDebug() << "Password:" << pref->getWebUiPassword();
|
qDebug() << "Password:" << pref->getWebUiPassword();
|
||||||
if (pref->getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") {
|
if (pref->getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") {
|
||||||
std::cout << qPrintable(tr("The Web UI administrator password is still the default one: %1").arg("adminadmin")) << std::endl;
|
const QString warning = tr("The Web UI administrator password is still the default one: %1").arg("adminadmin") + '\n'
|
||||||
std::cout << qPrintable(tr("This is a security risk, please consider changing your password from program preferences.")) << std::endl;
|
+ tr("This is a security risk, please consider changing your password from program preferences.") + '\n';
|
||||||
|
printf("%s", qUtf8Printable(warning));
|
||||||
}
|
}
|
||||||
#endif // DISABLE_WEBUI
|
#endif // DISABLE_WEBUI
|
||||||
#else
|
#else
|
||||||
@@ -573,7 +613,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
|
|||||||
|
|
||||||
void Application::initializeTranslation()
|
void Application::initializeTranslation()
|
||||||
{
|
{
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
// Load translation
|
// Load translation
|
||||||
QString localeStr = pref->getLocale();
|
QString localeStr = pref->getLocale();
|
||||||
|
|
||||||
@@ -626,7 +666,7 @@ void Application::shutdownCleanup(QSessionManager &manager)
|
|||||||
// According to the qt docs we shouldn't call quit() inside a slot.
|
// According to the qt docs we shouldn't call quit() inside a slot.
|
||||||
// aboutToQuit() is never emitted if the user hits "Cancel" in
|
// aboutToQuit() is never emitted if the user hits "Cancel" in
|
||||||
// the above dialog.
|
// the above dialog.
|
||||||
QTimer::singleShot(0, qApp, SLOT(quit()));
|
QTimer::singleShot(0, qApp, &QCoreApplication::quit);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -639,7 +679,7 @@ void Application::cleanup()
|
|||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
if (m_window) {
|
if (m_window) {
|
||||||
// Hide the window and not leave it on screen as
|
// Hide the window and don't leave it on screen as
|
||||||
// unresponsive. Also for Windows take the WinId
|
// unresponsive. Also for Windows take the WinId
|
||||||
// after it's hidden, because hide() may cause a
|
// after it's hidden, because hide() may cause a
|
||||||
// WinId change.
|
// WinId change.
|
||||||
@@ -647,7 +687,7 @@ void Application::cleanup()
|
|||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
|
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
|
||||||
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonCreate");
|
const auto shutdownBRCreate = Utils::Misc::loadWinAPI<PSHUTDOWNBRCREATE>("User32.dll", "ShutdownBlockReasonCreate");
|
||||||
// Only available on Vista+
|
// Only available on Vista+
|
||||||
if (shutdownBRCreate)
|
if (shutdownBRCreate)
|
||||||
shutdownBRCreate((HWND)m_window->effectiveWinId(), tr("Saving torrent progress...").toStdWString().c_str());
|
shutdownBRCreate((HWND)m_window->effectiveWinId(), tr("Saving torrent progress...").toStdWString().c_str());
|
||||||
@@ -689,7 +729,7 @@ void Application::cleanup()
|
|||||||
if (m_window) {
|
if (m_window) {
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
||||||
PSHUTDOWNBRDESTROY shutdownBRDestroy = (PSHUTDOWNBRDESTROY)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonDestroy");
|
const auto shutdownBRDestroy = Utils::Misc::loadWinAPI<PSHUTDOWNBRDESTROY>("User32.dll", "ShutdownBlockReasonDestroy");
|
||||||
// Only available on Vista+
|
// Only available on Vista+
|
||||||
if (shutdownBRDestroy)
|
if (shutdownBRDestroy)
|
||||||
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
||||||
|
|||||||
@@ -40,15 +40,13 @@ typedef QtSingleApplication BaseApplication;
|
|||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QSessionManager;
|
class QSessionManager;
|
||||||
QT_END_NAMESPACE
|
|
||||||
#endif // Q_OS_WIN
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include "qtsinglecoreapplication.h"
|
#include "qtsinglecoreapplication.h"
|
||||||
typedef QtSingleCoreApplication BaseApplication;
|
typedef QtSingleCoreApplication BaseApplication;
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
@@ -110,9 +108,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
bool event(QEvent *);
|
bool event(QEvent *) override;
|
||||||
#endif
|
#endif
|
||||||
bool notify(QObject* receiver, QEvent* event);
|
bool notify(QObject *receiver, QEvent *event) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@@ -146,7 +144,7 @@ private:
|
|||||||
|
|
||||||
void initializeTranslation();
|
void initializeTranslation();
|
||||||
void processParams(const QStringList ¶ms);
|
void processParams(const QStringList ¶ms);
|
||||||
void runExternalProgram(BitTorrent::TorrentHandle *const torrent) const;
|
void runExternalProgram(const BitTorrent::TorrentHandle *torrent) const;
|
||||||
void sendNotificationEmail(const BitTorrent::TorrentHandle *torrent);
|
void sendNotificationEmail(const BitTorrent::TorrentHandle *torrent);
|
||||||
void validateCommandLineParameters();
|
void validateCommandLineParameters();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,13 +26,11 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
@@ -100,7 +98,7 @@ namespace
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Boolean option.
|
// Boolean option.
|
||||||
class BoolOption: protected Option
|
class BoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr BoolOption(const char *name, char shortcut = 0)
|
constexpr BoolOption(const char *name, char shortcut = 0)
|
||||||
@@ -118,7 +116,7 @@ namespace
|
|||||||
{
|
{
|
||||||
QString val = env.value(envVarName());
|
QString val = env.value(envVarName());
|
||||||
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
|
// we accept "1" and "true" (upper or lower cased) as boolean 'true' values
|
||||||
return (val == QLatin1String("1") || val.toUpper() == QLatin1String("TRUE"));
|
return ((val == QLatin1String("1")) || (val.toUpper() == QLatin1String("TRUE")));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString usage() const
|
QString usage() const
|
||||||
@@ -137,7 +135,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option with string value. May not have a shortcut
|
// Option with string value. May not have a shortcut
|
||||||
struct StringOption: protected Option
|
struct StringOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr StringOption(const char *name)
|
constexpr StringOption(const char *name)
|
||||||
@@ -184,7 +182,7 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Option with integer value. May not have a shortcut
|
// Option with integer value. May not have a shortcut
|
||||||
class IntOption: protected StringOption
|
class IntOption : protected StringOption
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr IntOption(const char *name)
|
constexpr IntOption(const char *name)
|
||||||
@@ -216,7 +214,7 @@ namespace
|
|||||||
int res = val.toInt(&ok);
|
int res = val.toInt(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
qDebug() << QObject::tr("Expected integer number in environment variable '%1', but got '%2'")
|
qDebug() << QObject::tr("Expected integer number in environment variable '%1', but got '%2'")
|
||||||
.arg(envVarName()).arg(val);
|
.arg(envVarName(), val);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -230,7 +228,7 @@ namespace
|
|||||||
|
|
||||||
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
|
// Option that is explicitly set to true or false, and whose value is undefined when unspecified.
|
||||||
// May not have a shortcut.
|
// May not have a shortcut.
|
||||||
class TriStateBoolOption: protected Option
|
class TriStateBoolOption : protected Option
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
constexpr TriStateBoolOption(const char *name, bool defaultValue)
|
||||||
@@ -260,10 +258,10 @@ namespace
|
|||||||
else if (parts.size() == 2) {
|
else if (parts.size() == 2) {
|
||||||
QString val = parts[1];
|
QString val = parts[1];
|
||||||
|
|
||||||
if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
|
if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
|
||||||
return TriStateBool::True;
|
return TriStateBool::True;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
|
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
|
||||||
return TriStateBool::False;
|
return TriStateBool::False;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,15 +283,15 @@ namespace
|
|||||||
else if (val == QLatin1String("-1")) {
|
else if (val == QLatin1String("-1")) {
|
||||||
return TriStateBool::Undefined;
|
return TriStateBool::Undefined;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("TRUE") || val == QLatin1String("1")) {
|
else if ((val.toUpper() == QLatin1String("TRUE")) || (val == QLatin1String("1"))) {
|
||||||
return TriStateBool::True;
|
return TriStateBool::True;
|
||||||
}
|
}
|
||||||
else if (val.toUpper() == QLatin1String("FALSE") || val == QLatin1String("0")) {
|
else if ((val.toUpper() == QLatin1String("FALSE")) || (val == QLatin1String("0"))) {
|
||||||
return TriStateBool::False;
|
return TriStateBool::False;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qDebug() << QObject::tr("Expected %1 in environment variable '%2', but got '%3'")
|
qDebug() << QObject::tr("Expected %1 in environment variable '%2', but got '%3'")
|
||||||
.arg(QLatin1String("true|false")).arg(envVarName()).arg(val);
|
.arg(QLatin1String("true|false"), envVarName(), val);
|
||||||
return TriStateBool::Undefined;
|
return TriStateBool::Undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +358,7 @@ QStringList QBtCommandLineParameters::paramList() const
|
|||||||
// the user has specified. Here we place special strings that are
|
// the user has specified. Here we place special strings that are
|
||||||
// almost certainly not going to collide with a file path or URL
|
// almost certainly not going to collide with a file path or URL
|
||||||
// specified by the user, and placing them at the beginning of the
|
// specified by the user, and placing them at the beginning of the
|
||||||
// string listr so that they will be processed before the list of
|
// string list so that they will be processed before the list of
|
||||||
// torrent paths or URLs.
|
// torrent paths or URLs.
|
||||||
|
|
||||||
if (!savePath.isEmpty())
|
if (!savePath.isEmpty())
|
||||||
@@ -404,9 +402,9 @@ QBtCommandLineParameters parseCommandLine(const QStringList &args)
|
|||||||
const QString &arg = args[i];
|
const QString &arg = args[i];
|
||||||
|
|
||||||
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|
if ((arg.startsWith("--") && !arg.endsWith(".torrent"))
|
||||||
|| (arg.startsWith("-") && (arg.size() == 2))) {
|
|| (arg.startsWith('-') && (arg.size() == 2))) {
|
||||||
// Parse known parameters
|
// Parse known parameters
|
||||||
if ((arg == SHOW_HELP_OPTION)) {
|
if (arg == SHOW_HELP_OPTION) {
|
||||||
result.showHelp = true;
|
result.showHelp = true;
|
||||||
}
|
}
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
@@ -501,7 +499,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
|
|||||||
|
|
||||||
foreach (const QString &word, words.mid(1)) {
|
foreach (const QString &word, words.mid(1)) {
|
||||||
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
|
if (lines.last().length() + word.length() + 1 < currentLineMaxLength) {
|
||||||
lines.last().append(" " + word);
|
lines.last().append(' ' + word);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lines.append(QString(initialIndentation, ' ') + word);
|
lines.append(QString(initialIndentation, ' ') + word);
|
||||||
@@ -509,7 +507,7 @@ QString wrapText(const QString &text, int initialIndentation = USAGE_TEXT_COLUMN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lines.join("\n");
|
return lines.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
QString makeUsage(const QString &prgName)
|
QString makeUsage(const QString &prgName)
|
||||||
@@ -578,7 +576,7 @@ QString makeUsage(const QString &prgName)
|
|||||||
void displayUsage(const QString &prgName)
|
void displayUsage(const QString &prgName)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
std::cout << qPrintable(makeUsage(prgName)) << std::endl;
|
printf("%s\n", qUtf8Printable(makeUsage(prgName)));
|
||||||
#else
|
#else
|
||||||
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prgName), QMessageBox::Ok);
|
QMessageBox msgBox(QMessageBox::Information, QObject::tr("Help"), makeUsage(prgName), QMessageBox::Ok);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
* Copyright (C) 2016 Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -26,8 +26,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef APP_OPTIONS_H
|
#ifndef APP_OPTIONS_H
|
||||||
@@ -62,11 +60,11 @@ struct QBtCommandLineParameters
|
|||||||
QStringList paramList() const;
|
QStringList paramList() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommandLineParameterError: public std::runtime_error
|
class CommandLineParameterError : public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CommandLineParameterError(const QString &messageForUser);
|
CommandLineParameterError(const QString &messageForUser);
|
||||||
const QString& messageForUser() const;
|
const QString &messageForUser() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QString m_messageForUser;
|
const QString m_messageForUser;
|
||||||
|
|||||||
@@ -26,11 +26,13 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "filelogger.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include "filelogger.h"
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
@@ -41,17 +43,17 @@ FileLogger::FileLogger(const QString &path, const bool backup, const int maxSize
|
|||||||
{
|
{
|
||||||
m_flusher.setInterval(0);
|
m_flusher.setInterval(0);
|
||||||
m_flusher.setSingleShot(true);
|
m_flusher.setSingleShot(true);
|
||||||
connect(&m_flusher, SIGNAL(timeout()), SLOT(flushLog()));
|
connect(&m_flusher, &QTimer::timeout, this, &FileLogger::flushLog);
|
||||||
|
|
||||||
changePath(path);
|
changePath(path);
|
||||||
if (deleteOld)
|
if (deleteOld)
|
||||||
this->deleteOld(age, ageType);
|
this->deleteOld(age, ageType);
|
||||||
|
|
||||||
const Logger* const logger = Logger::instance();
|
const Logger *const logger = Logger::instance();
|
||||||
foreach (const Log::Msg& msg, logger->getMessages())
|
foreach (const Log::Msg &msg, logger->getMessages())
|
||||||
addLogMessage(msg);
|
addLogMessage(msg);
|
||||||
|
|
||||||
connect(logger, SIGNAL(newLogMessage(const Log::Msg &)), SLOT(addLogMessage(const Log::Msg &)));
|
connect(logger, &Logger::newLogMessage, this, &FileLogger::addLogMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
FileLogger::~FileLogger()
|
FileLogger::~FileLogger()
|
||||||
@@ -61,7 +63,7 @@ FileLogger::~FileLogger()
|
|||||||
delete m_logFile;
|
delete m_logFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileLogger::changePath(const QString& newPath)
|
void FileLogger::changePath(const QString &newPath)
|
||||||
{
|
{
|
||||||
QString tmpPath = Utils::Fs::fromNativePath(newPath);
|
QString tmpPath = Utils::Fs::fromNativePath(newPath);
|
||||||
QDir dir(tmpPath);
|
QDir dir(tmpPath);
|
||||||
@@ -83,21 +85,21 @@ void FileLogger::changePath(const QString& newPath)
|
|||||||
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
void FileLogger::deleteOld(const int age, const FileLogAgeType ageType)
|
||||||
{
|
{
|
||||||
QDateTime date = QDateTime::currentDateTime();
|
QDateTime date = QDateTime::currentDateTime();
|
||||||
QDir dir(m_path);
|
QDir dir(Utils::Fs::branchPath(m_path));
|
||||||
|
|
||||||
switch (ageType) {
|
|
||||||
case DAYS:
|
|
||||||
date = date.addDays(age);
|
|
||||||
break;
|
|
||||||
case MONTHS:
|
|
||||||
date = date.addMonths(age);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
date = date.addYears(age);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
|
foreach (const QFileInfo file, dir.entryInfoList(QStringList("qbittorrent.log.bak*"), QDir::Files | QDir::Writable, QDir::Time | QDir::Reversed)) {
|
||||||
if (file.lastModified() < date)
|
QDateTime modificationDate = file.lastModified();
|
||||||
|
switch (ageType) {
|
||||||
|
case DAYS:
|
||||||
|
modificationDate = modificationDate.addDays(age);
|
||||||
|
break;
|
||||||
|
case MONTHS:
|
||||||
|
modificationDate = modificationDate.addMonths(age);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
modificationDate = modificationDate.addYears(age);
|
||||||
|
}
|
||||||
|
if (modificationDate > date)
|
||||||
break;
|
break;
|
||||||
Utils::Fs::forceRemove(file.absoluteFilePath());
|
Utils::Fs::forceRemove(file.absoluteFilePath());
|
||||||
}
|
}
|
||||||
@@ -165,7 +167,7 @@ void FileLogger::openLogFile()
|
|||||||
|| !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
|
|| !m_logFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner)) {
|
||||||
delete m_logFile;
|
delete m_logFile;
|
||||||
m_logFile = nullptr;
|
m_logFile = nullptr;
|
||||||
Logger::instance()->addMessage(tr("An error occured while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
|
Logger::instance()->addMessage(tr("An error occurred while trying to open the log file. Logging to file is disabled."), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,4 +76,3 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILELOGGER_H
|
#endif // FILELOGGER_H
|
||||||
|
|
||||||
|
|||||||
110
src/app/main.cpp
110
src/app/main.cpp
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
@@ -55,33 +55,28 @@ Q_IMPORT_PLUGIN(QICOPlugin)
|
|||||||
#endif
|
#endif
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#ifdef STACKTRACE
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
#include <signal.h>
|
|
||||||
#include <execinfo.h>
|
|
||||||
#include "stacktrace.h"
|
#include "stacktrace.h"
|
||||||
#endif // Q_OS_UNIX
|
#else
|
||||||
|
|
||||||
#ifdef STACKTRACE_WIN
|
|
||||||
#include <signal.h>
|
|
||||||
#include "stacktrace_win.h"
|
#include "stacktrace_win.h"
|
||||||
#include "stacktrace_win_dlg.h"
|
#include "stacktracedialog.h"
|
||||||
#endif //STACKTRACE_WIN
|
#endif // Q_OS_UNIX
|
||||||
|
#endif //STACKTRACE
|
||||||
|
|
||||||
#include <cstdlib>
|
#include "base/preferences.h"
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "application.h"
|
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/preferences.h"
|
#include "application.h"
|
||||||
#include "cmdoptions.h"
|
#include "cmdoptions.h"
|
||||||
|
|
||||||
#include "upgrade.h"
|
#include "upgrade.h"
|
||||||
|
|
||||||
// Signal handlers
|
// Signal handlers
|
||||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
|
||||||
void sigNormalHandler(int signum);
|
void sigNormalHandler(int signum);
|
||||||
|
#ifdef STACKTRACE
|
||||||
void sigAbnormalHandler(int signum);
|
void sigAbnormalHandler(int signum);
|
||||||
|
#endif
|
||||||
// sys_signame[] is only defined in BSD
|
// sys_signame[] is only defined in BSD
|
||||||
const char *sysSigName[] = {
|
const char *sysSigName[] = {
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
@@ -96,10 +91,9 @@ const char *sysSigName[] = {
|
|||||||
"SIGPWR", "SIGUNUSED"
|
"SIGPWR", "SIGUNUSED"
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
void reportToUser(const char* str);
|
void reportToUser(const char *str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void displayVersion();
|
void displayVersion();
|
||||||
@@ -108,10 +102,6 @@ void displayBadArgMessage(const QString &message);
|
|||||||
|
|
||||||
#if !defined(DISABLE_GUI)
|
#if !defined(DISABLE_GUI)
|
||||||
void showSplashScreen();
|
void showSplashScreen();
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
|
||||||
void setupDpi();
|
|
||||||
#endif // Q_OS_UNIX
|
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
// Main
|
// Main
|
||||||
@@ -126,10 +116,6 @@ int main(int argc, char *argv[])
|
|||||||
macMigratePlists();
|
macMigratePlists();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(DISABLE_GUI) && defined(Q_OS_UNIX)
|
|
||||||
setupDpi();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create Application
|
// Create Application
|
||||||
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
|
QString appId = QLatin1String("qBittorrent-") + Utils::Misc::getUserIDString();
|
||||||
@@ -169,7 +155,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Set environment variable
|
// Set environment variable
|
||||||
if (!qputenv("QBITTORRENT", QBT_VERSION))
|
if (!qputenv("QBITTORRENT", QBT_VERSION))
|
||||||
std::cerr << "Couldn't set environment variable...\n";
|
fprintf(stderr, "Couldn't set environment variable...\n");
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
if (!userAgreesWithLegalNotice())
|
if (!userAgreesWithLegalNotice())
|
||||||
@@ -240,7 +226,7 @@ int main(int argc, char *argv[])
|
|||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
if (params.shouldDaemonize) {
|
if (params.shouldDaemonize) {
|
||||||
app.reset(); // Destroy current application
|
app.reset(); // Destroy current application
|
||||||
if ((daemon(1, 0) == 0)) {
|
if (daemon(1, 0) == 0) {
|
||||||
app.reset(new Application(appId, argc, argv));
|
app.reset(new Application(appId, argc, argv));
|
||||||
if (app->isRunning()) {
|
if (app->isRunning()) {
|
||||||
// Another instance had time to start.
|
// Another instance had time to start.
|
||||||
@@ -257,9 +243,9 @@ int main(int argc, char *argv[])
|
|||||||
showSplashScreen();
|
showSplashScreen();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
|
||||||
signal(SIGINT, sigNormalHandler);
|
signal(SIGINT, sigNormalHandler);
|
||||||
signal(SIGTERM, sigNormalHandler);
|
signal(SIGTERM, sigNormalHandler);
|
||||||
|
#ifdef STACKTRACE
|
||||||
signal(SIGABRT, sigAbnormalHandler);
|
signal(SIGABRT, sigAbnormalHandler);
|
||||||
signal(SIGSEGV, sigAbnormalHandler);
|
signal(SIGSEGV, sigAbnormalHandler);
|
||||||
#endif
|
#endif
|
||||||
@@ -273,7 +259,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
void reportToUser(const char* str)
|
void reportToUser(const char *str)
|
||||||
{
|
{
|
||||||
const size_t strLen = strlen(str);
|
const size_t strLen = strlen(str);
|
||||||
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
|
if (write(STDERR_FILENO, str, strLen) < static_cast<ssize_t>(strLen)) {
|
||||||
@@ -283,7 +269,6 @@ void reportToUser(const char* str)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
|
||||||
void sigNormalHandler(int signum)
|
void sigNormalHandler(int signum)
|
||||||
{
|
{
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
@@ -297,6 +282,7 @@ void sigNormalHandler(int signum)
|
|||||||
qApp->exit(); // unsafe, but exit anyway
|
qApp->exit(); // unsafe, but exit anyway
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef STACKTRACE
|
||||||
void sigAbnormalHandler(int signum)
|
void sigAbnormalHandler(int signum)
|
||||||
{
|
{
|
||||||
const char *sigName = sysSigName[signum];
|
const char *sigName = sysSigName[signum];
|
||||||
@@ -309,47 +295,41 @@ void sigAbnormalHandler(int signum)
|
|||||||
reportToUser(sigName);
|
reportToUser(sigName);
|
||||||
reportToUser("\n");
|
reportToUser("\n");
|
||||||
print_stacktrace(); // unsafe
|
print_stacktrace(); // unsafe
|
||||||
#endif // !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#endif
|
||||||
#ifdef STACKTRACE_WIN
|
|
||||||
StraceDlg dlg; // unsafe
|
#if defined Q_OS_WIN
|
||||||
|
StacktraceDialog dlg; // unsafe
|
||||||
dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
|
dlg.setStacktraceString(QLatin1String(sigName), straceWin::getBacktrace());
|
||||||
dlg.exec();
|
dlg.exec();
|
||||||
#endif // STACKTRACE_WIN
|
#endif
|
||||||
|
|
||||||
signal(signum, SIG_DFL);
|
signal(signum, SIG_DFL);
|
||||||
raise(signum);
|
raise(signum);
|
||||||
}
|
}
|
||||||
#endif // defined(Q_OS_UNIX) || defined(STACKTRACE_WIN)
|
#endif // STACKTRACE
|
||||||
|
|
||||||
#if !defined(DISABLE_GUI)
|
#if !defined(DISABLE_GUI)
|
||||||
void showSplashScreen()
|
void showSplashScreen()
|
||||||
{
|
{
|
||||||
QPixmap splash_img(":/icons/skin/splash.png");
|
QPixmap splashImg(":/icons/skin/splash.png");
|
||||||
QPainter painter(&splash_img);
|
QPainter painter(&splashImg);
|
||||||
QString version = QBT_VERSION;
|
QString version = QBT_VERSION;
|
||||||
painter.setPen(QPen(Qt::white));
|
painter.setPen(QPen(Qt::white));
|
||||||
painter.setFont(QFont("Arial", 22, QFont::Black));
|
painter.setFont(QFont("Arial", 22, QFont::Black));
|
||||||
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
|
painter.drawText(224 - painter.fontMetrics().width(version), 270, version);
|
||||||
QSplashScreen *splash = new QSplashScreen(splash_img);
|
QSplashScreen *splash = new QSplashScreen(splashImg);
|
||||||
splash->show();
|
splash->show();
|
||||||
QTimer::singleShot(1500, splash, SLOT(deleteLater()));
|
QTimer::singleShot(1500, splash, &QObject::deleteLater);
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
|
||||||
void setupDpi()
|
|
||||||
{
|
|
||||||
if (qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR").isEmpty())
|
|
||||||
qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "1");
|
|
||||||
}
|
|
||||||
#endif // Q_OS_UNIX
|
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
void displayVersion()
|
void displayVersion()
|
||||||
{
|
{
|
||||||
std::cout << qPrintable(qApp->applicationName()) << " " << QBT_VERSION << std::endl;
|
printf("%s %s\n", qUtf8Printable(qApp->applicationName()), QBT_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
void displayBadArgMessage(const QString& message)
|
void displayBadArgMessage(const QString &message)
|
||||||
{
|
{
|
||||||
QString help = QObject::tr("Run application with -h option to read about command line parameters.");
|
QString help = QObject::tr("Run application with -h option to read about command line parameters.");
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
@@ -359,24 +339,28 @@ void displayBadArgMessage(const QString& message)
|
|||||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
#else
|
#else
|
||||||
std::cerr << qPrintable(QObject::tr("Bad command line: "));
|
const QString errMsg = QObject::tr("Bad command line: ") + '\n'
|
||||||
std::cerr << qPrintable(message) << std::endl;
|
+ message + '\n'
|
||||||
std::cerr << qPrintable(help) << std::endl;
|
+ help + '\n';
|
||||||
|
fprintf(stderr, "%s", qUtf8Printable(errMsg));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool userAgreesWithLegalNotice()
|
bool userAgreesWithLegalNotice()
|
||||||
{
|
{
|
||||||
Preferences* const pref = Preferences::instance();
|
Preferences *const pref = Preferences::instance();
|
||||||
if (pref->getAcceptedLegal()) // Already accepted once
|
if (pref->getAcceptedLegal()) // Already accepted once
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
std::cout << std::endl << "*** " << qPrintable(QObject::tr("Legal Notice")) << " ***" << std::endl;
|
const QString eula = QString("\n*** %1 ***\n").arg(QObject::tr("Legal Notice"))
|
||||||
std::cout << qPrintable(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued.")) << std::endl << std::endl;
|
+ QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.") + "\n\n"
|
||||||
std::cout << qPrintable(QObject::tr("Press %1 key to accept and continue...").arg("'y'")) << std::endl;
|
+ QObject::tr("No further notices will be issued.") + "\n\n"
|
||||||
|
+ QObject::tr("Press %1 key to accept and continue...").arg("'y'") + '\n';
|
||||||
|
printf("%s", qUtf8Printable(eula));
|
||||||
|
|
||||||
char ret = getchar(); // Read pressed key
|
char ret = getchar(); // Read pressed key
|
||||||
if (ret == 'y' || ret == 'Y') {
|
if ((ret == 'y') || (ret == 'Y')) {
|
||||||
// Save the answer
|
// Save the answer
|
||||||
pref->setAcceptedLegal(true);
|
pref->setAcceptedLegal(true);
|
||||||
return true;
|
return true;
|
||||||
@@ -386,16 +370,16 @@ bool userAgreesWithLegalNotice()
|
|||||||
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
msgBox.setText(QObject::tr("qBittorrent is a file sharing program. When you run a torrent, its data will be made available to others by means of upload. Any content you share is your sole responsibility.\n\nNo further notices will be issued."));
|
||||||
msgBox.setWindowTitle(QObject::tr("Legal notice"));
|
msgBox.setWindowTitle(QObject::tr("Legal notice"));
|
||||||
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
msgBox.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
|
||||||
QAbstractButton *agree_button = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
QAbstractButton *agreeButton = msgBox.addButton(QObject::tr("I Agree"), QMessageBox::AcceptRole);
|
||||||
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
msgBox.show(); // Need to be shown or to moveToCenter does not work
|
||||||
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
msgBox.move(Utils::Misc::screenCenter(&msgBox));
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
if (msgBox.clickedButton() == agree_button) {
|
if (msgBox.clickedButton() == agreeButton) {
|
||||||
// Save the answer
|
// Save the answer
|
||||||
pref->setAcceptedLegal(true);
|
pref->setAcceptedLegal(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,24 @@ set(QBT_QTSINGLEAPPLICATION_SOURCES
|
|||||||
qtlocalpeer.cpp
|
qtlocalpeer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
|
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsingleapplication.h)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
|
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsingleapplication.cpp)
|
||||||
else (GUI)
|
else (Qt5Widgets_FOUND)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
|
list(APPEND QBT_QTSINGLEAPPLICATION_HEADERS qtsinglecoreapplication.h)
|
||||||
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
|
list(APPEND QBT_QTSINGLEAPPLICATION_SOURCES qtsinglecoreapplication.cpp)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
|
add_library(qtsingleapplication STATIC ${QBT_QTSINGLEAPPLICATION_HEADERS} ${QBT_QTSINGLEAPPLICATION_SOURCES})
|
||||||
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
|
target_include_directories(qtsingleapplication INTERFACE "${qtsingleapplication_SOURCE_DIR}")
|
||||||
target_link_qt_components(qtsingleapplication Network)
|
target_link_libraries(qtsingleapplication PRIVATE Qt5::Network)
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
|
target_compile_options(qtsingleapplication PRIVATE "-w") # disable warning for 3rdparty code
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
target_link_qt_components(qtsingleapplication Widgets)
|
target_link_libraries(qtsingleapplication PRIVATE Qt5::Widgets)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
|
add_library(QtSingleApplication::QtSingleApplication ALIAS qtsingleapplication)
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ bool straceWin::makeRelativePath(const QString& dir, QString& file)
|
|||||||
|
|
||||||
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
|
QString straceWin::getSourcePathAndLineNumber(HANDLE hProcess, DWORD64 addr)
|
||||||
{
|
{
|
||||||
IMAGEHLP_LINE64 line = {0};
|
IMAGEHLP_LINE64 line {};
|
||||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||||
DWORD dwDisplacement = 0;
|
DWORD dwDisplacement = 0;
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@ const QString straceWin::getBacktrace()
|
|||||||
demangle(funcName);
|
demangle(funcName);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// now ihsf.InstructionOffset points to the instruction that follows CALL instuction
|
// now ihsf.InstructionOffset points to the instruction that follows CALL instruction
|
||||||
// decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
|
// decrease the query address by one byte to point somewhere in the CALL instruction byte sequence
|
||||||
sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
|
sourceFile = getSourcePathAndLineNumber(hProcess, ihsf.InstructionOffset - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,20 +27,21 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef STACKTRACE_WIN_DLG_H
|
#ifndef STACKTRACEDIALOG_H
|
||||||
#define STACKTRACE_WIN_DLG_H
|
#define STACKTRACEDIALOG_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "base/utils/misc.h"
|
|
||||||
#include "ui_stacktrace_win_dlg.h"
|
|
||||||
|
|
||||||
class StraceDlg : public QDialog, private Ui::errorDialog
|
#include "base/utils/misc.h"
|
||||||
|
#include "ui_stacktracedialog.h"
|
||||||
|
|
||||||
|
class StacktraceDialog : public QDialog, private Ui::StacktraceDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StraceDlg(QWidget *parent = nullptr)
|
StacktraceDialog(QWidget *parent = nullptr)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
@@ -83,4 +84,4 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif // STACKTRACEDIALOG_H
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>errorDialog</class>
|
<class>StacktraceDialog</class>
|
||||||
<widget class="QDialog" name="errorDialog">
|
<widget class="QDialog" name="StacktraceDialog">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
@@ -29,42 +29,43 @@
|
|||||||
#ifndef UPGRADE_H
|
#ifndef UPGRADE_H
|
||||||
#define UPGRADE_H
|
#define UPGRADE_H
|
||||||
|
|
||||||
#include <libtorrent/version.hpp>
|
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
|
||||||
#include <libtorrent/bdecode.hpp>
|
|
||||||
#endif
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM >= 10100
|
||||||
|
#include <libtorrent/bdecode.hpp>
|
||||||
|
#else
|
||||||
#include <libtorrent/lazy_entry.hpp>
|
#include <libtorrent/lazy_entry.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#ifndef DISABLE_GUI
|
#ifndef DISABLE_GUI
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#endif
|
#endif
|
||||||
#include <QRegExp>
|
|
||||||
#include <QString>
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "base/preferences.h"
|
|
||||||
|
|
||||||
bool userAcceptsUpgrade()
|
bool userAcceptsUpgrade()
|
||||||
{
|
{
|
||||||
#ifdef DISABLE_GUI
|
#ifdef DISABLE_GUI
|
||||||
std::cout << std::endl << "*** " << qPrintable(QObject::tr("Upgrade")) << " ***" << std::endl;
|
printf("\n*** %s ***\n", qUtf8Printable(QObject::tr("Upgrade")));
|
||||||
char ret = '\0';
|
char ret = '\0';
|
||||||
do {
|
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;
|
printf("%s\n"
|
||||||
|
, qUtf8Printable(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]")));
|
||||||
ret = getchar(); // Read pressed key
|
ret = getchar(); // Read pressed key
|
||||||
}
|
}
|
||||||
while ((ret != 'y') && (ret != 'Y') && (ret != 'n') && (ret != 'N'));
|
while ((ret != 'y') && (ret != 'Y') && (ret != 'n') && (ret != 'N'));
|
||||||
@@ -113,12 +114,28 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
|
|||||||
bool v3_3 = false;
|
bool v3_3 = false;
|
||||||
int queuePosition = 0;
|
int queuePosition = 0;
|
||||||
QString outFilePath = filepath;
|
QString outFilePath = filepath;
|
||||||
QRegExp rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(\\d+)$"));
|
static const QRegularExpression rx(QLatin1String("([A-Fa-f0-9]{40})\\.fastresume\\.(.+)$"));
|
||||||
if (rx.indexIn(filepath) != -1) {
|
const QRegularExpressionMatch rxMatch = rx.match(filepath);
|
||||||
// old v3.3.x format
|
if (rxMatch.hasMatch()) {
|
||||||
queuePosition = rx.cap(2).toInt();
|
// Old v3.3.x format had a number at the end indicating the queue position.
|
||||||
|
// The naming scheme was '<infohash>.fastresume.<queueposition>'.
|
||||||
|
// However, QSaveFile, which uses QTemporaryFile internally, might leave
|
||||||
|
// non-commited files behind eg after a crash. These files have the
|
||||||
|
// naming scheme '<infohash>.fastresume.XXXXXX' where each X is a random
|
||||||
|
// character. So we detect if the last part is present. Then check if it
|
||||||
|
// is 6 chars long. If all the 6 chars are digits we assume it is an old
|
||||||
|
// v3.3.x format. Otherwise it is considered a non-commited fastresume
|
||||||
|
// and is deleted, because it may be a corrupted/incomplete fastresume.
|
||||||
|
// NOTE: When the upgrade code is removed, we must continue to perform
|
||||||
|
// cleanup of non-commited QSaveFile/QTemporaryFile fastresumes
|
||||||
|
queuePosition = rxMatch.captured(2).toInt();
|
||||||
|
if ((rxMatch.captured(2).size() == 6) && (queuePosition <= 99999)) {
|
||||||
|
Utils::Fs::forceRemove(filepath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
v3_3 = true;
|
v3_3 = true;
|
||||||
outFilePath.replace(QRegExp("\\.\\d+$"), "");
|
outFilePath.replace(QRegularExpression("\\.fastresume\\..+$"), ".fastresume");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
|
queuePosition = fastOld.dict_find_int_value("qBt-queuePosition", 0);
|
||||||
@@ -129,6 +146,15 @@ bool upgradeResumeFile(const QString &filepath, const QVariantHash &oldTorrent =
|
|||||||
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
|
// in versions < 3.3 we have -1 for seeding torrents, so we convert it to 0
|
||||||
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
fastNew["qBt-queuePosition"] = (queuePosition >= 0 ? queuePosition : 0);
|
||||||
|
|
||||||
|
if (v3_3) {
|
||||||
|
QFileInfo oldFile(filepath);
|
||||||
|
QFileInfo newFile(outFilePath);
|
||||||
|
if (newFile.exists()
|
||||||
|
&& (oldFile.lastModified() < newFile.lastModified())) {
|
||||||
|
Utils::Fs::forceRemove(filepath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
QFile file2(outFilePath);
|
QFile file2(outFilePath);
|
||||||
QVector<char> out;
|
QVector<char> out;
|
||||||
libtorrent::bencode(std::back_inserter(out), fastNew);
|
libtorrent::bencode(std::back_inserter(out), fastNew);
|
||||||
@@ -173,22 +199,25 @@ bool upgrade(bool ask = true)
|
|||||||
|
|
||||||
QStringList backupFiles = backupFolderDir.entryList(
|
QStringList backupFiles = backupFolderDir.entryList(
|
||||||
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
QStringList(QLatin1String("*.fastresume")), QDir::Files, QDir::Unsorted);
|
||||||
QRegExp rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
const QRegularExpression rx(QLatin1String("^([A-Fa-f0-9]{40})\\.fastresume$"));
|
||||||
foreach (QString backupFile, backupFiles) {
|
foreach (QString backupFile, backupFiles) {
|
||||||
if (rx.indexIn(backupFile) != -1) {
|
const QRegularExpressionMatch rxMatch = rx.match(backupFile);
|
||||||
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[rx.cap(1)].toHash()))
|
if (rxMatch.hasMatch()) {
|
||||||
oldResumeData.remove(rx.cap(1));
|
const QString hashStr = rxMatch.captured(1);
|
||||||
|
if (upgradeResumeFile(backupFolderDir.absoluteFilePath(backupFile), oldResumeData[hashStr].toHash()))
|
||||||
|
oldResumeData.remove(hashStr);
|
||||||
else
|
else
|
||||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(rx.cap(1)), Log::WARNING);
|
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent with hash: %1").arg(hashStr), Log::WARNING);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
|
Logger::instance()->addMessage(QObject::tr("Couldn't migrate torrent. Invalid fastresume file name: %1").arg(backupFile), Log::WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (const QString &hash, oldResumeData.keys()) {
|
for (auto i = oldResumeData.cbegin(); i != oldResumeData.cend(); ++i) {
|
||||||
QVariantHash oldTorrent = oldResumeData[hash].toHash();
|
const QVariantHash oldTorrent = i.value().toHash();
|
||||||
if (oldTorrent.value("is_magnet", false).toBool()) {
|
if (oldTorrent.value("is_magnet", false).toBool()) {
|
||||||
|
const QString &hash = i.key();
|
||||||
libtorrent::entry resumeData;
|
libtorrent::entry resumeData;
|
||||||
resumeData["qBt-magnetUri"] = oldTorrent.value("magnet_uri").toString().toStdString();
|
resumeData["qBt-magnetUri"] = oldTorrent.value("magnet_uri").toString().toStdString();
|
||||||
resumeData["qBt-paused"] = false;
|
resumeData["qBt-paused"] = false;
|
||||||
@@ -270,6 +299,6 @@ void migrateRSS()
|
|||||||
qBTRSSLegacy->remove("old_items");
|
qBTRSSLegacy->remove("old_items");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#endif // UPGRADE_H
|
#endif // UPGRADE_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
find_package(ZLIB 1.2.5.2 REQUIRED)
|
find_package(ZLIB 1.2.5.2 REQUIRED)
|
||||||
|
|
||||||
set(QBT_BASE_HEADERS
|
add_library(qbt_base STATIC
|
||||||
|
# headers
|
||||||
bittorrent/addtorrentparams.h
|
bittorrent/addtorrentparams.h
|
||||||
bittorrent/cachestatus.h
|
bittorrent/cachestatus.h
|
||||||
bittorrent/infohash.h
|
bittorrent/infohash.h
|
||||||
@@ -19,6 +20,7 @@ bittorrent/torrentinfo.h
|
|||||||
bittorrent/tracker.h
|
bittorrent/tracker.h
|
||||||
bittorrent/trackerentry.h
|
bittorrent/trackerentry.h
|
||||||
http/connection.h
|
http/connection.h
|
||||||
|
http/httperror.h
|
||||||
http/irequesthandler.h
|
http/irequesthandler.h
|
||||||
http/requestparser.h
|
http/requestparser.h
|
||||||
http/responsebuilder.h
|
http/responsebuilder.h
|
||||||
@@ -43,6 +45,11 @@ rss/rss_feed.h
|
|||||||
rss/rss_folder.h
|
rss/rss_folder.h
|
||||||
rss/rss_item.h
|
rss/rss_item.h
|
||||||
rss/rss_session.h
|
rss/rss_session.h
|
||||||
|
search/searchdownloadhandler.h
|
||||||
|
search/searchhandler.h
|
||||||
|
search/searchpluginmanager.h
|
||||||
|
utils/bytearray.h
|
||||||
|
utils/foreignapps.h
|
||||||
utils/fs.h
|
utils/fs.h
|
||||||
utils/gzip.h
|
utils/gzip.h
|
||||||
utils/misc.h
|
utils/misc.h
|
||||||
@@ -50,7 +57,9 @@ utils/net.h
|
|||||||
utils/random.h
|
utils/random.h
|
||||||
utils/string.h
|
utils/string.h
|
||||||
utils/version.h
|
utils/version.h
|
||||||
|
algorithm.h
|
||||||
asyncfilestorage.h
|
asyncfilestorage.h
|
||||||
|
exceptions.h
|
||||||
filesystemwatcher.h
|
filesystemwatcher.h
|
||||||
global.h
|
global.h
|
||||||
iconprovider.h
|
iconprovider.h
|
||||||
@@ -59,16 +68,14 @@ logger.h
|
|||||||
preferences.h
|
preferences.h
|
||||||
profile.h
|
profile.h
|
||||||
scanfoldersmodel.h
|
scanfoldersmodel.h
|
||||||
searchengine.h
|
|
||||||
settingsstorage.h
|
settingsstorage.h
|
||||||
torrentfileguard.h
|
torrentfileguard.h
|
||||||
torrentfilter.h
|
torrentfilter.h
|
||||||
tristatebool.h
|
tristatebool.h
|
||||||
types.h
|
types.h
|
||||||
unicodestrings.h
|
unicodestrings.h
|
||||||
)
|
|
||||||
|
|
||||||
set(QBT_BASE_SOURCES
|
# sources
|
||||||
bittorrent/infohash.cpp
|
bittorrent/infohash.cpp
|
||||||
bittorrent/magneturi.cpp
|
bittorrent/magneturi.cpp
|
||||||
bittorrent/peerinfo.cpp
|
bittorrent/peerinfo.cpp
|
||||||
@@ -84,6 +91,7 @@ bittorrent/torrentinfo.cpp
|
|||||||
bittorrent/tracker.cpp
|
bittorrent/tracker.cpp
|
||||||
bittorrent/trackerentry.cpp
|
bittorrent/trackerentry.cpp
|
||||||
http/connection.cpp
|
http/connection.cpp
|
||||||
|
http/httperror.cpp
|
||||||
http/requestparser.cpp
|
http/requestparser.cpp
|
||||||
http/responsebuilder.cpp
|
http/responsebuilder.cpp
|
||||||
http/responsegenerator.cpp
|
http/responsegenerator.cpp
|
||||||
@@ -106,6 +114,11 @@ rss/rss_feed.cpp
|
|||||||
rss/rss_folder.cpp
|
rss/rss_folder.cpp
|
||||||
rss/rss_item.cpp
|
rss/rss_item.cpp
|
||||||
rss/rss_session.cpp
|
rss/rss_session.cpp
|
||||||
|
search/searchdownloadhandler.cpp
|
||||||
|
search/searchhandler.cpp
|
||||||
|
search/searchpluginmanager.cpp
|
||||||
|
utils/bytearray.cpp
|
||||||
|
utils/foreignapps.cpp
|
||||||
utils/fs.cpp
|
utils/fs.cpp
|
||||||
utils/gzip.cpp
|
utils/gzip.cpp
|
||||||
utils/misc.cpp
|
utils/misc.cpp
|
||||||
@@ -113,29 +126,33 @@ utils/net.cpp
|
|||||||
utils/random.cpp
|
utils/random.cpp
|
||||||
utils/string.cpp
|
utils/string.cpp
|
||||||
asyncfilestorage.cpp
|
asyncfilestorage.cpp
|
||||||
|
exceptions.cpp
|
||||||
filesystemwatcher.cpp
|
filesystemwatcher.cpp
|
||||||
iconprovider.cpp
|
iconprovider.cpp
|
||||||
logger.cpp
|
logger.cpp
|
||||||
preferences.cpp
|
preferences.cpp
|
||||||
profile.cpp
|
profile.cpp
|
||||||
scanfoldersmodel.cpp
|
scanfoldersmodel.cpp
|
||||||
searchengine.cpp
|
|
||||||
settingsstorage.cpp
|
settingsstorage.cpp
|
||||||
torrentfileguard.cpp
|
torrentfileguard.cpp
|
||||||
torrentfilter.cpp
|
torrentfilter.cpp
|
||||||
tristatebool.cpp
|
tristatebool.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(qbt_base STATIC ${QBT_BASE_HEADERS} ${QBT_BASE_SOURCES})
|
target_link_libraries(qbt_base
|
||||||
target_link_libraries(qbt_base PRIVATE ZLIB::ZLIB PUBLIC LibtorrentRasterbar::LibTorrent)
|
PRIVATE
|
||||||
target_link_qt_components(qbt_base PUBLIC Core Network Xml)
|
ZLIB::ZLIB
|
||||||
|
PUBLIC
|
||||||
|
LibtorrentRasterbar::torrent-rasterbar
|
||||||
|
Qt5::Core Qt5::Network Qt5::Xml
|
||||||
|
)
|
||||||
|
|
||||||
if (GUI)
|
if (Qt5Widgets_FOUND)
|
||||||
target_link_libraries(qbt_base PUBLIC Qt5::Gui Qt5::Widgets)
|
target_link_libraries(qbt_base PUBLIC Qt5::Gui Qt5::Widgets)
|
||||||
endif (GUI)
|
endif (Qt5Widgets_FOUND)
|
||||||
|
|
||||||
if (DBUS)
|
if (Qt5DBus_FOUND)
|
||||||
target_link_qt_components(qbt_base PRIVATE DBus)
|
target_link_libraries(qbt_base PRIVATE Qt5::DBus)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
|||||||
41
src/base/algorithm.h
Normal file
41
src/base/algorithm.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Dict
|
||||||
|
{
|
||||||
|
// To be used with QMap, QHash and it's variants
|
||||||
|
template <typename Dictionary, typename BinaryPredicate>
|
||||||
|
void removeIf(Dictionary &&dict, BinaryPredicate p)
|
||||||
|
{
|
||||||
|
auto it = dict.begin();
|
||||||
|
while (it != dict.end())
|
||||||
|
it = (p(it.key(), it.value()) ? dict.erase(it) : it + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,14 +34,14 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class AsyncFileStorageError: public std::runtime_error
|
class AsyncFileStorageError : public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AsyncFileStorageError(const QString &message);
|
explicit AsyncFileStorageError(const QString &message);
|
||||||
QString message() const;
|
QString message() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncFileStorage: public QObject
|
class AsyncFileStorage : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
$$PWD/algorithm.h \
|
||||||
$$PWD/asyncfilestorage.h \
|
$$PWD/asyncfilestorage.h \
|
||||||
$$PWD/bittorrent/addtorrentparams.h \
|
$$PWD/bittorrent/addtorrentparams.h \
|
||||||
$$PWD/bittorrent/cachestatus.h \
|
$$PWD/bittorrent/cachestatus.h \
|
||||||
@@ -17,9 +18,11 @@ HEADERS += \
|
|||||||
$$PWD/bittorrent/torrentinfo.h \
|
$$PWD/bittorrent/torrentinfo.h \
|
||||||
$$PWD/bittorrent/tracker.h \
|
$$PWD/bittorrent/tracker.h \
|
||||||
$$PWD/bittorrent/trackerentry.h \
|
$$PWD/bittorrent/trackerentry.h \
|
||||||
|
$$PWD/exceptions.h \
|
||||||
$$PWD/filesystemwatcher.h \
|
$$PWD/filesystemwatcher.h \
|
||||||
$$PWD/global.h \
|
$$PWD/global.h \
|
||||||
$$PWD/http/connection.h \
|
$$PWD/http/connection.h \
|
||||||
|
$$PWD/http/httperror.h \
|
||||||
$$PWD/http/irequesthandler.h \
|
$$PWD/http/irequesthandler.h \
|
||||||
$$PWD/http/requestparser.h \
|
$$PWD/http/requestparser.h \
|
||||||
$$PWD/http/responsebuilder.h \
|
$$PWD/http/responsebuilder.h \
|
||||||
@@ -50,7 +53,9 @@ HEADERS += \
|
|||||||
$$PWD/rss/rss_item.h \
|
$$PWD/rss/rss_item.h \
|
||||||
$$PWD/rss/rss_session.h \
|
$$PWD/rss/rss_session.h \
|
||||||
$$PWD/scanfoldersmodel.h \
|
$$PWD/scanfoldersmodel.h \
|
||||||
$$PWD/searchengine.h \
|
$$PWD/search/searchhandler.h \
|
||||||
|
$$PWD/search/searchdownloadhandler.h \
|
||||||
|
$$PWD/search/searchpluginmanager.h \
|
||||||
$$PWD/settingsstorage.h \
|
$$PWD/settingsstorage.h \
|
||||||
$$PWD/settingvalue.h \
|
$$PWD/settingvalue.h \
|
||||||
$$PWD/torrentfileguard.h \
|
$$PWD/torrentfileguard.h \
|
||||||
@@ -58,6 +63,8 @@ HEADERS += \
|
|||||||
$$PWD/tristatebool.h \
|
$$PWD/tristatebool.h \
|
||||||
$$PWD/types.h \
|
$$PWD/types.h \
|
||||||
$$PWD/unicodestrings.h \
|
$$PWD/unicodestrings.h \
|
||||||
|
$$PWD/utils/bytearray.h \
|
||||||
|
$$PWD/utils/foreignapps.h \
|
||||||
$$PWD/utils/fs.h \
|
$$PWD/utils/fs.h \
|
||||||
$$PWD/utils/gzip.h \
|
$$PWD/utils/gzip.h \
|
||||||
$$PWD/utils/misc.h \
|
$$PWD/utils/misc.h \
|
||||||
@@ -82,8 +89,10 @@ SOURCES += \
|
|||||||
$$PWD/bittorrent/torrentinfo.cpp \
|
$$PWD/bittorrent/torrentinfo.cpp \
|
||||||
$$PWD/bittorrent/tracker.cpp \
|
$$PWD/bittorrent/tracker.cpp \
|
||||||
$$PWD/bittorrent/trackerentry.cpp \
|
$$PWD/bittorrent/trackerentry.cpp \
|
||||||
|
$$PWD/exceptions.cpp \
|
||||||
$$PWD/filesystemwatcher.cpp \
|
$$PWD/filesystemwatcher.cpp \
|
||||||
$$PWD/http/connection.cpp \
|
$$PWD/http/connection.cpp \
|
||||||
|
$$PWD/http/httperror.cpp \
|
||||||
$$PWD/http/requestparser.cpp \
|
$$PWD/http/requestparser.cpp \
|
||||||
$$PWD/http/responsebuilder.cpp \
|
$$PWD/http/responsebuilder.cpp \
|
||||||
$$PWD/http/responsegenerator.cpp \
|
$$PWD/http/responsegenerator.cpp \
|
||||||
@@ -111,11 +120,15 @@ SOURCES += \
|
|||||||
$$PWD/rss/rss_item.cpp \
|
$$PWD/rss/rss_item.cpp \
|
||||||
$$PWD/rss/rss_session.cpp \
|
$$PWD/rss/rss_session.cpp \
|
||||||
$$PWD/scanfoldersmodel.cpp \
|
$$PWD/scanfoldersmodel.cpp \
|
||||||
$$PWD/searchengine.cpp \
|
$$PWD/search/searchdownloadhandler.cpp \
|
||||||
|
$$PWD/search/searchhandler.cpp \
|
||||||
|
$$PWD/search/searchpluginmanager.cpp \
|
||||||
$$PWD/settingsstorage.cpp \
|
$$PWD/settingsstorage.cpp \
|
||||||
$$PWD/torrentfileguard.cpp \
|
$$PWD/torrentfileguard.cpp \
|
||||||
$$PWD/torrentfilter.cpp \
|
$$PWD/torrentfilter.cpp \
|
||||||
$$PWD/tristatebool.cpp \
|
$$PWD/tristatebool.cpp \
|
||||||
|
$$PWD/utils/bytearray.cpp \
|
||||||
|
$$PWD/utils/foreignapps.cpp \
|
||||||
$$PWD/utils/fs.cpp \
|
$$PWD/utils/fs.cpp \
|
||||||
$$PWD/utils/gzip.cpp \
|
$$PWD/utils/gzip.cpp \
|
||||||
$$PWD/utils/misc.cpp \
|
$$PWD/utils/misc.cpp \
|
||||||
|
|||||||
@@ -26,9 +26,10 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
InfoHash::InfoHash()
|
InfoHash::InfoHash()
|
||||||
@@ -91,7 +92,7 @@ bool InfoHash::operator!=(const InfoHash &other) const
|
|||||||
return (m_nativeHash != other.m_nativeHash);
|
return (m_nativeHash != other.m_nativeHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint qHash(const InfoHash &key, uint seed)
|
uint BitTorrent::qHash(const InfoHash &key, uint seed)
|
||||||
{
|
{
|
||||||
return qHash(static_cast<QString>(key), seed);
|
return qHash(static_cast<QString>(key), seed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
#ifndef BITTORRENT_INFOHASH_H
|
#ifndef BITTORRENT_INFOHASH_H
|
||||||
#define BITTORRENT_INFOHASH_H
|
#define BITTORRENT_INFOHASH_H
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <libtorrent/sha1_hash.hpp>
|
#include <libtorrent/sha1_hash.hpp>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
@@ -54,8 +54,8 @@ namespace BitTorrent
|
|||||||
libtorrent::sha1_hash m_nativeHash;
|
libtorrent::sha1_hash m_nativeHash;
|
||||||
QString m_hashString;
|
QString m_hashString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint qHash(const InfoHash &key, uint seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint qHash(const BitTorrent::InfoHash &key, uint seed);
|
|
||||||
|
|
||||||
#endif // BITTORRENT_INFOHASH_H
|
#endif // BITTORRENT_INFOHASH_H
|
||||||
|
|||||||
@@ -26,16 +26,17 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QByteArray>
|
#include "magneturi.h"
|
||||||
#include <QRegExp>
|
|
||||||
#include <QStringList>
|
|
||||||
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
#include <libtorrent/magnet_uri.hpp>
|
#include <libtorrent/magnet_uri.hpp>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "magneturi.h"
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -45,7 +46,7 @@ namespace
|
|||||||
rawBc = rawBc.mid(8); // skip bc://bt/
|
rawBc = rawBc.mid(8); // skip bc://bt/
|
||||||
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
|
rawBc = QByteArray::fromBase64(rawBc); // Decode base64
|
||||||
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
// Format is now AA/url_encoded_filename/size_bytes/info_hash/ZZ
|
||||||
QStringList parts = QString(rawBc).split("/");
|
QStringList parts = QString(rawBc).split('/');
|
||||||
if (parts.size() != 5) return QString();
|
if (parts.size() != 5) return QString();
|
||||||
|
|
||||||
QString filename = parts.at(1);
|
QString filename = parts.at(1);
|
||||||
@@ -69,8 +70,8 @@ MagnetUri::MagnetUri(const QString &source)
|
|||||||
qDebug("Creating magnet link from bc link");
|
qDebug("Creating magnet link from bc link");
|
||||||
m_url = bcLinkToMagnet(source);
|
m_url = bcLinkToMagnet(source);
|
||||||
}
|
}
|
||||||
else if (((source.size() == 40) && !source.contains(QRegExp("[^0-9A-Fa-f]")))
|
else if (((source.size() == 40) && !source.contains(QRegularExpression("[^0-9A-Fa-f]")))
|
||||||
|| ((source.size() == 32) && !source.contains(QRegExp("[^2-7A-Za-z]")))) {
|
|| ((source.size() == 32) && !source.contains(QRegularExpression("[^2-7A-Za-z]")))) {
|
||||||
m_url = "magnet:?xt=urn:btih:" + source;
|
m_url = "magnet:?xt=urn:btih:" + source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -246,8 +246,8 @@ QString PeerInfo::connectionType() const
|
|||||||
|
|
||||||
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
|
void PeerInfo::calcRelevance(const TorrentHandle *torrent)
|
||||||
{
|
{
|
||||||
const QBitArray &allPieces = torrent->pieces();
|
const QBitArray allPieces = torrent->pieces();
|
||||||
const QBitArray &peerPieces = pieces();
|
const QBitArray peerPieces = pieces();
|
||||||
|
|
||||||
int localMissing = 0;
|
int localMissing = 0;
|
||||||
int remoteHaves = 0;
|
int remoteHaves = 0;
|
||||||
@@ -377,7 +377,7 @@ void PeerInfo::determineFlags()
|
|||||||
|
|
||||||
// L = Peer is local
|
// L = Peer is local
|
||||||
if (fromLSD()) {
|
if (fromLSD()) {
|
||||||
m_flags += "L";
|
m_flags += 'L';
|
||||||
flagsDescriptionList += "L = "
|
flagsDescriptionList += "L = "
|
||||||
+ tr("peer from LSD");
|
+ tr("peer from LSD");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ void BandwidthScheduler::start()
|
|||||||
|
|
||||||
bool BandwidthScheduler::isTimeForAlternative() const
|
bool BandwidthScheduler::isTimeForAlternative() const
|
||||||
{
|
{
|
||||||
const Preferences* const pref = Preferences::instance();
|
const Preferences *const pref = Preferences::instance();
|
||||||
|
|
||||||
QTime start = pref->getSchedulerStartTime();
|
QTime start = pref->getSchedulerStartTime();
|
||||||
QTime end = pref->getSchedulerEndTime();
|
QTime end = pref->getSchedulerEndTime();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
class BandwidthScheduler: public QObject
|
class BandwidthScheduler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(BandwidthScheduler)
|
Q_DISABLE_COPY(BandwidthScheduler)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libt.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -147,13 +147,13 @@ int FilterParserThread::parseDATFilterFile()
|
|||||||
if (bytesRead < 0)
|
if (bytesRead < 0)
|
||||||
break;
|
break;
|
||||||
int dataSize = bytesRead + offset;
|
int dataSize = bytesRead + offset;
|
||||||
if (bytesRead == 0 && dataSize == 0)
|
if ((bytesRead == 0) && (dataSize == 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (start = 0; start < dataSize; ++start) {
|
for (start = 0; start < dataSize; ++start) {
|
||||||
endOfLine = -1;
|
endOfLine = -1;
|
||||||
// The file might have ended without the last line having a newline
|
// The file might have ended without the last line having a newline
|
||||||
if (!(bytesRead == 0 && dataSize > 0)) {
|
if (!((bytesRead == 0) && (dataSize > 0))) {
|
||||||
for (int i = start; i < dataSize; ++i) {
|
for (int i = start; i < dataSize; ++i) {
|
||||||
if (buffer[i] == '\n') {
|
if (buffer[i] == '\n') {
|
||||||
endOfLine = i;
|
endOfLine = i;
|
||||||
@@ -295,13 +295,13 @@ int FilterParserThread::parseP2PFilterFile()
|
|||||||
if (bytesRead < 0)
|
if (bytesRead < 0)
|
||||||
break;
|
break;
|
||||||
int dataSize = bytesRead + offset;
|
int dataSize = bytesRead + offset;
|
||||||
if (bytesRead == 0 && dataSize == 0)
|
if ((bytesRead == 0) && (dataSize == 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (start = 0; start < dataSize; ++start) {
|
for (start = 0; start < dataSize; ++start) {
|
||||||
endOfLine = -1;
|
endOfLine = -1;
|
||||||
// The file might have ended without the last line having a newline
|
// The file might have ended without the last line having a newline
|
||||||
if (!(bytesRead == 0 && dataSize > 0)) {
|
if (!((bytesRead == 0) && (dataSize > 0))) {
|
||||||
for (int i = start; i < dataSize; ++i) {
|
for (int i = start; i < dataSize; ++i) {
|
||||||
if (buffer[i] == '\n') {
|
if (buffer[i] == '\n') {
|
||||||
endOfLine = i;
|
endOfLine = i;
|
||||||
@@ -407,24 +407,24 @@ int FilterParserThread::parseP2PFilterFile()
|
|||||||
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
int FilterParserThread::getlineInStream(QDataStream &stream, std::string &name, char delim)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
int total_read = 0;
|
int totalRead = 0;
|
||||||
int read;
|
int read;
|
||||||
do {
|
do {
|
||||||
read = stream.readRawData(&c, 1);
|
read = stream.readRawData(&c, 1);
|
||||||
total_read += read;
|
totalRead += read;
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
if (c != delim) {
|
if (c != delim) {
|
||||||
name += c;
|
name += c;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Delim found
|
// Delim found
|
||||||
return total_read;
|
return totalRead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while(read > 0);
|
while (read > 0);
|
||||||
|
|
||||||
return total_read;
|
return totalRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parser for PeerGuardian ip filter in p2p format
|
// Parser for PeerGuardian ip filter in p2p format
|
||||||
@@ -455,7 +455,7 @@ int FilterParserThread::parseP2BFilterFile()
|
|||||||
unsigned int start, end;
|
unsigned int start, end;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
while(getlineInStream(stream, name, '\0') && !m_abort) {
|
while (getlineInStream(stream, name, '\0') && !m_abort) {
|
||||||
if (!stream.readRawData(reinterpret_cast<char*>(&start), sizeof(start))
|
if (!stream.readRawData(reinterpret_cast<char*>(&start), sizeof(start))
|
||||||
|| !stream.readRawData(reinterpret_cast<char*>(&end), sizeof(end))) {
|
|| !stream.readRawData(reinterpret_cast<char*>(&end), sizeof(end))) {
|
||||||
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
LogMsg(tr("Parsing Error: The filter file is not a valid PeerGuardian P2B file."), Log::CRITICAL);
|
||||||
@@ -610,7 +610,7 @@ int FilterParserThread::findAndNullDelimiter(char *const data, char delimiter, i
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FilterParserThread::trim(char* const data, int start, int end)
|
int FilterParserThread::trim(char *const data, int start, int end)
|
||||||
{
|
{
|
||||||
if (start >= end) return start;
|
if (start >= end) return start;
|
||||||
int newStart = start;
|
int newStart = start;
|
||||||
|
|||||||
@@ -26,12 +26,13 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "resumedatasavingmanager.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QSaveFile>
|
#include <QSaveFile>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "resumedatasavingmanager.h"
|
|
||||||
|
|
||||||
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath)
|
||||||
: m_resumeDataDir(resumeFolderPath)
|
: m_resumeDataDir(resumeFolderPath)
|
||||||
@@ -49,7 +50,7 @@ void ResumeDataSavingManager::saveResumeData(QString infoHash, QByteArray data)
|
|||||||
resumeFile.write(data);
|
resumeFile.write(data);
|
||||||
if (!resumeFile.commit()) {
|
if (!resumeFile.commit()) {
|
||||||
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
|
Logger::instance()->addMessage(QString("Couldn't save resume data in %1. Error: %2")
|
||||||
.arg(filepath).arg(resumeFile.errorString()), Log::WARNING);
|
.arg(filepath, resumeFile.errorString()), Log::WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Statistics::Statistics(Session *session)
|
|||||||
, m_dirty(false)
|
, m_dirty(false)
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gather()));
|
connect(&m_timer, &QTimer::timeout, this, &Statistics::gather);
|
||||||
m_timer.start(60 * 1000);
|
m_timer.start(60 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace BitTorrent
|
|||||||
class Session;
|
class Session;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Statistics : QObject
|
class Statistics : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(Statistics)
|
Q_DISABLE_COPY(Statistics)
|
||||||
@@ -28,7 +28,6 @@ private:
|
|||||||
void save() const;
|
void save() const;
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
private:
|
|
||||||
BitTorrent::Session *m_session;
|
BitTorrent::Session *m_session;
|
||||||
// Will overflow at 15.9 EiB
|
// Will overflow at 15.9 EiB
|
||||||
quint64 m_alltimeUL;
|
quint64 m_alltimeUL;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -30,18 +30,14 @@
|
|||||||
#ifndef BITTORRENT_SESSION_H
|
#ifndef BITTORRENT_SESSION_H
|
||||||
#define BITTORRENT_SESSION_H
|
#define BITTORRENT_SESSION_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM >= 10100
|
#include <vector>
|
||||||
#include <QElapsedTimer>
|
|
||||||
#endif
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
|
||||||
#include <QMutex>
|
|
||||||
#endif
|
|
||||||
#include <QNetworkConfigurationManager>
|
#include <QNetworkConfigurationManager>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -49,6 +45,12 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
#include <QMutex>
|
||||||
|
#else
|
||||||
|
#include <QElapsedTimer>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
#include "base/tristatebool.h"
|
#include "base/tristatebool.h"
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
@@ -111,7 +113,6 @@ class QTimer;
|
|||||||
class QStringList;
|
class QStringList;
|
||||||
class QString;
|
class QString;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
template<typename T> class QList;
|
|
||||||
|
|
||||||
class FilterParserThread;
|
class FilterParserThread;
|
||||||
class BandwidthScheduler;
|
class BandwidthScheduler;
|
||||||
@@ -137,7 +138,7 @@ namespace BitTorrent
|
|||||||
class Tracker;
|
class Tracker;
|
||||||
class MagnetUri;
|
class MagnetUri;
|
||||||
class TrackerEntry;
|
class TrackerEntry;
|
||||||
struct AddTorrentData;
|
struct CreateTorrentParams;
|
||||||
|
|
||||||
struct TorrentStatusReport
|
struct TorrentStatusReport
|
||||||
{
|
{
|
||||||
@@ -374,6 +375,8 @@ namespace BitTorrent
|
|||||||
void setAnnounceToAllTrackers(bool val);
|
void setAnnounceToAllTrackers(bool val);
|
||||||
bool announceToAllTiers() const;
|
bool announceToAllTiers() const;
|
||||||
void setAnnounceToAllTiers(bool val);
|
void setAnnounceToAllTiers(bool val);
|
||||||
|
int asyncIOThreads() const;
|
||||||
|
void setAsyncIOThreads(int num);
|
||||||
int diskCacheSize() const;
|
int diskCacheSize() const;
|
||||||
void setDiskCacheSize(int size);
|
void setDiskCacheSize(int size);
|
||||||
int diskCacheTTL() const;
|
int diskCacheTTL() const;
|
||||||
@@ -382,6 +385,8 @@ namespace BitTorrent
|
|||||||
void setUseOSCache(bool use);
|
void setUseOSCache(bool use);
|
||||||
bool isGuidedReadCacheEnabled() const;
|
bool isGuidedReadCacheEnabled() const;
|
||||||
void setGuidedReadCacheEnabled(bool enabled);
|
void setGuidedReadCacheEnabled(bool enabled);
|
||||||
|
bool isCoalesceReadWriteEnabled() const;
|
||||||
|
void setCoalesceReadWriteEnabled(bool enabled);
|
||||||
bool isSuggestModeEnabled() const;
|
bool isSuggestModeEnabled() const;
|
||||||
void setSuggestMode(bool mode);
|
void setSuggestMode(bool mode);
|
||||||
int sendBufferWatermark() const;
|
int sendBufferWatermark() const;
|
||||||
@@ -396,6 +401,12 @@ namespace BitTorrent
|
|||||||
void setQueueingSystemEnabled(bool enabled);
|
void setQueueingSystemEnabled(bool enabled);
|
||||||
bool ignoreSlowTorrentsForQueueing() const;
|
bool ignoreSlowTorrentsForQueueing() const;
|
||||||
void setIgnoreSlowTorrentsForQueueing(bool ignore);
|
void setIgnoreSlowTorrentsForQueueing(bool ignore);
|
||||||
|
int downloadRateForSlowTorrents() const;
|
||||||
|
void setDownloadRateForSlowTorrents(int rateInKibiBytes);
|
||||||
|
int uploadRateForSlowTorrents() const;
|
||||||
|
void setUploadRateForSlowTorrents(int rateInKibiBytes);
|
||||||
|
int slowTorrentsInactivityTimer() const;
|
||||||
|
void setSlowTorrentsInactivityTimer(int timeInSeconds);
|
||||||
int outgoingPortsMin() const;
|
int outgoingPortsMin() const;
|
||||||
void setOutgoingPortsMin(int min);
|
void setOutgoingPortsMin(int min);
|
||||||
int outgoingPortsMax() const;
|
int outgoingPortsMax() const;
|
||||||
@@ -443,6 +454,7 @@ namespace BitTorrent
|
|||||||
TorrentStatusReport torrentStatusReport() const;
|
TorrentStatusReport torrentStatusReport() const;
|
||||||
bool hasActiveTorrents() const;
|
bool hasActiveTorrents() const;
|
||||||
bool hasUnfinishedTorrents() const;
|
bool hasUnfinishedTorrents() const;
|
||||||
|
bool hasRunningSeed() const;
|
||||||
const SessionStatus &status() const;
|
const SessionStatus &status() const;
|
||||||
const CacheStatus &cacheStatus() const;
|
const CacheStatus &cacheStatus() const;
|
||||||
quint64 getAlltimeDL() const;
|
quint64 getAlltimeDL() const;
|
||||||
@@ -469,6 +481,8 @@ namespace BitTorrent
|
|||||||
|
|
||||||
// TorrentHandle interface
|
// TorrentHandle interface
|
||||||
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
|
void handleTorrentShareLimitChanged(TorrentHandle *const torrent);
|
||||||
|
void handleTorrentsPrioritiesChanged();
|
||||||
|
void handleTorrentNameChanged(TorrentHandle *const torrent);
|
||||||
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
void handleTorrentSavePathChanged(TorrentHandle *const torrent);
|
||||||
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
void handleTorrentCategoryChanged(TorrentHandle *const torrent, const QString &oldCategory);
|
||||||
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
|
void handleTorrentTagAdded(TorrentHandle *const torrent, const QString &tag);
|
||||||
@@ -538,7 +552,7 @@ namespace BitTorrent
|
|||||||
void generateResumeData(bool final = false);
|
void generateResumeData(bool final = false);
|
||||||
void handleIPFilterParsed(int ruleCount);
|
void handleIPFilterParsed(int ruleCount);
|
||||||
void handleIPFilterError();
|
void handleIPFilterError();
|
||||||
void handleDownloadFinished(const QString &url, const QString &filePath);
|
void handleDownloadFinished(const QString &url, const QByteArray &data);
|
||||||
void handleDownloadFailed(const QString &url, const QString &reason);
|
void handleDownloadFailed(const QString &url, const QString &reason);
|
||||||
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
void handleRedirectedToMagnet(const QString &url, const QString &magnetUri);
|
||||||
|
|
||||||
@@ -554,7 +568,7 @@ namespace BitTorrent
|
|||||||
bool requestedFileDeletion;
|
bool requestedFileDeletion;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Session(QObject *parent = 0);
|
explicit Session(QObject *parent = nullptr);
|
||||||
~Session();
|
~Session();
|
||||||
|
|
||||||
bool hasPerTorrentRatioLimit() const;
|
bool hasPerTorrentRatioLimit() const;
|
||||||
@@ -586,14 +600,14 @@ namespace BitTorrent
|
|||||||
void enableIPFilter();
|
void enableIPFilter();
|
||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
|
|
||||||
bool addTorrent_impl(AddTorrentData addData, const MagnetUri &magnetUri,
|
bool addTorrent_impl(CreateTorrentParams params, const MagnetUri &magnetUri,
|
||||||
TorrentInfo torrentInfo = TorrentInfo(),
|
TorrentInfo torrentInfo = TorrentInfo(),
|
||||||
const QByteArray &fastresumeData = QByteArray());
|
const QByteArray &fastresumeData = QByteArray());
|
||||||
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
|
bool findIncompleteFiles(TorrentInfo &torrentInfo, QString &savePath) const;
|
||||||
|
|
||||||
void updateSeedingLimitTimer();
|
void updateSeedingLimitTimer();
|
||||||
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
void exportTorrentFile(TorrentHandle *const torrent, TorrentExportFolder folder = TorrentExportFolder::Regular);
|
||||||
void saveTorrentResumeData(TorrentHandle *const torrent, bool finalSave = false);
|
void saveTorrentResumeData(TorrentHandle *const torrent);
|
||||||
|
|
||||||
void handleAlert(libtorrent::alert *a);
|
void handleAlert(libtorrent::alert *a);
|
||||||
void dispatchTorrentAlert(libtorrent::alert *a);
|
void dispatchTorrentAlert(libtorrent::alert *a);
|
||||||
@@ -642,10 +656,12 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<QString> m_IPFilterFile;
|
CachedSettingValue<QString> m_IPFilterFile;
|
||||||
CachedSettingValue<bool> m_announceToAllTrackers;
|
CachedSettingValue<bool> m_announceToAllTrackers;
|
||||||
CachedSettingValue<bool> m_announceToAllTiers;
|
CachedSettingValue<bool> m_announceToAllTiers;
|
||||||
|
CachedSettingValue<int> m_asyncIOThreads;
|
||||||
CachedSettingValue<int> m_diskCacheSize;
|
CachedSettingValue<int> m_diskCacheSize;
|
||||||
CachedSettingValue<int> m_diskCacheTTL;
|
CachedSettingValue<int> m_diskCacheTTL;
|
||||||
CachedSettingValue<bool> m_useOSCache;
|
CachedSettingValue<bool> m_useOSCache;
|
||||||
CachedSettingValue<bool> m_guidedReadCacheEnabled;
|
CachedSettingValue<bool> m_guidedReadCacheEnabled;
|
||||||
|
CachedSettingValue<bool> m_coalesceReadWriteEnabled;
|
||||||
CachedSettingValue<bool> m_isSuggestMode;
|
CachedSettingValue<bool> m_isSuggestMode;
|
||||||
CachedSettingValue<int> m_sendBufferWatermark;
|
CachedSettingValue<int> m_sendBufferWatermark;
|
||||||
CachedSettingValue<int> m_sendBufferLowWatermark;
|
CachedSettingValue<int> m_sendBufferLowWatermark;
|
||||||
@@ -656,6 +672,9 @@ namespace BitTorrent
|
|||||||
CachedSettingValue<int> m_maxActiveUploads;
|
CachedSettingValue<int> m_maxActiveUploads;
|
||||||
CachedSettingValue<int> m_maxActiveTorrents;
|
CachedSettingValue<int> m_maxActiveTorrents;
|
||||||
CachedSettingValue<bool> m_ignoreSlowTorrentsForQueueing;
|
CachedSettingValue<bool> m_ignoreSlowTorrentsForQueueing;
|
||||||
|
CachedSettingValue<int> m_downloadRateForSlowTorrents;
|
||||||
|
CachedSettingValue<int> m_uploadRateForSlowTorrents;
|
||||||
|
CachedSettingValue<int> m_slowTorrentsInactivityTimer;
|
||||||
CachedSettingValue<int> m_outgoingPortsMin;
|
CachedSettingValue<int> m_outgoingPortsMin;
|
||||||
CachedSettingValue<int> m_outgoingPortsMax;
|
CachedSettingValue<int> m_outgoingPortsMax;
|
||||||
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
|
CachedSettingValue<bool> m_ignoreLimitsOnLAN;
|
||||||
@@ -741,13 +760,17 @@ namespace BitTorrent
|
|||||||
|
|
||||||
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
QHash<InfoHash, TorrentInfo> m_loadedMetadata;
|
||||||
QHash<InfoHash, TorrentHandle *> m_torrents;
|
QHash<InfoHash, TorrentHandle *> m_torrents;
|
||||||
QHash<InfoHash, AddTorrentData> m_addingTorrents;
|
QHash<InfoHash, CreateTorrentParams> m_addingTorrents;
|
||||||
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
QHash<QString, AddTorrentParams> m_downloadedTorrents;
|
||||||
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
|
QHash<InfoHash, RemovingTorrentData> m_removingTorrents;
|
||||||
TorrentStatusReport m_torrentStatusReport;
|
TorrentStatusReport m_torrentStatusReport;
|
||||||
QStringMap m_categories;
|
QStringMap m_categories;
|
||||||
QSet<QString> m_tags;
|
QSet<QString> m_tags;
|
||||||
|
|
||||||
|
// I/O errored torrents
|
||||||
|
QSet<InfoHash> m_recentErroredTorrents;
|
||||||
|
QTimer *m_recentErroredTorrentsTimer;
|
||||||
|
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QMutex m_alertsMutex;
|
QMutex m_alertsMutex;
|
||||||
QWaitCondition m_alertsWaitCondition;
|
QWaitCondition m_alertsWaitCondition;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "torrentcreatorthread.h"
|
#include "torrentcreatorthread.h"
|
||||||
@@ -37,9 +35,14 @@
|
|||||||
#include <libtorrent/create_torrent.hpp>
|
#include <libtorrent/create_torrent.hpp>
|
||||||
#include <libtorrent/storage.hpp>
|
#include <libtorrent/storage.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QHash>
|
||||||
|
|
||||||
|
#include "base/global.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
@@ -86,13 +89,56 @@ void TorrentCreatorThread::run()
|
|||||||
emit updateProgress(0);
|
emit updateProgress(0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
libt::file_storage fs;
|
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + '/';
|
||||||
|
|
||||||
// Adding files to the torrent
|
// Adding files to the torrent
|
||||||
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
|
libt::file_storage fs;
|
||||||
|
if (QFileInfo(m_params.inputPath).isFile()) {
|
||||||
|
libt::add_files(fs, Utils::Fs::toNativePath(m_params.inputPath).toStdString(), fileFilter);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// need to sort the file names by natural sort order
|
||||||
|
QStringList dirs = {m_params.inputPath};
|
||||||
|
|
||||||
|
QDirIterator dirIter(m_params.inputPath, (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories);
|
||||||
|
while (dirIter.hasNext()) {
|
||||||
|
dirIter.next();
|
||||||
|
dirs += dirIter.filePath();
|
||||||
|
}
|
||||||
|
std::sort(dirs.begin(), dirs.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||||
|
|
||||||
|
QStringList fileNames;
|
||||||
|
QHash<QString, boost::int64_t> fileSizeMap;
|
||||||
|
|
||||||
|
for (const auto &dir : qAsConst(dirs)) {
|
||||||
|
QStringList tmpNames; // natural sort files within each dir
|
||||||
|
|
||||||
|
QDirIterator fileIter(dir, QDir::Files);
|
||||||
|
while (fileIter.hasNext()) {
|
||||||
|
fileIter.next();
|
||||||
|
|
||||||
|
const QString relFilePath = fileIter.filePath().mid(parentPath.length());
|
||||||
|
tmpNames += relFilePath;
|
||||||
|
fileSizeMap[relFilePath] = fileIter.fileInfo().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(tmpNames.begin(), tmpNames.end(), Utils::String::naturalLessThan<Qt::CaseInsensitive>);
|
||||||
|
fileNames += tmpNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &fileName : qAsConst(fileNames))
|
||||||
|
fs.add_file(fileName.toStdString(), fileSizeMap[fileName]);
|
||||||
|
}
|
||||||
|
|
||||||
if (isInterruptionRequested()) return;
|
if (isInterruptionRequested()) return;
|
||||||
|
|
||||||
libt::create_torrent newTorrent(fs, m_params.pieceSize);
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
|
||||||
|
, (m_params.isAlignmentOptimized ? libt::create_torrent::optimize : 0));
|
||||||
|
#else
|
||||||
|
libt::create_torrent newTorrent(fs, m_params.pieceSize, -1
|
||||||
|
, (m_params.isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0));
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add url seeds
|
// Add url seeds
|
||||||
foreach (QString seed, m_params.urlSeeds) {
|
foreach (QString seed, m_params.urlSeeds) {
|
||||||
@@ -112,7 +158,6 @@ void TorrentCreatorThread::run()
|
|||||||
if (isInterruptionRequested()) return;
|
if (isInterruptionRequested()) return;
|
||||||
|
|
||||||
// calculate the hash for all pieces
|
// calculate the hash for all pieces
|
||||||
const QString parentPath = Utils::Fs::branchPath(m_params.inputPath) + "/";
|
|
||||||
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
libt::set_piece_hashes(newTorrent, Utils::Fs::toNativePath(parentPath).toStdString()
|
||||||
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
|
, [this, &newTorrent](const int n) { sendProgressSignal(n, newTorrent.num_pieces()); });
|
||||||
// Set qBittorrent as creator and add user comment to
|
// Set qBittorrent as creator and add user comment to
|
||||||
@@ -156,12 +201,19 @@ void TorrentCreatorThread::run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize)
|
int TorrentCreatorThread::calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized)
|
||||||
{
|
{
|
||||||
if (inputPath.isEmpty())
|
if (inputPath.isEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
libt::file_storage fs;
|
libt::file_storage fs;
|
||||||
libt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
|
libt::add_files(fs, Utils::Fs::toNativePath(inputPath).toStdString(), fileFilter);
|
||||||
return libt::create_torrent(fs, pieceSize).num_pieces();
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
return libt::create_torrent(fs, pieceSize, -1
|
||||||
|
, (isAlignmentOptimized ? libt::create_torrent::optimize : 0)).num_pieces();
|
||||||
|
#else
|
||||||
|
return libt::create_torrent(fs, pieceSize, -1
|
||||||
|
, (isAlignmentOptimized ? libt::create_torrent::optimize_alignment : 0)).num_pieces();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2010 Christophe Dumez
|
* Copyright (C) 2010 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
#ifndef BITTORRENT_TORRENTCREATORTHREAD_H
|
||||||
@@ -39,6 +37,7 @@ namespace BitTorrent
|
|||||||
struct TorrentCreatorParams
|
struct TorrentCreatorParams
|
||||||
{
|
{
|
||||||
bool isPrivate;
|
bool isPrivate;
|
||||||
|
bool isAlignmentOptimized;
|
||||||
int pieceSize;
|
int pieceSize;
|
||||||
QString inputPath;
|
QString inputPath;
|
||||||
QString savePath;
|
QString savePath;
|
||||||
@@ -58,7 +57,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
void create(const TorrentCreatorParams ¶ms);
|
void create(const TorrentCreatorParams ¶ms);
|
||||||
|
|
||||||
static int calculateTotalPieces(const QString &inputPath, const int pieceSize);
|
static int calculateTotalPieces(const QString &inputPath, const int pieceSize, const bool isAlignmentOptimized);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run();
|
void run();
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
|
|
||||||
const QString QB_EXT {".!qB"};
|
const QString QB_EXT {QStringLiteral(".!qB")};
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
@@ -85,16 +85,16 @@ namespace
|
|||||||
|
|
||||||
// AddTorrentData
|
// AddTorrentData
|
||||||
|
|
||||||
AddTorrentData::AddTorrentData()
|
CreateTorrentParams::CreateTorrentParams()
|
||||||
: resumed(false)
|
: restored(false)
|
||||||
, disableTempPath(false)
|
, disableTempPath(false)
|
||||||
, sequential(false)
|
, sequential(false)
|
||||||
, firstLastPiecePriority(false)
|
, firstLastPiecePriority(false)
|
||||||
, hasSeedStatus(false)
|
, hasSeedStatus(false)
|
||||||
, skipChecking(false)
|
, skipChecking(false)
|
||||||
, hasRootFolder(true)
|
, hasRootFolder(true)
|
||||||
, addForced(false)
|
, forced(false)
|
||||||
, addPaused(false)
|
, paused(false)
|
||||||
, uploadLimit(-1)
|
, uploadLimit(-1)
|
||||||
, downloadLimit(-1)
|
, downloadLimit(-1)
|
||||||
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
||||||
@@ -102,8 +102,8 @@ AddTorrentData::AddTorrentData()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
CreateTorrentParams::CreateTorrentParams(const AddTorrentParams ¶ms)
|
||||||
: resumed(false)
|
: restored(false)
|
||||||
, name(params.name)
|
, name(params.name)
|
||||||
, category(params.category)
|
, category(params.category)
|
||||||
, tags(params.tags)
|
, tags(params.tags)
|
||||||
@@ -116,8 +116,8 @@ AddTorrentData::AddTorrentData(const AddTorrentParams ¶ms)
|
|||||||
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
|
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
|
||||||
? Session::instance()->isCreateTorrentSubfolder()
|
? Session::instance()->isCreateTorrentSubfolder()
|
||||||
: params.createSubfolder == TriStateBool::True)
|
: params.createSubfolder == TriStateBool::True)
|
||||||
, addForced(params.addForced == TriStateBool::True)
|
, forced(params.addForced == TriStateBool::True)
|
||||||
, addPaused(params.addPaused == TriStateBool::Undefined
|
, paused(params.addPaused == TriStateBool::Undefined
|
||||||
? Session::instance()->isAddTorrentPaused()
|
? Session::instance()->isAddTorrentPaused()
|
||||||
: params.addPaused == TriStateBool::True)
|
: params.addPaused == TriStateBool::True)
|
||||||
, uploadLimit(params.uploadLimit)
|
, uploadLimit(params.uploadLimit)
|
||||||
@@ -156,8 +156,6 @@ const int TorrentHandle::MAX_SEEDING_TIME = 525600;
|
|||||||
// The following can be removed after one or two libtorrent releases on each branch.
|
// The following can be removed after one or two libtorrent releases on each branch.
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const char i18nContext[] = "TorrentHandle";
|
|
||||||
|
|
||||||
// new constructor is available
|
// new constructor is available
|
||||||
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
|
template<typename T, typename std::enable_if<std::is_constructible<T, libt::torrent_info, bool>::value, int>::type = 0>
|
||||||
T makeTorrentCreator(const libtorrent::torrent_info & ti)
|
T makeTorrentCreator(const libtorrent::torrent_info & ti)
|
||||||
@@ -174,26 +172,25 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||||
const AddTorrentData &data)
|
const CreateTorrentParams ¶ms)
|
||||||
: QObject(session)
|
: QObject(session)
|
||||||
, m_session(session)
|
, m_session(session)
|
||||||
, m_nativeHandle(nativeHandle)
|
, m_nativeHandle(nativeHandle)
|
||||||
, m_state(TorrentState::Unknown)
|
, m_state(TorrentState::Unknown)
|
||||||
, m_renameCount(0)
|
, m_renameCount(0)
|
||||||
, m_useAutoTMM(data.savePath.isEmpty())
|
, m_useAutoTMM(params.savePath.isEmpty())
|
||||||
, m_name(data.name)
|
, m_name(params.name)
|
||||||
, m_savePath(Utils::Fs::toNativePath(data.savePath))
|
, m_savePath(Utils::Fs::toNativePath(params.savePath))
|
||||||
, m_category(data.category)
|
, m_category(params.category)
|
||||||
, m_tags(data.tags)
|
, m_tags(params.tags)
|
||||||
, m_hasSeedStatus(data.hasSeedStatus)
|
, m_hasSeedStatus(params.hasSeedStatus)
|
||||||
, m_ratioLimit(data.ratioLimit)
|
, m_ratioLimit(params.ratioLimit)
|
||||||
, m_seedingTimeLimit(data.seedingTimeLimit)
|
, m_seedingTimeLimit(params.seedingTimeLimit)
|
||||||
, m_tempPathDisabled(data.disableTempPath)
|
, m_tempPathDisabled(params.disableTempPath)
|
||||||
, m_hasMissingFiles(false)
|
, m_hasMissingFiles(false)
|
||||||
, m_hasRootFolder(data.hasRootFolder)
|
, m_hasRootFolder(params.hasRootFolder)
|
||||||
, m_needsToSetFirstLastPiecePriority(false)
|
, m_needsToSetFirstLastPiecePriority(false)
|
||||||
, m_pauseAfterRecheck(false)
|
, m_pauseAfterRecheck(false)
|
||||||
, m_needSaveResumeData(false)
|
|
||||||
{
|
{
|
||||||
if (m_useAutoTMM)
|
if (m_useAutoTMM)
|
||||||
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
||||||
@@ -209,15 +206,29 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
|
|||||||
// download sequentially or have first/last piece priority enabled when
|
// download sequentially or have first/last piece priority enabled when
|
||||||
// its resume data was saved. These two settings are restored later. But
|
// its resume data was saved. These two settings are restored later. But
|
||||||
// if we set them to false now, both will erroneously not be restored.
|
// if we set them to false now, both will erroneously not be restored.
|
||||||
if (!data.resumed || data.sequential)
|
if (!params.restored || params.sequential)
|
||||||
setSequentialDownload(data.sequential);
|
setSequentialDownload(params.sequential);
|
||||||
if (!data.resumed || data.firstLastPiecePriority)
|
if (!params.restored || params.firstLastPiecePriority)
|
||||||
setFirstLastPiecePriority(data.firstLastPiecePriority);
|
setFirstLastPiecePriority(params.firstLastPiecePriority);
|
||||||
|
|
||||||
if (!data.resumed && hasMetadata()) {
|
if (!params.restored && hasMetadata()) {
|
||||||
if (filesCount() == 1)
|
if (filesCount() == 1)
|
||||||
m_hasRootFolder = false;
|
m_hasRootFolder = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "started" means "all initialization has completed and torrent has started regular processing".
|
||||||
|
// When torrent added/restored in "paused" state it become "started" immediately after construction.
|
||||||
|
// When it is added/restored in "resumed" state, it become "started" after it is really resumed
|
||||||
|
// (i.e. after receiving "torrent resumed" alert).
|
||||||
|
m_started = (params.restored && hasMetadata() ? isPaused() : params.paused);
|
||||||
|
|
||||||
|
if (!m_started) {
|
||||||
|
if (!params.restored || !hasMetadata()) {
|
||||||
|
// Resume torrent because it was added in "resumed" state
|
||||||
|
// but it's actually paused during initialization
|
||||||
|
resume(params.forced);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentHandle::~TorrentHandle() {}
|
TorrentHandle::~TorrentHandle() {}
|
||||||
@@ -317,7 +328,7 @@ QString TorrentHandle::rootPath(bool actual) const
|
|||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
QString firstFilePath = filePath(0);
|
QString firstFilePath = filePath(0);
|
||||||
const int slashIndex = firstFilePath.indexOf("/");
|
const int slashIndex = firstFilePath.indexOf('/');
|
||||||
if (slashIndex >= 0)
|
if (slashIndex >= 0)
|
||||||
return QDir(savePath(actual)).absoluteFilePath(firstFilePath.left(slashIndex));
|
return QDir(savePath(actual)).absoluteFilePath(firstFilePath.left(slashIndex));
|
||||||
else
|
else
|
||||||
@@ -490,18 +501,12 @@ bool TorrentHandle::connectPeer(const PeerAddress &peerAddress)
|
|||||||
|
|
||||||
bool TorrentHandle::needSaveResumeData() const
|
bool TorrentHandle::needSaveResumeData() const
|
||||||
{
|
{
|
||||||
if (m_needSaveResumeData) return true;
|
|
||||||
|
|
||||||
return m_nativeHandle.need_save_resume_data();
|
return m_nativeHandle.need_save_resume_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::saveResumeData(bool updateStatus)
|
void TorrentHandle::saveResumeData()
|
||||||
{
|
{
|
||||||
if (updateStatus) // to update queue_position, see discussion in PR #6154
|
|
||||||
this->updateStatus();
|
|
||||||
|
|
||||||
m_nativeHandle.save_resume_data();
|
m_nativeHandle.save_resume_data();
|
||||||
m_needSaveResumeData = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int TorrentHandle::filesCount() const
|
int TorrentHandle::filesCount() const
|
||||||
@@ -521,15 +526,19 @@ int TorrentHandle::piecesHave() const
|
|||||||
|
|
||||||
qreal TorrentHandle::progress() const
|
qreal TorrentHandle::progress() const
|
||||||
{
|
{
|
||||||
if (!m_nativeStatus.total_wanted)
|
if (!isChecking()) {
|
||||||
return 0.;
|
if (!m_nativeStatus.total_wanted)
|
||||||
|
return 0.;
|
||||||
|
|
||||||
if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
|
if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
|
||||||
return 1.;
|
return 1.;
|
||||||
|
|
||||||
float progress = static_cast<float>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
||||||
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
||||||
return progress;
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_nativeStatus.progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TorrentHandle::category() const
|
QString TorrentHandle::category() const
|
||||||
@@ -544,7 +553,7 @@ bool TorrentHandle::belongsToCategory(const QString &category) const
|
|||||||
|
|
||||||
if (m_category == category) return true;
|
if (m_category == category) return true;
|
||||||
|
|
||||||
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + "/"))
|
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + '/'))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -571,7 +580,6 @@ bool TorrentHandle::addTag(const QString &tag)
|
|||||||
return false;
|
return false;
|
||||||
m_tags.insert(tag);
|
m_tags.insert(tag);
|
||||||
m_session->handleTorrentTagAdded(this, tag);
|
m_session->handleTorrentTagAdded(this, tag);
|
||||||
m_needSaveResumeData = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -581,7 +589,6 @@ bool TorrentHandle::removeTag(const QString &tag)
|
|||||||
{
|
{
|
||||||
if (m_tags.remove(tag)) {
|
if (m_tags.remove(tag)) {
|
||||||
m_session->handleTorrentTagRemoved(this, tag);
|
m_session->handleTorrentTagRemoved(this, tag);
|
||||||
m_needSaveResumeData = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -736,7 +743,8 @@ bool TorrentHandle::isActive() const
|
|||||||
|| m_state == TorrentState::Downloading
|
|| m_state == TorrentState::Downloading
|
||||||
|| m_state == TorrentState::ForcedDownloading
|
|| m_state == TorrentState::ForcedDownloading
|
||||||
|| m_state == TorrentState::Uploading
|
|| m_state == TorrentState::Uploading
|
||||||
|| m_state == TorrentState::ForcedUploading;
|
|| m_state == TorrentState::ForcedUploading
|
||||||
|
|| m_state == TorrentState::Moving;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentHandle::isInactive() const
|
bool TorrentHandle::isInactive() const
|
||||||
@@ -778,28 +786,18 @@ bool TorrentHandle::hasFirstLastPiecePriority() const
|
|||||||
if (!hasMetadata())
|
if (!hasMetadata())
|
||||||
return m_needsToSetFirstLastPiecePriority;
|
return m_needsToSetFirstLastPiecePriority;
|
||||||
|
|
||||||
// Get int first media file
|
const std::vector<int> filePriorities = nativeHandle().file_priorities();
|
||||||
std::vector<int> fp;
|
for (int i = 0; i < static_cast<int>(filePriorities.size()); ++i) {
|
||||||
fp = m_nativeHandle.file_priorities();
|
if (filePriorities[i] <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
TorrentInfo::PieceRange extremities;
|
const TorrentInfo::PieceRange extremities = info().filePieces(i);
|
||||||
bool found = false;
|
const int firstPiecePrio = nativeHandle().piece_priority(extremities.first());
|
||||||
int count = static_cast<int>(fp.size());
|
const int lastPiecePrio = nativeHandle().piece_priority(extremities.last());
|
||||||
for (int i = 0; i < count; ++i) {
|
return ((firstPiecePrio == 7) && (lastPiecePrio == 7));
|
||||||
const QString ext = Utils::Fs::fileExtension(filePath(i));
|
|
||||||
if (Utils::Misc::isPreviewable(ext) && (fp[i] > 0)) {
|
|
||||||
extremities = info().filePieces(i);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) return false; // No media file
|
return false;
|
||||||
|
|
||||||
int first = m_nativeHandle.piece_priority(extremities.first());
|
|
||||||
int last = m_nativeHandle.piece_priority(extremities.last());
|
|
||||||
|
|
||||||
return ((first == 7) && (last == 7));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentState TorrentHandle::state() const
|
TorrentState TorrentHandle::state() const
|
||||||
@@ -809,7 +807,10 @@ TorrentState TorrentHandle::state() const
|
|||||||
|
|
||||||
void TorrentHandle::updateState()
|
void TorrentHandle::updateState()
|
||||||
{
|
{
|
||||||
if (isPaused()) {
|
if (isMoveInProgress()) {
|
||||||
|
m_state = TorrentState::Moving;
|
||||||
|
}
|
||||||
|
else if (isPaused()) {
|
||||||
if (hasMissingFiles())
|
if (hasMissingFiles())
|
||||||
m_state = TorrentState::MissingFiles;
|
m_state = TorrentState::MissingFiles;
|
||||||
else if (hasError())
|
else if (hasError())
|
||||||
@@ -1202,23 +1203,18 @@ void TorrentHandle::setName(const QString &name)
|
|||||||
{
|
{
|
||||||
if (m_name != name) {
|
if (m_name != name) {
|
||||||
m_name = name;
|
m_name = name;
|
||||||
m_needSaveResumeData = true;
|
m_session->handleTorrentNameChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentHandle::setCategory(const QString &category)
|
bool TorrentHandle::setCategory(const QString &category)
|
||||||
{
|
{
|
||||||
if (m_category != category) {
|
if (m_category != category) {
|
||||||
if (!category.isEmpty()) {
|
if (!category.isEmpty() && !m_session->categories().contains(category))
|
||||||
if (!Session::isValidCategoryName(category)) return false;
|
return false;
|
||||||
if (!m_session->categories().contains(category))
|
|
||||||
if (!m_session->addCategory(category))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString oldCategory = m_category;
|
QString oldCategory = m_category;
|
||||||
m_category = category;
|
m_category = category;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
||||||
|
|
||||||
if (m_useAutoTMM) {
|
if (m_useAutoTMM) {
|
||||||
@@ -1256,7 +1252,6 @@ void TorrentHandle::move_impl(QString path, bool overwrite)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_savePath = path;
|
m_savePath = path;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentSavePathChanged(this);
|
m_session->handleTorrentSavePathChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1277,7 +1272,7 @@ void TorrentHandle::forceRecheck()
|
|||||||
|
|
||||||
if (isPaused()) {
|
if (isPaused()) {
|
||||||
m_pauseAfterRecheck = true;
|
m_pauseAfterRecheck = true;
|
||||||
resume();
|
resume_impl(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nativeHandle.force_recheck();
|
m_nativeHandle.force_recheck();
|
||||||
@@ -1296,39 +1291,45 @@ void TorrentHandle::toggleSequentialDownload()
|
|||||||
setSequentialDownload(!isSequentialDownload());
|
setSequentialDownload(!isSequentialDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::setFirstLastPiecePriority(bool b)
|
void TorrentHandle::setFirstLastPiecePriority(const bool enabled)
|
||||||
{
|
{
|
||||||
|
setFirstLastPiecePriorityImpl(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentHandle::setFirstLastPiecePriorityImpl(const bool enabled, const QVector<int> &updatedFilePrio)
|
||||||
|
{
|
||||||
|
// Download first and last pieces first for every file in the torrent
|
||||||
|
|
||||||
if (!hasMetadata()) {
|
if (!hasMetadata()) {
|
||||||
m_needsToSetFirstLastPiecePriority = b;
|
m_needsToSetFirstLastPiecePriority = enabled;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> fp = m_nativeHandle.file_priorities();
|
// Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it
|
||||||
std::vector<int> pp = m_nativeHandle.piece_priorities();
|
// we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
|
||||||
|
const std::vector<int> filePriorities = !updatedFilePrio.isEmpty() ? updatedFilePrio.toStdVector() : nativeHandle().file_priorities();
|
||||||
|
std::vector<int> piecePriorities = nativeHandle().piece_priorities();
|
||||||
|
for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index) {
|
||||||
|
const int filePrio = filePriorities[index];
|
||||||
|
if (filePrio <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Download first and last pieces first for all media files in the torrent
|
// Determine the priority to set
|
||||||
int nbfiles = static_cast<int>(fp.size());
|
const int newPrio = enabled ? 7 : filePrio;
|
||||||
for (int index = 0; index < nbfiles; ++index) {
|
const TorrentInfo::PieceRange extremities = info().filePieces(index);
|
||||||
const QString path = filePath(index);
|
|
||||||
const QString ext = Utils::Fs::fileExtension(path);
|
|
||||||
if (Utils::Misc::isPreviewable(ext) && (fp[index] > 0)) {
|
|
||||||
qDebug() << "File" << path << "is previewable, toggle downloading of first/last pieces first";
|
|
||||||
|
|
||||||
// Determine the priority to set
|
// worst case: AVI index = 1% of total file size (at the end of the file)
|
||||||
int prio = b ? 7 : fp[index];
|
const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
|
||||||
|
for (int i = 0; i < nNumPieces; ++i) {
|
||||||
TorrentInfo::PieceRange extremities = info().filePieces(index);
|
piecePriorities[extremities.first() + i] = newPrio;
|
||||||
|
piecePriorities[extremities.last() - i] = newPrio;
|
||||||
// worst case: AVI index = 1% of total file size (at the end of the file)
|
|
||||||
int nNumPieces = ceil(fileSize(index) * 0.01 / pieceLength());
|
|
||||||
for (int i = 0; i < nNumPieces; ++i) {
|
|
||||||
pp[extremities.first() + i] = prio;
|
|
||||||
pp[extremities.last() - i] = prio;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nativeHandle.prioritize_pieces(pp);
|
m_nativeHandle.prioritize_pieces(piecePriorities);
|
||||||
|
|
||||||
|
LogMsg(tr("Download first and last piece first: %1, torrent: '%2'")
|
||||||
|
.arg((enabled ? tr("On") : tr("Off")), name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::toggleFirstLastPiecePriority()
|
void TorrentHandle::toggleFirstLastPiecePriority()
|
||||||
@@ -1342,15 +1343,26 @@ void TorrentHandle::pause()
|
|||||||
|
|
||||||
m_nativeHandle.auto_managed(false);
|
m_nativeHandle.auto_managed(false);
|
||||||
m_nativeHandle.pause();
|
m_nativeHandle.pause();
|
||||||
|
|
||||||
|
// Libtorrent doesn't emit a torrent_paused_alert when the
|
||||||
|
// torrent is queued (no I/O)
|
||||||
|
// We test on the cached m_nativeStatus
|
||||||
|
if (isQueued())
|
||||||
|
m_session->handleTorrentPaused(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::resume(bool forced)
|
void TorrentHandle::resume(bool forced)
|
||||||
|
{
|
||||||
|
resume_impl(forced, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentHandle::resume_impl(bool forced, bool uploadMode)
|
||||||
{
|
{
|
||||||
if (hasError())
|
if (hasError())
|
||||||
m_nativeHandle.clear_error();
|
m_nativeHandle.clear_error();
|
||||||
m_hasMissingFiles = false;
|
m_hasMissingFiles = false;
|
||||||
m_nativeHandle.set_upload_mode(false);
|
|
||||||
m_nativeHandle.auto_managed(!forced);
|
m_nativeHandle.auto_managed(!forced);
|
||||||
|
m_nativeHandle.set_upload_mode(uploadMode);
|
||||||
m_nativeHandle.resume();
|
m_nativeHandle.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1371,6 +1383,7 @@ void TorrentHandle::moveStorage(const QString &newPath, bool overwrite)
|
|||||||
, (overwrite ? libt::always_replace_files : libt::dont_replace));
|
, (overwrite ? libt::always_replace_files : libt::dont_replace));
|
||||||
m_moveStorageInfo.oldPath = oldPath;
|
m_moveStorageInfo.oldPath = oldPath;
|
||||||
m_moveStorageInfo.newPath = newPath;
|
m_moveStorageInfo.newPath = newPath;
|
||||||
|
updateState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1420,7 +1433,7 @@ void TorrentHandle::handleStateUpdate(const libt::torrent_status &nativeStatus)
|
|||||||
updateStatus(nativeStatus);
|
updateStatus(nativeStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
void TorrentHandle::handleStorageMovedAlert(const libtorrent::storage_moved_alert *p)
|
||||||
{
|
{
|
||||||
if (!isMoveInProgress()) {
|
if (!isMoveInProgress()) {
|
||||||
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
||||||
@@ -1437,8 +1450,8 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug("Torrent is successfully moved from %s to %s"
|
LogMsg(tr("Successfully moved torrent: %1. New path: %2").arg(name(), m_moveStorageInfo.newPath));
|
||||||
, qUtf8Printable(m_moveStorageInfo.oldPath), qUtf8Printable(m_moveStorageInfo.newPath));
|
|
||||||
const QDir oldDir {m_moveStorageInfo.oldPath};
|
const QDir oldDir {m_moveStorageInfo.oldPath};
|
||||||
if ((oldDir == QDir(m_session->torrentTempPath(info())))
|
if ((oldDir == QDir(m_session->torrentTempPath(info())))
|
||||||
&& (oldDir != QDir(m_session->tempPath()))) {
|
&& (oldDir != QDir(m_session->tempPath()))) {
|
||||||
@@ -1447,9 +1460,10 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
qDebug() << "Removing torrent temp folder:" << m_moveStorageInfo.oldPath;
|
qDebug() << "Removing torrent temp folder:" << m_moveStorageInfo.oldPath;
|
||||||
Utils::Fs::smartRemoveEmptyFolderTree(m_moveStorageInfo.oldPath);
|
Utils::Fs::smartRemoveEmptyFolderTree(m_moveStorageInfo.oldPath);
|
||||||
}
|
}
|
||||||
updateStatus();
|
|
||||||
|
|
||||||
m_moveStorageInfo.newPath.clear();
|
m_moveStorageInfo.newPath.clear();
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
||||||
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
||||||
m_moveStorageInfo.queuedPath.clear();
|
m_moveStorageInfo.queuedPath.clear();
|
||||||
@@ -1464,17 +1478,19 @@ void TorrentHandle::handleStorageMovedAlert(libtorrent::storage_moved_alert *p)
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p)
|
void TorrentHandle::handleStorageMovedFailedAlert(const libtorrent::storage_moved_failed_alert *p)
|
||||||
{
|
{
|
||||||
if (!isMoveInProgress()) {
|
if (!isMoveInProgress()) {
|
||||||
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogMsg(QCoreApplication::translate(i18nContext, "Could not move torrent: '%1'. Reason: %2")
|
LogMsg(tr("Could not move torrent: '%1'. Reason: %2")
|
||||||
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
|
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
||||||
|
|
||||||
m_moveStorageInfo.newPath.clear();
|
m_moveStorageInfo.newPath.clear();
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
||||||
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
||||||
m_moveStorageInfo.queuedPath.clear();
|
m_moveStorageInfo.queuedPath.clear();
|
||||||
@@ -1484,7 +1500,7 @@ void TorrentHandle::handleStorageMovedFailedAlert(libtorrent::storage_moved_fail
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p)
|
void TorrentHandle::handleTrackerReplyAlert(const libtorrent::tracker_reply_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
QString trackerUrl = QString::fromStdString(p->url);
|
||||||
@@ -1499,32 +1515,32 @@ void TorrentHandle::handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p)
|
|||||||
m_session->handleTorrentTrackerReply(this, trackerUrl);
|
m_session->handleTorrentTrackerReply(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p)
|
void TorrentHandle::handleTrackerWarningAlert(const libtorrent::tracker_warning_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
const QString trackerUrl = QString::fromStdString(p->url);
|
||||||
QString message = QString::fromStdString(p->msg);
|
const QString message = QString::fromStdString(p->msg);
|
||||||
#else
|
#else
|
||||||
QString trackerUrl(p->tracker_url());
|
const QString trackerUrl = p->tracker_url();
|
||||||
QString message = QString::fromStdString(p->message());
|
const QString message = p->warning_message();
|
||||||
#endif
|
#endif
|
||||||
qDebug("Received a tracker warning for %s: %s", qUtf8Printable(trackerUrl), qUtf8Printable(message));
|
|
||||||
// Connection was successful now but there is a warning message
|
// Connection was successful now but there is a warning message
|
||||||
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
|
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
|
||||||
|
|
||||||
m_session->handleTorrentTrackerWarning(this, trackerUrl);
|
m_session->handleTorrentTrackerWarning(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p)
|
void TorrentHandle::handleTrackerErrorAlert(const libtorrent::tracker_error_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString trackerUrl = QString::fromStdString(p->url);
|
const QString trackerUrl = QString::fromStdString(p->url);
|
||||||
QString message = QString::fromStdString(p->msg);
|
const QString message = QString::fromStdString(p->msg);
|
||||||
#else
|
#else
|
||||||
QString trackerUrl(p->tracker_url());
|
const QString trackerUrl = p->tracker_url();
|
||||||
QString message = QString::fromStdString(p->message());
|
const QString message = p->error_message();
|
||||||
#endif
|
#endif
|
||||||
qDebug("Received a tracker error for %s: %s", qUtf8Printable(trackerUrl), qUtf8Printable(message));
|
|
||||||
m_trackerInfos[trackerUrl].lastMessage = message;
|
m_trackerInfos[trackerUrl].lastMessage = message;
|
||||||
|
|
||||||
if (p->status_code == 401)
|
if (p->status_code == 401)
|
||||||
@@ -1533,7 +1549,7 @@ void TorrentHandle::handleTrackerErrorAlert(libtorrent::tracker_error_alert *p)
|
|||||||
m_session->handleTorrentTrackerError(this, trackerUrl);
|
m_session->handleTorrentTrackerError(this, trackerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p)
|
void TorrentHandle::handleTorrentCheckedAlert(const libtorrent::torrent_checked_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("%s have just finished checking", qUtf8Printable(hash()));
|
qDebug("%s have just finished checking", qUtf8Printable(hash()));
|
||||||
@@ -1556,7 +1572,7 @@ void TorrentHandle::handleTorrentCheckedAlert(libtorrent::torrent_checked_alert
|
|||||||
m_session->handleTorrentChecked(this);
|
m_session->handleTorrentChecked(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p)
|
void TorrentHandle::handleTorrentFinishedAlert(const libtorrent::torrent_finished_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("Got a torrent finished alert for %s", qUtf8Printable(name()));
|
qDebug("Got a torrent finished alert for %s", qUtf8Printable(name()));
|
||||||
@@ -1583,7 +1599,7 @@ void TorrentHandle::handleTorrentFinishedAlert(libtorrent::torrent_finished_aler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p)
|
void TorrentHandle::handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
updateStatus();
|
updateStatus();
|
||||||
@@ -1591,13 +1607,17 @@ void TorrentHandle::handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p
|
|||||||
m_session->handleTorrentPaused(this);
|
m_session->handleTorrentPaused(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p)
|
void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
m_session->handleTorrentResumed(this);
|
|
||||||
|
if (m_started)
|
||||||
|
m_session->handleTorrentResumed(this);
|
||||||
|
else
|
||||||
|
m_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p)
|
void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p)
|
||||||
{
|
{
|
||||||
const bool useDummyResumeData = !(p && p->resume_data);
|
const bool useDummyResumeData = !(p && p->resume_data);
|
||||||
libtorrent::entry dummyEntry;
|
libtorrent::entry dummyEntry;
|
||||||
@@ -1625,42 +1645,37 @@ void TorrentHandle::handleSaveResumeDataAlert(libtorrent::save_resume_data_alert
|
|||||||
resumeData["qBt-name"] = m_name.toStdString();
|
resumeData["qBt-name"] = m_name.toStdString();
|
||||||
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
||||||
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
||||||
resumeData["qBt-queuePosition"] = queuePosition();
|
resumeData["qBt-queuePosition"] = (nativeHandle().queue_position() + 1); // qBt starts queue at 1
|
||||||
resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
|
resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
|
||||||
|
|
||||||
m_session->handleTorrentResumeDataReady(this, resumeData);
|
m_session->handleTorrentResumeDataReady(this, resumeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p)
|
void TorrentHandle::handleSaveResumeDataFailedAlert(const libtorrent::save_resume_data_failed_alert *p)
|
||||||
{
|
{
|
||||||
// if torrent has no metadata we should save dummy fastresume data
|
// if torrent has no metadata we should save dummy fastresume data
|
||||||
// containing Magnet URI and qBittorrent own resume data only
|
// containing Magnet URI and qBittorrent own resume data only
|
||||||
if (p->error.value() == libt::errors::no_metadata)
|
if (p->error.value() == libt::errors::no_metadata)
|
||||||
handleSaveResumeDataAlert(0);
|
handleSaveResumeDataAlert(nullptr);
|
||||||
else
|
else
|
||||||
m_session->handleTorrentResumeDataFailed(this);
|
m_session->handleTorrentResumeDataFailed(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p)
|
void TorrentHandle::handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p)
|
||||||
{
|
{
|
||||||
qDebug("/!\\ Fast resume failed for %s, reason: %s", qUtf8Printable(name()), p->message().c_str());
|
|
||||||
Logger *const logger = Logger::instance();
|
|
||||||
|
|
||||||
updateStatus();
|
|
||||||
if (p->error.value() == libt::errors::mismatching_file_size) {
|
if (p->error.value() == libt::errors::mismatching_file_size) {
|
||||||
// Mismatching file size (files were probably moved)
|
// Mismatching file size (files were probably moved)
|
||||||
logger->addMessage(QCoreApplication::translate(i18nContext, "File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
pause();
|
||||||
m_hasMissingFiles = true;
|
m_hasMissingFiles = true;
|
||||||
if (!isPaused())
|
LogMsg(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
||||||
pause();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger->addMessage(QCoreApplication::translate(i18nContext, "Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
||||||
.arg(name()).arg(QString::fromStdString(p->message())), Log::CRITICAL);
|
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
void TorrentHandle::handleFileRenamedAlert(const libtorrent::file_renamed_alert *p)
|
||||||
{
|
{
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
QString newName = Utils::Fs::fromNativePath(QString::fromStdString(p->name));
|
QString newName = Utils::Fs::fromNativePath(QString::fromStdString(p->name));
|
||||||
@@ -1671,15 +1686,15 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
|||||||
// TODO: Check this!
|
// TODO: Check this!
|
||||||
if (filesCount() > 1) {
|
if (filesCount() > 1) {
|
||||||
// Check if folders were renamed
|
// Check if folders were renamed
|
||||||
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split("/");
|
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/');
|
||||||
oldPathParts.removeLast();
|
oldPathParts.removeLast();
|
||||||
QString oldPath = oldPathParts.join("/");
|
QString oldPath = oldPathParts.join('/');
|
||||||
QStringList newPathParts = newName.split("/");
|
QStringList newPathParts = newName.split('/');
|
||||||
newPathParts.removeLast();
|
newPathParts.removeLast();
|
||||||
QString newPath = newPathParts.join("/");
|
QString newPath = newPathParts.join('/');
|
||||||
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
|
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
|
||||||
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
|
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
|
||||||
oldPath = QString("%1/%2").arg(savePath(true)).arg(oldPath);
|
oldPath = QString("%1/%2").arg(savePath(true), oldPath);
|
||||||
qDebug("Detected folder renaming, attempt to delete old folder: %s", qUtf8Printable(oldPath));
|
qDebug("Detected folder renaming, attempt to delete old folder: %s", qUtf8Printable(oldPath));
|
||||||
QDir().rmpath(oldPath);
|
QDir().rmpath(oldPath);
|
||||||
}
|
}
|
||||||
@@ -1692,7 +1707,7 @@ void TorrentHandle::handleFileRenamedAlert(libtorrent::file_renamed_alert *p)
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p)
|
void TorrentHandle::handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
|
|
||||||
@@ -1701,7 +1716,7 @@ void TorrentHandle::handleFileRenameFailedAlert(libtorrent::file_rename_failed_a
|
|||||||
m_moveFinishedTriggers.takeFirst()();
|
m_moveFinishedTriggers.takeFirst()();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p)
|
void TorrentHandle::handleFileCompletedAlert(const libtorrent::file_completed_alert *p)
|
||||||
{
|
{
|
||||||
updateStatus();
|
updateStatus();
|
||||||
|
|
||||||
@@ -1717,7 +1732,7 @@ void TorrentHandle::handleFileCompletedAlert(libtorrent::file_completed_alert *p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleStatsAlert(libtorrent::stats_alert *p)
|
void TorrentHandle::handleStatsAlert(const libtorrent::stats_alert *p)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p->interval >= 1000);
|
Q_ASSERT(p->interval >= 1000);
|
||||||
SpeedSample transferred(p->transferred[libt::stats_alert::download_payload] * 1000LL / p->interval,
|
SpeedSample transferred(p->transferred[libt::stats_alert::download_payload] * 1000LL / p->interval,
|
||||||
@@ -1725,7 +1740,7 @@ void TorrentHandle::handleStatsAlert(libtorrent::stats_alert *p)
|
|||||||
m_speedMonitor.addSample(transferred);
|
m_speedMonitor.addSample(transferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandle::handleMetadataReceivedAlert(libt::metadata_received_alert *p)
|
void TorrentHandle::handleMetadataReceivedAlert(const libt::metadata_received_alert *p)
|
||||||
{
|
{
|
||||||
Q_UNUSED(p);
|
Q_UNUSED(p);
|
||||||
qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
|
qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
|
||||||
@@ -1804,6 +1819,9 @@ void TorrentHandle::handleAlert(libtorrent::alert *a)
|
|||||||
case libt::torrent_paused_alert::alert_type:
|
case libt::torrent_paused_alert::alert_type:
|
||||||
handleTorrentPausedAlert(static_cast<libt::torrent_paused_alert*>(a));
|
handleTorrentPausedAlert(static_cast<libt::torrent_paused_alert*>(a));
|
||||||
break;
|
break;
|
||||||
|
case libt::torrent_resumed_alert::alert_type:
|
||||||
|
handleTorrentResumedAlert(static_cast<libt::torrent_resumed_alert*>(a));
|
||||||
|
break;
|
||||||
case libt::tracker_error_alert::alert_type:
|
case libt::tracker_error_alert::alert_type:
|
||||||
handleTrackerErrorAlert(static_cast<libt::tracker_error_alert*>(a));
|
handleTrackerErrorAlert(static_cast<libt::tracker_error_alert*>(a));
|
||||||
break;
|
break;
|
||||||
@@ -1829,7 +1847,7 @@ void TorrentHandle::manageIncompleteFiles()
|
|||||||
{
|
{
|
||||||
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
||||||
QVector<qreal> fp = filesProgress();
|
QVector<qreal> fp = filesProgress();
|
||||||
if( fp.size() != filesCount() ) {
|
if (fp.size() != filesCount()) {
|
||||||
qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
|
qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1926,7 +1944,6 @@ void TorrentHandle::setRatioLimit(qreal limit)
|
|||||||
|
|
||||||
if (m_ratioLimit != limit) {
|
if (m_ratioLimit != limit) {
|
||||||
m_ratioLimit = limit;
|
m_ratioLimit = limit;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentShareLimitChanged(this);
|
m_session->handleTorrentShareLimitChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1940,7 +1957,6 @@ void TorrentHandle::setSeedingTimeLimit(int limit)
|
|||||||
|
|
||||||
if (m_seedingTimeLimit != limit) {
|
if (m_seedingTimeLimit != limit) {
|
||||||
m_seedingTimeLimit = limit;
|
m_seedingTimeLimit = limit;
|
||||||
m_needSaveResumeData = true;
|
|
||||||
m_session->handleTorrentShareLimitChanged(this);
|
m_session->handleTorrentShareLimitChanged(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1978,7 +1994,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
if (priorities.size() != filesCount()) return;
|
if (priorities.size() != filesCount()) return;
|
||||||
|
|
||||||
// Save first/last piece first option state
|
// Save first/last piece first option state
|
||||||
bool firstLastPieceFirst = hasFirstLastPiecePriority();
|
const bool firstLastPieceFirst = hasFirstLastPiecePriority();
|
||||||
|
|
||||||
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
||||||
// 'torrent_finished_alert' and eg show tray notifications
|
// 'torrent_finished_alert' and eg show tray notifications
|
||||||
@@ -2005,7 +2021,7 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
// Make sure the file does not already exists
|
// Make sure the file does not already exists
|
||||||
if (QDir(parentAbsPath).dirName() != ".unwanted") {
|
if (QDir(parentAbsPath).dirName() != ".unwanted") {
|
||||||
QString unwantedAbsPath = parentAbsPath + "/.unwanted";
|
QString unwantedAbsPath = parentAbsPath + "/.unwanted";
|
||||||
QString newAbsPath = unwantedAbsPath + "/" + Utils::Fs::fileName(filepath);
|
QString newAbsPath = unwantedAbsPath + '/' + Utils::Fs::fileName(filepath);
|
||||||
qDebug() << "Unwanted path is" << unwantedAbsPath;
|
qDebug() << "Unwanted path is" << unwantedAbsPath;
|
||||||
if (QFile::exists(newAbsPath)) {
|
if (QFile::exists(newAbsPath)) {
|
||||||
qWarning() << "File" << newAbsPath << "already exists at destination.";
|
qWarning() << "File" << newAbsPath << "already exists at destination.";
|
||||||
@@ -2025,8 +2041,8 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
QString parentPath = Utils::Fs::branchPath(filepath);
|
QString parentPath = Utils::Fs::branchPath(filepath);
|
||||||
if (!parentPath.isEmpty() && !parentPath.endsWith("/"))
|
if (!parentPath.isEmpty() && !parentPath.endsWith('/'))
|
||||||
parentPath += "/";
|
parentPath += '/';
|
||||||
renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath));
|
renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2043,15 +2059,15 @@ void TorrentHandle::prioritizeFiles(const QVector<int> &priorities)
|
|||||||
renameFile(i, QDir(newRelPath).filePath(oldName));
|
renameFile(i, QDir(newRelPath).filePath(oldName));
|
||||||
|
|
||||||
// Remove .unwanted directory if empty
|
// Remove .unwanted directory if empty
|
||||||
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + "/" + newRelPath).absoluteFilePath(".unwanted");
|
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
||||||
QDir(spath + "/" + newRelPath).rmdir(".unwanted");
|
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore first/last piece first option if necessary
|
// Restore first/last piece first option if necessary
|
||||||
if (firstLastPieceFirst)
|
if (firstLastPieceFirst)
|
||||||
setFirstLastPiecePriority(true);
|
setFirstLastPiecePriorityImpl(true, priorities);
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,10 +88,10 @@ namespace BitTorrent
|
|||||||
class TrackerEntry;
|
class TrackerEntry;
|
||||||
struct AddTorrentParams;
|
struct AddTorrentParams;
|
||||||
|
|
||||||
struct AddTorrentData
|
struct CreateTorrentParams
|
||||||
{
|
{
|
||||||
bool resumed;
|
bool restored; // is existing torrent job?
|
||||||
// for both new and resumed torrents
|
// for both new and restored torrents
|
||||||
QString name;
|
QString name;
|
||||||
QString category;
|
QString category;
|
||||||
QSet<QString> tags;
|
QSet<QString> tags;
|
||||||
@@ -102,18 +102,18 @@ namespace BitTorrent
|
|||||||
bool hasSeedStatus;
|
bool hasSeedStatus;
|
||||||
bool skipChecking;
|
bool skipChecking;
|
||||||
bool hasRootFolder;
|
bool hasRootFolder;
|
||||||
bool addForced;
|
bool forced;
|
||||||
bool addPaused;
|
bool paused;
|
||||||
int uploadLimit;
|
int uploadLimit;
|
||||||
int downloadLimit;
|
int downloadLimit;
|
||||||
// for new torrents
|
// for new torrents
|
||||||
QVector<int> filePriorities;
|
QVector<int> filePriorities;
|
||||||
// for resumed torrents
|
// for restored torrents
|
||||||
qreal ratioLimit;
|
qreal ratioLimit;
|
||||||
int seedingTimeLimit;
|
int seedingTimeLimit;
|
||||||
|
|
||||||
AddTorrentData();
|
CreateTorrentParams();
|
||||||
AddTorrentData(const AddTorrentParams ¶ms);
|
CreateTorrentParams(const AddTorrentParams ¶ms);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrackerInfo
|
struct TrackerInfo
|
||||||
@@ -149,6 +149,8 @@ namespace BitTorrent
|
|||||||
PausedDownloading,
|
PausedDownloading,
|
||||||
PausedUploading,
|
PausedUploading,
|
||||||
|
|
||||||
|
Moving,
|
||||||
|
|
||||||
MissingFiles,
|
MissingFiles,
|
||||||
Error
|
Error
|
||||||
};
|
};
|
||||||
@@ -168,7 +170,7 @@ namespace BitTorrent
|
|||||||
static const int MAX_SEEDING_TIME;
|
static const int MAX_SEEDING_TIME;
|
||||||
|
|
||||||
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
TorrentHandle(Session *session, const libtorrent::torrent_handle &nativeHandle,
|
||||||
const AddTorrentData &data);
|
const CreateTorrentParams ¶ms);
|
||||||
~TorrentHandle();
|
~TorrentHandle();
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
@@ -331,7 +333,7 @@ namespace BitTorrent
|
|||||||
void setName(const QString &name);
|
void setName(const QString &name);
|
||||||
void setSequentialDownload(bool b);
|
void setSequentialDownload(bool b);
|
||||||
void toggleSequentialDownload();
|
void toggleSequentialDownload();
|
||||||
void setFirstLastPiecePriority(bool b);
|
void setFirstLastPiecePriority(bool enabled);
|
||||||
void toggleFirstLastPiecePriority();
|
void toggleFirstLastPiecePriority();
|
||||||
void pause();
|
void pause();
|
||||||
void resume(bool forced = false);
|
void resume(bool forced = false);
|
||||||
@@ -370,7 +372,7 @@ namespace BitTorrent
|
|||||||
void handleTempPathChanged();
|
void handleTempPathChanged();
|
||||||
void handleCategorySavePathChanged();
|
void handleCategorySavePathChanged();
|
||||||
void handleAppendExtensionToggled();
|
void handleAppendExtensionToggled();
|
||||||
void saveResumeData(bool updateStatus = false);
|
void saveResumeData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief fraction of file pieces that are available at least from one peer
|
* @brief fraction of file pieces that are available at least from one peer
|
||||||
@@ -388,24 +390,25 @@ namespace BitTorrent
|
|||||||
void updateState();
|
void updateState();
|
||||||
void updateTorrentInfo();
|
void updateTorrentInfo();
|
||||||
|
|
||||||
void handleStorageMovedAlert(libtorrent::storage_moved_alert *p);
|
void handleStorageMovedAlert(const libtorrent::storage_moved_alert *p);
|
||||||
void handleStorageMovedFailedAlert(libtorrent::storage_moved_failed_alert *p);
|
void handleStorageMovedFailedAlert(const libtorrent::storage_moved_failed_alert *p);
|
||||||
void handleTrackerReplyAlert(libtorrent::tracker_reply_alert *p);
|
void handleTrackerReplyAlert(const libtorrent::tracker_reply_alert *p);
|
||||||
void handleTrackerWarningAlert(libtorrent::tracker_warning_alert *p);
|
void handleTrackerWarningAlert(const libtorrent::tracker_warning_alert *p);
|
||||||
void handleTrackerErrorAlert(libtorrent::tracker_error_alert *p);
|
void handleTrackerErrorAlert(const libtorrent::tracker_error_alert *p);
|
||||||
void handleTorrentCheckedAlert(libtorrent::torrent_checked_alert *p);
|
void handleTorrentCheckedAlert(const libtorrent::torrent_checked_alert *p);
|
||||||
void handleTorrentFinishedAlert(libtorrent::torrent_finished_alert *p);
|
void handleTorrentFinishedAlert(const libtorrent::torrent_finished_alert *p);
|
||||||
void handleTorrentPausedAlert(libtorrent::torrent_paused_alert *p);
|
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
|
||||||
void handleTorrentResumedAlert(libtorrent::torrent_resumed_alert *p);
|
void handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p);
|
||||||
void handleSaveResumeDataAlert(libtorrent::save_resume_data_alert *p);
|
void handleSaveResumeDataAlert(const libtorrent::save_resume_data_alert *p);
|
||||||
void handleSaveResumeDataFailedAlert(libtorrent::save_resume_data_failed_alert *p);
|
void handleSaveResumeDataFailedAlert(const libtorrent::save_resume_data_failed_alert *p);
|
||||||
void handleFastResumeRejectedAlert(libtorrent::fastresume_rejected_alert *p);
|
void handleFastResumeRejectedAlert(const libtorrent::fastresume_rejected_alert *p);
|
||||||
void handleFileRenamedAlert(libtorrent::file_renamed_alert *p);
|
void handleFileRenamedAlert(const libtorrent::file_renamed_alert *p);
|
||||||
void handleFileRenameFailedAlert(libtorrent::file_rename_failed_alert *p);
|
void handleFileRenameFailedAlert(const libtorrent::file_rename_failed_alert *p);
|
||||||
void handleFileCompletedAlert(libtorrent::file_completed_alert *p);
|
void handleFileCompletedAlert(const libtorrent::file_completed_alert *p);
|
||||||
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p);
|
void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
|
||||||
void handleStatsAlert(libtorrent::stats_alert *p);
|
void handleStatsAlert(const libtorrent::stats_alert *p);
|
||||||
|
|
||||||
|
void resume_impl(bool forced, bool uploadMode);
|
||||||
bool isMoveInProgress() const;
|
bool isMoveInProgress() const;
|
||||||
QString nativeActualSavePath() const;
|
QString nativeActualSavePath() const;
|
||||||
|
|
||||||
@@ -417,6 +420,7 @@ namespace BitTorrent
|
|||||||
bool addTracker(const TrackerEntry &tracker);
|
bool addTracker(const TrackerEntry &tracker);
|
||||||
bool addUrlSeed(const QUrl &urlSeed);
|
bool addUrlSeed(const QUrl &urlSeed);
|
||||||
bool removeUrlSeed(const QUrl &urlSeed);
|
bool removeUrlSeed(const QUrl &urlSeed);
|
||||||
|
void setFirstLastPiecePriorityImpl(bool enabled, const QVector<int> &updatedFilePrio = {});
|
||||||
|
|
||||||
Session *const m_session;
|
Session *const m_session;
|
||||||
libtorrent::torrent_handle m_nativeHandle;
|
libtorrent::torrent_handle m_nativeHandle;
|
||||||
@@ -458,8 +462,9 @@ namespace BitTorrent
|
|||||||
bool m_needsToSetFirstLastPiecePriority;
|
bool m_needsToSetFirstLastPiecePriority;
|
||||||
|
|
||||||
bool m_pauseAfterRecheck;
|
bool m_pauseAfterRecheck;
|
||||||
bool m_needSaveResumeData;
|
|
||||||
QHash<QString, TrackerInfo> m_trackerInfos;
|
QHash<QString, TrackerInfo> m_trackerInfos;
|
||||||
|
|
||||||
|
bool m_started = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,20 +26,20 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
#include "torrentinfo.h"
|
||||||
#include <QString>
|
|
||||||
#include <QList>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QDateTime>
|
|
||||||
|
|
||||||
#include <libtorrent/error_code.hpp>
|
#include <libtorrent/error_code.hpp>
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QString>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "infohash.h"
|
#include "infohash.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
#include "torrentinfo.h"
|
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
@@ -60,23 +60,76 @@ TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString &error)
|
TorrentInfo TorrentInfo::load(const QByteArray &data, QString *error) noexcept
|
||||||
{
|
{
|
||||||
error.clear();
|
// 2-step construction to overcome default limits of `depth_limit` & `token_limit` which are
|
||||||
|
// used in `torrent_info()` constructor
|
||||||
|
const int depthLimit = 100;
|
||||||
|
const int tokenLimit = 10000000;
|
||||||
libt::error_code ec;
|
libt::error_code ec;
|
||||||
TorrentInfo info(NativePtr(new libt::torrent_info(Utils::Fs::toNativePath(path).toStdString(), ec)));
|
|
||||||
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
|
libt::lazy_entry node;
|
||||||
|
libt::lazy_bdecode(data.constData(), (data.constData() + data.size()), node, ec
|
||||||
|
, nullptr, depthLimit, tokenLimit);
|
||||||
|
#else
|
||||||
|
libt::bdecode_node node;
|
||||||
|
bdecode(data.constData(), (data.constData() + data.size()), node, ec
|
||||||
|
, nullptr, depthLimit, tokenLimit);
|
||||||
|
#endif
|
||||||
if (ec) {
|
if (ec) {
|
||||||
error = QString::fromUtf8(ec.message().c_str());
|
if (error)
|
||||||
qDebug("Cannot load .torrent file: %s", qUtf8Printable(error));
|
*error = QString::fromStdString(ec.message());
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentInfo info {NativePtr(new libt::torrent_info(node, ec))};
|
||||||
|
if (ec) {
|
||||||
|
if (error)
|
||||||
|
*error = QString::fromStdString(ec.message());
|
||||||
|
return TorrentInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentInfo TorrentInfo::loadFromFile(const QString &path)
|
TorrentInfo TorrentInfo::loadFromFile(const QString &path, QString *error) noexcept
|
||||||
{
|
{
|
||||||
QString error;
|
if (error)
|
||||||
return loadFromFile(path, error);
|
error->clear();
|
||||||
|
|
||||||
|
QFile file {path};
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
if (error)
|
||||||
|
*error = file.errorString();
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
const qint64 fileSizeLimit = 100 * 1024 * 1024; // 100 MB
|
||||||
|
if (file.size() > fileSizeLimit) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("File size exceeds max limit %1").arg(fileSizeLimit);
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
try {
|
||||||
|
data = file.readAll();
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc &e) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("Torrent file read error: %1").arg(e.what());
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
if (data.size() != file.size()) {
|
||||||
|
if (error)
|
||||||
|
*error = tr("Torrent file read error: size mismatch");
|
||||||
|
return TorrentInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return load(data, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentInfo::isValid() const
|
bool TorrentInfo::isValid() const
|
||||||
@@ -261,7 +314,7 @@ QVector<QByteArray> TorrentInfo::pieceHashes() const
|
|||||||
return hashes;
|
return hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString& file) const
|
TorrentInfo::PieceRange TorrentInfo::filePieces(const QString &file) const
|
||||||
{
|
{
|
||||||
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
if (!isValid()) // if we do not check here the debug message will be printed, which would be not correct
|
||||||
return {};
|
return {};
|
||||||
@@ -286,12 +339,12 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(int fileIndex) const
|
|||||||
|
|
||||||
const libt::file_storage &files = nativeInfo()->files();
|
const libt::file_storage &files = nativeInfo()->files();
|
||||||
const auto fileSize = files.file_size(fileIndex);
|
const auto fileSize = files.file_size(fileIndex);
|
||||||
const auto firstOffset = files.file_offset(fileIndex);
|
const auto fileOffset = files.file_offset(fileIndex);
|
||||||
return makeInterval(static_cast<int>(firstOffset / pieceLength()),
|
return makeInterval(static_cast<int>(fileOffset / pieceLength()),
|
||||||
static_cast<int>((firstOffset + fileSize - 1) / pieceLength()));
|
static_cast<int>((fileOffset + fileSize - 1) / pieceLength()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentInfo::renameFile(uint index, const QString &newPath)
|
void TorrentInfo::renameFile(const int index, const QString &newPath)
|
||||||
{
|
{
|
||||||
if (!isValid()) return;
|
if (!isValid()) return;
|
||||||
nativeInfo()->rename_file(index, Utils::Fs::toNativePath(newPath).toStdString());
|
nativeInfo()->rename_file(index, Utils::Fs::toNativePath(newPath).toStdString());
|
||||||
@@ -299,8 +352,8 @@ void TorrentInfo::renameFile(uint index, const QString &newPath)
|
|||||||
|
|
||||||
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
||||||
{
|
{
|
||||||
// the check whether the object valid is not needed here
|
// the check whether the object is valid is not needed here
|
||||||
// because filesCount() returns -1 in that case and the loop exits immediately
|
// because if filesCount() returns -1 the loop exits immediately
|
||||||
for (int i = 0; i < filesCount(); ++i)
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
if (fileName == filePath(i))
|
if (fileName == filePath(i))
|
||||||
return i;
|
return i;
|
||||||
@@ -308,24 +361,29 @@ int BitTorrent::TorrentInfo::fileIndex(const QString& fileName) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TorrentInfo::hasRootFolder() const
|
QString TorrentInfo::rootFolder() const
|
||||||
{
|
{
|
||||||
QString testRootFolder;
|
QString rootFolder;
|
||||||
for (int i = 0; i < filesCount(); ++i) {
|
for (int i = 0; i < filesCount(); ++i) {
|
||||||
const QString filePath = this->filePath(i);
|
const QString filePath = this->filePath(i);
|
||||||
if (QDir::isAbsolutePath(filePath)) continue;
|
if (QDir::isAbsolutePath(filePath)) continue;
|
||||||
|
|
||||||
const auto filePathElements = filePath.splitRef('/');
|
const auto filePathElements = filePath.splitRef('/');
|
||||||
// if at least one file has no root folder, no common root folder exists
|
// if at least one file has no root folder, no common root folder exists
|
||||||
if (filePathElements.count() <= 1) return false;
|
if (filePathElements.count() <= 1) return "";
|
||||||
|
|
||||||
if (testRootFolder.isEmpty())
|
if (rootFolder.isEmpty())
|
||||||
testRootFolder = filePathElements.at(0).toString();
|
rootFolder = filePathElements.at(0).toString();
|
||||||
else if (testRootFolder != filePathElements.at(0))
|
else if (rootFolder != filePathElements.at(0))
|
||||||
return false;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return rootFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TorrentInfo::hasRootFolder() const
|
||||||
|
{
|
||||||
|
return !rootFolder().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentInfo::stripRootFolder()
|
void TorrentInfo::stripRootFolder()
|
||||||
|
|||||||
@@ -29,20 +29,21 @@
|
|||||||
#ifndef BITTORRENT_TORRENTINFO_H
|
#ifndef BITTORRENT_TORRENTINFO_H
|
||||||
#define BITTORRENT_TORRENTINFO_H
|
#define BITTORRENT_TORRENTINFO_H
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QList>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
|
|
||||||
class QString;
|
|
||||||
class QUrl;
|
|
||||||
class QDateTime;
|
|
||||||
class QStringList;
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
template<typename T> class QList;
|
class QDateTime;
|
||||||
template<typename T> class QVector;
|
class QString;
|
||||||
|
class QStringList;
|
||||||
|
class QUrl;
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,8 @@ namespace BitTorrent
|
|||||||
|
|
||||||
class TorrentInfo
|
class TorrentInfo
|
||||||
{
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#if LIBTORRENT_VERSION_NUM < 10100
|
#if LIBTORRENT_VERSION_NUM < 10100
|
||||||
typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
|
typedef boost::intrusive_ptr<const libtorrent::torrent_info> NativeConstPtr;
|
||||||
@@ -63,8 +66,8 @@ namespace BitTorrent
|
|||||||
explicit TorrentInfo(NativeConstPtr nativeInfo = NativeConstPtr());
|
explicit TorrentInfo(NativeConstPtr nativeInfo = NativeConstPtr());
|
||||||
TorrentInfo(const TorrentInfo &other);
|
TorrentInfo(const TorrentInfo &other);
|
||||||
|
|
||||||
static TorrentInfo loadFromFile(const QString &path, QString &error);
|
static TorrentInfo load(const QByteArray &data, QString *error = nullptr) noexcept;
|
||||||
static TorrentInfo loadFromFile(const QString &path);
|
static TorrentInfo loadFromFile(const QString &path, QString *error = nullptr) noexcept;
|
||||||
|
|
||||||
TorrentInfo &operator=(const TorrentInfo &other);
|
TorrentInfo &operator=(const TorrentInfo &other);
|
||||||
|
|
||||||
@@ -99,8 +102,9 @@ namespace BitTorrent
|
|||||||
PieceRange filePieces(const QString &file) const;
|
PieceRange filePieces(const QString &file) const;
|
||||||
PieceRange filePieces(int fileIndex) const;
|
PieceRange filePieces(int fileIndex) const;
|
||||||
|
|
||||||
void renameFile(uint index, const QString &newPath);
|
void renameFile(int index, const QString &newPath);
|
||||||
|
|
||||||
|
QString rootFolder() const;
|
||||||
bool hasRootFolder() const;
|
bool hasRootFolder() const;
|
||||||
void stripRootFolder();
|
void stripRootFolder();
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
@@ -27,15 +27,18 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "tracker.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
|
|
||||||
|
#include "base/global.h"
|
||||||
#include "base/http/server.h"
|
#include "base/http/server.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
|
#include "base/utils/bytearray.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "tracker.h"
|
|
||||||
|
|
||||||
// static limits
|
// static limits
|
||||||
static const int MAX_TORRENTS = 100;
|
static const int MAX_TORRENTS = 100;
|
||||||
@@ -57,7 +60,7 @@ bool Peer::operator==(const Peer &other) const
|
|||||||
|
|
||||||
QString Peer::uid() const
|
QString Peer::uid() const
|
||||||
{
|
{
|
||||||
return ip + ":" + QString::number(port);
|
return ip + ':' + QString::number(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
libtorrent::entry Peer::toEntry(bool noPeerId) const
|
||||||
@@ -74,7 +77,7 @@ libtorrent::entry Peer::toEntry(bool noPeerId) const
|
|||||||
// Tracker
|
// Tracker
|
||||||
|
|
||||||
Tracker::Tracker(QObject *parent)
|
Tracker::Tracker(QObject *parent)
|
||||||
: Http::ResponseBuilder(parent)
|
: QObject(parent)
|
||||||
, m_server(new Http::Server(this, this))
|
, m_server(new Http::Server(this, this))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -130,19 +133,30 @@ Http::Response Tracker::processRequest(const Http::Request &request, const Http:
|
|||||||
|
|
||||||
void Tracker::respondToAnnounceRequest()
|
void Tracker::respondToAnnounceRequest()
|
||||||
{
|
{
|
||||||
const QStringMap &gets = m_request.gets;
|
QMap<QString, QByteArray> queryParams;
|
||||||
|
// Parse GET parameters
|
||||||
|
using namespace Utils::ByteArray;
|
||||||
|
for (const QByteArray ¶m : copyAsConst(splitToViews(m_request.query, "&"))) {
|
||||||
|
const int sepPos = param.indexOf('=');
|
||||||
|
if (sepPos <= 0) continue; // ignores params without name
|
||||||
|
|
||||||
|
const QString paramName {QString::fromUtf8(param.constData(), sepPos)};
|
||||||
|
const QByteArray paramValue {param.mid(sepPos + 1)};
|
||||||
|
queryParams[paramName] = paramValue;
|
||||||
|
}
|
||||||
|
|
||||||
TrackerAnnounceRequest annonceReq;
|
TrackerAnnounceRequest annonceReq;
|
||||||
|
|
||||||
// IP
|
// IP
|
||||||
annonceReq.peer.ip = m_env.clientAddress.toString();
|
annonceReq.peer.ip = m_env.clientAddress.toString();
|
||||||
|
|
||||||
// 1. Get info_hash
|
// 1. Get info_hash
|
||||||
if (!gets.contains("info_hash")) {
|
if (!queryParams.contains("info_hash")) {
|
||||||
qDebug("Tracker: Missing info_hash");
|
qDebug("Tracker: Missing info_hash");
|
||||||
status(101, "Missing info_hash");
|
status(101, "Missing info_hash");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annonceReq.infoHash = gets.value("info_hash");
|
annonceReq.infoHash = queryParams.value("info_hash");
|
||||||
// info_hash cannot be longer than 20 bytes
|
// info_hash cannot be longer than 20 bytes
|
||||||
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
/*if (annonce_req.info_hash.toLatin1().length() > 20) {
|
||||||
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
qDebug("Tracker: Info_hash is not 20 byte long: %s (%d)", qUtf8Printable(annonce_req.info_hash), annonce_req.info_hash.toLatin1().length());
|
||||||
@@ -151,12 +165,12 @@ void Tracker::respondToAnnounceRequest()
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 2. Get peer ID
|
// 2. Get peer ID
|
||||||
if (!gets.contains("peer_id")) {
|
if (!queryParams.contains("peer_id")) {
|
||||||
qDebug("Tracker: Missing peer_id");
|
qDebug("Tracker: Missing peer_id");
|
||||||
status(102, "Missing peer_id");
|
status(102, "Missing peer_id");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
annonceReq.peer.peerId = gets.value("peer_id");
|
annonceReq.peer.peerId = queryParams.value("peer_id");
|
||||||
// peer_id cannot be longer than 20 bytes
|
// peer_id cannot be longer than 20 bytes
|
||||||
/*if (annonce_req.peer.peer_id.length() > 20) {
|
/*if (annonce_req.peer.peer_id.length() > 20) {
|
||||||
qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id));
|
qDebug("Tracker: peer_id is not 20 byte long: %s", qUtf8Printable(annonce_req.peer.peer_id));
|
||||||
@@ -165,14 +179,14 @@ void Tracker::respondToAnnounceRequest()
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 3. Get port
|
// 3. Get port
|
||||||
if (!gets.contains("port")) {
|
if (!queryParams.contains("port")) {
|
||||||
qDebug("Tracker: Missing port");
|
qDebug("Tracker: Missing port");
|
||||||
status(103, "Missing port");
|
status(103, "Missing port");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
annonceReq.peer.port = gets.value("port").toInt(&ok);
|
annonceReq.peer.port = queryParams.value("port").toInt(&ok);
|
||||||
if (!ok || annonceReq.peer.port < 1 || annonceReq.peer.port > 65535) {
|
if (!ok || (annonceReq.peer.port < 0) || (annonceReq.peer.port > 65535)) {
|
||||||
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
|
qDebug("Tracker: Invalid port number (%d)", annonceReq.peer.port);
|
||||||
status(103, "Missing port");
|
status(103, "Missing port");
|
||||||
return;
|
return;
|
||||||
@@ -180,15 +194,15 @@ void Tracker::respondToAnnounceRequest()
|
|||||||
|
|
||||||
// 4. Get event
|
// 4. Get event
|
||||||
annonceReq.event = "";
|
annonceReq.event = "";
|
||||||
if (gets.contains("event")) {
|
if (queryParams.contains("event")) {
|
||||||
annonceReq.event = gets.value("event");
|
annonceReq.event = queryParams.value("event");
|
||||||
qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event));
|
qDebug("Tracker: event is %s", qUtf8Printable(annonceReq.event));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Get numwant
|
// 5. Get numwant
|
||||||
annonceReq.numwant = 50;
|
annonceReq.numwant = 50;
|
||||||
if (gets.contains("numwant")) {
|
if (queryParams.contains("numwant")) {
|
||||||
int tmp = gets.value("numwant").toInt();
|
int tmp = queryParams.value("numwant").toInt();
|
||||||
if (tmp > 0) {
|
if (tmp > 0) {
|
||||||
qDebug("Tracker: numwant = %d", tmp);
|
qDebug("Tracker: numwant = %d", tmp);
|
||||||
annonceReq.numwant = tmp;
|
annonceReq.numwant = tmp;
|
||||||
@@ -197,37 +211,51 @@ void Tracker::respondToAnnounceRequest()
|
|||||||
|
|
||||||
// 6. no_peer_id (extension)
|
// 6. no_peer_id (extension)
|
||||||
annonceReq.noPeerId = false;
|
annonceReq.noPeerId = false;
|
||||||
if (gets.contains("no_peer_id"))
|
if (queryParams.contains("no_peer_id"))
|
||||||
annonceReq.noPeerId = true;
|
annonceReq.noPeerId = true;
|
||||||
|
|
||||||
// 7. TODO: support "compact" extension
|
// 7. TODO: support "compact" extension
|
||||||
|
|
||||||
// Done parsing, now let's reply
|
// Done parsing, now let's reply
|
||||||
if (m_torrents.contains(annonceReq.infoHash)) {
|
if (annonceReq.event == "stopped") {
|
||||||
if (annonceReq.event == "stopped") {
|
unregisterPeer(annonceReq);
|
||||||
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
|
|
||||||
m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
registerPeer(annonceReq);
|
||||||
|
replyWithPeerList(annonceReq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tracker::registerPeer(const TrackerAnnounceRequest &annonceReq)
|
||||||
|
{
|
||||||
|
if (annonceReq.peer.port == 0) return;
|
||||||
|
|
||||||
|
if (!m_torrents.contains(annonceReq.infoHash)) {
|
||||||
// Unknown torrent
|
// Unknown torrent
|
||||||
if (m_torrents.size() == MAX_TORRENTS) {
|
if (m_torrents.size() == MAX_TORRENTS) {
|
||||||
// Reached max size, remove a random torrent
|
// Reached max size, remove a random torrent
|
||||||
m_torrents.erase(m_torrents.begin());
|
m_torrents.erase(m_torrents.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the user
|
// Register the user
|
||||||
PeerList peers = m_torrents.value(annonceReq.infoHash);
|
PeerList &peers = m_torrents[annonceReq.infoHash];
|
||||||
if (peers.size() == MAX_PEERS_PER_TORRENT) {
|
if (!peers.contains(annonceReq.peer.uid())) {
|
||||||
// Too many peers, remove a random one
|
// Unknown peer
|
||||||
peers.erase(peers.begin());
|
if (peers.size() == MAX_PEERS_PER_TORRENT) {
|
||||||
|
// Too many peers, remove a random one
|
||||||
|
peers.erase(peers.begin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
peers[annonceReq.peer.uid()] = annonceReq.peer;
|
peers[annonceReq.peer.uid()] = annonceReq.peer;
|
||||||
m_torrents[annonceReq.infoHash] = peers;
|
}
|
||||||
|
|
||||||
// Reply
|
void Tracker::unregisterPeer(const TrackerAnnounceRequest &annonceReq)
|
||||||
replyWithPeerList(annonceReq);
|
{
|
||||||
|
if (annonceReq.peer.port == 0) return;
|
||||||
|
|
||||||
|
if (m_torrents[annonceReq.infoHash].remove(annonceReq.peer.uid()) > 0)
|
||||||
|
qDebug("Tracker: Peer stopped downloading, deleting it from the list");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
||||||
@@ -235,22 +263,18 @@ void Tracker::replyWithPeerList(const TrackerAnnounceRequest &annonceReq)
|
|||||||
// Prepare the entry for bencoding
|
// Prepare the entry for bencoding
|
||||||
libtorrent::entry::dictionary_type replyDict;
|
libtorrent::entry::dictionary_type replyDict;
|
||||||
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
replyDict["interval"] = libtorrent::entry(ANNOUNCE_INTERVAL);
|
||||||
QList<Peer> peers = m_torrents.value(annonceReq.infoHash).values();
|
|
||||||
libtorrent::entry::list_type peerList;
|
libtorrent::entry::list_type peerList;
|
||||||
foreach (const Peer &p, peers) {
|
for (const Peer &p : m_torrents.value(annonceReq.infoHash))
|
||||||
//if (p != annonce_req.peer)
|
|
||||||
peerList.push_back(p.toEntry(annonceReq.noPeerId));
|
peerList.push_back(p.toEntry(annonceReq.noPeerId));
|
||||||
}
|
|
||||||
replyDict["peers"] = libtorrent::entry(peerList);
|
replyDict["peers"] = libtorrent::entry(peerList);
|
||||||
libtorrent::entry replyEntry(replyDict);
|
|
||||||
|
const libtorrent::entry replyEntry(replyDict);
|
||||||
// bencode
|
// bencode
|
||||||
std::vector<char> buf;
|
QByteArray reply;
|
||||||
libtorrent::bencode(std::back_inserter(buf), replyEntry);
|
libtorrent::bencode(std::back_inserter(reply), replyEntry);
|
||||||
QByteArray reply(&buf[0], static_cast<int>(buf.size()));
|
|
||||||
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
|
qDebug("Tracker: reply with the following bencoded data:\n %s", reply.constData());
|
||||||
|
|
||||||
// HTTP reply
|
// HTTP reply
|
||||||
print(reply, Http::CONTENT_TYPE_TXT);
|
print(reply, Http::CONTENT_TYPE_TXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#define BITTORRENT_TRACKER_H
|
#define BITTORRENT_TRACKER_H
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "base/http/irequesthandler.h"
|
#include "base/http/irequesthandler.h"
|
||||||
#include "base/http/responsebuilder.h"
|
#include "base/http/responsebuilder.h"
|
||||||
@@ -51,7 +52,7 @@ namespace BitTorrent
|
|||||||
struct Peer
|
struct Peer
|
||||||
{
|
{
|
||||||
QString ip;
|
QString ip;
|
||||||
QString peerId;
|
QByteArray peerId;
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
bool operator!=(const Peer &other) const;
|
bool operator!=(const Peer &other) const;
|
||||||
@@ -62,7 +63,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
struct TrackerAnnounceRequest
|
struct TrackerAnnounceRequest
|
||||||
{
|
{
|
||||||
QString infoHash;
|
QByteArray infoHash;
|
||||||
QString event;
|
QString event;
|
||||||
int numwant;
|
int numwant;
|
||||||
Peer peer;
|
Peer peer;
|
||||||
@@ -71,11 +72,11 @@ namespace BitTorrent
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef QHash<QString, Peer> PeerList;
|
typedef QHash<QString, Peer> PeerList;
|
||||||
typedef QHash<QString, PeerList> TorrentList;
|
typedef QHash<QByteArray, PeerList> TorrentList;
|
||||||
|
|
||||||
/* Basic Bittorrent tracker implementation in Qt */
|
/* Basic Bittorrent tracker implementation in Qt */
|
||||||
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
/* Following http://wiki.theory.org/BitTorrent_Tracker_Protocol */
|
||||||
class Tracker : public Http::ResponseBuilder, public Http::IRequestHandler
|
class Tracker : public QObject, public Http::IRequestHandler, private Http::ResponseBuilder
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(Tracker)
|
Q_DISABLE_COPY(Tracker)
|
||||||
@@ -89,6 +90,8 @@ namespace BitTorrent
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void respondToAnnounceRequest();
|
void respondToAnnounceRequest();
|
||||||
|
void registerPeer(const TrackerAnnounceRequest &annonceReq);
|
||||||
|
void unregisterPeer(const TrackerAnnounceRequest &annonceReq);
|
||||||
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
|
void replyWithPeerList(const TrackerAnnounceRequest &annonceReq);
|
||||||
|
|
||||||
Http::Server *m_server;
|
Http::Server *m_server;
|
||||||
|
|||||||
@@ -26,11 +26,12 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "trackerentry.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "trackerentry.h"
|
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
|
||||||
|
|||||||
40
src/base/exceptions.cpp
Normal file
40
src/base/exceptions.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 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 "exceptions.h"
|
||||||
|
|
||||||
|
RuntimeError::RuntimeError(const QString &message)
|
||||||
|
: std::runtime_error {message.toUtf8().data()}
|
||||||
|
, m_message {message}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RuntimeError::message() const
|
||||||
|
{
|
||||||
|
return m_message;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt4 and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006-2012 Ishan Arora and Christophe Dumez
|
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,24 +24,19 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PREFJSON_H
|
#pragma once
|
||||||
#define PREFJSON_H
|
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class prefjson
|
class RuntimeError : public std::runtime_error
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
prefjson();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QByteArray getPreferences();
|
explicit RuntimeError(const QString &message = "");
|
||||||
static void setPreferences(const QString& json);
|
QString message() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString m_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PREFJSON_H
|
|
||||||
@@ -1,176 +1,161 @@
|
|||||||
#include <QtGlobal>
|
/*
|
||||||
#ifndef Q_OS_WIN
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
#include <QSet>
|
* Copyright (C) 2018
|
||||||
#include <iostream>
|
*
|
||||||
#include <errno.h>
|
* This program is free software; you can redistribute it and/or
|
||||||
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)
|
* modify it under the terms of the GNU General Public License
|
||||||
#include <sys/param.h>
|
* as published by the Free Software Foundation; either version 2
|
||||||
#include <sys/mount.h>
|
* of the License, or (at your option) any later version.
|
||||||
#include <string.h>
|
*
|
||||||
#elif !defined Q_OS_HAIKU
|
* This program is distributed in the hope that it will be useful,
|
||||||
#include <sys/vfs.h>
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
#endif
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
#endif
|
* 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 "base/bittorrent/magneturi.h"
|
|
||||||
#include "base/bittorrent/torrentinfo.h"
|
|
||||||
#include "base/preferences.h"
|
|
||||||
#include "filesystemwatcher.h"
|
#include "filesystemwatcher.h"
|
||||||
|
|
||||||
#ifndef CIFS_MAGIC_NUMBER
|
#include <QtGlobal>
|
||||||
#define CIFS_MAGIC_NUMBER 0xFF534D42
|
|
||||||
|
#if defined(Q_OS_MAC) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
#include <cstring>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NFS_SUPER_MAGIC
|
#include "base/algorithm.h"
|
||||||
#define NFS_SUPER_MAGIC 0x6969
|
#include "base/bittorrent/magneturi.h"
|
||||||
#endif
|
#include "base/bittorrent/torrentinfo.h"
|
||||||
|
#include "base/global.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
#ifndef SMB_SUPER_MAGIC
|
namespace
|
||||||
#define SMB_SUPER_MAGIC 0x517B
|
{
|
||||||
#endif
|
const int WATCH_INTERVAL = 10000; // 10 sec
|
||||||
|
const int MAX_PARTIAL_RETRIES = 5;
|
||||||
const int WATCH_INTERVAL = 10000; // 10 sec
|
}
|
||||||
const int MAX_PARTIAL_RETRIES = 5;
|
|
||||||
|
|
||||||
FileSystemWatcher::FileSystemWatcher(QObject *parent)
|
FileSystemWatcher::FileSystemWatcher(QObject *parent)
|
||||||
: QFileSystemWatcher(parent)
|
: QFileSystemWatcher(parent)
|
||||||
{
|
{
|
||||||
m_filters << "*.torrent" << "*.magnet";
|
connect(this, &QFileSystemWatcher::directoryChanged, this, &FileSystemWatcher::scanLocalFolder);
|
||||||
connect(this, SIGNAL(directoryChanged(QString)), SLOT(scanLocalFolder(QString)));
|
|
||||||
}
|
m_partialTorrentTimer.setSingleShot(true);
|
||||||
|
connect(&m_partialTorrentTimer, &QTimer::timeout, this, &FileSystemWatcher::processPartialTorrents);
|
||||||
|
|
||||||
FileSystemWatcher::~FileSystemWatcher()
|
|
||||||
{
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
if (m_watchTimer)
|
connect(&m_watchTimer, &QTimer::timeout, this, &FileSystemWatcher::scanNetworkFolders);
|
||||||
delete m_watchTimer;
|
|
||||||
#endif
|
#endif
|
||||||
if (m_partialTorrentTimer)
|
|
||||||
delete m_partialTorrentTimer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList FileSystemWatcher::directories() const
|
QStringList FileSystemWatcher::directories() const
|
||||||
{
|
{
|
||||||
QStringList dirs;
|
QStringList dirs = QFileSystemWatcher::directories();
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
if (m_watchTimer) {
|
for (const QDir &dir : qAsConst(m_watchedFolders))
|
||||||
foreach (const QDir &dir, m_watchedFolders)
|
dirs << dir.canonicalPath();
|
||||||
dirs << dir.canonicalPath();
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
dirs << QFileSystemWatcher::directories();
|
|
||||||
return dirs;
|
return dirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::addPath(const QString &path)
|
void FileSystemWatcher::addPath(const QString &path)
|
||||||
{
|
{
|
||||||
|
if (path.isEmpty()) return;
|
||||||
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
||||||
QDir dir(path);
|
const QDir dir(path);
|
||||||
if (!dir.exists()) return;
|
if (!dir.exists()) return;
|
||||||
|
|
||||||
// Check if the path points to a network file system or not
|
// Check if the path points to a network file system or not
|
||||||
if (isNetworkFileSystem(path)) {
|
if (Utils::Fs::isNetworkFileSystem(path)) {
|
||||||
// Network mode
|
// Network mode
|
||||||
qDebug("Network folder detected: %s", qUtf8Printable(path));
|
qDebug("Network folder detected: %s", qUtf8Printable(path));
|
||||||
qDebug("Using file polling mode instead of inotify...");
|
qDebug("Using file polling mode instead of inotify...");
|
||||||
m_watchedFolders << dir;
|
m_watchedFolders << dir;
|
||||||
// Set up the watch timer
|
|
||||||
if (!m_watchTimer) {
|
m_watchTimer.start(WATCH_INTERVAL);
|
||||||
m_watchTimer = new QTimer(this);
|
return;
|
||||||
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", qUtf8Printable(path));
|
|
||||||
QFileSystemWatcher::addPath(path);
|
|
||||||
scanLocalFolder(path);
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Normal mode
|
||||||
|
qDebug("FS Watcher is watching %s in normal mode", qUtf8Printable(path));
|
||||||
|
QFileSystemWatcher::addPath(path);
|
||||||
|
scanLocalFolder(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::removePath(const QString &path)
|
void FileSystemWatcher::removePath(const QString &path)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
QDir dir(path);
|
if (m_watchedFolders.removeOne(path)) {
|
||||||
for (int i = 0; i < m_watchedFolders.count(); ++i) {
|
if (m_watchedFolders.isEmpty())
|
||||||
if (QDir(m_watchedFolders.at(i)) == dir) {
|
m_watchTimer.stop();
|
||||||
m_watchedFolders.removeAt(i);
|
return;
|
||||||
if (m_watchedFolders.isEmpty())
|
|
||||||
delete m_watchTimer;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Normal mode
|
// Normal mode
|
||||||
QFileSystemWatcher::removePath(path);
|
QFileSystemWatcher::removePath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::scanLocalFolder(QString path)
|
void FileSystemWatcher::scanLocalFolder(const QString &path)
|
||||||
{
|
{
|
||||||
qDebug("scanLocalFolder(%s) called", qUtf8Printable(path));
|
QTimer::singleShot(2000, this, [this, path]() { processTorrentsInDir(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", qUtf8Printable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
void FileSystemWatcher::scanNetworkFolders()
|
void FileSystemWatcher::scanNetworkFolders()
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_WIN
|
for (const QDir &dir : qAsConst(m_watchedFolders))
|
||||||
qDebug("scanNetworkFolders() called");
|
processTorrentsInDir(dir);
|
||||||
QStringList torrents;
|
|
||||||
// Network folders scan
|
|
||||||
foreach (const QDir &dir, m_watchedFolders) {
|
|
||||||
//qDebug("FSWatcher: Polling manually folder %s", qUtf8Printable(dir.path()));
|
|
||||||
addTorrentsFromDir(dir, torrents);
|
|
||||||
}
|
|
||||||
// Report detected torrent files
|
|
||||||
if (!torrents.empty()) {
|
|
||||||
qDebug("The following files are being reported: %s", qUtf8Printable(torrents.join("\n")));
|
|
||||||
emit torrentsAdded(torrents);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void FileSystemWatcher::processPartialTorrents()
|
void FileSystemWatcher::processPartialTorrents()
|
||||||
{
|
{
|
||||||
QStringList noLongerPartial;
|
QStringList noLongerPartial;
|
||||||
|
|
||||||
// Check which torrents are still partial
|
// Check which torrents are still partial
|
||||||
foreach (const QString &torrentPath, m_partialTorrents.keys()) {
|
Dict::removeIf(m_partialTorrents, [&noLongerPartial](const QString &torrentPath, int &value)
|
||||||
if (!QFile::exists(torrentPath)) {
|
{
|
||||||
m_partialTorrents.remove(torrentPath);
|
if (!QFile::exists(torrentPath))
|
||||||
}
|
return true;
|
||||||
else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
|
|
||||||
|
if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
|
||||||
noLongerPartial << torrentPath;
|
noLongerPartial << torrentPath;
|
||||||
m_partialTorrents.remove(torrentPath);
|
return true;
|
||||||
}
|
}
|
||||||
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
|
|
||||||
m_partialTorrents.remove(torrentPath);
|
if (value >= MAX_PARTIAL_RETRIES) {
|
||||||
QFile::rename(torrentPath, torrentPath + ".invalid");
|
QFile::rename(torrentPath, torrentPath + ".qbt_rejected");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
++m_partialTorrents[torrentPath];
|
++value;
|
||||||
}
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
// Stop the partial timer if necessary
|
// Stop the partial timer if necessary
|
||||||
if (m_partialTorrents.empty()) {
|
if (m_partialTorrents.empty()) {
|
||||||
m_partialTorrentTimer->stop();
|
m_partialTorrentTimer.stop();
|
||||||
m_partialTorrentTimer->deleteLater();
|
|
||||||
qDebug("No longer any partial torrent.");
|
qDebug("No longer any partial torrent.");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
qDebug("Still %d partial torrents after delayed processing.", m_partialTorrents.count());
|
||||||
m_partialTorrentTimer->start(WATCH_INTERVAL);
|
m_partialTorrentTimer.start(WATCH_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify of new torrents
|
// Notify of new torrents
|
||||||
@@ -178,99 +163,23 @@ void FileSystemWatcher::processPartialTorrents()
|
|||||||
emit torrentsAdded(noLongerPartial);
|
emit torrentsAdded(noLongerPartial);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemWatcher::startPartialTorrentTimer()
|
void FileSystemWatcher::processTorrentsInDir(const QDir &dir)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_partialTorrents.isEmpty());
|
QStringList torrents;
|
||||||
if (!m_partialTorrentTimer) {
|
const QStringList files = dir.entryList({"*.torrent", "*.magnet"}, QDir::Files);
|
||||||
m_partialTorrentTimer = new QTimer();
|
for (const QString &file : files) {
|
||||||
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);
|
const QString fileAbsPath = dir.absoluteFilePath(file);
|
||||||
if (fileAbsPath.endsWith(".magnet")) {
|
if (file.endsWith(".magnet"))
|
||||||
torrents << fileAbsPath;
|
torrents << fileAbsPath;
|
||||||
}
|
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid())
|
||||||
else if (BitTorrent::TorrentInfo::loadFromFile(fileAbsPath).isValid()) {
|
|
||||||
torrents << fileAbsPath;
|
torrents << fileAbsPath;
|
||||||
}
|
else if (!m_partialTorrents.contains(fileAbsPath))
|
||||||
else if (!m_partialTorrents.contains(fileAbsPath)) {
|
m_partialTorrents[fileAbsPath] = 0;
|
||||||
qDebug("Partial torrent detected at: %s", qUtf8Printable(fileAbsPath));
|
|
||||||
qDebug("Delay the file's processing...");
|
|
||||||
m_partialTorrents.insert(fileAbsPath, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_partialTorrents.empty())
|
if (!torrents.empty())
|
||||||
startPartialTorrentTimer();
|
emit torrentsAdded(torrents);
|
||||||
|
|
||||||
|
if (!m_partialTorrents.empty() && !m_partialTorrentTimer.isActive())
|
||||||
|
m_partialTorrentTimer.start(WATCH_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#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 == static_cast<long>(CIFS_MAGIC_NUMBER))
|
|
||||||
|| (buf.f_type == static_cast<long>(NFS_SUPER_MAGIC))
|
|
||||||
|| (buf.f_type == static_cast<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
|
|
||||||
|
|||||||
@@ -1,10 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018
|
||||||
|
*
|
||||||
|
* 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 FILESYSTEMWATCHER_H
|
#ifndef FILESYSTEMWATCHER_H
|
||||||
#define FILESYSTEMWATCHER_H
|
#define FILESYSTEMWATCHER_H
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QPointer>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
@@ -18,7 +45,6 @@ class FileSystemWatcher : public QFileSystemWatcher
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileSystemWatcher(QObject *parent = nullptr);
|
explicit FileSystemWatcher(QObject *parent = nullptr);
|
||||||
~FileSystemWatcher();
|
|
||||||
|
|
||||||
QStringList directories() const;
|
QStringList directories() const;
|
||||||
void addPath(const QString &path);
|
void addPath(const QString &path);
|
||||||
@@ -28,25 +54,23 @@ signals:
|
|||||||
void torrentsAdded(const QStringList &pathList);
|
void torrentsAdded(const QStringList &pathList);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void scanLocalFolder(QString path);
|
void scanLocalFolder(const QString &path);
|
||||||
void scanNetworkFolders();
|
|
||||||
void processPartialTorrents();
|
void processPartialTorrents();
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
void scanNetworkFolders();
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startPartialTorrentTimer();
|
void processTorrentsInDir(const QDir &dir);
|
||||||
void addTorrentsFromDir(const QDir &dir, QStringList &torrents);
|
|
||||||
#if !defined Q_OS_WIN && !defined Q_OS_HAIKU
|
// Partial torrents
|
||||||
static bool isNetworkFileSystem(QString path);
|
QHash<QString, int> m_partialTorrents;
|
||||||
#endif
|
QTimer m_partialTorrentTimer;
|
||||||
|
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
QList<QDir> m_watchedFolders;
|
QList<QDir> m_watchedFolders;
|
||||||
QPointer<QTimer> m_watchTimer;
|
QTimer m_watchTimer;
|
||||||
#endif
|
#endif
|
||||||
QStringList m_filters;
|
|
||||||
// Partial torrents
|
|
||||||
QHash<QString, int> m_partialTorrents;
|
|
||||||
QPointer<QTimer> m_partialTorrentTimer;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILESYSTEMWATCHER_H
|
#endif // FILESYSTEMWATCHER_H
|
||||||
|
|||||||
@@ -26,5 +26,23 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
|
const char C_TORRENT_FILE_EXTENSION[] = ".torrent";
|
||||||
|
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; }
|
||||||
|
|
||||||
|
// prevent rvalue arguments:
|
||||||
|
template <typename T>
|
||||||
|
void qAsConst(const T &&) = delete;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// returns a const object copy
|
||||||
|
template <typename T>
|
||||||
|
constexpr typename std::add_const<T>::type copyAsConst(T &&t) noexcept { return std::move(t); }
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,15 +26,13 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
|
|
||||||
#include <QRegExp>
|
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "base/logger.h"
|
||||||
#include "irequesthandler.h"
|
#include "irequesthandler.h"
|
||||||
#include "requestparser.h"
|
#include "requestparser.h"
|
||||||
#include "responsegenerator.h"
|
#include "responsegenerator.h"
|
||||||
@@ -47,7 +46,7 @@ Connection::Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObj
|
|||||||
{
|
{
|
||||||
m_socket->setParent(this);
|
m_socket->setParent(this);
|
||||||
m_idleTimer.start();
|
m_idleTimer.start();
|
||||||
connect(m_socket, SIGNAL(readyRead()), SLOT(read()));
|
connect(m_socket, &QTcpSocket::readyRead, this, &Connection::read);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection()
|
Connection::~Connection()
|
||||||
@@ -58,37 +57,64 @@ Connection::~Connection()
|
|||||||
void Connection::read()
|
void Connection::read()
|
||||||
{
|
{
|
||||||
m_idleTimer.restart();
|
m_idleTimer.restart();
|
||||||
|
|
||||||
m_receivedData.append(m_socket->readAll());
|
m_receivedData.append(m_socket->readAll());
|
||||||
Request request;
|
|
||||||
RequestParser::ErrorCode err = RequestParser::parse(m_receivedData, request); // TODO: transform request headers to lowercase
|
|
||||||
|
|
||||||
switch (err) {
|
while (!m_receivedData.isEmpty()) {
|
||||||
case RequestParser::IncompleteRequest:
|
const RequestParser::ParseResult result = RequestParser::parse(m_receivedData);
|
||||||
// Partial request waiting for the rest
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RequestParser::BadRequest:
|
switch (result.status) {
|
||||||
sendResponse(Response(400, "Bad Request"));
|
case RequestParser::ParseStatus::Incomplete: {
|
||||||
m_receivedData.clear();
|
const long bufferLimit = RequestParser::MAX_CONTENT_SIZE * 1.1; // some margin for headers
|
||||||
break;
|
if (m_receivedData.size() > bufferLimit) {
|
||||||
|
Logger::instance()->addMessage(tr("Http request size exceeds limiation, closing socket. Limit: %ld, IP: %s")
|
||||||
|
.arg(bufferLimit).arg(m_socket->peerAddress().toString()), Log::WARNING);
|
||||||
|
|
||||||
case RequestParser::NoError:
|
Response resp(413, "Payload Too Large");
|
||||||
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
|
resp.headers[HEADER_CONNECTION] = "close";
|
||||||
|
|
||||||
Response response = m_requestHandler->processRequest(request, env);
|
sendResponse(resp);
|
||||||
if (acceptsGzipEncoding(request.headers["accept-encoding"]))
|
m_socket->close();
|
||||||
response.headers[HEADER_CONTENT_ENCODING] = "gzip";
|
}
|
||||||
sendResponse(response);
|
}
|
||||||
m_receivedData.clear();
|
return;
|
||||||
break;
|
|
||||||
|
case RequestParser::ParseStatus::BadRequest: {
|
||||||
|
Logger::instance()->addMessage(tr("Bad Http request, closing socket. IP: %s")
|
||||||
|
.arg(m_socket->peerAddress().toString()), Log::WARNING);
|
||||||
|
|
||||||
|
Response resp(400, "Bad Request");
|
||||||
|
resp.headers[HEADER_CONNECTION] = "close";
|
||||||
|
|
||||||
|
sendResponse(resp);
|
||||||
|
m_socket->close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case RequestParser::ParseStatus::OK: {
|
||||||
|
const Environment env {m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort()};
|
||||||
|
|
||||||
|
Response resp = m_requestHandler->processRequest(result.request, env);
|
||||||
|
|
||||||
|
if (acceptsGzipEncoding(result.request.headers["accept-encoding"]))
|
||||||
|
resp.headers[HEADER_CONTENT_ENCODING] = "gzip";
|
||||||
|
|
||||||
|
resp.headers[HEADER_CONNECTION] = "keep-alive";
|
||||||
|
|
||||||
|
sendResponse(resp);
|
||||||
|
m_receivedData = m_receivedData.mid(result.frameSize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::sendResponse(const Response &response)
|
void Connection::sendResponse(const Response &response) const
|
||||||
{
|
{
|
||||||
m_socket->write(toByteArray(response));
|
m_socket->write(toByteArray(response));
|
||||||
m_socket->close(); // TODO: remove when HTTP pipelining is supported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::hasExpired(const qint64 timeout) const
|
bool Connection::hasExpired(const qint64 timeout) const
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -25,8 +25,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -50,7 +48,7 @@ namespace Http
|
|||||||
Q_DISABLE_COPY(Connection)
|
Q_DISABLE_COPY(Connection)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = 0);
|
Connection(QTcpSocket *socket, IRequestHandler *requestHandler, QObject *parent = nullptr);
|
||||||
~Connection();
|
~Connection();
|
||||||
|
|
||||||
bool hasExpired(qint64 timeout) const;
|
bool hasExpired(qint64 timeout) const;
|
||||||
@@ -61,7 +59,7 @@ namespace Http
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static bool acceptsGzipEncoding(QString codings);
|
static bool acceptsGzipEncoding(QString codings);
|
||||||
void sendResponse(const Response &response);
|
void sendResponse(const Response &response) const;
|
||||||
|
|
||||||
QTcpSocket *m_socket;
|
QTcpSocket *m_socket;
|
||||||
IRequestHandler *m_requestHandler;
|
IRequestHandler *m_requestHandler;
|
||||||
|
|||||||
81
src/base/http/httperror.cpp
Normal file
81
src/base/http/httperror.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2017 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 "httperror.h"
|
||||||
|
|
||||||
|
HTTPError::HTTPError(int statusCode, const QString &statusText, const QString &message)
|
||||||
|
: RuntimeError {message}
|
||||||
|
, m_statusCode {statusCode}
|
||||||
|
, m_statusText {statusText}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int HTTPError::statusCode() const
|
||||||
|
{
|
||||||
|
return m_statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString HTTPError::statusText() const
|
||||||
|
{
|
||||||
|
return m_statusText;
|
||||||
|
}
|
||||||
|
|
||||||
|
BadRequestHTTPError::BadRequestHTTPError(const QString &message)
|
||||||
|
: HTTPError(400, QLatin1String("Bad Request"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConflictHTTPError::ConflictHTTPError(const QString &message)
|
||||||
|
: HTTPError(409, QLatin1String("Conflict"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ForbiddenHTTPError::ForbiddenHTTPError(const QString &message)
|
||||||
|
: HTTPError(403, QLatin1String("Forbidden"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
NotFoundHTTPError::NotFoundHTTPError(const QString &message)
|
||||||
|
: HTTPError(404, QLatin1String("Not Found"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UnsupportedMediaTypeHTTPError::UnsupportedMediaTypeHTTPError(const QString &message)
|
||||||
|
: HTTPError(415, QLatin1String("Unsupported Media Type"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UnauthorizedHTTPError::UnauthorizedHTTPError(const QString &message)
|
||||||
|
: HTTPError(401, QLatin1String("Unauthorized"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalServerErrorHTTPError::InternalServerErrorHTTPError(const QString &message)
|
||||||
|
: HTTPError(500, QLatin1String("Internal Server Error"), message)
|
||||||
|
{
|
||||||
|
}
|
||||||
86
src/base/http/httperror.h
Normal file
86
src/base/http/httperror.h
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/exceptions.h"
|
||||||
|
|
||||||
|
class HTTPError : public RuntimeError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HTTPError(int statusCode, const QString &statusText, const QString &message = "");
|
||||||
|
|
||||||
|
int statusCode() const;
|
||||||
|
QString statusText() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const int m_statusCode;
|
||||||
|
const QString m_statusText;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BadRequestHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit BadRequestHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class ForbiddenHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ForbiddenHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class NotFoundHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NotFoundHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConflictHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ConflictHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnsupportedMediaTypeHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit UnsupportedMediaTypeHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnauthorizedHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit UnauthorizedHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
|
|
||||||
|
class InternalServerErrorHTTPError : public HTTPError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit InternalServerErrorHTTPError(const QString &message = "");
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
@@ -30,242 +31,215 @@
|
|||||||
#include "requestparser.h"
|
#include "requestparser.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
const QByteArray EOL("\r\n");
|
#include "base/utils/bytearray.h"
|
||||||
const QByteArray EOH("\r\n\r\n");
|
#include "base/utils/string.h"
|
||||||
|
|
||||||
inline QString unquoted(const QString &str)
|
|
||||||
{
|
|
||||||
if ((str[0] == '\"') && (str[str.length() - 1] == '\"'))
|
|
||||||
return str.mid(1, str.length() - 2);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
using namespace Http;
|
using namespace Http;
|
||||||
|
using namespace Utils::ByteArray;
|
||||||
|
using QStringPair = QPair<QString, QString>;
|
||||||
|
|
||||||
RequestParser::ErrorCode RequestParser::parse(const QByteArray &data, Request &request, uint maxContentLength)
|
namespace
|
||||||
{
|
{
|
||||||
return RequestParser(maxContentLength).parseHttpRequest(data, request);
|
const QByteArray EOH = QByteArray(CRLF).repeated(2);
|
||||||
}
|
|
||||||
|
|
||||||
RequestParser::RequestParser(uint maxContentLength)
|
const QByteArray viewWithoutEndingWith(const QByteArray &in, const QByteArray &str)
|
||||||
: m_maxContentLength(maxContentLength)
|
{
|
||||||
{
|
if (in.endsWith(str))
|
||||||
}
|
return QByteArray::fromRawData(in.constData(), (in.size() - str.size()));
|
||||||
|
return in;
|
||||||
RequestParser::ErrorCode RequestParser::parseHttpRequest(const QByteArray &data, Request &request)
|
|
||||||
{
|
|
||||||
m_request = Request();
|
|
||||||
|
|
||||||
// Parse HTTP request header
|
|
||||||
const int headerEnd = data.indexOf(EOH);
|
|
||||||
if (headerEnd < 0) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "incomplete request";
|
|
||||||
return IncompleteRequest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parseHttpHeader(data.left(headerEnd))) {
|
bool parseHeaderLine(const QString &line, QStringMap &out)
|
||||||
qWarning() << Q_FUNC_INFO << "header parsing error";
|
{
|
||||||
return BadRequest;
|
// [rfc7230] 3.2. Header Fields
|
||||||
}
|
const int i = line.indexOf(':');
|
||||||
|
if (i <= 0) {
|
||||||
// Parse HTTP request message
|
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
|
||||||
if (m_request.headers.contains("content-length")) {
|
return false;
|
||||||
int contentLength = m_request.headers["content-length"].toInt();
|
|
||||||
if (contentLength < 0) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "bad request: content-length is negative";
|
|
||||||
return BadRequest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentLength > static_cast<int>(m_maxContentLength)) {
|
const QString name = line.leftRef(i).trimmed().toString().toLower();
|
||||||
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
const QString value = line.midRef(i + 1).trimmed().toString();
|
||||||
return BadRequest;
|
out[name] = value;
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray content = data.mid(headerEnd + EOH.length(), contentLength);
|
|
||||||
if (content.length() < contentLength) {
|
|
||||||
qDebug() << Q_FUNC_INFO << "incomplete request";
|
|
||||||
return IncompleteRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((contentLength > 0) && !parseContent(content)) {
|
|
||||||
qWarning() << Q_FUNC_INFO << "message parsing error";
|
|
||||||
return BadRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// qDebug() << Q_FUNC_INFO;
|
|
||||||
// qDebug() << "HTTP Request header:";
|
|
||||||
// qDebug() << data.left(headerEnd) << "\n";
|
|
||||||
|
|
||||||
request = m_request;
|
|
||||||
return NoError;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestParser::parseStartingLine(const QString &line)
|
|
||||||
{
|
|
||||||
const QRegExp rx("^([A-Z]+)\\s+(\\S+)\\s+HTTP/\\d\\.\\d$");
|
|
||||||
|
|
||||||
if (rx.indexIn(line.trimmed()) >= 0) {
|
|
||||||
m_request.method = rx.cap(1);
|
|
||||||
|
|
||||||
QUrl url = QUrl::fromEncoded(rx.cap(2).toLatin1());
|
|
||||||
m_request.path = url.path(); // Path
|
|
||||||
|
|
||||||
// Parse GET parameters
|
|
||||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems());
|
|
||||||
while (i.hasNext()) {
|
|
||||||
QPair<QString, QString> pair = i.next();
|
|
||||||
m_request.gets[pair.first] = pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestParser::parseHeaderLine(const QString &line, QPair<QString, QString> &out)
|
RequestParser::RequestParser()
|
||||||
{
|
{
|
||||||
int i = line.indexOf(QLatin1Char(':'));
|
}
|
||||||
if (i == -1) {
|
|
||||||
|
RequestParser::ParseResult RequestParser::parse(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// Warning! Header names are converted to lowercase
|
||||||
|
return RequestParser().doParse(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestParser::ParseResult RequestParser::doParse(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// we don't handle malformed requests which use double `LF` as delimiter
|
||||||
|
const int headerEnd = data.indexOf(EOH);
|
||||||
|
if (headerEnd < 0) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "incomplete request";
|
||||||
|
return {ParseStatus::Incomplete, Request(), 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString httpHeaders = QString::fromLatin1(data.constData(), headerEnd);
|
||||||
|
if (!parseStartLines(httpHeaders)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "header parsing error";
|
||||||
|
return {ParseStatus::BadRequest, Request(), 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const int headerLength = headerEnd + EOH.length();
|
||||||
|
|
||||||
|
// handle supported methods
|
||||||
|
if ((m_request.method == HEADER_REQUEST_METHOD_GET) || (m_request.method == HEADER_REQUEST_METHOD_HEAD))
|
||||||
|
return {ParseStatus::OK, m_request, headerLength};
|
||||||
|
if (m_request.method == HEADER_REQUEST_METHOD_POST) {
|
||||||
|
bool ok = false;
|
||||||
|
const int contentLength = m_request.headers[HEADER_CONTENT_LENGTH].toInt(&ok);
|
||||||
|
if (!ok || (contentLength < 0)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "bad request: content-length invalid";
|
||||||
|
return {ParseStatus::BadRequest, Request(), 0};
|
||||||
|
}
|
||||||
|
if (contentLength > MAX_CONTENT_SIZE) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "bad request: message too long";
|
||||||
|
return {ParseStatus::BadRequest, Request(), 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentLength > 0) {
|
||||||
|
const QByteArray httpBodyView = midView(data, headerLength, contentLength);
|
||||||
|
if (httpBodyView.length() < contentLength) {
|
||||||
|
qDebug() << Q_FUNC_INFO << "incomplete request";
|
||||||
|
return {ParseStatus::Incomplete, Request(), 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parsePostMessage(httpBodyView)) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "message body parsing error";
|
||||||
|
return {ParseStatus::BadRequest, Request(), 0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {ParseStatus::OK, m_request, (headerLength + contentLength)};
|
||||||
|
}
|
||||||
|
|
||||||
|
qWarning() << Q_FUNC_INFO << "unsupported request method: " << m_request.method;
|
||||||
|
return {ParseStatus::BadRequest, Request(), 0}; // TODO: SHOULD respond "501 Not Implemented"
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestParser::parseStartLines(const QString &data)
|
||||||
|
{
|
||||||
|
// we don't handle malformed request which uses `LF` for newline
|
||||||
|
const QVector<QStringRef> lines = data.splitRef(CRLF, QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
// [rfc7230] 3.2.2. Field Order
|
||||||
|
QStringList requestLines;
|
||||||
|
for (const auto &line : lines) {
|
||||||
|
if (line.at(0).isSpace() && !requestLines.isEmpty()) {
|
||||||
|
// continuation of previous line
|
||||||
|
requestLines.last() += line.toString();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
requestLines += line.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestLines.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!parseRequestLine(requestLines[0]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto i = ++(requestLines.begin()); i != requestLines.end(); ++i) {
|
||||||
|
if (!parseHeaderLine(*i, m_request.headers))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RequestParser::parseRequestLine(const QString &line)
|
||||||
|
{
|
||||||
|
// [rfc7230] 3.1.1. Request Line
|
||||||
|
|
||||||
|
const QRegularExpression re(QLatin1String("^([A-Z]+)\\s+(\\S+)\\s+HTTP\\/(\\d\\.\\d)$"));
|
||||||
|
const QRegularExpressionMatch match = re.match(line);
|
||||||
|
|
||||||
|
if (!match.hasMatch()) {
|
||||||
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
|
qWarning() << Q_FUNC_INFO << "invalid http header:" << line;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
out = qMakePair(line.left(i).trimmed().toLower(), line.mid(i + 1).trimmed());
|
// Request Methods
|
||||||
return true;
|
m_request.method = match.captured(1);
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestParser::parseHttpHeader(const QByteArray &data)
|
// Request Target
|
||||||
{
|
const QByteArray decodedUrl {QByteArray::fromPercentEncoding(match.captured(2).toLatin1())};
|
||||||
QString str = QString::fromUtf8(data);
|
const int sepPos = decodedUrl.indexOf('?');
|
||||||
QStringList lines = str.trimmed().split(EOL);
|
m_request.path = QString::fromUtf8(decodedUrl.constData(), (sepPos == -1 ? decodedUrl.size() : sepPos));
|
||||||
|
if (sepPos >= 0)
|
||||||
|
m_request.query = decodedUrl.mid(sepPos + 1);
|
||||||
|
|
||||||
QStringList headerLines;
|
// HTTP-version
|
||||||
foreach (const QString &line, lines) {
|
m_request.version = match.captured(3);
|
||||||
if (line[0].isSpace()) { // header line continuation
|
|
||||||
if (!headerLines.isEmpty()) { // really continuation
|
|
||||||
headerLines.last() += QLatin1Char(' ');
|
|
||||||
headerLines.last() += line.trimmed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
headerLines.append(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (headerLines.isEmpty())
|
|
||||||
return false; // Empty header
|
|
||||||
|
|
||||||
QStringList::Iterator it = headerLines.begin();
|
|
||||||
if (!parseStartingLine(*it))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
++it;
|
|
||||||
for (; it != headerLines.end(); ++it) {
|
|
||||||
QPair<QString, QString> header;
|
|
||||||
if (!parseHeaderLine(*it, header))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_request.headers[header.first] = header.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QByteArray> RequestParser::splitMultipartData(const QByteArray &data, const QByteArray &boundary)
|
bool RequestParser::parsePostMessage(const QByteArray &data)
|
||||||
{
|
{
|
||||||
QList<QByteArray> ret;
|
// parse POST message-body
|
||||||
QByteArray sep = boundary + EOL;
|
const QString contentType = m_request.headers[HEADER_CONTENT_TYPE];
|
||||||
const int sepLength = sep.size();
|
const QString contentTypeLower = contentType.toLower();
|
||||||
|
|
||||||
int start = 0, end = 0;
|
// application/x-www-form-urlencoded
|
||||||
if ((end = data.indexOf(sep, start)) >= 0) {
|
if (contentTypeLower.startsWith(CONTENT_TYPE_FORM_ENCODED)) {
|
||||||
start = end + sepLength; // skip first boundary
|
QListIterator<QStringPair> i(QUrlQuery(data).queryItems(QUrl::FullyDecoded));
|
||||||
|
|
||||||
while ((end = data.indexOf(sep, start)) >= 0) {
|
|
||||||
ret << data.mid(start, end - EOL.length() - start);
|
|
||||||
start = end + sepLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
// last or single part
|
|
||||||
sep = boundary + "--" + EOL;
|
|
||||||
if ((end = data.indexOf(sep, start)) >= 0)
|
|
||||||
ret << data.mid(start, end - EOL.length() - start);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestParser::parseContent(const QByteArray &data)
|
|
||||||
{
|
|
||||||
// Parse message content
|
|
||||||
qDebug() << Q_FUNC_INFO << "Content-Length: " << m_request.headers["content-length"];
|
|
||||||
qDebug() << Q_FUNC_INFO << "data.size(): " << data.size();
|
|
||||||
|
|
||||||
// Parse url-encoded POST data
|
|
||||||
if (m_request.headers["content-type"].startsWith("application/x-www-form-urlencoded")) {
|
|
||||||
QUrl url;
|
|
||||||
url.setQuery(data);
|
|
||||||
QListIterator<QPair<QString, QString> > i(QUrlQuery(url).queryItems(QUrl::FullyDecoded));
|
|
||||||
while (i.hasNext()) {
|
while (i.hasNext()) {
|
||||||
QPair<QString, QString> pair = i.next();
|
const QStringPair pair = i.next();
|
||||||
m_request.posts[pair.first.toLower()] = pair.second;
|
m_request.posts[pair.first] = pair.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse multipart/form data (torrent file)
|
// multipart/form-data
|
||||||
/**
|
if (contentTypeLower.startsWith(CONTENT_TYPE_FORM_DATA)) {
|
||||||
data has the following format (if boundary is "cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5")
|
// [rfc2046] 5.1.1. Common Syntax
|
||||||
|
|
||||||
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
|
// find boundary delimiter
|
||||||
Content-Disposition: form-data; name=\"Filename\"
|
const QLatin1String boundaryFieldName("boundary=");
|
||||||
|
const int idx = contentType.indexOf(boundaryFieldName);
|
||||||
PB020344.torrent
|
if (idx < 0) {
|
||||||
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
|
qWarning() << Q_FUNC_INFO << "Could not find boundary in multipart/form-data header!";
|
||||||
Content-Disposition: form-data; name=\"torrentfile"; filename=\"PB020344.torrent\"
|
return false;
|
||||||
Content-Type: application/x-bittorrent
|
|
||||||
|
|
||||||
BINARY DATA IS HERE
|
|
||||||
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
|
|
||||||
Content-Disposition: form-data; name=\"Upload\"
|
|
||||||
|
|
||||||
Submit Query
|
|
||||||
--cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--
|
|
||||||
**/
|
|
||||||
QString contentType = m_request.headers["content-type"];
|
|
||||||
if (contentType.startsWith("multipart/form-data")) {
|
|
||||||
const QRegExp boundaryRegexQuoted("boundary=\"([ \\w'()+,-\\./:=\\?]+)\"");
|
|
||||||
const QRegExp boundaryRegexNotQuoted("boundary=([\\w'()+,-\\./:=\\?]+)");
|
|
||||||
|
|
||||||
QByteArray boundary;
|
|
||||||
if (boundaryRegexQuoted.indexIn(contentType) < 0) {
|
|
||||||
if (boundaryRegexNotQuoted.indexIn(contentType) < 0) {
|
|
||||||
qWarning() << "Could not find boundary in multipart/form-data header!";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
boundary = "--" + boundaryRegexNotQuoted.cap(1).toLatin1();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
boundary = "--" + boundaryRegexQuoted.cap(1).toLatin1();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Boundary is " << boundary;
|
const QByteArray delimiter = Utils::String::unquote(contentType.midRef(idx + boundaryFieldName.size())).toLatin1();
|
||||||
QList<QByteArray> parts = splitMultipartData(data, boundary);
|
if (delimiter.isEmpty()) {
|
||||||
qDebug() << parts.size() << "parts in data";
|
qWarning() << Q_FUNC_INFO << "boundary delimiter field empty!";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (const QByteArray& part, parts) {
|
// split data by "dash-boundary"
|
||||||
|
const QByteArray dashDelimiter = QByteArray("--") + delimiter + CRLF;
|
||||||
|
QList<QByteArray> multipart = splitToViews(data, dashDelimiter, QString::SkipEmptyParts);
|
||||||
|
if (multipart.isEmpty()) {
|
||||||
|
qWarning() << Q_FUNC_INFO << "multipart empty";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the ending delimiter
|
||||||
|
const QByteArray endDelimiter = QByteArray("--") + delimiter + QByteArray("--") + CRLF;
|
||||||
|
multipart.push_back(viewWithoutEndingWith(multipart.takeLast(), endDelimiter));
|
||||||
|
|
||||||
|
for (const auto &part : multipart) {
|
||||||
if (!parseFormData(part))
|
if (!parseFormData(part))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -273,71 +247,60 @@ Submit Query
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
qWarning() << Q_FUNC_INFO << "unknown content type:" << qPrintable(contentType);
|
qWarning() << Q_FUNC_INFO << "unknown content type:" << contentType;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RequestParser::parseFormData(const QByteArray &data)
|
bool RequestParser::parseFormData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
// Parse form data header
|
const QList<QByteArray> list = splitToViews(data, EOH, QString::KeepEmptyParts);
|
||||||
const int headerEnd = data.indexOf(EOH);
|
|
||||||
if (headerEnd < 0) {
|
if (list.size() != 2) {
|
||||||
qDebug() << "Invalid form data: \n" << data;
|
qWarning() << Q_FUNC_INFO << "multipart/form-data format error";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString headerStr = QString::fromUtf8(data.left(headerEnd));
|
const QString headers = QString::fromLatin1(list[0]);
|
||||||
QStringList lines = headerStr.trimmed().split(EOL);
|
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
|
||||||
QStringMap headers;
|
|
||||||
foreach (const QString& line, lines) {
|
|
||||||
QPair<QString, QString> header;
|
|
||||||
if (!parseHeaderLine(line, header))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
headers[header.first] = header.second;
|
QStringMap headersMap;
|
||||||
|
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
|
||||||
|
for (const auto &line : headerLines) {
|
||||||
|
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) {
|
||||||
|
// extract out filename & name
|
||||||
|
const QVector<QStringRef> directives = line.split(';', QString::SkipEmptyParts);
|
||||||
|
|
||||||
|
for (const auto &directive : directives) {
|
||||||
|
const int idx = directive.indexOf('=');
|
||||||
|
if (idx < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const QString name = directive.left(idx).trimmed().toString().toLower();
|
||||||
|
const QString value = Utils::String::unquote(directive.mid(idx + 1).trimmed()).toString();
|
||||||
|
headersMap[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!parseHeaderLine(line.toString(), headersMap))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringMap disposition;
|
// pick data
|
||||||
if (!headers.contains("content-disposition")
|
const QLatin1String filename("filename");
|
||||||
|| !parseHeaderValue(headers["content-disposition"], disposition)
|
const QLatin1String name("name");
|
||||||
|| !disposition.contains("name")) {
|
|
||||||
qDebug() << "Invalid form data header: \n" << headerStr;
|
if (headersMap.contains(filename)) {
|
||||||
return false;
|
m_request.files.append({headersMap[filename], headersMap[HEADER_CONTENT_TYPE], payload});
|
||||||
}
|
}
|
||||||
|
else if (headersMap.contains(name)) {
|
||||||
if (disposition.contains("filename")) {
|
m_request.posts[headersMap[name]] = payload;
|
||||||
UploadedFile ufile;
|
|
||||||
ufile.filename = disposition["filename"];
|
|
||||||
ufile.type = disposition["content-type"];
|
|
||||||
ufile.data = data.mid(headerEnd + EOH.length());
|
|
||||||
|
|
||||||
m_request.files.append(ufile);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_request.posts[disposition["name"]] = QString::fromUtf8(data.mid(headerEnd + EOH.length()));
|
// malformed
|
||||||
}
|
qWarning() << Q_FUNC_INFO << "multipart/form-data header error";
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RequestParser::parseHeaderValue(const QString &value, QStringMap &out)
|
|
||||||
{
|
|
||||||
int pos = value.indexOf(QLatin1Char(';'));
|
|
||||||
if (pos == -1) {
|
|
||||||
out[""] = value.trimmed();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
out[""] = value.left(pos).trimmed();
|
|
||||||
|
|
||||||
QRegExp rx(";\\s*([^=;\"]+)\\s*=\\s*(\"[^\"]*\"|[^\";\\s]+)\\s*");
|
|
||||||
while (rx.indexIn(value, pos) == pos) {
|
|
||||||
out[rx.cap(1).trimmed()] = unquoted(rx.cap(2));
|
|
||||||
pos += rx.cap(0).length();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos != value.length())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Ishan Arora and Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
@@ -37,32 +38,35 @@ namespace Http
|
|||||||
class RequestParser
|
class RequestParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum ErrorCode
|
enum class ParseStatus
|
||||||
{
|
{
|
||||||
NoError = 0,
|
OK,
|
||||||
IncompleteRequest,
|
Incomplete,
|
||||||
BadRequest
|
BadRequest
|
||||||
};
|
};
|
||||||
|
|
||||||
// when result != NoError parsed request is undefined
|
struct ParseResult
|
||||||
// Warning! Header names are converted to lower-case.
|
{
|
||||||
static ErrorCode parse(const QByteArray &data, Request &request, uint maxContentLength = 10000000 /* ~10MB */);
|
// when `status != ParseStatus::OK`, `request` & `frameSize` are undefined
|
||||||
|
ParseStatus status;
|
||||||
|
Request request;
|
||||||
|
long frameSize; // http request frame size (bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
static ParseResult parse(const QByteArray &data);
|
||||||
|
|
||||||
|
static const long MAX_CONTENT_SIZE = 64 * 1024 * 1024; // 64 MB
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RequestParser(uint maxContentLength);
|
RequestParser();
|
||||||
|
|
||||||
ErrorCode parseHttpRequest(const QByteArray &data, Request &request);
|
ParseResult doParse(const QByteArray &data);
|
||||||
|
bool parseStartLines(const QString &data);
|
||||||
|
bool parseRequestLine(const QString &line);
|
||||||
|
|
||||||
bool parseHttpHeader(const QByteArray &data);
|
bool parsePostMessage(const QByteArray &data);
|
||||||
bool parseStartingLine(const QString &line);
|
|
||||||
bool parseContent(const QByteArray &data);
|
|
||||||
bool parseFormData(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;
|
Request m_request;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,6 @@
|
|||||||
|
|
||||||
using namespace Http;
|
using namespace Http;
|
||||||
|
|
||||||
ResponseBuilder::ResponseBuilder(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ResponseBuilder::status(uint code, const QString &text)
|
void ResponseBuilder::status(uint code, const QString &text)
|
||||||
{
|
{
|
||||||
m_response.status = ResponseStatus(code, text);
|
m_response.status = ResponseStatus(code, text);
|
||||||
|
|||||||
@@ -29,17 +29,13 @@
|
|||||||
#ifndef HTTP_RESPONSEBUILDER_H
|
#ifndef HTTP_RESPONSEBUILDER_H
|
||||||
#define HTTP_RESPONSEBUILDER_H
|
#define HTTP_RESPONSEBUILDER_H
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
class ResponseBuilder : public QObject
|
class ResponseBuilder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ResponseBuilder(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||||
void header(const QString &name, const QString &value);
|
void header(const QString &name, const QString &value);
|
||||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2018 Mike Tzou (Chocobo1)
|
||||||
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -37,7 +38,12 @@
|
|||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
|
const char METHOD_GET[] = "GET";
|
||||||
|
const char METHOD_POST[] = "POST";
|
||||||
|
|
||||||
const char HEADER_CACHE_CONTROL[] = "cache-control";
|
const char HEADER_CACHE_CONTROL[] = "cache-control";
|
||||||
|
const char HEADER_CONNECTION[] = "connection";
|
||||||
|
const char HEADER_CONTENT_DISPOSITION[] = "content-disposition";
|
||||||
const char HEADER_CONTENT_ENCODING[] = "content-encoding";
|
const char HEADER_CONTENT_ENCODING[] = "content-encoding";
|
||||||
const char HEADER_CONTENT_LENGTH[] = "content-length";
|
const char HEADER_CONTENT_LENGTH[] = "content-length";
|
||||||
const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy";
|
const char HEADER_CONTENT_SECURITY_POLICY[] = "content-security-policy";
|
||||||
@@ -52,16 +58,21 @@ namespace Http
|
|||||||
const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options";
|
const char HEADER_X_FRAME_OPTIONS[] = "x-frame-options";
|
||||||
const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection";
|
const char HEADER_X_XSS_PROTECTION[] = "x-xss-protection";
|
||||||
|
|
||||||
const char CONTENT_TYPE_CSS[] = "text/css; charset=UTF-8";
|
const char HEADER_REQUEST_METHOD_GET[] = "GET";
|
||||||
const char CONTENT_TYPE_GIF[] = "image/gif";
|
const char HEADER_REQUEST_METHOD_HEAD[] = "HEAD";
|
||||||
const char CONTENT_TYPE_HTML[] = "text/html; charset=UTF-8";
|
const char HEADER_REQUEST_METHOD_POST[] = "POST";
|
||||||
const char CONTENT_TYPE_JS[] = "application/javascript; charset=UTF-8";
|
|
||||||
const char CONTENT_TYPE_JSON[] = "application/json";
|
|
||||||
const char CONTENT_TYPE_PNG[] = "image/png";
|
|
||||||
const char CONTENT_TYPE_TXT[] = "text/plain; charset=UTF-8";
|
|
||||||
const char CONTENT_TYPE_SVG[] = "image/svg+xml";
|
|
||||||
|
|
||||||
// portability: "\r\n" doesn't guarantee mapping to the correct value
|
const char CONTENT_TYPE_HTML[] = "text/html";
|
||||||
|
const char CONTENT_TYPE_CSS[] = "text/css";
|
||||||
|
const char CONTENT_TYPE_TXT[] = "text/plain";
|
||||||
|
const char CONTENT_TYPE_JS[] = "application/javascript";
|
||||||
|
const char CONTENT_TYPE_JSON[] = "application/json";
|
||||||
|
const char CONTENT_TYPE_GIF[] = "image/gif";
|
||||||
|
const char CONTENT_TYPE_PNG[] = "image/png";
|
||||||
|
const char CONTENT_TYPE_FORM_ENCODED[] = "application/x-www-form-urlencoded";
|
||||||
|
const char CONTENT_TYPE_FORM_DATA[] = "multipart/form-data";
|
||||||
|
|
||||||
|
// portability: "\r\n" doesn't guarantee mapping to the correct symbol
|
||||||
const char CRLF[] = {0x0D, 0x0A, '\0'};
|
const char CRLF[] = {0x0D, 0x0A, '\0'};
|
||||||
|
|
||||||
struct Environment
|
struct Environment
|
||||||
@@ -75,17 +86,18 @@ namespace Http
|
|||||||
|
|
||||||
struct UploadedFile
|
struct UploadedFile
|
||||||
{
|
{
|
||||||
QString filename; // original filename
|
QString filename;
|
||||||
QString type; // MIME type
|
QString type; // MIME type
|
||||||
QByteArray data; // File data
|
QByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
|
QString version;
|
||||||
QString method;
|
QString method;
|
||||||
QString path;
|
QString path;
|
||||||
|
QByteArray query;
|
||||||
QStringMap headers;
|
QStringMap headers;
|
||||||
QStringMap gets;
|
|
||||||
QStringMap posts;
|
QStringMap posts;
|
||||||
QVector<UploadedFile> files;
|
QVector<UploadedFile> files;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include "iconprovider.h"
|
#include "iconprovider.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
IconProvider::IconProvider(QObject *parent)
|
IconProvider::IconProvider(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
@@ -47,7 +48,7 @@ void IconProvider::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,9 +57,15 @@ IconProvider *IconProvider::instance()
|
|||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString IconProvider::getIconPath(const QString &iconId)
|
QString IconProvider::getIconPath(const QString &iconId) const
|
||||||
{
|
{
|
||||||
return ":/icons/qbt-theme/" + iconId + ".png";
|
// there are a few icons not available in svg
|
||||||
|
const QString pathSvg = ":/icons/qbt-theme/" + iconId + ".svg";
|
||||||
|
if (QFileInfo::exists(pathSvg))
|
||||||
|
return pathSvg;
|
||||||
|
|
||||||
|
const QString pathPng = ":/icons/qbt-theme/" + iconId + ".png";
|
||||||
|
return pathPng;
|
||||||
}
|
}
|
||||||
|
|
||||||
IconProvider *IconProvider::m_instance = 0;
|
IconProvider *IconProvider::m_instance = nullptr;
|
||||||
|
|||||||
@@ -31,8 +31,7 @@
|
|||||||
#define ICONPROVIDER_H
|
#define ICONPROVIDER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
class QString;
|
|
||||||
|
|
||||||
class IconProvider : public QObject
|
class IconProvider : public QObject
|
||||||
{
|
{
|
||||||
@@ -43,10 +42,10 @@ public:
|
|||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static IconProvider *instance();
|
static IconProvider *instance();
|
||||||
|
|
||||||
virtual QString getIconPath(const QString &iconId);
|
virtual QString getIconPath(const QString &iconId) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit IconProvider(QObject *parent = 0);
|
explicit IconProvider(QObject *parent = nullptr);
|
||||||
~IconProvider();
|
~IconProvider();
|
||||||
|
|
||||||
static IconProvider *m_instance;
|
static IconProvider *m_instance;
|
||||||
|
|||||||
@@ -38,30 +38,30 @@ class IndexInterval
|
|||||||
public:
|
public:
|
||||||
using IndexType = Index;
|
using IndexType = Index;
|
||||||
|
|
||||||
IndexInterval(IndexType first, IndexType last)
|
IndexInterval(IndexType first, IndexType last) // add constexpr when using C++14
|
||||||
: m_first {first}
|
: m_first {first}
|
||||||
, m_last {last}
|
, m_last {last}
|
||||||
{
|
{
|
||||||
Q_ASSERT(first <= last);
|
Q_ASSERT(first <= last);
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexType first() const
|
constexpr IndexType first() const
|
||||||
{
|
{
|
||||||
return m_first;
|
return m_first;
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexType last() const
|
constexpr IndexType last() const
|
||||||
{
|
{
|
||||||
return m_last;
|
return m_last;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IndexType m_first;
|
const IndexType m_first;
|
||||||
IndexType m_last;
|
const IndexType m_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline IndexInterval<T> makeInterval(T first, T last)
|
constexpr IndexInterval<T> makeInterval(T first, T last)
|
||||||
{
|
{
|
||||||
return {first, last};
|
return {first, last};
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ public:
|
|||||||
|
|
||||||
constexpr IndexType end() const
|
constexpr IndexType end() const
|
||||||
{
|
{
|
||||||
return m_first + m_size;
|
return (m_first + m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr IndexDiffType size() const
|
constexpr IndexDiffType size() const
|
||||||
@@ -114,12 +114,12 @@ public:
|
|||||||
|
|
||||||
constexpr IndexType last() const
|
constexpr IndexType last() const
|
||||||
{
|
{
|
||||||
return m_first + m_size - 1;
|
return (m_first + m_size - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool isEmpty() const
|
constexpr bool isEmpty() const
|
||||||
{
|
{
|
||||||
return m_size == 0;
|
return (m_size == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
|
||||||
Logger* Logger::m_instance = 0;
|
Logger *Logger::m_instance = nullptr;
|
||||||
|
|
||||||
Logger::Logger()
|
Logger::Logger()
|
||||||
: lock(QReadWriteLock::Recursive)
|
: m_lock(QReadWriteLock::Recursive)
|
||||||
, msgCounter(0)
|
, m_msgCounter(0)
|
||||||
, peerCounter(0)
|
, m_peerCounter(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,15 +29,15 @@ void Logger::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&lock);
|
QWriteLocker locker(&m_lock);
|
||||||
|
|
||||||
Log::Msg temp = { msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped() };
|
Log::Msg temp = {m_msgCounter++, QDateTime::currentMSecsSinceEpoch(), type, message.toHtmlEscaped()};
|
||||||
m_messages.push_back(temp);
|
m_messages.push_back(temp);
|
||||||
|
|
||||||
if (m_messages.size() >= MAX_LOG_MESSAGES)
|
if (m_messages.size() >= MAX_LOG_MESSAGES)
|
||||||
@@ -48,9 +48,9 @@ void Logger::addMessage(const QString &message, const Log::MsgType &type)
|
|||||||
|
|
||||||
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&lock);
|
QWriteLocker locker(&m_lock);
|
||||||
|
|
||||||
Log::Peer temp = { peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped() };
|
Log::Peer temp = {m_peerCounter++, QDateTime::currentMSecsSinceEpoch(), ip.toHtmlEscaped(), blocked, reason.toHtmlEscaped()};
|
||||||
m_peers.push_back(temp);
|
m_peers.push_back(temp);
|
||||||
|
|
||||||
if (m_peers.size() >= MAX_LOG_MESSAGES)
|
if (m_peers.size() >= MAX_LOG_MESSAGES)
|
||||||
@@ -61,9 +61,9 @@ void Logger::addPeer(const QString &ip, bool blocked, const QString &reason)
|
|||||||
|
|
||||||
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
||||||
{
|
{
|
||||||
QReadLocker locker(&lock);
|
QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
int diff = msgCounter - lastKnownId - 1;
|
int diff = m_msgCounter - lastKnownId - 1;
|
||||||
int size = m_messages.size();
|
int size = m_messages.size();
|
||||||
|
|
||||||
if ((lastKnownId == -1) || (diff >= size))
|
if ((lastKnownId == -1) || (diff >= size))
|
||||||
@@ -77,9 +77,9 @@ QVector<Log::Msg> Logger::getMessages(int lastKnownId) const
|
|||||||
|
|
||||||
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
|
QVector<Log::Peer> Logger::getPeers(int lastKnownId) const
|
||||||
{
|
{
|
||||||
QReadLocker locker(&lock);
|
QReadLocker locker(&m_lock);
|
||||||
|
|
||||||
int diff = peerCounter - lastKnownId - 1;
|
int diff = m_peerCounter - lastKnownId - 1;
|
||||||
int size = m_peers.size();
|
int size = m_peers.size();
|
||||||
|
|
||||||
if ((lastKnownId == -1) || (diff >= size))
|
if ((lastKnownId == -1) || (diff >= size))
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#ifndef LOGGER_H
|
#ifndef LOGGER_H
|
||||||
#define LOGGER_H
|
#define LOGGER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QReadWriteLock>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QReadWriteLock>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
const int MAX_LOG_MESSAGES = 20000;
|
const int MAX_LOG_MESSAGES = 20000;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ namespace Log
|
|||||||
NORMAL = 0x1,
|
NORMAL = 0x1,
|
||||||
INFO = 0x2,
|
INFO = 0x2,
|
||||||
WARNING = 0x4,
|
WARNING = 0x4,
|
||||||
CRITICAL = 0x8 //ERROR is defined by libtorrent and results in compiler error
|
CRITICAL = 0x8 // ERROR is defined by libtorrent and results in compiler error
|
||||||
};
|
};
|
||||||
Q_DECLARE_FLAGS(MsgTypes, MsgType)
|
Q_DECLARE_FLAGS(MsgTypes, MsgType)
|
||||||
|
|
||||||
@@ -63,12 +63,12 @@ private:
|
|||||||
Logger();
|
Logger();
|
||||||
~Logger();
|
~Logger();
|
||||||
|
|
||||||
static Logger* m_instance;
|
static Logger *m_instance;
|
||||||
QVector<Log::Msg> m_messages;
|
QVector<Log::Msg> m_messages;
|
||||||
QVector<Log::Peer> m_peers;
|
QVector<Log::Peer> m_peers;
|
||||||
mutable QReadWriteLock lock;
|
mutable QReadWriteLock m_lock;
|
||||||
int msgCounter;
|
int m_msgCounter;
|
||||||
int peerCounter;
|
int m_peerCounter;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper function
|
// Helper function
|
||||||
|
|||||||
@@ -26,15 +26,15 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "dnsupdater.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QRegExp>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/downloadhandler.h"
|
#include "base/net/downloadhandler.h"
|
||||||
#include "base/net/downloadmanager.h"
|
#include "base/net/downloadmanager.h"
|
||||||
#include "dnsupdater.h"
|
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ DNSUpdater::DNSUpdater(QObject *parent)
|
|||||||
|
|
||||||
// Start IP checking timer
|
// Start IP checking timer
|
||||||
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
m_ipCheckTimer.setInterval(IP_CHECK_INTERVAL_MS);
|
||||||
connect(&m_ipCheckTimer, SIGNAL(timeout()), SLOT(checkPublicIP()));
|
connect(&m_ipCheckTimer, &QTimer::timeout, this, &DNSUpdater::checkPublicIP);
|
||||||
m_ipCheckTimer.start();
|
m_ipCheckTimer.start();
|
||||||
|
|
||||||
// Check lastUpdate to avoid flooding
|
// Check lastUpdate to avoid flooding
|
||||||
@@ -74,11 +74,11 @@ void DNSUpdater::checkPublicIP()
|
|||||||
{
|
{
|
||||||
Q_ASSERT(m_state == OK);
|
Q_ASSERT(m_state == OK);
|
||||||
|
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
|
DownloadHandler *handler = DownloadManager::instance()->download(
|
||||||
"http://checkip.dyndns.org", false, 0, false,
|
DownloadRequest("http://checkip.dyndns.org").userAgent("qBittorrent/" QBT_VERSION_2));
|
||||||
"qBittorrent/" QBT_VERSION_2);
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipRequestFinished(QString, QByteArray)));
|
, this, &DNSUpdater::ipRequestFinished);
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipRequestFailed(QString, QString)));
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipRequestFailed);
|
||||||
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
@@ -88,9 +88,9 @@ void DNSUpdater::ipRequestFinished(const QString &url, const QByteArray &data)
|
|||||||
Q_UNUSED(url);
|
Q_UNUSED(url);
|
||||||
|
|
||||||
// Parse response
|
// Parse response
|
||||||
QRegExp ipregex("Current IP Address:\\s+([^<]+)</body>");
|
const QRegularExpressionMatch ipRegexMatch = QRegularExpression("Current IP Address:\\s+([^<]+)</body>").match(data);
|
||||||
if (ipregex.indexIn(data) >= 0) {
|
if (ipRegexMatch.hasMatch()) {
|
||||||
QString ipStr = ipregex.cap(1);
|
QString ipStr = ipRegexMatch.captured(1);
|
||||||
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
|
qDebug() << Q_FUNC_INFO << "Regular expression captured the following IP:" << ipStr;
|
||||||
QHostAddress newIp(ipStr);
|
QHostAddress newIp(ipStr);
|
||||||
if (!newIp.isNull()) {
|
if (!newIp.isNull()) {
|
||||||
@@ -121,11 +121,11 @@ void DNSUpdater::updateDNSService()
|
|||||||
qDebug() << Q_FUNC_INFO;
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
m_lastIPCheckTime = QDateTime::currentDateTime();
|
m_lastIPCheckTime = QDateTime::currentDateTime();
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(
|
DownloadHandler *handler = DownloadManager::instance()->download(
|
||||||
getUpdateUrl(), false, 0, false,
|
DownloadRequest(getUpdateUrl()).userAgent("qBittorrent/" QBT_VERSION_2));
|
||||||
"qBittorrent/" QBT_VERSION_2);
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(ipUpdateFinished(QString, QByteArray)));
|
, this, &DNSUpdater::ipUpdateFinished);
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(ipUpdateFailed(QString, QString)));
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &DNSUpdater::ipUpdateFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString DNSUpdater::getUpdateUrl() const
|
QString DNSUpdater::getUpdateUrl() const
|
||||||
@@ -181,7 +181,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
|
|||||||
{
|
{
|
||||||
Logger *const logger = Logger::instance();
|
Logger *const logger = Logger::instance();
|
||||||
qDebug() << Q_FUNC_INFO << reply;
|
qDebug() << Q_FUNC_INFO << reply;
|
||||||
QString code = reply.split(" ").first();
|
QString code = reply.split(' ').first();
|
||||||
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
qDebug() << Q_FUNC_INFO << "Code:" << code;
|
||||||
|
|
||||||
if ((code == "good") || (code == "nochg")) {
|
if ((code == "good") || (code == "nochg")) {
|
||||||
@@ -196,7 +196,7 @@ void DNSUpdater::processIPUpdateReply(const QString &reply)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything bellow is an error, stop updating until the user updates something
|
// Everything below is an error, stop updating until the user updates something
|
||||||
m_ipCheckTimer.stop();
|
m_ipCheckTimer.stop();
|
||||||
m_lastIP.clear();
|
m_lastIP.clear();
|
||||||
if (code == "nohost") {
|
if (code == "nohost") {
|
||||||
@@ -244,8 +244,8 @@ void DNSUpdater::updateCredentials()
|
|||||||
}
|
}
|
||||||
if (m_domain != pref->getDynDomainName()) {
|
if (m_domain != pref->getDynDomainName()) {
|
||||||
m_domain = pref->getDynDomainName();
|
m_domain = pref->getDynDomainName();
|
||||||
QRegExp domain_regex("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$");
|
const QRegularExpressionMatch domainRegexMatch = QRegularExpression("^(?:(?!\\d|-)[a-zA-Z0-9\\-]{1,63}\\.)+[a-zA-Z]{2,}$").match(m_domain);
|
||||||
if (domain_regex.indexIn(m_domain) < 0) {
|
if (!domainRegexMatch.hasMatch()) {
|
||||||
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
logger->addMessage(tr("Dynamic DNS error: supplied domain name is invalid."), Log::CRITICAL);
|
||||||
m_lastIP.clear();
|
m_lastIP.clear();
|
||||||
m_ipCheckTimer.stop();
|
m_ipCheckTimer.stop();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
|
|
||||||
#include "downloadhandler.h"
|
#include "downloadhandler.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkCookie>
|
#include <QNetworkCookie>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
@@ -43,35 +43,57 @@
|
|||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
#include "downloadmanager.h"
|
#include "downloadmanager.h"
|
||||||
|
|
||||||
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
namespace
|
||||||
|
{
|
||||||
|
bool saveToFile(const QByteArray &replyData, QString &filePath)
|
||||||
|
{
|
||||||
|
QTemporaryFile tmpfile {Utils::Fs::tempPath() + "XXXXXX"};
|
||||||
|
tmpfile.setAutoRemove(false);
|
||||||
|
|
||||||
using namespace Net;
|
if (!tmpfile.open())
|
||||||
|
return false;
|
||||||
|
|
||||||
DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile, qint64 limit, bool handleRedirectToMagnet)
|
filePath = tmpfile.fileName();
|
||||||
|
|
||||||
|
tmpfile.write(replyData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadHandler::DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest)
|
||||||
: QObject(manager)
|
: QObject(manager)
|
||||||
, m_reply(reply)
|
, m_reply(reply)
|
||||||
, m_manager(manager)
|
, m_manager(manager)
|
||||||
, m_saveToFile(saveToFile)
|
, m_downloadRequest(downloadRequest)
|
||||||
, m_sizeLimit(limit)
|
|
||||||
, m_handleRedirectToMagnet(handleRedirectToMagnet)
|
|
||||||
, m_url(reply->url().toString())
|
|
||||||
{
|
{
|
||||||
init();
|
if (reply)
|
||||||
|
assignNetworkReply(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadHandler::~DownloadHandler()
|
Net::DownloadHandler::~DownloadHandler()
|
||||||
{
|
{
|
||||||
if (m_reply)
|
if (m_reply)
|
||||||
delete m_reply;
|
delete m_reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns original url
|
void Net::DownloadHandler::assignNetworkReply(QNetworkReply *reply)
|
||||||
QString DownloadHandler::url() const
|
|
||||||
{
|
{
|
||||||
return m_url;
|
Q_ASSERT(reply);
|
||||||
|
|
||||||
|
m_reply = reply;
|
||||||
|
m_reply->setParent(this);
|
||||||
|
if (m_downloadRequest.limit() > 0)
|
||||||
|
connect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
|
||||||
|
connect(m_reply, &QNetworkReply::finished, this, &Net::DownloadHandler::processFinishedDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::processFinishedDownload()
|
// Returns original url
|
||||||
|
QString Net::DownloadHandler::url() const
|
||||||
|
{
|
||||||
|
return m_downloadRequest.url();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Net::DownloadHandler::processFinishedDownload()
|
||||||
{
|
{
|
||||||
QString url = m_reply->url().toString();
|
QString url = m_reply->url().toString();
|
||||||
qDebug("Download finished: %s", qUtf8Printable(url));
|
qDebug("Download finished: %s", qUtf8Printable(url));
|
||||||
@@ -79,7 +101,7 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
if (m_reply->error() != QNetworkReply::NoError) {
|
if (m_reply->error() != QNetworkReply::NoError) {
|
||||||
// Failure
|
// Failure
|
||||||
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
|
qDebug("Download failure (%s), reason: %s", qUtf8Printable(url), qUtf8Printable(errorCodeToString(m_reply->error())));
|
||||||
emit downloadFailed(m_url, errorCodeToString(m_reply->error()));
|
emit downloadFailed(m_downloadRequest.url(), errorCodeToString(m_reply->error()));
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -97,15 +119,15 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
replyData = Utils::Gzip::decompress(replyData);
|
replyData = Utils::Gzip::decompress(replyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_saveToFile) {
|
if (m_downloadRequest.saveToFile()) {
|
||||||
QString filePath;
|
QString filePath;
|
||||||
if (saveToFile(replyData, filePath))
|
if (saveToFile(replyData, filePath))
|
||||||
emit downloadFinished(m_url, filePath);
|
emit downloadFinished(m_downloadRequest.url(), filePath);
|
||||||
else
|
else
|
||||||
emit downloadFailed(m_url, tr("I/O Error"));
|
emit downloadFailed(m_downloadRequest.url(), tr("I/O Error"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
emit downloadFinished(m_url, replyData);
|
emit downloadFinished(m_downloadRequest.url(), replyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
@@ -113,137 +135,116 @@ void DownloadHandler::processFinishedDownload()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
void Net::DownloadHandler::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal)
|
||||||
{
|
{
|
||||||
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
QString msg = tr("The file size is %1. It exceeds the download limit of %2.");
|
||||||
|
|
||||||
if (bytesTotal > 0) {
|
if (bytesTotal > 0) {
|
||||||
// Total number of bytes is available
|
// Total number of bytes is available
|
||||||
if (bytesTotal > m_sizeLimit) {
|
if (bytesTotal > m_downloadRequest.limit()) {
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesTotal)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesTotal), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
disconnect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(checkDownloadSize(qint64, qint64)));
|
disconnect(m_reply, &QNetworkReply::downloadProgress, this, &Net::DownloadHandler::checkDownloadSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (bytesReceived > m_sizeLimit) {
|
else if (bytesReceived > m_downloadRequest.limit()) {
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
emit downloadFailed(m_url, msg.arg(Utils::Misc::friendlyUnit(bytesReceived)).arg(Utils::Misc::friendlyUnit(m_sizeLimit)));
|
emit downloadFailed(m_downloadRequest.url(), msg.arg(Utils::Misc::friendlyUnit(bytesReceived), Utils::Misc::friendlyUnit(m_downloadRequest.limit())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadHandler::init()
|
void Net::DownloadHandler::handleRedirection(QUrl newUrl)
|
||||||
{
|
|
||||||
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(Utils::Fs::tempPath() + "XXXXXX");
|
|
||||||
if (!tmpfile->open()) {
|
|
||||||
delete tmpfile;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpfile->setAutoRemove(false);
|
|
||||||
filePath = tmpfile->fileName();
|
|
||||||
qDebug("Temporary filename is: %s", qUtf8Printable(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
|
// Resolve relative urls
|
||||||
if (newUrl.isRelative())
|
if (newUrl.isRelative())
|
||||||
newUrl = m_reply->url().resolved(newUrl);
|
newUrl = m_reply->url().resolved(newUrl);
|
||||||
|
|
||||||
const QString newUrlString = newUrl.toString();
|
const QString newUrlString = newUrl.toString();
|
||||||
qDebug("Redirecting from %s to %s", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
|
qDebug("Redirecting from %s to %s...", qUtf8Printable(m_reply->url().toString()), qUtf8Printable(newUrlString));
|
||||||
|
|
||||||
// Redirect to magnet workaround
|
// Redirect to magnet workaround
|
||||||
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
if (newUrlString.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||||
qDebug("Magnet redirect detected.");
|
qDebug("Magnet redirect detected.");
|
||||||
m_reply->abort();
|
m_reply->abort();
|
||||||
if (m_handleRedirectToMagnet)
|
if (m_downloadRequest.handleRedirectToMagnet())
|
||||||
emit redirectedToMagnet(m_url, newUrlString);
|
emit redirectedToMagnet(m_downloadRequest.url(), newUrlString);
|
||||||
else
|
else
|
||||||
emit downloadFailed(m_url, tr("Unexpected redirect to magnet URI."));
|
emit downloadFailed(m_downloadRequest.url(), tr("Unexpected redirect to magnet URI."));
|
||||||
|
|
||||||
this->deleteLater();
|
this->deleteLater();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DownloadHandler *tmp = m_manager->downloadUrl(newUrlString, m_saveToFile, m_sizeLimit, m_handleRedirectToMagnet);
|
DownloadHandler *redirected = m_manager->download(DownloadRequest(m_downloadRequest).url(newUrlString));
|
||||||
m_reply->deleteLater();
|
connect(redirected, &DownloadHandler::destroyed, this, &DownloadHandler::deleteLater);
|
||||||
m_reply = tmp->m_reply;
|
connect(redirected, &DownloadHandler::downloadFailed, this, [this](const QString &, const QString &reason)
|
||||||
init();
|
{
|
||||||
tmp->m_reply = nullptr;
|
emit downloadFailed(url(), reason);
|
||||||
delete tmp;
|
});
|
||||||
|
connect(redirected, &DownloadHandler::redirectedToMagnet, this, [this](const QString &, const QString &magnetUri)
|
||||||
|
{
|
||||||
|
emit redirectedToMagnet(url(), magnetUri);
|
||||||
|
});
|
||||||
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished)
|
||||||
|
, this, [this](const QString &, const QString &fileName)
|
||||||
|
{
|
||||||
|
emit downloadFinished(url(), fileName);
|
||||||
|
});
|
||||||
|
connect(redirected, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished)
|
||||||
|
, this, [this](const QString &, const QByteArray &data)
|
||||||
|
{
|
||||||
|
emit downloadFinished(url(), data);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString errorCodeToString(QNetworkReply::NetworkError status)
|
QString Net::DownloadHandler::errorCodeToString(const QNetworkReply::NetworkError status)
|
||||||
{
|
{
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case QNetworkReply::HostNotFoundError:
|
case QNetworkReply::HostNotFoundError:
|
||||||
return QObject::tr("The remote host name was not found (invalid hostname)");
|
return tr("The remote host name was not found (invalid hostname)");
|
||||||
case QNetworkReply::OperationCanceledError:
|
case QNetworkReply::OperationCanceledError:
|
||||||
return QObject::tr("The operation was canceled");
|
return tr("The operation was canceled");
|
||||||
case QNetworkReply::RemoteHostClosedError:
|
case QNetworkReply::RemoteHostClosedError:
|
||||||
return QObject::tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
return tr("The remote server closed the connection prematurely, before the entire reply was received and processed");
|
||||||
case QNetworkReply::TimeoutError:
|
case QNetworkReply::TimeoutError:
|
||||||
return QObject::tr("The connection to the remote server timed out");
|
return tr("The connection to the remote server timed out");
|
||||||
case QNetworkReply::SslHandshakeFailedError:
|
case QNetworkReply::SslHandshakeFailedError:
|
||||||
return QObject::tr("SSL/TLS handshake failed");
|
return tr("SSL/TLS handshake failed");
|
||||||
case QNetworkReply::ConnectionRefusedError:
|
case QNetworkReply::ConnectionRefusedError:
|
||||||
return QObject::tr("The remote server refused the connection");
|
return tr("The remote server refused the connection");
|
||||||
case QNetworkReply::ProxyConnectionRefusedError:
|
case QNetworkReply::ProxyConnectionRefusedError:
|
||||||
return QObject::tr("The connection to the proxy server was refused");
|
return tr("The connection to the proxy server was refused");
|
||||||
case QNetworkReply::ProxyConnectionClosedError:
|
case QNetworkReply::ProxyConnectionClosedError:
|
||||||
return QObject::tr("The proxy server closed the connection prematurely");
|
return tr("The proxy server closed the connection prematurely");
|
||||||
case QNetworkReply::ProxyNotFoundError:
|
case QNetworkReply::ProxyNotFoundError:
|
||||||
return QObject::tr("The proxy host name was not found");
|
return tr("The proxy host name was not found");
|
||||||
case QNetworkReply::ProxyTimeoutError:
|
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");
|
return tr("The connection to the proxy timed out or the proxy did not reply in time to the request sent");
|
||||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||||
return QObject::tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
|
return tr("The proxy requires authentication in order to honor the request but did not accept any credentials offered");
|
||||||
case QNetworkReply::ContentAccessDenied:
|
case QNetworkReply::ContentAccessDenied:
|
||||||
return QObject::tr("The access to the remote content was denied (401)");
|
return tr("The access to the remote content was denied (401)");
|
||||||
case QNetworkReply::ContentOperationNotPermittedError:
|
case QNetworkReply::ContentOperationNotPermittedError:
|
||||||
return QObject::tr("The operation requested on the remote content is not permitted");
|
return tr("The operation requested on the remote content is not permitted");
|
||||||
case QNetworkReply::ContentNotFoundError:
|
case QNetworkReply::ContentNotFoundError:
|
||||||
return QObject::tr("The remote content was not found at the server (404)");
|
return tr("The remote content was not found at the server (404)");
|
||||||
case QNetworkReply::AuthenticationRequiredError:
|
case QNetworkReply::AuthenticationRequiredError:
|
||||||
return QObject::tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
return tr("The remote server requires authentication to serve the content but the credentials provided were not accepted");
|
||||||
case QNetworkReply::ProtocolUnknownError:
|
case QNetworkReply::ProtocolUnknownError:
|
||||||
return QObject::tr("The Network Access API cannot honor the request because the protocol is not known");
|
return tr("The Network Access API cannot honor the request because the protocol is not known");
|
||||||
case QNetworkReply::ProtocolInvalidOperationError:
|
case QNetworkReply::ProtocolInvalidOperationError:
|
||||||
return QObject::tr("The requested operation is invalid for this protocol");
|
return tr("The requested operation is invalid for this protocol");
|
||||||
case QNetworkReply::UnknownNetworkError:
|
case QNetworkReply::UnknownNetworkError:
|
||||||
return QObject::tr("An unknown network-related error was detected");
|
return tr("An unknown network-related error was detected");
|
||||||
case QNetworkReply::UnknownProxyError:
|
case QNetworkReply::UnknownProxyError:
|
||||||
return QObject::tr("An unknown proxy-related error was detected");
|
return tr("An unknown proxy-related error was detected");
|
||||||
case QNetworkReply::UnknownContentError:
|
case QNetworkReply::UnknownContentError:
|
||||||
return QObject::tr("An unknown error related to the remote content was detected");
|
return tr("An unknown error related to the remote content was detected");
|
||||||
case QNetworkReply::ProtocolFailure:
|
case QNetworkReply::ProtocolFailure:
|
||||||
return QObject::tr("A breakdown in protocol was detected");
|
return tr("A breakdown in protocol was detected");
|
||||||
default:
|
default:
|
||||||
return QObject::tr("Unknown error");
|
return tr("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -30,10 +30,11 @@
|
|||||||
#ifndef NET_DOWNLOADHANDLER_H
|
#ifndef NET_DOWNLOADHANDLER_H
|
||||||
#define NET_DOWNLOADHANDLER_H
|
#define NET_DOWNLOADHANDLER_H
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
#include "downloadmanager.h"
|
||||||
class QNetworkReply;
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
@@ -43,10 +44,14 @@ namespace Net
|
|||||||
class DownloadHandler : public QObject
|
class DownloadHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(DownloadHandler)
|
||||||
|
|
||||||
|
friend class DownloadManager;
|
||||||
|
|
||||||
|
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, const DownloadRequest &downloadRequest);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DownloadHandler(QNetworkReply *reply, DownloadManager *manager, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false);
|
~DownloadHandler() override;
|
||||||
~DownloadHandler();
|
|
||||||
|
|
||||||
QString url() const;
|
QString url() const;
|
||||||
|
|
||||||
@@ -61,16 +66,14 @@ namespace Net
|
|||||||
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void assignNetworkReply(QNetworkReply *reply);
|
||||||
bool saveToFile(const QByteArray &replyData, QString &filePath);
|
|
||||||
void handleRedirection(QUrl newUrl);
|
void handleRedirection(QUrl newUrl);
|
||||||
|
|
||||||
|
static QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||||
|
|
||||||
QNetworkReply *m_reply;
|
QNetworkReply *m_reply;
|
||||||
DownloadManager *m_manager;
|
DownloadManager *m_manager;
|
||||||
bool m_saveToFile;
|
const DownloadRequest m_downloadRequest;
|
||||||
qint64 m_sizeLimit;
|
|
||||||
bool m_handleRedirectToMagnet;
|
|
||||||
QString m_url;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -48,10 +48,10 @@ const char DEFAULT_USER_AGENT[] = "Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
class NetworkCookieJar: public QNetworkCookieJar
|
class NetworkCookieJar : public QNetworkCookieJar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit NetworkCookieJar(QObject *parent = 0)
|
explicit NetworkCookieJar(QObject *parent = nullptr)
|
||||||
: QNetworkCookieJar(parent)
|
: QNetworkCookieJar(parent)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
@@ -64,7 +64,7 @@ namespace
|
|||||||
setAllCookies(cookies);
|
setAllCookies(cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
~NetworkCookieJar()
|
~NetworkCookieJar() override
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime now = QDateTime::currentDateTime();
|
||||||
QList<QNetworkCookie> cookies = allCookies();
|
QList<QNetworkCookie> cookies = allCookies();
|
||||||
@@ -103,91 +103,113 @@ namespace
|
|||||||
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
|
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QNetworkRequest createNetworkRequest(const Net::DownloadRequest &downloadRequest)
|
||||||
|
{
|
||||||
|
QNetworkRequest request {downloadRequest.url()};
|
||||||
|
|
||||||
|
if (downloadRequest.userAgent().isEmpty())
|
||||||
|
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
|
||||||
|
else
|
||||||
|
request.setRawHeader("User-Agent", downloadRequest.userAgent().toUtf8());
|
||||||
|
|
||||||
|
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
||||||
|
request.setRawHeader("Referer", request.url().toEncoded().data());
|
||||||
|
// Accept gzip
|
||||||
|
request.setRawHeader("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Net;
|
Net::DownloadManager *Net::DownloadManager::m_instance = nullptr;
|
||||||
|
|
||||||
DownloadManager *DownloadManager::m_instance = 0;
|
Net::DownloadManager::DownloadManager(QObject *parent)
|
||||||
|
|
||||||
DownloadManager::DownloadManager(QObject *parent)
|
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
connect(&m_networkManager, SIGNAL(sslErrors(QNetworkReply *, QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply *, QList<QSslError>)));
|
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &Net::DownloadManager::ignoreSslErrors);
|
||||||
#endif
|
#endif
|
||||||
|
connect(&m_networkManager, &QNetworkAccessManager::finished, this, &DownloadManager::handleReplyFinished);
|
||||||
|
connect(ProxyConfigurationManager::instance(), &ProxyConfigurationManager::proxyConfigurationChanged
|
||||||
|
, this, &DownloadManager::applyProxySettings);
|
||||||
m_networkManager.setCookieJar(new NetworkCookieJar(this));
|
m_networkManager.setCookieJar(new NetworkCookieJar(this));
|
||||||
|
applyProxySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::initInstance()
|
void Net::DownloadManager::initInstance()
|
||||||
{
|
{
|
||||||
if (!m_instance)
|
if (!m_instance)
|
||||||
m_instance = new DownloadManager;
|
m_instance = new DownloadManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::freeInstance()
|
void Net::DownloadManager::freeInstance()
|
||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadManager *DownloadManager::instance()
|
Net::DownloadManager *Net::DownloadManager::instance()
|
||||||
{
|
{
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet, const QString &userAgent)
|
Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &downloadRequest)
|
||||||
{
|
{
|
||||||
// Update proxy settings
|
|
||||||
applyProxySettings();
|
|
||||||
|
|
||||||
// Process download request
|
// Process download request
|
||||||
qDebug("url is %s", qUtf8Printable(url));
|
const QNetworkRequest request = createNetworkRequest(downloadRequest);
|
||||||
const QUrl qurl = QUrl(url);
|
const ServiceID id = ServiceID::fromURL(request.url());
|
||||||
QNetworkRequest request(qurl);
|
const bool isSequentialService = m_sequentialServices.contains(id);
|
||||||
|
if (!isSequentialService || !m_busyServices.contains(id)) {
|
||||||
|
qDebug("Downloading %s...", qUtf8Printable(downloadRequest.url()));
|
||||||
|
if (isSequentialService)
|
||||||
|
m_busyServices.insert(id);
|
||||||
|
return new DownloadHandler {
|
||||||
|
m_networkManager.get(request), this, downloadRequest};
|
||||||
|
}
|
||||||
|
|
||||||
if (userAgent.isEmpty())
|
auto *downloadHandler = new DownloadHandler {nullptr, this, downloadRequest};
|
||||||
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
|
connect(downloadHandler, &DownloadHandler::destroyed, this, [this, id, downloadHandler]()
|
||||||
else
|
{
|
||||||
request.setRawHeader("User-Agent", userAgent.toUtf8());
|
m_waitingJobs[id].removeOne(downloadHandler);
|
||||||
|
});
|
||||||
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
m_waitingJobs[id].enqueue(downloadHandler);
|
||||||
request.setRawHeader("Referer", request.url().toEncoded().data());
|
return downloadHandler;
|
||||||
|
|
||||||
qDebug("Downloading %s...", request.url().toEncoded().data());
|
|
||||||
qDebug() << "Cookies:" << m_networkManager.cookieJar()->cookiesForUrl(request.url());
|
|
||||||
// accept gzip
|
|
||||||
request.setRawHeader("Accept-Encoding", "gzip");
|
|
||||||
return new DownloadHandler(m_networkManager.get(request), this, saveToFile, limit, handleRedirectToMagnet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QUrl &url) const
|
void Net::DownloadManager::registerSequentialService(const Net::ServiceID &serviceID)
|
||||||
|
{
|
||||||
|
m_sequentialServices.insert(serviceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QNetworkCookie> Net::DownloadManager::cookiesForUrl(const QUrl &url) const
|
||||||
{
|
{
|
||||||
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
return m_networkManager.cookieJar()->cookiesForUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
bool Net::DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
|
||||||
{
|
{
|
||||||
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QNetworkCookie> DownloadManager::allCookies() const
|
QList<QNetworkCookie> Net::DownloadManager::allCookies() const
|
||||||
{
|
{
|
||||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
|
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
void Net::DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
||||||
{
|
{
|
||||||
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
|
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
|
bool Net::DownloadManager::deleteCookie(const QNetworkCookie &cookie)
|
||||||
{
|
{
|
||||||
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
|
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadManager::applyProxySettings()
|
void Net::DownloadManager::applyProxySettings()
|
||||||
{
|
{
|
||||||
auto proxyManager = ProxyConfigurationManager::instance();
|
auto proxyManager = ProxyConfigurationManager::instance();
|
||||||
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
|
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
|
||||||
@@ -208,7 +230,7 @@ void DownloadManager::applyProxySettings()
|
|||||||
}
|
}
|
||||||
// Authentication?
|
// Authentication?
|
||||||
if (proxyManager->isAuthenticationRequired()) {
|
if (proxyManager->isAuthenticationRequired()) {
|
||||||
qDebug("Proxy requires authentication, authenticating");
|
qDebug("Proxy requires authentication, authenticating...");
|
||||||
proxy.setUser(proxyConfig.username);
|
proxy.setUser(proxyConfig.username);
|
||||||
proxy.setPassword(proxyConfig.password);
|
proxy.setPassword(proxyConfig.password);
|
||||||
}
|
}
|
||||||
@@ -220,11 +242,101 @@ void DownloadManager::applyProxySettings()
|
|||||||
m_networkManager.setProxy(proxy);
|
m_networkManager.setProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
const ServiceID id = ServiceID::fromURL(reply->url());
|
||||||
|
auto waitingJobsIter = m_waitingJobs.find(id);
|
||||||
|
if ((waitingJobsIter == m_waitingJobs.end()) || waitingJobsIter.value().isEmpty()) {
|
||||||
|
m_busyServices.remove(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler *handler = waitingJobsIter.value().dequeue();
|
||||||
|
qDebug("Downloading %s...", qUtf8Printable(handler->m_downloadRequest.url()));
|
||||||
|
handler->assignNetworkReply(m_networkManager.get(createNetworkRequest(handler->m_downloadRequest)));
|
||||||
|
handler->disconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
void Net::DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||||
{
|
{
|
||||||
Q_UNUSED(errors)
|
Q_UNUSED(errors)
|
||||||
// Ignore all SSL errors
|
// Ignore all SSL errors
|
||||||
reply->ignoreSslErrors();
|
reply->ignoreSslErrors();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Net::DownloadRequest::DownloadRequest(const QString &url)
|
||||||
|
: m_url {url}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Net::DownloadRequest::url() const
|
||||||
|
{
|
||||||
|
return m_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::url(const QString &value)
|
||||||
|
{
|
||||||
|
m_url = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Net::DownloadRequest::userAgent() const
|
||||||
|
{
|
||||||
|
return m_userAgent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::userAgent(const QString &value)
|
||||||
|
{
|
||||||
|
m_userAgent = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 Net::DownloadRequest::limit() const
|
||||||
|
{
|
||||||
|
return m_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::limit(qint64 value)
|
||||||
|
{
|
||||||
|
m_limit = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::DownloadRequest::saveToFile() const
|
||||||
|
{
|
||||||
|
return m_saveToFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::saveToFile(bool value)
|
||||||
|
{
|
||||||
|
m_saveToFile = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::DownloadRequest::handleRedirectToMagnet() const
|
||||||
|
{
|
||||||
|
return m_handleRedirectToMagnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::DownloadRequest &Net::DownloadRequest::handleRedirectToMagnet(bool value)
|
||||||
|
{
|
||||||
|
m_handleRedirectToMagnet = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net::ServiceID Net::ServiceID::fromURL(const QUrl &url)
|
||||||
|
{
|
||||||
|
return {url.host(), url.port(80)};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint Net::qHash(const ServiceID &serviceID, uint seed)
|
||||||
|
{
|
||||||
|
return ::qHash(serviceID.hostName, seed) ^ serviceID.port;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Net::operator==(const ServiceID &lhs, const ServiceID &rhs)
|
||||||
|
{
|
||||||
|
return ((lhs.hostName == rhs.hostName) && (lhs.port == rhs.port));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
@@ -30,8 +30,12 @@
|
|||||||
#ifndef NET_DOWNLOADMANAGER_H
|
#ifndef NET_DOWNLOADMANAGER_H
|
||||||
#define NET_DOWNLOADMANAGER_H
|
#define NET_DOWNLOADMANAGER_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QHash>
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQueue>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class QNetworkCookie;
|
class QNetworkCookie;
|
||||||
@@ -42,16 +46,57 @@ namespace Net
|
|||||||
{
|
{
|
||||||
class DownloadHandler;
|
class DownloadHandler;
|
||||||
|
|
||||||
|
class DownloadRequest
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DownloadRequest(const QString &url);
|
||||||
|
DownloadRequest(const DownloadRequest &other) = default;
|
||||||
|
|
||||||
|
QString url() const;
|
||||||
|
DownloadRequest &url(const QString &value);
|
||||||
|
|
||||||
|
QString userAgent() const;
|
||||||
|
DownloadRequest &userAgent(const QString &value);
|
||||||
|
|
||||||
|
qint64 limit() const;
|
||||||
|
DownloadRequest &limit(qint64 value);
|
||||||
|
|
||||||
|
bool saveToFile() const;
|
||||||
|
DownloadRequest &saveToFile(bool value);
|
||||||
|
|
||||||
|
bool handleRedirectToMagnet() const;
|
||||||
|
DownloadRequest &handleRedirectToMagnet(bool value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_url;
|
||||||
|
QString m_userAgent;
|
||||||
|
qint64 m_limit = 0;
|
||||||
|
bool m_saveToFile = false;
|
||||||
|
bool m_handleRedirectToMagnet = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ServiceID
|
||||||
|
{
|
||||||
|
QString hostName;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
static ServiceID fromURL(const QUrl &url);
|
||||||
|
};
|
||||||
|
|
||||||
class DownloadManager : public QObject
|
class DownloadManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(DownloadManager)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void initInstance();
|
static void initInstance();
|
||||||
static void freeInstance();
|
static void freeInstance();
|
||||||
static DownloadManager *instance();
|
static DownloadManager *instance();
|
||||||
|
|
||||||
DownloadHandler *downloadUrl(const QString &url, bool saveToFile = false, qint64 limit = 0, bool handleRedirectToMagnet = false, const QString &userAgent = "");
|
DownloadHandler *download(const DownloadRequest &downloadRequest);
|
||||||
|
|
||||||
|
void registerSequentialService(const ServiceID &serviceID);
|
||||||
|
|
||||||
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
||||||
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||||
QList<QNetworkCookie> allCookies() const;
|
QList<QNetworkCookie> allCookies() const;
|
||||||
@@ -60,17 +105,25 @@ namespace Net
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
void ignoreSslErrors(QNetworkReply *,const QList<QSslError> &);
|
void ignoreSslErrors(QNetworkReply *, const QList<QSslError> &);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit DownloadManager(QObject *parent = 0);
|
explicit DownloadManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
void applyProxySettings();
|
void applyProxySettings();
|
||||||
|
void handleReplyFinished(QNetworkReply *reply);
|
||||||
|
|
||||||
static DownloadManager *m_instance;
|
static DownloadManager *m_instance;
|
||||||
QNetworkAccessManager m_networkManager;
|
QNetworkAccessManager m_networkManager;
|
||||||
|
|
||||||
|
QSet<ServiceID> m_sequentialServices;
|
||||||
|
QSet<ServiceID> m_busyServices;
|
||||||
|
QHash<ServiceID, QQueue<DownloadHandler *>> m_waitingJobs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint qHash(const ServiceID &serviceID, uint seed);
|
||||||
|
bool operator==(const ServiceID &lhs, const ServiceID &rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // NET_DOWNLOADMANAGER_H
|
#endif // NET_DOWNLOADMANAGER_H
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ GeoIPManager::GeoIPManager()
|
|||||||
, m_geoIPDatabase(nullptr)
|
, m_geoIPDatabase(nullptr)
|
||||||
{
|
{
|
||||||
configure();
|
configure();
|
||||||
connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure()));
|
connect(Preferences::instance(), &Preferences::changed, this, &GeoIPManager::configure);
|
||||||
}
|
}
|
||||||
|
|
||||||
GeoIPManager::~GeoIPManager()
|
GeoIPManager::~GeoIPManager()
|
||||||
@@ -96,15 +96,14 @@ void GeoIPManager::loadDatabase()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString filepath = Utils::Fs::expandPathAbs(
|
QString filepath = Utils::Fs::expandPathAbs(
|
||||||
QString("%1%2/%3").arg(specialFolderLocation(SpecialFolder::Data))
|
QString("%1%2/%3").arg(specialFolderLocation(SpecialFolder::Data), GEOIP_FOLDER, GEOIP_FILENAME));
|
||||||
.arg(GEOIP_FOLDER).arg(GEOIP_FILENAME));
|
|
||||||
|
|
||||||
QString error;
|
QString error;
|
||||||
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
m_geoIPDatabase = GeoIPDatabase::load(filepath, error);
|
||||||
if (m_geoIPDatabase)
|
if (m_geoIPDatabase)
|
||||||
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
||||||
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
|
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||||
Log::INFO);
|
Log::INFO);
|
||||||
else
|
else
|
||||||
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
|
Logger::instance()->addMessage(tr("Couldn't load GeoIP database. Reason: %1").arg(error), Log::WARNING);
|
||||||
|
|
||||||
@@ -119,9 +118,10 @@ void GeoIPManager::manageDatabaseUpdate()
|
|||||||
|
|
||||||
void GeoIPManager::downloadDatabaseFile()
|
void GeoIPManager::downloadDatabaseFile()
|
||||||
{
|
{
|
||||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(DATABASE_URL);
|
DownloadHandler *handler = DownloadManager::instance()->download({DATABASE_URL});
|
||||||
connect(handler, SIGNAL(downloadFinished(QString, QByteArray)), SLOT(downloadFinished(QString, QByteArray)));
|
connect(handler, static_cast<void (Net::DownloadHandler::*)(const QString &, const QByteArray &)>(&Net::DownloadHandler::downloadFinished)
|
||||||
connect(handler, SIGNAL(downloadFailed(QString, QString)), SLOT(downloadFailed(QString, QString)));
|
, this, &GeoIPManager::downloadFinished);
|
||||||
|
connect(handler, &Net::DownloadHandler::downloadFailed, this, &GeoIPManager::downloadFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
|
QString GeoIPManager::lookup(const QHostAddress &hostAddr) const
|
||||||
@@ -432,13 +432,13 @@ void GeoIPManager::downloadFinished(const QString &url, QByteArray data)
|
|||||||
delete m_geoIPDatabase;
|
delete m_geoIPDatabase;
|
||||||
m_geoIPDatabase = geoIPDatabase;
|
m_geoIPDatabase = geoIPDatabase;
|
||||||
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
Logger::instance()->addMessage(tr("GeoIP database loaded. Type: %1. Build time: %2.")
|
||||||
.arg(m_geoIPDatabase->type()).arg(m_geoIPDatabase->buildEpoch().toString()),
|
.arg(m_geoIPDatabase->type(), m_geoIPDatabase->buildEpoch().toString()),
|
||||||
Log::INFO);
|
Log::INFO);
|
||||||
QString targetPath = Utils::Fs::expandPathAbs(
|
QString targetPath = Utils::Fs::expandPathAbs(
|
||||||
specialFolderLocation(SpecialFolder::Data) + GEOIP_FOLDER);
|
specialFolderLocation(SpecialFolder::Data) + GEOIP_FOLDER);
|
||||||
if (!QDir(targetPath).exists())
|
if (!QDir(targetPath).exists())
|
||||||
QDir().mkpath(targetPath);
|
QDir().mkpath(targetPath);
|
||||||
QFile targetFile(QString("%1/%2").arg(targetPath).arg(GEOIP_FILENAME));
|
QFile targetFile(QString("%1/%2").arg(targetPath, GEOIP_FILENAME));
|
||||||
if (!targetFile.open(QFile::WriteOnly) || (targetFile.write(data) == -1)) {
|
if (!targetFile.open(QFile::WriteOnly) || (targetFile.write(data) == -1)) {
|
||||||
Logger::instance()->addMessage(
|
Logger::instance()->addMessage(
|
||||||
tr("Couldn't save downloaded GeoIP database file."), Log::WARNING);
|
tr("Couldn't save downloaded GeoIP database file."), Log::WARNING);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
|
|
||||||
static const QString KEY_ENABLED = QLatin1String("Network/PortForwardingEnabled");
|
static const QString KEY_ENABLED = QStringLiteral("Network/PortForwardingEnabled");
|
||||||
|
|
||||||
namespace libt = libtorrent;
|
namespace libt = libtorrent;
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
@@ -121,8 +121,10 @@ void PortForwarder::start()
|
|||||||
settingsPack.set_bool(libt::settings_pack::enable_natpmp, true);
|
settingsPack.set_bool(libt::settings_pack::enable_natpmp, true);
|
||||||
m_provider->apply_settings(settingsPack);
|
m_provider->apply_settings(settingsPack);
|
||||||
#endif
|
#endif
|
||||||
foreach (quint16 port, m_mappedPorts.keys())
|
for (auto i = m_mappedPorts.begin(); i != m_mappedPorts.end(); ++i) {
|
||||||
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port);
|
// quint16 port = i.key();
|
||||||
|
i.value() = m_provider->add_port_mapping(libt::session::tcp, i.key(), i.key());
|
||||||
|
}
|
||||||
m_active = true;
|
m_active = true;
|
||||||
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
|
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,25 +26,22 @@
|
|||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QVariant>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
#include "base/types.h"
|
#include "base/types.h"
|
||||||
#include "geoipdatabase.h"
|
#include "geoipdatabase.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
const quint32 __ENDIAN_TEST__ = 0x00000001;
|
|
||||||
const bool __IS_LITTLE_ENDIAN__ = (reinterpret_cast<const uchar *>(&__ENDIAN_TEST__)[0] == 0x01);
|
|
||||||
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
const qint32 MAX_FILE_SIZE = 67108864; // 64MB
|
||||||
const char DB_TYPE[] = "GeoLite2-Country";
|
const char DB_TYPE[] = "GeoLite2-Country";
|
||||||
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
const quint32 MAX_METADATA_SIZE = 131072; // 128KB
|
||||||
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
const char METADATA_BEGIN_MARK[] = "\xab\xcd\xefMaxMind.com";
|
||||||
const char DATA_SECTION_SEPARATOR[16] = { 0 };
|
const char DATA_SECTION_SEPARATOR[16] = {0};
|
||||||
|
|
||||||
enum class DataType
|
enum class DataType
|
||||||
{
|
{
|
||||||
@@ -91,16 +88,16 @@ GeoIPDatabase::GeoIPDatabase(quint32 size)
|
|||||||
|
|
||||||
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
||||||
{
|
{
|
||||||
GeoIPDatabase *db = 0;
|
GeoIPDatabase *db = nullptr;
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
if (file.size() > MAX_FILE_SIZE) {
|
if (file.size() > MAX_FILE_SIZE) {
|
||||||
error = tr("Unsupported database file size.");
|
error = tr("Unsupported database file size.");
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.open(QFile::ReadOnly)) {
|
if (!file.open(QFile::ReadOnly)) {
|
||||||
error = file.errorString();
|
error = file.errorString();
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
db = new GeoIPDatabase(file.size());
|
db = new GeoIPDatabase(file.size());
|
||||||
@@ -108,13 +105,13 @@ GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
|||||||
if (file.read(reinterpret_cast<char *>(db->m_data), db->m_size) != db->m_size) {
|
if (file.read(reinterpret_cast<char *>(db->m_data), db->m_size) != db->m_size) {
|
||||||
error = file.errorString();
|
error = file.errorString();
|
||||||
delete db;
|
delete db;
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
||||||
delete db;
|
delete db;
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
@@ -122,10 +119,10 @@ GeoIPDatabase *GeoIPDatabase::load(const QString &filename, QString &error)
|
|||||||
|
|
||||||
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
||||||
{
|
{
|
||||||
GeoIPDatabase *db = 0;
|
GeoIPDatabase *db = nullptr;
|
||||||
if (data.size() > MAX_FILE_SIZE) {
|
if (data.size() > MAX_FILE_SIZE) {
|
||||||
error = tr("Unsupported database file size.");
|
error = tr("Unsupported database file size.");
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
db = new GeoIPDatabase(data.size());
|
db = new GeoIPDatabase(data.size());
|
||||||
@@ -134,7 +131,7 @@ GeoIPDatabase *GeoIPDatabase::load(const QByteArray &data, QString &error)
|
|||||||
|
|
||||||
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
if (!db->parseMetadata(db->readMetadata(), error) || !db->loadDB(error)) {
|
||||||
delete db;
|
delete db;
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
@@ -448,8 +445,12 @@ bool GeoIPDatabase::readDataFieldDescriptor(quint32 &offset, DataFieldDescriptor
|
|||||||
|
|
||||||
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
|
void GeoIPDatabase::fromBigEndian(uchar *buf, quint32 len) const
|
||||||
{
|
{
|
||||||
if (__IS_LITTLE_ENDIAN__)
|
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
|
||||||
std::reverse(buf, buf + len);
|
std::reverse(buf, buf + len);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(buf);
|
||||||
|
Q_UNUSED(len);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
|
QVariant GeoIPDatabase::readMapValue(quint32 &offset, quint32 count) const
|
||||||
|
|||||||
@@ -29,13 +29,13 @@
|
|||||||
#ifndef GEOIPDATABASE_H
|
#ifndef GEOIPDATABASE_H
|
||||||
#define GEOIPDATABASE_H
|
#define GEOIPDATABASE_H
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
class QHostAddress;
|
|
||||||
class QString;
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
|
class QHostAddress;
|
||||||
|
class QString;
|
||||||
|
|
||||||
struct DataFieldDescriptor;
|
struct DataFieldDescriptor;
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "proxyconfigurationmanager.h"
|
#include "proxyconfigurationmanager.h"
|
||||||
|
|
||||||
#include "base/settingsstorage.h"
|
#include "base/settingsstorage.h"
|
||||||
|
|
||||||
#define SETTINGS_KEY(name) "Network/Proxy/" name
|
#define SETTINGS_KEY(name) QStringLiteral("Network/Proxy/" name)
|
||||||
const QString KEY_ONLY_FOR_TORRENTS = SETTINGS_KEY("OnlyForTorrents");
|
const QString KEY_ONLY_FOR_TORRENTS = SETTINGS_KEY("OnlyForTorrents");
|
||||||
const QString KEY_TYPE = SETTINGS_KEY("Type");
|
const QString KEY_TYPE = SETTINGS_KEY("Type");
|
||||||
const QString KEY_IP = SETTINGS_KEY("IP");
|
const QString KEY_IP = SETTINGS_KEY("IP");
|
||||||
@@ -80,7 +81,7 @@ void ProxyConfigurationManager::freeInstance()
|
|||||||
{
|
{
|
||||||
if (m_instance) {
|
if (m_instance) {
|
||||||
delete m_instance;
|
delete m_instance;
|
||||||
m_instance = 0;
|
m_instance = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,18 +136,18 @@ void ProxyConfigurationManager::configureProxy()
|
|||||||
if (!m_isProxyOnlyForTorrents) {
|
if (!m_isProxyOnlyForTorrents) {
|
||||||
switch (m_config.type) {
|
switch (m_config.type) {
|
||||||
case ProxyType::HTTP_PW:
|
case ProxyType::HTTP_PW:
|
||||||
proxyStrHTTP = QString("http://%1:%2@%3:%4").arg(m_config.username)
|
proxyStrHTTP = QString("http://%1:%2@%3:%4").arg(m_config.username
|
||||||
.arg(m_config.password).arg(m_config.ip).arg(m_config.port);
|
, m_config.password, m_config.ip, QString::number(m_config.port));
|
||||||
break;
|
break;
|
||||||
case ProxyType::HTTP:
|
case ProxyType::HTTP:
|
||||||
proxyStrHTTP = QString("http://%1:%2").arg(m_config.ip).arg(m_config.port);
|
proxyStrHTTP = QString("http://%1:%2").arg(m_config.ip, QString::number(m_config.port));
|
||||||
break;
|
break;
|
||||||
case ProxyType::SOCKS5:
|
case ProxyType::SOCKS5:
|
||||||
proxyStrSOCK = QString("%1:%2").arg(m_config.ip).arg(m_config.port);
|
proxyStrSOCK = QString("%1:%2").arg(m_config.ip, QString::number(m_config.port));
|
||||||
break;
|
break;
|
||||||
case ProxyType::SOCKS5_PW:
|
case ProxyType::SOCKS5_PW:
|
||||||
proxyStrSOCK = QString("%1:%2@%3:%4").arg(m_config.username)
|
proxyStrSOCK = QString("%1:%2@%3:%4").arg(m_config.username
|
||||||
.arg(m_config.password).arg(m_config.ip).arg(m_config.port);
|
, m_config.password, m_config.ip, QString::number(m_config.port));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
qDebug("Disabling HTTP communications proxy");
|
qDebug("Disabling HTTP communications proxy");
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Net
|
|||||||
QString password;
|
QString password;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProxyConfigurationManager: public QObject
|
class ProxyConfigurationManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY(ProxyConfigurationManager)
|
Q_DISABLE_COPY(ProxyConfigurationManager)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,26 +24,24 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "reverseresolution.h"
|
||||||
|
|
||||||
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include <boost/version.hpp>
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
|
||||||
|
|
||||||
#include "reverseresolution.h"
|
|
||||||
|
|
||||||
const int CACHE_SIZE = 500;
|
const int CACHE_SIZE = 500;
|
||||||
|
|
||||||
using namespace Net;
|
using namespace Net;
|
||||||
|
|
||||||
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
static inline bool isUsefulHostName(const QString &hostname, const QString &ip)
|
||||||
{
|
{
|
||||||
return (!hostname.isEmpty() && hostname != ip);
|
return (!hostname.isEmpty() && (hostname != ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReverseResolution::ReverseResolution(QObject *parent)
|
ReverseResolution::ReverseResolution(QObject *parent)
|
||||||
@@ -67,13 +65,17 @@ void ReverseResolution::resolve(const QString &ip)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Actually resolve the ip
|
// Actually resolve the ip
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
|
||||||
|
m_lookups.insert(QHostInfo::lookupHost(ip, this, &ReverseResolution::hostResolved), ip);
|
||||||
|
#else
|
||||||
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
m_lookups.insert(QHostInfo::lookupHost(ip, this, SLOT(hostResolved(QHostInfo))), ip);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReverseResolution::hostResolved(const QHostInfo &host)
|
void ReverseResolution::hostResolved(const QHostInfo &host)
|
||||||
{
|
{
|
||||||
const QString &ip = m_lookups.take(host.lookupId());
|
const QString ip = m_lookups.take(host.lookupId());
|
||||||
Q_ASSERT(!ip.isNull());
|
Q_ASSERT(!ip.isNull());
|
||||||
|
|
||||||
if (host.error() != QHostInfo::NoError) {
|
if (host.error() != QHostInfo::NoError) {
|
||||||
@@ -81,7 +83,7 @@ void ReverseResolution::hostResolved(const QHostInfo &host)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString &hostname = host.hostName();
|
const QString hostname = host.hostName();
|
||||||
|
|
||||||
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
qDebug() << Q_FUNC_INFO << ip << QString("->") << hostname;
|
||||||
m_cache.insert(ip, new QString(hostname));
|
m_cache.insert(ip, new QString(hostname));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2006 Christophe Dumez
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -24,8 +24,6 @@
|
|||||||
* modify file(s), you may extend this exception to your version of the file(s),
|
* 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
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
* exception statement from your version.
|
* exception statement from your version.
|
||||||
*
|
|
||||||
* Contact : chris@qbittorrent.org
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef NET_REVERSERESOLUTION_H
|
#ifndef NET_REVERSERESOLUTION_H
|
||||||
@@ -34,10 +32,8 @@
|
|||||||
#include <QCache>
|
#include <QCache>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QHostInfo;
|
class QHostInfo;
|
||||||
class QString;
|
class QString;
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
@@ -47,7 +43,7 @@ namespace Net
|
|||||||
Q_DISABLE_COPY(ReverseResolution)
|
Q_DISABLE_COPY(ReverseResolution)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ReverseResolution(QObject *parent = 0);
|
explicit ReverseResolution(QObject *parent = nullptr);
|
||||||
~ReverseResolution();
|
~ReverseResolution();
|
||||||
|
|
||||||
void resolve(const QString &ip);
|
void resolve(const QString &ip);
|
||||||
|
|||||||
@@ -34,12 +34,9 @@
|
|||||||
|
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QHostAddress>
|
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
#include <QNetworkInterface>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QTextStream>
|
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
#else
|
#else
|
||||||
@@ -67,7 +64,7 @@ namespace
|
|||||||
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
// ascii characters 0x36 ("6") and 0x5c ("\") are selected because they have large
|
||||||
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
// Hamming distance (http://en.wikipedia.org/wiki/Hamming_distance)
|
||||||
|
|
||||||
for (int i = 0; i < key.length(); i++) {
|
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
|
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
|
outerPadding[i] = outerPadding[i] ^ key.at(i); // XOR operation between every byte in key and outerpadding, of key length
|
||||||
}
|
}
|
||||||
@@ -111,9 +108,10 @@ Smtp::Smtp(QObject *parent)
|
|||||||
m_socket = new QTcpSocket(this);
|
m_socket = new QTcpSocket(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
|
connect(m_socket, &QIODevice::readyRead, this, &Smtp::readyRead);
|
||||||
connect(m_socket, SIGNAL(disconnected()), SLOT(deleteLater()));
|
connect(m_socket, &QAbstractSocket::disconnected, this, &QObject::deleteLater);
|
||||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(error(QAbstractSocket::SocketError)));
|
connect(m_socket, static_cast<void (QAbstractSocket::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error)
|
||||||
|
, this, &Smtp::error);
|
||||||
|
|
||||||
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
// Test hmacMD5 function (http://www.faqs.org/rfcs/rfc2202.html)
|
||||||
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
Q_ASSERT(hmacMD5("Jefe", "what do ya want for nothing?").toHex()
|
||||||
@@ -129,8 +127,8 @@ Smtp::~Smtp()
|
|||||||
|
|
||||||
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
|
||||||
{
|
{
|
||||||
const Preferences* const pref = Preferences::instance();
|
const Preferences *const pref = Preferences::instance();
|
||||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
QTextCodec *latin1 = QTextCodec::codecForName("latin1");
|
||||||
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
|
m_message = "Date: " + getCurrentDateTime().toLatin1() + "\r\n"
|
||||||
+ encodeMimeHeader("From", from, latin1)
|
+ encodeMimeHeader("From", from, latin1)
|
||||||
+ encodeMimeHeader("Subject", subject, latin1)
|
+ encodeMimeHeader("Subject", subject, latin1)
|
||||||
@@ -140,8 +138,8 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
|||||||
+ "Content-Transfer-Encoding: base64\r\n"
|
+ "Content-Transfer-Encoding: base64\r\n"
|
||||||
+ "\r\n";
|
+ "\r\n";
|
||||||
// Encode the body in base64
|
// Encode the body in base64
|
||||||
QString crlf_body = body;
|
QString crlfBody = body;
|
||||||
QByteArray b = crlf_body.replace("\n", "\r\n").toUtf8().toBase64();
|
QByteArray b = crlfBody.replace("\n", "\r\n").toUtf8().toBase64();
|
||||||
int ct = b.length();
|
int ct = b.length();
|
||||||
for (int i = 0; i < ct; i += 78)
|
for (int i = 0; i < ct; i += 78)
|
||||||
m_message += b.mid(i, 78);
|
m_message += b.mid(i, 78);
|
||||||
@@ -164,7 +162,7 @@ void Smtp::sendMail(const QString &from, const QString &to, const QString &subje
|
|||||||
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
m_socket->connectToHost(pref->getMailNotificationSMTP(), DEFAULT_PORT);
|
||||||
m_useSsl = false;
|
m_useSsl = false;
|
||||||
#ifndef QT_NO_OPENSSL
|
#ifndef QT_NO_OPENSSL
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +181,7 @@ void Smtp::readyRead()
|
|||||||
QByteArray code = line.left(3);
|
QByteArray code = line.left(3);
|
||||||
|
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case Init: {
|
case Init:
|
||||||
if (code[0] == '2') {
|
if (code[0] == '2') {
|
||||||
// The server may send a multiline greeting/INIT/220 response.
|
// The server may send a multiline greeting/INIT/220 response.
|
||||||
// We wait until it finishes.
|
// We wait until it finishes.
|
||||||
@@ -197,7 +195,6 @@ void Smtp::readyRead()
|
|||||||
m_state = Close;
|
m_state = Close;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case EhloSent:
|
case EhloSent:
|
||||||
case HeloSent:
|
case HeloSent:
|
||||||
case EhloGreetReceived:
|
case EhloGreetReceived:
|
||||||
@@ -302,7 +299,7 @@ QByteArray Smtp::encodeMimeHeader(const QString &key, const QString &value, QTex
|
|||||||
if (firstWord)
|
if (firstWord)
|
||||||
line += word;
|
line += word;
|
||||||
else
|
else
|
||||||
line += " " + word;
|
line += ' ' + word;
|
||||||
firstWord = false;
|
firstWord = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,7 +423,7 @@ void Smtp::authenticate()
|
|||||||
// Skip authentication
|
// Skip authentication
|
||||||
logError("The SMTP server does not seem to support any of the authentications modes "
|
logError("The SMTP server does not seem to support any of the authentications modes "
|
||||||
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
"we support [CRAM-MD5|PLAIN|LOGIN], skipping authentication, "
|
||||||
"knowing it is likely to fail... Server Auth Modes: " + auth.join("|"));
|
"knowing it is likely to fail... Server Auth Modes: " + auth.join('|'));
|
||||||
m_state = Authenticated;
|
m_state = Authenticated;
|
||||||
// At this point the server will not send any response
|
// At this point the server will not send any response
|
||||||
// So fill the buffer with a fake one to pass the tests
|
// So fill the buffer with a fake one to pass the tests
|
||||||
@@ -447,7 +444,7 @@ void Smtp::startTLS()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Smtp::authCramMD5(const QByteArray& challenge)
|
void Smtp::authCramMD5(const QByteArray &challenge)
|
||||||
{
|
{
|
||||||
if (m_state != AuthRequestSent) {
|
if (m_state != AuthRequestSent) {
|
||||||
m_socket->write("auth cram-md5\r\n");
|
m_socket->write("auth cram-md5\r\n");
|
||||||
@@ -506,7 +503,7 @@ void Smtp::authLogin()
|
|||||||
void Smtp::logError(const QString &msg)
|
void Smtp::logError(const QString &msg)
|
||||||
{
|
{
|
||||||
qDebug() << "Email Notification Error:" << msg;
|
qDebug() << "Email Notification Error:" << msg;
|
||||||
Logger::instance()->addMessage(tr("Email Notification Error:") + " " + msg, Log::CRITICAL);
|
Logger::instance()->addMessage(tr("Email Notification Error:") + ' ' + msg, Log::CRITICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Smtp::getCurrentDateTime() const
|
QString Smtp::getCurrentDateTime() const
|
||||||
@@ -532,7 +529,7 @@ QString Smtp::getCurrentDateTime() const
|
|||||||
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
std::snprintf(buf, sizeof(buf), "%+05d", timeOffset);
|
||||||
QString timeOffsetStr = buf;
|
QString timeOffsetStr = buf;
|
||||||
|
|
||||||
QString ret = weekDayStr + ", " + dayStr + " " + monthStr + " " + yearStr + " " + timeStr + " " + timeOffsetStr;
|
QString ret = weekDayStr + ", " + dayStr + ' ' + monthStr + ' ' + yearStr + ' ' + timeStr + ' ' + timeOffsetStr;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ class QSslSocket;
|
|||||||
class QTcpSocket;
|
class QTcpSocket;
|
||||||
#endif
|
#endif
|
||||||
class QTextCodec;
|
class QTextCodec;
|
||||||
class QTextStream;
|
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user