Compare commits
1049 Commits
release-2.
...
release-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd29de365a | ||
|
|
de78fc495b | ||
|
|
0b89d9b1e9 | ||
|
|
93731f1b3f | ||
|
|
2e9e10299c | ||
|
|
9f627b5ec9 | ||
|
|
ad252f432a | ||
|
|
eb1feddea6 | ||
|
|
1634014dde | ||
|
|
e863772159 | ||
|
|
7c34f4e93c | ||
|
|
91032056e1 | ||
|
|
f427d267b6 | ||
|
|
a7a8265053 | ||
|
|
827fceca83 | ||
|
|
7aaa3e8f51 | ||
|
|
1fe49198d1 | ||
|
|
c582aff563 | ||
|
|
5272f1cba3 | ||
|
|
b72d356083 | ||
|
|
fbd6533eee | ||
|
|
2998dbd0a1 | ||
|
|
cb8da634f4 | ||
|
|
e8f229816e | ||
|
|
fe0fc952e8 | ||
|
|
29a76ae600 | ||
|
|
5471600dfb | ||
|
|
df42c4d59f | ||
|
|
8047495b2e | ||
|
|
de04b47c29 | ||
|
|
b709bec24d | ||
|
|
35f93e8e16 | ||
|
|
554a84f8b0 | ||
|
|
f26fcabde1 | ||
|
|
75b91c204d | ||
|
|
0837c25602 | ||
|
|
34f8937b8d | ||
|
|
4a7f3ee415 | ||
|
|
b218531ff7 | ||
|
|
e96dee2038 | ||
|
|
080105c088 | ||
|
|
5d2535c1c1 | ||
|
|
49b21059fa | ||
|
|
c1af948649 | ||
|
|
36ad3df9e7 | ||
|
|
e607d721a2 | ||
|
|
5bfbfeb665 | ||
|
|
1f1eabb1e9 | ||
|
|
762050f8fc | ||
|
|
e3c1270205 | ||
|
|
414685910b | ||
|
|
d61b9c5d4d | ||
|
|
cf86a1cecd | ||
|
|
a3f3287e24 | ||
|
|
6e18d780ba | ||
|
|
7dde763fc6 | ||
|
|
8c85ffca5f | ||
|
|
321e568d86 | ||
|
|
e0649a7e78 | ||
|
|
4cd3233cd0 | ||
|
|
c554528afe | ||
|
|
7ab7f4b0fc | ||
|
|
58c0ac7638 | ||
|
|
2514224a3a | ||
|
|
247f6b4a8e | ||
|
|
3d89864b75 | ||
|
|
7ac75ad772 | ||
|
|
caa628371f | ||
|
|
fd31f632d9 | ||
|
|
a25b6e087b | ||
|
|
1626d938d1 | ||
|
|
a4c7640499 | ||
|
|
2c1b93a12b | ||
|
|
8f19025c2a | ||
|
|
20d41bcff2 | ||
|
|
1ad77e00ff | ||
|
|
0b65c02666 | ||
|
|
294095367a | ||
|
|
05a6ffd554 | ||
|
|
e0a30dc40c | ||
|
|
1940bc4e73 | ||
|
|
a7e4e0273a | ||
|
|
29a9d57cdc | ||
|
|
9c7a4e4983 | ||
|
|
35ead5bcf2 | ||
|
|
645bd58d79 | ||
|
|
6c47a552ab | ||
|
|
f54bc8bea4 | ||
|
|
eb54d81c5e | ||
|
|
431d2f082c | ||
|
|
a0ff0cdc7e | ||
|
|
fb91558261 | ||
|
|
fc2a47ca31 | ||
|
|
dcccbaad59 | ||
|
|
99d040de3f | ||
|
|
ee30a75b57 | ||
|
|
8c001aa478 | ||
|
|
849bc11a01 | ||
|
|
5024e0b092 | ||
|
|
d93447489b | ||
|
|
33988e70ab | ||
|
|
d2b41d70c8 | ||
|
|
8e1e51d268 | ||
|
|
53500ea005 | ||
|
|
e048389dea | ||
|
|
fa7b1a205d | ||
|
|
7dd6b7e9bb | ||
|
|
78b96accda | ||
|
|
73d0e2568a | ||
|
|
92fc212a0e | ||
|
|
6f4d7b7c1b | ||
|
|
63ee1c36e8 | ||
|
|
7d07debcb5 | ||
|
|
dd67e1ee9d | ||
|
|
493efdbf10 | ||
|
|
e323175275 | ||
|
|
c1fa7f8645 | ||
|
|
275b962363 | ||
|
|
3847c33017 | ||
|
|
9e21f52213 | ||
|
|
25cf9b33ec | ||
|
|
c7a2d3589f | ||
|
|
d8dd3834c3 | ||
|
|
ee01c2c745 | ||
|
|
e7e5a2b4e9 | ||
|
|
ff16f59be1 | ||
|
|
2cd4937ddc | ||
|
|
0e9abc1762 | ||
|
|
e24ce87946 | ||
|
|
308e358d3f | ||
|
|
d15e6a4847 | ||
|
|
e311239a28 | ||
|
|
df677789d2 | ||
|
|
0af44eadb6 | ||
|
|
85cafe530e | ||
|
|
7609db28f1 | ||
|
|
fefda39284 | ||
|
|
b2f98bd059 | ||
|
|
26c69fe6d4 | ||
|
|
bf4f1a7c37 | ||
|
|
9b0dd39d9d | ||
|
|
66d4cc2ab8 | ||
|
|
0bcbaf6521 | ||
|
|
e074872b24 | ||
|
|
63ec1e618e | ||
|
|
331c15b76c | ||
|
|
d2089c9aad | ||
|
|
3c8326d3b6 | ||
|
|
2d0713ca1d | ||
|
|
afaca423cc | ||
|
|
ff08abe177 | ||
|
|
159be479cc | ||
|
|
f3066b07b5 | ||
|
|
aa58636832 | ||
|
|
5d1a584eac | ||
|
|
7ee9abd646 | ||
|
|
4d977a8d46 | ||
|
|
5c891724c4 | ||
|
|
185af18790 | ||
|
|
fa6da97cf9 | ||
|
|
9503d9b024 | ||
|
|
de7b6ca553 | ||
|
|
1788078594 | ||
|
|
3caf473424 | ||
|
|
5435bd2354 | ||
|
|
1cdb22a7e3 | ||
|
|
95a9424ae7 | ||
|
|
902196a176 | ||
|
|
dbfef8665b | ||
|
|
b3d8f2400f | ||
|
|
c12ae58868 | ||
|
|
4b77bb57ad | ||
|
|
a6b1d308c2 | ||
|
|
b483f09d11 | ||
|
|
bbb5ad31c4 | ||
|
|
3d4c1fe7da | ||
|
|
7a2c0d5d5a | ||
|
|
39d86c5f61 | ||
|
|
e2cdbbb184 | ||
|
|
e83f9245a9 | ||
|
|
52f25c44eb | ||
|
|
1ed565d8e5 | ||
|
|
10801f111b | ||
|
|
efe1655834 | ||
|
|
7f71e3981e | ||
|
|
8113f8df63 | ||
|
|
62f579fdbe | ||
|
|
1d4454c6eb | ||
|
|
310433fb54 | ||
|
|
c0520146e9 | ||
|
|
35f6675d86 | ||
|
|
9494b15bd5 | ||
|
|
3b82120923 | ||
|
|
b9a0adc311 | ||
|
|
1e21ac3d79 | ||
|
|
864bb8285e | ||
|
|
208e988c70 | ||
|
|
5cbcfb8efb | ||
|
|
107b833703 | ||
|
|
e2208a484e | ||
|
|
5ab7c91d49 | ||
|
|
a91ad3c9c7 | ||
|
|
385f950c2b | ||
|
|
4667a84939 | ||
|
|
5af0cacf0c | ||
|
|
bceb00d35f | ||
|
|
51068294f1 | ||
|
|
bcdf4e42fa | ||
|
|
214bb13843 | ||
|
|
7998395532 | ||
|
|
35aee18112 | ||
|
|
8ddbdd34ed | ||
|
|
66a5e479c7 | ||
|
|
8f5bd2bc9f | ||
|
|
5be2624cb1 | ||
|
|
f9684d662d | ||
|
|
9373796dd5 | ||
|
|
2634517cdb | ||
|
|
121a636b4e | ||
|
|
aad42d4a57 | ||
|
|
c8b8518608 | ||
|
|
0c40a8ab05 | ||
|
|
b9241ad5ad | ||
|
|
d354d7133e | ||
|
|
a1157c78b2 | ||
|
|
fb8d52e6e0 | ||
|
|
80c42cae1a | ||
|
|
f242fce5fd | ||
|
|
6223514704 | ||
|
|
8fb7445e20 | ||
|
|
3172c6859d | ||
|
|
58be573d02 | ||
|
|
82f7bdb9cc | ||
|
|
b41c39b7ce | ||
|
|
9e4958e96a | ||
|
|
65091f7cb9 | ||
|
|
75b758bb1f | ||
|
|
5028ff3ddd | ||
|
|
a246f76ec3 | ||
|
|
eeec067481 | ||
|
|
3ee44ad0c2 | ||
|
|
c38ff5b0b7 | ||
|
|
e51dc6758c | ||
|
|
7bb32643fa | ||
|
|
3172e1f530 | ||
|
|
7778877fa7 | ||
|
|
b4f6745251 | ||
|
|
4fd7f16911 | ||
|
|
ca29b5d7fc | ||
|
|
79d5a635a4 | ||
|
|
7f615c580e | ||
|
|
967dbbda89 | ||
|
|
452c0411d2 | ||
|
|
32d3ed2144 | ||
|
|
4efae21f1d | ||
|
|
c43efd7fb3 | ||
|
|
93b1dbd931 | ||
|
|
571896b888 | ||
|
|
9f5d31b2b1 | ||
|
|
c01f7102e6 | ||
|
|
df39e46c84 | ||
|
|
7f12bb5dfb | ||
|
|
ca762139f6 | ||
|
|
67f41ad991 | ||
|
|
621ec11ae3 | ||
|
|
de3135c43b | ||
|
|
faed0ba45a | ||
|
|
2104e4e2ce | ||
|
|
907a17a73b | ||
|
|
a1a9f8bc7e | ||
|
|
a9a3bb74e7 | ||
|
|
b7de3fcebd | ||
|
|
0b70b857b9 | ||
|
|
9c8a957dd7 | ||
|
|
cf65e4d77d | ||
|
|
762962270c | ||
|
|
a2067af4ea | ||
|
|
2f4f06ca8b | ||
|
|
56dba2cb10 | ||
|
|
f824357358 | ||
|
|
778cfff4b3 | ||
|
|
8ee762695b | ||
|
|
7dd9680e26 | ||
|
|
92661e1d53 | ||
|
|
09d5665ff9 | ||
|
|
c5dfe623c8 | ||
|
|
2665e2ef39 | ||
|
|
dfecdb1956 | ||
|
|
5a82aee76b | ||
|
|
4f07426010 | ||
|
|
9e575e45b7 | ||
|
|
39b32bf1cd | ||
|
|
54131001b0 | ||
|
|
f8134b1a62 | ||
|
|
13927599c8 | ||
|
|
0d85d0a777 | ||
|
|
0da83694e0 | ||
|
|
9bdac4d1e3 | ||
|
|
00c92d6d61 | ||
|
|
9628a9e156 | ||
|
|
f1bfcb4822 | ||
|
|
87879b6738 | ||
|
|
a4ff7047d1 | ||
|
|
da599da36d | ||
|
|
335e7366fd | ||
|
|
a993b2b3f1 | ||
|
|
b81c8d29ba | ||
|
|
08a08e4b24 | ||
|
|
8b5886cc6e | ||
|
|
22b3d8d594 | ||
|
|
17efa04257 | ||
|
|
e71f144c40 | ||
|
|
5e7cafb8c0 | ||
|
|
d01385a7ac | ||
|
|
ac80dd78a8 | ||
|
|
061f1000cf | ||
|
|
c36fed3081 | ||
|
|
75002906aa | ||
|
|
a451cf2d5f | ||
|
|
ca10c0ab09 | ||
|
|
80d76ae038 | ||
|
|
f699ee1363 | ||
|
|
bd97a4bd7c | ||
|
|
ec3b749ef6 | ||
|
|
44fc86e7da | ||
|
|
2cd940e7c7 | ||
|
|
cc6d371667 | ||
|
|
8fcc33b375 | ||
|
|
e44b570c3d | ||
|
|
8576847a99 | ||
|
|
c9fc9373e5 | ||
|
|
ae46d449e5 | ||
|
|
fe57cef756 | ||
|
|
3affb38ba2 | ||
|
|
a0dc6aa173 | ||
|
|
a357a690ce | ||
|
|
bce7959332 | ||
|
|
eef6f600c9 | ||
|
|
a3822c5aa8 | ||
|
|
eeaa1e83a9 | ||
|
|
387fb93b5b | ||
|
|
d470059c56 | ||
|
|
2e382fa88c | ||
|
|
e1e7e782d5 | ||
|
|
6f314502f6 | ||
|
|
3284d7a8e6 | ||
|
|
609ea7b4c2 | ||
|
|
7e8d21145e | ||
|
|
c31b5f1f97 | ||
|
|
11fe685a6b | ||
|
|
1127947f52 | ||
|
|
789b2cf326 | ||
|
|
b9394f2ba2 | ||
|
|
7ab206ebac | ||
|
|
0beb9fff73 | ||
|
|
65b8d0c24d | ||
|
|
715fe46f45 | ||
|
|
8209f341fb | ||
|
|
0d4b55ca32 | ||
|
|
ae7362706f | ||
|
|
4ca648a79b | ||
|
|
4ca0b14300 | ||
|
|
4386b6c2c4 | ||
|
|
61246f81c7 | ||
|
|
70f2bfdd9a | ||
|
|
5f23cbc470 | ||
|
|
2e0c8f848f | ||
|
|
f8add0ee04 | ||
|
|
f23051c1fd | ||
|
|
60636af544 | ||
|
|
2e685ea851 | ||
|
|
b61a06350f | ||
|
|
c9ece4695e | ||
|
|
4805690dbe | ||
|
|
8ec1621334 | ||
|
|
682377ff66 | ||
|
|
656beed82b | ||
|
|
ba4a0fac77 | ||
|
|
b719bfaecb | ||
|
|
4e1366bf0d | ||
|
|
b67938aa3f | ||
|
|
f4c29c07bf | ||
|
|
63a0f4bf11 | ||
|
|
a8a2ba188d | ||
|
|
a0d685bfe2 | ||
|
|
a3041b7f9f | ||
|
|
83a2ae7ad3 | ||
|
|
830a6250c6 | ||
|
|
f0e23ab1fc | ||
|
|
5b07c488b0 | ||
|
|
f61bccf700 | ||
|
|
4fd10bc5f5 | ||
|
|
c1a72f075d | ||
|
|
5c4450f3cd | ||
|
|
bb606e0b2f | ||
|
|
eed86aa9e1 | ||
|
|
338d4fd31e | ||
|
|
58a36f7cfd | ||
|
|
8b436f70d6 | ||
|
|
223fd4f0ba | ||
|
|
2e51e69cc9 | ||
|
|
bbac79c030 | ||
|
|
607bba4625 | ||
|
|
1a0cc3215c | ||
|
|
f0981c81c8 | ||
|
|
472604d1b0 | ||
|
|
781d33b869 | ||
|
|
8db8841c9c | ||
|
|
6e3b570be4 | ||
|
|
defd77b94c | ||
|
|
c09294df2f | ||
|
|
cc97ee86df | ||
|
|
10d6dd5a23 | ||
|
|
29e79dc54d | ||
|
|
982390f074 | ||
|
|
963d73af4e | ||
|
|
bdd8cbb1d8 | ||
|
|
80bd232054 | ||
|
|
c2da227254 | ||
|
|
e846c67bfa | ||
|
|
475c7094ab | ||
|
|
4434566d85 | ||
|
|
6bd557d22c | ||
|
|
4832a24ac5 | ||
|
|
b9a3890dd5 | ||
|
|
4cf9de6515 | ||
|
|
994c798264 | ||
|
|
119bda1595 | ||
|
|
bf73c47bc5 | ||
|
|
ea99f44893 | ||
|
|
4ec1fd3968 | ||
|
|
0b9e25ee65 | ||
|
|
73a9985599 | ||
|
|
a65f7bfa65 | ||
|
|
72cc1eb4d4 | ||
|
|
ed7ae32a69 | ||
|
|
54b53f3aba | ||
|
|
2f7728f987 | ||
|
|
f3a93af242 | ||
|
|
5a4ad2f822 | ||
|
|
c669240037 | ||
|
|
ce4fd8429e | ||
|
|
cc532d95da | ||
|
|
c800a0a6b5 | ||
|
|
5675c045ae | ||
|
|
e16ca862f4 | ||
|
|
29e757bb3a | ||
|
|
822f1a9557 | ||
|
|
7acfb27a1f | ||
|
|
83d6731fa9 | ||
|
|
de4f0272c1 | ||
|
|
df50a875df | ||
|
|
5f814f23b4 | ||
|
|
98ec373f8c | ||
|
|
0bbeab9ad2 | ||
|
|
ed4c711ef5 | ||
|
|
fc33702b81 | ||
|
|
70c79f31c3 | ||
|
|
82dc2103b5 | ||
|
|
529ab304b4 | ||
|
|
53919446d4 | ||
|
|
1052cd019b | ||
|
|
4f87819abf | ||
|
|
b94ecb2383 | ||
|
|
c00d83dee9 | ||
|
|
34052ce8f9 | ||
|
|
0a0c8948ee | ||
|
|
0cc9283275 | ||
|
|
6b2e9f057b | ||
|
|
3a1fc9ec77 | ||
|
|
e4399412b6 | ||
|
|
37650d8156 | ||
|
|
b4fa164dc3 | ||
|
|
6148e6b192 | ||
|
|
057743a779 | ||
|
|
530ba2a0bd | ||
|
|
977b2bda7c | ||
|
|
76eb93ba1a | ||
|
|
ae3c50c074 | ||
|
|
31017602fc | ||
|
|
ef7e2abdaf | ||
|
|
a6abedd67d | ||
|
|
9824d86a3c | ||
|
|
1eac3d1700 | ||
|
|
48585bb049 | ||
|
|
843e49a5a8 | ||
|
|
db01276f1d | ||
|
|
772028106e | ||
|
|
0cd5253857 | ||
|
|
eaf321facb | ||
|
|
81ecb5c7c8 | ||
|
|
3b3a452df6 | ||
|
|
65b1e29414 | ||
|
|
a6e3b9afac | ||
|
|
a7792701a2 | ||
|
|
846372e48b | ||
|
|
1597007ea1 | ||
|
|
dbceed21f5 | ||
|
|
3254dae59f | ||
|
|
12881dd4c4 | ||
|
|
35ec93ccb1 | ||
|
|
cb159ed00d | ||
|
|
e5b8b41373 | ||
|
|
492814a56e | ||
|
|
d639712303 | ||
|
|
f5d1c8a679 | ||
|
|
aea3d4be7a | ||
|
|
8d1fd7ee8e | ||
|
|
a98a002d57 | ||
|
|
fb874695c9 | ||
|
|
63457c034f | ||
|
|
daff6dce4c | ||
|
|
94c7c5cebe | ||
|
|
f2c4901dd0 | ||
|
|
95d8eb9714 | ||
|
|
62165445fe | ||
|
|
6b12371f50 | ||
|
|
38cb4ccbd3 | ||
|
|
8ccaaae085 | ||
|
|
6f7ae4770a | ||
|
|
5aa348a574 | ||
|
|
7b8fa49482 | ||
|
|
789cf654d0 | ||
|
|
a300a6094e | ||
|
|
37b09e70b0 | ||
|
|
530fbfc9b4 | ||
|
|
73c312ec1a | ||
|
|
318a959470 | ||
|
|
f9cc98791a | ||
|
|
263eb3c632 | ||
|
|
52b3926f92 | ||
|
|
af3755bf91 | ||
|
|
419d719ab8 | ||
|
|
3877cf9ab8 | ||
|
|
912056a364 | ||
|
|
165b33a94e | ||
|
|
12fff1b966 | ||
|
|
128eba7ec3 | ||
|
|
5bb370df0e | ||
|
|
6431343ad7 | ||
|
|
bd292076e3 | ||
|
|
ce0ddbe517 | ||
|
|
5451bb185e | ||
|
|
057cf3648e | ||
|
|
80d5c5d85c | ||
|
|
671a997092 | ||
|
|
f2fbfdf017 | ||
|
|
1ea7340111 | ||
|
|
7b38ea4151 | ||
|
|
c4ed12bbd1 | ||
|
|
c32b352ec1 | ||
|
|
9b2565dff9 | ||
|
|
f833f26212 | ||
|
|
66a7082b5c | ||
|
|
cc3a6350a5 | ||
|
|
9202ce8757 | ||
|
|
c0e4f126de | ||
|
|
44ca0a4904 | ||
|
|
64ddf393aa | ||
|
|
a60615a2d8 | ||
|
|
d9fa9c3566 | ||
|
|
5115b8739e | ||
|
|
854fb4acc0 | ||
|
|
07ef81fd95 | ||
|
|
17e39f56dd | ||
|
|
040e94cab7 | ||
|
|
93563365ac | ||
|
|
c287879e55 | ||
|
|
537c73d548 | ||
|
|
ad3f5f6e20 | ||
|
|
a468404ab5 | ||
|
|
6d7fba1a6c | ||
|
|
5c734a80f8 | ||
|
|
edfee5193c | ||
|
|
9900651c55 | ||
|
|
948adbe350 | ||
|
|
ade0ba794c | ||
|
|
5494c33a89 | ||
|
|
c8f5cab636 | ||
|
|
4a8a40a6f2 | ||
|
|
70d41cdc79 | ||
|
|
aec99ed14f | ||
|
|
9a645b4b19 | ||
|
|
bea3c33a46 | ||
|
|
e082c02630 | ||
|
|
291b94bef0 | ||
|
|
f119be3ed5 | ||
|
|
9034094cf9 | ||
|
|
c48766aeb7 | ||
|
|
28a6afeb02 | ||
|
|
c8d0f5a104 | ||
|
|
3c396257de | ||
|
|
a9be841d2d | ||
|
|
7d0581a7a5 | ||
|
|
4efeb66b73 | ||
|
|
3b05f8b4b4 | ||
|
|
6b4f09d740 | ||
|
|
f1b02c1280 | ||
|
|
3d546a4c5d | ||
|
|
19368bcefa | ||
|
|
dbbf26449c | ||
|
|
8f28804f8c | ||
|
|
b7edfea4ce | ||
|
|
23b2f94c40 | ||
|
|
8f40f41fef | ||
|
|
33f868144b | ||
|
|
b2545bb709 | ||
|
|
3b6e1e82d9 | ||
|
|
7710c88797 | ||
|
|
4d5001d18d | ||
|
|
0f4f108eb5 | ||
|
|
f0d0bb7170 | ||
|
|
66157da5c2 | ||
|
|
13493e1afe | ||
|
|
f6bfacda2c | ||
|
|
f7a86b5484 | ||
|
|
66cd3f8184 | ||
|
|
3a237c93be | ||
|
|
4c34066727 | ||
|
|
1960008c83 | ||
|
|
deffbd6321 | ||
|
|
53927c9aa0 | ||
|
|
d84346616a | ||
|
|
e1183dbc0b | ||
|
|
602f1574ca | ||
|
|
358f7d16da | ||
|
|
e4006d6175 | ||
|
|
949b4ce4e9 | ||
|
|
cbafac8ea9 | ||
|
|
e4bf116ce8 | ||
|
|
bccdad4b1b | ||
|
|
9b372b3cce | ||
|
|
98d0c00f85 | ||
|
|
9d2f2230ee | ||
|
|
81c6958428 | ||
|
|
f976eda6a9 | ||
|
|
fe8d5a3528 | ||
|
|
240c3508fe | ||
|
|
1065f5fb86 | ||
|
|
2b37986007 | ||
|
|
22d0c4a241 | ||
|
|
dd47ce6767 | ||
|
|
5c80ce42e0 | ||
|
|
adb727d282 | ||
|
|
e17ca355ae | ||
|
|
b80940ac4f | ||
|
|
1eca139db9 | ||
|
|
25278beb2f | ||
|
|
5922ffff62 | ||
|
|
ff084e9681 | ||
|
|
86e5d219d2 | ||
|
|
b78e0a54ed | ||
|
|
4bfacb8b91 | ||
|
|
e9ad58a373 | ||
|
|
09c48539ad | ||
|
|
3693ecdd30 | ||
|
|
ddc66e6005 | ||
|
|
fd0b1f8931 | ||
|
|
1a4f638ff6 | ||
|
|
77239db3c5 | ||
|
|
0ea59c8d58 | ||
|
|
54e2a8c7fe | ||
|
|
245a8e0a3a | ||
|
|
51e474c893 | ||
|
|
81d3e64518 | ||
|
|
95da161be3 | ||
|
|
8618f13b7a | ||
|
|
e24e7578f2 | ||
|
|
a03ad3de23 | ||
|
|
9bd40a9b79 | ||
|
|
b4b61b9b7d | ||
|
|
5656fe9a9b | ||
|
|
fb79146ae6 | ||
|
|
ba27191b2a | ||
|
|
dc87aa3d5c | ||
|
|
83cf3aebab | ||
|
|
dedd9bd03c | ||
|
|
ab36a358b7 | ||
|
|
6ea97f09cf | ||
|
|
5f7822d202 | ||
|
|
6a87225dd0 | ||
|
|
b6f56c0812 | ||
|
|
c4ce2a2549 | ||
|
|
41650da297 | ||
|
|
74c61e6805 | ||
|
|
e8dd7bbcc9 | ||
|
|
335d012d55 | ||
|
|
17fc58840a | ||
|
|
0e8c55b9f5 | ||
|
|
d581f653c6 | ||
|
|
48dbaf05ae | ||
|
|
540da69d61 | ||
|
|
06efd64a80 | ||
|
|
62d872984b | ||
|
|
1dd11dd8f8 | ||
|
|
2ce375d8eb | ||
|
|
8ed0e58d63 | ||
|
|
660a6929fd | ||
|
|
44f6c972d4 | ||
|
|
6a6077bf1d | ||
|
|
30234a4e78 | ||
|
|
dc9edf7538 | ||
|
|
92574458d0 | ||
|
|
c35ef9ad15 | ||
|
|
4059bcc0fa | ||
|
|
1840d1c49f | ||
|
|
cbd948f6f3 | ||
|
|
454c093033 | ||
|
|
45eaf7ce58 | ||
|
|
04b7af4df5 | ||
|
|
8c6978be82 | ||
|
|
6c9e7156f7 | ||
|
|
bc89845523 | ||
|
|
9ffe9c2006 | ||
|
|
1d598d7772 | ||
|
|
7f576ccc82 | ||
|
|
d5da8a6277 | ||
|
|
a297204b27 | ||
|
|
e0182bb03e | ||
|
|
ca5bf5e9d7 | ||
|
|
5475d730ff | ||
|
|
7796520580 | ||
|
|
340500c351 | ||
|
|
043d33ff91 | ||
|
|
baf991b342 | ||
|
|
dc47e90126 | ||
|
|
67e3f9e686 | ||
|
|
aaeb6b90ed | ||
|
|
d124ada755 | ||
|
|
31105aefe4 | ||
|
|
44c258796e | ||
|
|
26100120a9 | ||
|
|
0db8ce891b | ||
|
|
979aba5685 | ||
|
|
396da6bd94 | ||
|
|
1b0d86220f | ||
|
|
957d2c0d1a | ||
|
|
455657912a | ||
|
|
e3da1902df | ||
|
|
d0f8e9208f | ||
|
|
de50346428 | ||
|
|
c7ca51f950 | ||
|
|
4522174555 | ||
|
|
126230ad08 | ||
|
|
6aa1f4156a | ||
|
|
2726faa090 | ||
|
|
230b84032d | ||
|
|
9c4c5e2d1a | ||
|
|
2bf8e2d2d7 | ||
|
|
9bb05c8dea | ||
|
|
2707aa2818 | ||
|
|
93c644da88 | ||
|
|
4a114c0fdc | ||
|
|
f95108155d | ||
|
|
75f75854c2 | ||
|
|
e20c3cd0b6 | ||
|
|
2273ea4099 | ||
|
|
b115b0c6a3 | ||
|
|
9d79a51f18 | ||
|
|
db9005158e | ||
|
|
4491999a8c | ||
|
|
113a502a7e | ||
|
|
7ad39003f4 | ||
|
|
4b43871708 | ||
|
|
aa8f7552a2 | ||
|
|
19d94b53d2 | ||
|
|
ca98a261e6 | ||
|
|
535ec2ced6 | ||
|
|
a300c236ba | ||
|
|
b16c26b24c | ||
|
|
1b1dde3fc8 | ||
|
|
bd5880843f | ||
|
|
abf35a31f3 | ||
|
|
015638035a | ||
|
|
8e27fe83f1 | ||
|
|
ce4b105065 | ||
|
|
37a7b79a6f | ||
|
|
ef19e8aeef | ||
|
|
f6886b4749 | ||
|
|
8306959cb4 | ||
|
|
d1918ee206 | ||
|
|
b2b76f9e35 | ||
|
|
a07fb264e8 | ||
|
|
881f2d04d2 | ||
|
|
e1b629df88 | ||
|
|
eab6a194e1 | ||
|
|
3733bc9148 | ||
|
|
ae5746a636 | ||
|
|
cb509cebb3 | ||
|
|
511c4474c7 | ||
|
|
d063aea977 | ||
|
|
447efc52d8 | ||
|
|
296acf820c | ||
|
|
6ae49acb69 | ||
|
|
af1b06c5be | ||
|
|
4732c8565d | ||
|
|
52dd1476db | ||
|
|
63170b9214 | ||
|
|
2203b399df | ||
|
|
d80f7a6ef3 | ||
|
|
88c56d8250 | ||
|
|
fca24a8f84 | ||
|
|
6d140a141a | ||
|
|
a18e325c18 | ||
|
|
c4d9c51e49 | ||
|
|
27e980de28 | ||
|
|
3c4906bb4d | ||
|
|
da796c80c3 | ||
|
|
7a2af0f506 | ||
|
|
3e562e7450 | ||
|
|
4fc777268b | ||
|
|
037e57b687 | ||
|
|
2b289655c1 | ||
|
|
118ea6093d | ||
|
|
cac6f7428c | ||
|
|
ae6acc4ca2 | ||
|
|
641c6f0132 | ||
|
|
56dc8cad71 | ||
|
|
7cc07d842c | ||
|
|
baaadf07fa | ||
|
|
719b32ba36 | ||
|
|
af5c1096b5 | ||
|
|
3008222b92 | ||
|
|
5ef9faacd5 | ||
|
|
ff65b6ea2f | ||
|
|
4c8a087b81 | ||
|
|
8f4bf93f02 | ||
|
|
824b2f6fff | ||
|
|
ba0c7334b7 | ||
|
|
a1dd724220 | ||
|
|
a7820e5f08 | ||
|
|
1a2eee8060 | ||
|
|
4a2713a9f0 | ||
|
|
5ece0b4f70 | ||
|
|
44e8ec2b37 | ||
|
|
d7e5f4b895 | ||
|
|
b0c7bdf82b | ||
|
|
a239ac1a52 | ||
|
|
99459dc55d | ||
|
|
78ba99778a | ||
|
|
96d3df3135 | ||
|
|
cbe4bbac6a | ||
|
|
f502e82ec4 | ||
|
|
a5d8766a9e | ||
|
|
55d8e3d76b | ||
|
|
7b7fb4d278 | ||
|
|
09c7c50ad3 | ||
|
|
131250dc03 | ||
|
|
130187723c | ||
|
|
7bac95c9ad | ||
|
|
add2475700 | ||
|
|
533e402bca | ||
|
|
b87a23037e | ||
|
|
6d88bb5b83 | ||
|
|
435801c893 | ||
|
|
9d0577fa5c | ||
|
|
f2891dae74 | ||
|
|
a43a1f5b67 | ||
|
|
ee9ca1ece7 | ||
|
|
8271e20fac | ||
|
|
132b1f7ffe | ||
|
|
77b4d97069 | ||
|
|
2168d5a30e | ||
|
|
7ad90b1b80 | ||
|
|
f60ef5dbd5 | ||
|
|
4dc26d0a77 | ||
|
|
551273b831 | ||
|
|
d5a09674ae | ||
|
|
907e620e9e | ||
|
|
f96ec75eec | ||
|
|
4d00db2b42 | ||
|
|
db564b4521 | ||
|
|
3f2076d195 | ||
|
|
ed13c43e93 | ||
|
|
b5a9fe71e1 | ||
|
|
d0037d90f4 | ||
|
|
e885ee0d35 | ||
|
|
ff5e5d1062 | ||
|
|
1bfadf5219 | ||
|
|
4f9eca78d9 | ||
|
|
7aa2994078 | ||
|
|
1128b3ea83 | ||
|
|
9d06947125 | ||
|
|
14cc600a8b | ||
|
|
4f3fd86f91 | ||
|
|
e663717d64 | ||
|
|
8b5d388e77 | ||
|
|
22d781edd5 | ||
|
|
4252832ba5 | ||
|
|
fab58296af | ||
|
|
e142877e10 | ||
|
|
6f6e453ae4 | ||
|
|
50e620daf2 | ||
|
|
c334439df6 | ||
|
|
2d9b4f273e | ||
|
|
15c7099e85 | ||
|
|
d2b1f6176f | ||
|
|
8306d7931b | ||
|
|
b07fbb726e | ||
|
|
9586f0e61c | ||
|
|
7d66c07cef | ||
|
|
28ecb2fe1d | ||
|
|
a9cafeaa76 | ||
|
|
5fc69ccb73 | ||
|
|
bc626e3512 | ||
|
|
98561f9db9 | ||
|
|
b56dee2a92 | ||
|
|
885a7f592e | ||
|
|
6965448a24 | ||
|
|
475d63dad8 | ||
|
|
c87a0bde0b | ||
|
|
c1bc9886b7 | ||
|
|
88f8437c5a | ||
|
|
411a1c641d | ||
|
|
dc0ad73eca | ||
|
|
04e008afa0 | ||
|
|
a6207f70d5 | ||
|
|
b8a30be7bc | ||
|
|
67d60766c1 | ||
|
|
97c0b28a98 | ||
|
|
c61aded388 | ||
|
|
1fd57b5d63 | ||
|
|
755b8dec30 | ||
|
|
ecd5c1fcc3 | ||
|
|
db5402385a | ||
|
|
09ef3073a1 | ||
|
|
6dd3833e0f | ||
|
|
d4762a4d8d | ||
|
|
38cc55ea09 | ||
|
|
5d60295db3 | ||
|
|
18bd3b855c | ||
|
|
868d423c82 | ||
|
|
edb6857de0 | ||
|
|
ed803fb994 | ||
|
|
83d83a364d | ||
|
|
690bb92154 | ||
|
|
8e39eef7aa | ||
|
|
1923a51c59 | ||
|
|
3dc1107b40 | ||
|
|
dd22e9009c | ||
|
|
c7ccf39abf | ||
|
|
af166f53d3 | ||
|
|
21eb26a374 | ||
|
|
e2aaf5d1de | ||
|
|
0b7ca15c4f | ||
|
|
07ee2a4aff | ||
|
|
141d3b6737 | ||
|
|
e2c3e6dbaa | ||
|
|
b1126556c0 | ||
|
|
69cb8d1398 | ||
|
|
094d979ce6 | ||
|
|
92dd669597 | ||
|
|
32a8dec0e7 | ||
|
|
2dd5bccd49 | ||
|
|
18d0283463 | ||
|
|
9fe807b6f1 | ||
|
|
84f00bf3f6 | ||
|
|
d3687fd863 | ||
|
|
d1a549a6cc | ||
|
|
c1552ff254 | ||
|
|
54a396bc6e | ||
|
|
ed732bca63 | ||
|
|
02a4464e8f | ||
|
|
7f97df4462 | ||
|
|
9d20af02a2 | ||
|
|
cbaef050f0 | ||
|
|
148139c2c4 | ||
|
|
793f8a103d | ||
|
|
ffa48b64ac | ||
|
|
59a8e651f9 | ||
|
|
fb9f2a01b5 | ||
|
|
944e25a5d8 | ||
|
|
ad597c72d2 | ||
|
|
abbbf1e562 | ||
|
|
d2dd29c35a | ||
|
|
906f2bab1e | ||
|
|
bb6468005d | ||
|
|
a2423e97eb | ||
|
|
ed1d75ecb1 | ||
|
|
1e4200345a | ||
|
|
be11f4e039 | ||
|
|
22ec528cdf | ||
|
|
ebf341a6de | ||
|
|
68ba6322e1 | ||
|
|
968a5f3017 | ||
|
|
8df1708491 | ||
|
|
da51ce1b71 | ||
|
|
95a6de9b00 | ||
|
|
269bbdf01d | ||
|
|
df03b042d6 | ||
|
|
3ec118d59b | ||
|
|
bce575ee68 | ||
|
|
83da56b0eb | ||
|
|
fa2891c225 | ||
|
|
c4bf42524c | ||
|
|
a096105f18 | ||
|
|
51f842aaef | ||
|
|
76c4466fb6 | ||
|
|
facd02a7ec | ||
|
|
ed8da2ef66 | ||
|
|
998352f9f1 | ||
|
|
8c1a135d16 | ||
|
|
b976d39207 | ||
|
|
f0a18ec1f5 | ||
|
|
f9a5afe446 | ||
|
|
b13c7abca0 | ||
|
|
1f104f62f5 | ||
|
|
f8f7729e77 | ||
|
|
eca262f5f4 | ||
|
|
df5f5a943b | ||
|
|
497cb07852 | ||
|
|
10ab8f107c | ||
|
|
3a6ff19418 | ||
|
|
ab317a3a62 | ||
|
|
617724c2b1 | ||
|
|
422c03b4ec | ||
|
|
4af05dc63c | ||
|
|
ac92287b43 | ||
|
|
c7634b3828 | ||
|
|
5d1d26b285 | ||
|
|
644fea69a1 | ||
|
|
4dc8959f67 | ||
|
|
63be5ffc74 | ||
|
|
c5a9e27cb3 | ||
|
|
ecf48b3a7b | ||
|
|
63e5339690 | ||
|
|
d8c27ac8c4 | ||
|
|
925597c43a | ||
|
|
ba8fd62900 | ||
|
|
3f61e176f0 | ||
|
|
542338972a | ||
|
|
fe91599eb4 | ||
|
|
d9817795ed | ||
|
|
6ede368f63 | ||
|
|
6885f46f4b | ||
|
|
9323660c79 | ||
|
|
88b77a63ed | ||
|
|
4d1ef40f6e | ||
|
|
9a68e0cc25 | ||
|
|
396427e3b6 | ||
|
|
e460f1c365 | ||
|
|
b9531c8245 | ||
|
|
27aff04f11 | ||
|
|
08fe5814c5 | ||
|
|
5af8b7c0e2 | ||
|
|
c1acbda38f | ||
|
|
afa5213c94 | ||
|
|
17b8ba27b3 | ||
|
|
3f9b74430a |
34
AUTHORS
@@ -3,19 +3,26 @@ Author:
|
|||||||
|
|
||||||
Contributors:
|
Contributors:
|
||||||
* Stefanos Antaris <santaris@csd.auth.gr>
|
* Stefanos Antaris <santaris@csd.auth.gr>
|
||||||
|
* Mohammad Dib <mdib@qbittorrent.org>
|
||||||
|
* Mirco Chinelli <infinity89@fastwebmail.it>
|
||||||
* Ishan Arora <ishan@qbittorrent.org>
|
* Ishan Arora <ishan@qbittorrent.org>
|
||||||
* Arnaud Demaizière <arnaud@qbittorrent.org>
|
* Arnaud Demaizière <arnaud@qbittorrent.org>
|
||||||
* Grigis Gaëtan <cipher16@gmail.com>
|
* Grigis Gaëtan <cipher16@gmail.com>
|
||||||
|
* Christian Kandeler <zambesi@users.sourceforge.net>
|
||||||
|
* Silvan Scherrer <silvan.scherrer@aroa.ch>
|
||||||
|
|
||||||
Code from other projects:
|
Code from other projects:
|
||||||
|
* files src/qtsingleapp/* src/lineedit/*
|
||||||
|
copyright: Nokia Corporation
|
||||||
|
license: LGPL
|
||||||
|
|
||||||
* files src/ico.cpp src/ico.h
|
* files src/ico.cpp src/ico.h
|
||||||
copyright: Malte Starostik <malte@kde.org>
|
copyright: Malte Starostik <malte@kde.org>
|
||||||
license: LGPL
|
license: LGPL
|
||||||
|
|
||||||
GeoIP database author:
|
* files src/search_engine/socks.py
|
||||||
* files: src/geoip/GeoIP.dat
|
copyright: Dan Haim <negativeiq@users.sourceforge.net>
|
||||||
copyright: MaxMind, Inc (http://maxmind.com/)
|
license: BSD
|
||||||
license: Open Data License (see src/geoip/LICENSE.txt)
|
|
||||||
|
|
||||||
Images Authors:
|
Images Authors:
|
||||||
* files: src/Icons/*.png
|
* files: src/Icons/*.png
|
||||||
@@ -60,11 +67,13 @@ Images Authors:
|
|||||||
Translations authors:
|
Translations authors:
|
||||||
* files: src/lang/*.ts
|
* files: src/lang/*.ts
|
||||||
copyright:
|
copyright:
|
||||||
|
- Arabic: SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)
|
||||||
- Brazilian: Nick Marinho (nickmarinho@gmail.com)
|
- Brazilian: Nick Marinho (nickmarinho@gmail.com)
|
||||||
- Bulgarian: Tsvetan & Boiko Bankov (emerge_life@users.sourceforge.net)
|
- Bulgarian: Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)
|
||||||
- Catalan: Gekko Dam Beer (gekko04@users.sourceforge.net)
|
- Catalan: Francisco Luque Contreras (frannoe@ya.com)
|
||||||
- Chinese (Simplified): Guo Yue (yue.guo0418@gmail.com)
|
- Chinese (Simplified): Guo Yue (yue.guo0418@gmail.com)
|
||||||
- Chinese (Traditional): Yi-Shun Wang (dnextstep@gmail.com)
|
- Chinese (Traditional): Yi-Shun Wang (dnextstep@gmail.com)
|
||||||
|
- Croatian: Oliver Mucafir (oliver.untwist@gmail.com)
|
||||||
- Czech: Jirka Vilim (web@tets.cz)
|
- Czech: Jirka Vilim (web@tets.cz)
|
||||||
- Danish: Mathias Nielsen (comoneo@gmail.com)
|
- Danish: Mathias Nielsen (comoneo@gmail.com)
|
||||||
- Dutch: Joost Schipper (heavyjoost@users.sourceforge.net)
|
- Dutch: Joost Schipper (heavyjoost@users.sourceforge.net)
|
||||||
@@ -72,19 +81,20 @@ Translations authors:
|
|||||||
- Finnish: Niklas Laxström (nikerabbit@users.sourceforge.net)
|
- Finnish: Niklas Laxström (nikerabbit@users.sourceforge.net)
|
||||||
- French: Christophe Dumez (chris@qbittorrent.org)
|
- French: Christophe Dumez (chris@qbittorrent.org)
|
||||||
- German: Niels Hoffmann (zentralmaschine@users.sourceforge.net)
|
- German: Niels Hoffmann (zentralmaschine@users.sourceforge.net)
|
||||||
- Greek: Tsvetan Bankov (emerge_life@users.sourceforge.net)
|
- Greek: Tsvetan Bankov (emerge_life@users.sourceforge.net) and Stephanos Antaris (santaris@csd.auth.gr)
|
||||||
- Hungarian: Majoros Péter (majoros.j.p@t-online.hu)
|
- Hungarian: Majoros Péter (majoros.j.p@t-online.hu)
|
||||||
- Italian: Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)
|
- Italian: Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)
|
||||||
- Japanese: Nardog (nardog@e2umail.com)
|
- Japanese: Nardog (alphisation@gmail.com)
|
||||||
- Korean: Jin Woo Sin (jin828sin@users.sourceforge.net)
|
- Korean: Jin Woo Sin (jin828sin@users.sourceforge.net)
|
||||||
- Norwegian: Lars-Erik Labori (hamil@users.sourceforge.net)
|
- Norwegian: Lars-Erik Labori (hamil@users.sourceforge.net)
|
||||||
- Polish: Jarek Smieja (ajep9691@wp.pl)
|
- Polish: Mariusz Fik (fisiu@opensuse.org)
|
||||||
- Portuguese: Nick Marinho (nickmarinho@gmail.com)
|
- Portuguese: Nick Marinho (nickmarinho@gmail.com)
|
||||||
- Romanian: Obada Denis (obadadenis@users.sourceforge.net)
|
- Romanian: Obada Denis (obadadenis@users.sourceforge.net)
|
||||||
- Russian: Nick Khazov (m2k3d0n at users.sourceforge.net)
|
- Russian: Nick Khazov (m2k3d0n at users.sourceforge.net)
|
||||||
|
- Serbian: Anaximandar Milet (anaximandar at operamail.com)
|
||||||
- Slovak: helix84
|
- Slovak: helix84
|
||||||
- Spanish: Vicente Raul Plata Fonseca (silverxnt@users.sourceforge.net)
|
- Spanish: Francisco Luque Contreras (frannoe@ya.com)
|
||||||
- Swedish: Daniel Nylander (po@danielnylander.se)
|
- Swedish: Daniel Nylander (po@danielnylander.se)
|
||||||
- Turkish: Erdem Bingöl (erdem84@gmail.com)
|
- Turkish: Hasan Yilmaz (iletisim@hedefturkce.com)
|
||||||
- Ukrainian: Andrey Shpachenko (masterfix@users.sourceforge.net)
|
- Ukrainian: Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com)
|
||||||
license: GPLv2
|
license: GPLv2
|
||||||
|
|||||||
266
Changelog
@@ -1,10 +1,220 @@
|
|||||||
* Unknown - Christophe Dumez <chris@qbittorrent.org> - v2.0.0
|
* Sun Oct 24 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.8
|
||||||
|
- BUGFIX: Fix possible crash on manual peer ban
|
||||||
|
- BUGFIX: Improved hostname resolution code
|
||||||
|
- BUGFIX: Several search plugins fixed
|
||||||
|
- BUGFIX: Auto-disable the shutdown feature
|
||||||
|
- BUGFIX: Remember the current property tab on startup
|
||||||
|
- BUGFIX: Fix status list widget height issue on style change
|
||||||
|
- BUGFIX: Fix rounding issue in torrent progress display
|
||||||
|
- BUGFIX: Fix issue when altering files priorities of a seeding torrent
|
||||||
|
- BUGFIX: Better fix for save path editing issues in torrent addition dialog
|
||||||
|
- BUGFIX: Peers can now be sorted by country
|
||||||
|
|
||||||
|
* Tue Oct 19 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.7
|
||||||
|
- BUGFIX: Display the priority column when the queueing system gets enabled
|
||||||
|
- BUGFIX: Fix encoding problem in file renaming
|
||||||
|
- BUGFIX: Delete uneeded files on torrent "soft" deletion
|
||||||
|
- BUGFIX: Fix issues when marking a file as 'not downloaded' causes the torrent to complete
|
||||||
|
- BUGFIX: Improved "Set Location" and "Change save path" dialogs
|
||||||
|
- BUGFIX: Fix display of queued seeding torrents
|
||||||
|
|
||||||
|
* Sun Oct 17 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.6
|
||||||
|
- BUGFIX: Fix "torrent seeding after creation" feature
|
||||||
|
- BUGFIX: The properties panel data would sometimes not match the selected torrent
|
||||||
|
- BUGFIX: Fix detection of files at final destination when temp dir is used
|
||||||
|
- BUGFIX: Fix moving of a torrent to an unexisting directory
|
||||||
|
|
||||||
|
* Tue Oct 12 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.5
|
||||||
|
- BUGFIX: Remember torrent completion date correctly
|
||||||
|
- BUGFIX: Fix feature to keep incomplete torrents in a separate folder
|
||||||
|
- BUGFIX: Fix display of URL seeds in the UI
|
||||||
|
- BUGFIX: Improved peer hostname resolution with caching
|
||||||
|
- BUGFIX: Piece availability/downloaded widgets performance improvement
|
||||||
|
|
||||||
|
* Fri Oct 1 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.4
|
||||||
|
- BUGFIX: Clean program exit on system shutdown/logout
|
||||||
|
- BUGFIX: Fix possible search engine plugin update
|
||||||
|
|
||||||
|
* Tue Sep 28 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.3
|
||||||
|
- BUGFIX: Fix encoding issue in command line parameters processing
|
||||||
|
- BUGFIX: Fix possible crash when changing the save path in addition dialog
|
||||||
|
- BUGFIX: Fix wrong mapping to source model
|
||||||
|
|
||||||
|
* Sun Sep 26 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.2
|
||||||
|
- BUGFIX: Fix display of torrent content in addition dialog
|
||||||
|
- BUGFIX: Really fix manual editing of save path in torrent addition dialog
|
||||||
|
|
||||||
|
* Sun Sep 26 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.1
|
||||||
|
- I18N: Updated Arabic translation
|
||||||
|
- I18N: Fixes to German translation
|
||||||
|
- BUGFIX: Save path can now be edited in torrent addition dialog
|
||||||
|
- BUGFIX: Fix save path encoding on non-utf8 systems
|
||||||
|
- BUGFIX: Fix saving to drive root on Windows
|
||||||
|
- BUGFIX: OGV can now be previewed
|
||||||
|
- BUGFIX: Maximum download limit is now 10MB/s
|
||||||
|
- BUGFIX: Fix 'download in scan dir' persistence
|
||||||
|
- BUGFIX: Add .torrent extension only when missing (torrent creator)
|
||||||
|
- BUGFIX: Fix possible issue with temporary download path persistence
|
||||||
|
- BUGFIX: Added support for | (OR) operator in RSS feed downloader
|
||||||
|
- BUGFIX: Fix Web UI for spanish users
|
||||||
|
- BUGFIX: Fix locale switching from Web UI
|
||||||
|
- BUGFIX: Use AND operator for torrentdownloads.net searches
|
||||||
|
- BUGFIX: Limit torrent addition dialog width to fit the screen
|
||||||
|
- COSMETIC: Fix progress bars style on Windows
|
||||||
|
|
||||||
|
* Tue Aug 24 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.4.0
|
||||||
|
- FEATURE: Added actions to "Move to top/bottom" of priority queue
|
||||||
|
- FEATURE: Auto-Shutdown on downloads completion
|
||||||
|
- FEATURE: Email notification on download completion
|
||||||
|
- FEATURE: Added button to password-lock the UI
|
||||||
|
- FEATURE: Added label-level Pause/Resume/Delete actions
|
||||||
|
- FEATURE: Torrents can now be filtered by name
|
||||||
|
- FEATURE: Run external program on torrent completion
|
||||||
|
- FEATURE: Detect executable updates in order to advise the user to restart
|
||||||
|
|
||||||
|
* Tue Jul 27 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.3.0
|
||||||
|
- FEATURE: Simplified torrent root folder renaming/truncating (< v2.3.0 is no longer forward compatible)
|
||||||
|
- FEATURE: Remember previous save paths in torrent addition dialog
|
||||||
|
- FEATURE: Max number of half-open connections can now be edited
|
||||||
|
- FEATURE: Added support for strict super seeding
|
||||||
|
- FEATURE: The user can force listening on a particular network interface
|
||||||
|
- FEATURE: Added cookie support for RSS feeds
|
||||||
|
- FEATURE: User can force tracker reannounce
|
||||||
|
- FEATURE: Added "No action" setting for double-click action
|
||||||
|
- FEATURE: Several torrents can be moved at once
|
||||||
|
- FEATURE: Added error state for torrents (error is displayed in a tooltip)
|
||||||
|
- FEATURE: Added filter for paused/error torrents
|
||||||
|
- FEATURE: Add Check/Uncheck all feature in Web UI
|
||||||
|
- FEATURE: Search engine can now be disabled
|
||||||
|
- FEATURE: Torrents can be automatically paused once they reach a given ratio
|
||||||
|
- FEATURE: Several files can now be disabled at once
|
||||||
|
- FEATURE: Added "Select All/None" buttons to files list
|
||||||
|
- FEATURE: Added support for BitComet links (bc://bt/...)
|
||||||
|
- BUGFIX: Hide seeding torrents files priorities in Web UI
|
||||||
|
- BUGFIX: The user can disable permanently recursive torrent download
|
||||||
|
- BUGFIX: Peer Exchange status is now correctly reported
|
||||||
|
- BUGFIX: Use an INI file instead of the registry on Windows (More reliable)
|
||||||
|
- BUGFIX: Removed client spoofing feature to avoid tracker blacklisting
|
||||||
|
- COSMETIC: Display peers country name in tooltip
|
||||||
|
- COSMETIC: Display number of torrents in transfers tab label
|
||||||
|
- COSMETIC: Simplified program preferences
|
||||||
|
- COSMETIC: Fix naming of actions opening new dialogs (use Name...)
|
||||||
|
|
||||||
|
* Sun Mar 14 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.2.0
|
||||||
|
- FEATURE: User can set alternative speed limits for fast toggling
|
||||||
|
- FEATURE: Bandwidth scheduler (automatically use alternative speed limits for a given period)
|
||||||
|
- FEATURE: Added "Added/Completed On" columns to transfer list
|
||||||
|
- FEATURE: Added "Upload/Download limit" columns to transfer list
|
||||||
|
- FEATURE: Torrent files can be exported to a given directory
|
||||||
|
- FEATURE: Outgoing ports range can be customized (for QoS)
|
||||||
|
- FEATURE: User can choose to apply transfer limits on LAN too
|
||||||
|
- FEATURE: User can choose to include the protocol overhead in transfer limits
|
||||||
|
- FEATURE: Torrents can be automatically rechecked on completion
|
||||||
|
- FEATURE: If 2 torrents have the same hash, add new trackers/URL seeds to the existing torrent
|
||||||
|
- FEATURE: Trackers can be added from Web UI
|
||||||
|
- FEATURE: Global transfer information are displayed in the new Web UI status bar
|
||||||
|
- FEATURE: Allow to change the priority of several files at once
|
||||||
|
- FEATURE: Support for multiple scan folders (Patch by Christian Kandeler)
|
||||||
|
- BUGFIX: Only one log window can be opened at a time
|
||||||
|
- BUGFIX: Optimized RSS module memory usage
|
||||||
|
- BUGFIX: Consider HTTP downloads >1MB as invalid .torrent files and abort
|
||||||
|
- BUGFIX: Fix Web UI authentication with some browsers
|
||||||
|
- BUGFIX: Set Web UI ban period to 1 hour
|
||||||
|
- COSMETIC: Improved style management
|
||||||
|
|
||||||
|
* Mon Jan 18 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.1.0
|
||||||
|
- FEATURE: Graphical User Interface can be disabled at compilation time (headless running)
|
||||||
|
- FEATURE: Torrents can be labeled/categorized
|
||||||
|
- FEATURE: Labeled torrent can be downloaded corresponding subfolders
|
||||||
|
- FEATURE: Disk cache size can be set from preferences
|
||||||
|
- FEATURE: Peer Exchange (PeX) can be disabled from preferences
|
||||||
|
- FEATURE: Append !.qB extension to incomplete files option (libtorrent >= v0.15 only)
|
||||||
|
- FEATURE: Torrent files/folders can be renamed (torrent addition dialog or files properties)
|
||||||
|
- FEATURE: uTorrent compatible tracker list support (use torrentz.com url as a default)
|
||||||
|
- FEATURE: Better proxy support and preferences remodeling
|
||||||
|
- FEATURE: qBittorrent can identify itself as uTorrent, Vuze or KTorrent (Any stable version)
|
||||||
|
- FEATURE: Torrents can be renamed in transfer list
|
||||||
|
- FEATURE: Display torrent addition dialog for magnet links too
|
||||||
|
- FEATURE: Files contained in a torrent are opened on double click (files panel)
|
||||||
|
- FEATURE: Added support for magnet links in search engine
|
||||||
|
- FEATURE: Added vertor.com and torrentdownloads.net search plugins
|
||||||
|
- FEATURE: Search engine can now use a SOCKS5 proxy
|
||||||
|
- FEATURE: HTTP proxy support for peer communication
|
||||||
|
- BUGFIX: Search engine loads new proxy settings without program restart
|
||||||
|
- BUGFIX: Use XDG folders (.cache, .local) instead of .qbittorrent
|
||||||
|
- BUGFIX: Added legal notice on startup that the user must accept
|
||||||
|
- BUGFIX: Protect Web UI authentication against brute forcing
|
||||||
|
- BUGFIX: Use HTTP digest mode for Web UI authentication (instead of Basic)
|
||||||
|
- BUGFIX: Properly display torrents with one file in subfolder(s)
|
||||||
|
- BUGFIX: Display Web UI favicon
|
||||||
|
- BUGFIX: File priority can be set for finished torrents that have filtered files
|
||||||
|
- COSMETIC: Use checkboxes to filter torrent content instead of comboboxes
|
||||||
|
- COSMETIC: Use alternating row colors in transfer list (set in program preferences)
|
||||||
|
- COSMETIC: Added a spin box to speed limiting dialog for manual input
|
||||||
|
|
||||||
|
* Mon Jan 11 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.0.7
|
||||||
|
- BUGFIX: Fix 'Add in pause' setting in torrent addition dialog
|
||||||
|
- BUGFIX: Update RSS feed as soon as feed downloader is enabled
|
||||||
|
- BUGFIX: RSS Feed downloader ignores articles above maximum number of articles
|
||||||
|
- BUGFIX: Fix possible bug when deleting a RSS folder
|
||||||
|
- BUGFIX: Remove persistant data when a RSS feed is deleted
|
||||||
|
- BUGFIX: RSS filters are now alphabetically sorted
|
||||||
|
- BUGFIX: Fix crash when renaming currently displayed RSS filter
|
||||||
|
- BUGFIX: Remove overwriting confirmation when exporting RSS filters since Qt takes care of it
|
||||||
|
|
||||||
|
* Tue Jan 5 2010 - Christophe Dumez <chris@qbittorrent.org> - v2.0.6
|
||||||
|
- BUGFIX: Fix detection of invalid torrent files
|
||||||
|
- BUGFIX: Stop catching signals once one has been caught to avoid possible infinite loop
|
||||||
|
- BUGFIX: Force data recheck whenever a torrent is moved
|
||||||
|
- BUGFIX: Detect existing torrent data even if incomplete torrents are saved to a different folder
|
||||||
|
- COSMETIC: Improve torrent deletion confirmation dialog so that the text that not get truncated
|
||||||
|
|
||||||
|
* Thu Dec 31 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.5
|
||||||
|
- BUGFIX: Fix crash with downloaded/availability bars when the torrent has too many pieces
|
||||||
|
|
||||||
|
* Wed Dec 30 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.4
|
||||||
|
- BUGFIX: Fix PeerGuardian .p2b binary filter support
|
||||||
|
- BUGFIX: Fix possible crash when closing a search engine tab
|
||||||
|
- BUGFIX: Make sure service port does not change
|
||||||
|
- BUGFIX: Fix possible DHT port saving issue
|
||||||
|
- BUGFIX: Fix communication between qBittorrent and Web UI (Qt 4.6)
|
||||||
|
- BUGFIX: Use Wildcard matching instead of full regex in RSS feed downloader
|
||||||
|
- BUGFIX: Fix code for listening on a random port whenever it failed to listen on the one defined
|
||||||
|
- BUGFIX: Use global maximum transfer rates as maximum values in per-torrent speed limiting dialogs
|
||||||
|
- BUGFIX: Fix global download rate limiting from Web UI
|
||||||
|
- COSMETIC: Display a disconnected icon in status bar whenever qBittorrent failed to listen on the port defined
|
||||||
|
|
||||||
|
* Wed Dec 23 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.3
|
||||||
|
- BUGFIX: Minor cosmetic fix to program preferences
|
||||||
|
- BUGFIX: Fix "Temp path" button in program preferences
|
||||||
|
- BUGFIX: Handle paths with [~, ., ..] properly
|
||||||
|
- BUGFIX: Trackers are now displayed for torrents without metadata
|
||||||
|
- BUGFIX: Fix issue with speed limiting (unlimited was not handled properly)
|
||||||
|
- 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
|
||||||
|
- BUGFIX: Fix .qbittorrent folder not being created (critical bug introduced in v2.0.1 that makes qBittorrent unusuable for new users)
|
||||||
|
- 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: Fix Mininova, ThePirateBay search engine plugins
|
||||||
|
- BUGFIX: Read RSS articles are remembered on restart for feeds with no torrents attached
|
||||||
|
|
||||||
|
* Sun Dec 13 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.1
|
||||||
|
- BUGFIX: µTorrent user-agent is now spoofed correctly
|
||||||
|
- BUGFIX: Fix column hiding behavior when queueing system is disabled
|
||||||
|
- BUGFIX: Fix link to plugins.qbittorrent.org in plugins dialog
|
||||||
|
- BUGFIX: ~/qBT_dir is created only when it is actually used
|
||||||
|
- BUGFIX: Fix possible missing slot message (toggleSelectedTorrentsSuperSeeding)
|
||||||
|
- BUGFIX: Fix possible crash in torrent properties (files)
|
||||||
|
- BUGFIX: Added Hex Magnet Links support (Thanks Haypo)
|
||||||
|
|
||||||
|
* Thu Dec 10 2009 - Christophe Dumez <chris@qbittorrent.org> - v2.0.0
|
||||||
- FEATURE: Added program option to disable splash screen
|
- FEATURE: Added program option to disable splash screen
|
||||||
- FEATURE: Dropped dependency on libcurl and libzzip
|
- FEATURE: Dropped dependency on libcurl and libzzip
|
||||||
- FEATURE: Display more information regarding the torrent in its properties
|
- FEATURE: Display more information regarding the torrent in its properties
|
||||||
- FEATURE: Various optimizations to save CPU and memory
|
- FEATURE: Various optimizations to save CPU and memory
|
||||||
- FEATURE: Folder scanning now works with CIFS and NFS mounted folders
|
- FEATURE: Folder scanning now works with CIFS and NFS mounted folders
|
||||||
- FEATURE: Speed up qBittorrent startup
|
- FEATURE: Speed up qBittorrent startup and shutdown
|
||||||
- FEATURE: Display per-torrent peer list
|
- FEATURE: Display per-torrent peer list
|
||||||
- FEATURE: Make sure torrent files are always sorted by name
|
- FEATURE: Make sure torrent files are always sorted by name
|
||||||
- FEATURE: Seeds and Peers columns are now sortable
|
- FEATURE: Seeds and Peers columns are now sortable
|
||||||
@@ -21,6 +231,9 @@
|
|||||||
- FEATURE: Dropped Qt 4.3 support (Qt >= 4.4 is now required)
|
- FEATURE: Dropped Qt 4.3 support (Qt >= 4.4 is now required)
|
||||||
- FEATURE: Display close tab button into the tabs in search engine (Qt >= 4.5 only)
|
- FEATURE: Display close tab button into the tabs in search engine (Qt >= 4.5 only)
|
||||||
- FEATURE: Show official documentation when pressing F1 key
|
- FEATURE: Show official documentation when pressing F1 key
|
||||||
|
- FEATURE: Search engine plugins now handle HTTP protocol gzip compression
|
||||||
|
- FEATURE: Enabled lazy bitfield as a counter-measure for ISP speed throttling
|
||||||
|
- FEATURE: Fall back to a random port if qBittorrent could not listen on the chosen port
|
||||||
- FEATURE: Announce to all trackers specified for a torrent (µTorrent behavior) (libtorrent >= v0.15 only)
|
- FEATURE: Announce to all trackers specified for a torrent (µTorrent behavior) (libtorrent >= v0.15 only)
|
||||||
- FEATURE: Added per-torrent super seeding mode (libtorrent >= v0.15 only)
|
- FEATURE: Added per-torrent super seeding mode (libtorrent >= v0.15 only)
|
||||||
- FEATURE: Support for storing symbolic links in .torrent files (libtorrent >= v0.15 only)
|
- FEATURE: Support for storing symbolic links in .torrent files (libtorrent >= v0.15 only)
|
||||||
@@ -34,10 +247,16 @@
|
|||||||
- FEATURE: Include DHT traffic in the rate limiter (libtorrent >= v0.15 only)
|
- FEATURE: Include DHT traffic in the rate limiter (libtorrent >= v0.15 only)
|
||||||
- FEATURE: Support for bitcomet padding files (libtorrent >= v0.15 only)
|
- FEATURE: Support for bitcomet padding files (libtorrent >= v0.15 only)
|
||||||
- FEATURE: Option to skip file checking and start seeding immediately in torrent addition dialog (Stephanos Antaris) (libtorrent >= v0.15 only)
|
- FEATURE: Option to skip file checking and start seeding immediately in torrent addition dialog (Stephanos Antaris) (libtorrent >= v0.15 only)
|
||||||
|
- BUGFIX: Made sure qBittorrent does not scrape the tracker too frequently (libtorrent >= 0.15 only)
|
||||||
|
- BUGFIX: Fix Paste action in search engine field
|
||||||
|
- BUGFIX: Fix possible double free in search engine destructor
|
||||||
|
- BUGFIX: Properly handle trackers error messages
|
||||||
- WEB UI: Remodeled Web UI to match new qBittorrent UI (Properties and preferences available)
|
- WEB UI: Remodeled Web UI to match new qBittorrent UI (Properties and preferences available)
|
||||||
- WEB UI: Added internationalization support
|
- WEB UI: Added internationalization support
|
||||||
- WEB UI: Reduced computation in Javascript (do this one server side instead)
|
- WEB UI: Reduced computation in Javascript (do this one server side instead)
|
||||||
- WEB UI: Fixed Transfer list flickering
|
- WEB UI: Fixed Transfer list flickering
|
||||||
|
- WEB UI: Password is now stored as md5
|
||||||
|
- I18N: Added Serbian translation (By Anaximandar Milet)
|
||||||
- COSMETIC: Merged download / upload lists
|
- COSMETIC: Merged download / upload lists
|
||||||
- COSMETIC: Torrents can be filtered based on their status
|
- COSMETIC: Torrents can be filtered based on their status
|
||||||
- COSMETIC: Torrent properties are now displayed in main window
|
- COSMETIC: Torrent properties are now displayed in main window
|
||||||
@@ -50,6 +269,49 @@
|
|||||||
- COSMETIC: New torrent status icons by Mateusz Tolola
|
- COSMETIC: New torrent status icons by Mateusz Tolola
|
||||||
- COSMETIC: Make use of libnotify if available for system notifications (Ubuntu, ...)
|
- COSMETIC: Make use of libnotify if available for system notifications (Ubuntu, ...)
|
||||||
|
|
||||||
|
* Tue Nov 17 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.6
|
||||||
|
- BUGFIX: RSS feed articles can now be displayed using keyboard arrows
|
||||||
|
- BUGFIX: RSS feed downloader can only process unread articles now
|
||||||
|
- BUGFIX: Fixed memory leak in RSS parser
|
||||||
|
- BUGFIX: Fixed possible crash in search autocompletion
|
||||||
|
- BUGFIX: Improved ETA calculation for big torrents
|
||||||
|
- BUGFIX: Fixed per-torrent speed limiting
|
||||||
|
|
||||||
|
* Wed Nov 4 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.5
|
||||||
|
- BUGFIX: Fixed man page
|
||||||
|
- BUGFIX: Fix crash on torrent addition (if libtorrent-rasterbar has debug enabled)
|
||||||
|
- BUGFIX: Fix trackers addition to torrents (bug introduced in v1.5.4)
|
||||||
|
- 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: Added safety mecanism 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
|
||||||
|
- BUGFIX: Updated man page
|
||||||
|
- BUGFIX: Fixed possible crash with torrents containing unicode characters
|
||||||
|
- BUGFIX: Fixed problem when disabling systray integration and starting minimized
|
||||||
|
- BUGFIX: Fixed PirateBay search plugin
|
||||||
|
- BUGFIX: Using Download button in search results list now downloads the right torrents
|
||||||
|
- BUGFIX: The search results list is no longer sorted automatically when a row color is updated
|
||||||
|
|
||||||
|
* Wed Sep 30 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.3
|
||||||
|
- BUGFIX: Fix a possible crash when pausing then deleting a torrent quickly
|
||||||
|
- BUGFIX: Fix a race condition in folder scanning and torrent downloader
|
||||||
|
- BUGFIX: Hide download url column in search results
|
||||||
|
- BUGFIX: Fix a crash when scanned directory does not exist
|
||||||
|
- BUGFIX: Fix compilation on Mac OS
|
||||||
|
- BUGFIX: Added a command line parameter to disable splash screen
|
||||||
|
- BUGFIX: Ignore permanent deletion button when no torrent is selected
|
||||||
|
- BUGFIX: When a selected torrent is deleted, select next suitable torrent
|
||||||
|
|
||||||
|
* Sun Sep 20 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.2
|
||||||
|
- BUGFIX: Some torrents were pausing for no reason
|
||||||
|
- I18N: Updated Korean translation
|
||||||
|
|
||||||
|
* Thu Sep 7 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.1
|
||||||
|
- BUGFIX: Fix crash in torrent addition dialog when save path does not exist (closes #425227)
|
||||||
|
- BUGFIX: Fix downloading from URL (broken in v1.5.0)
|
||||||
|
- BUGFIX: Pause torrents in error state
|
||||||
|
|
||||||
* Thu Sep 3 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.0
|
* Thu Sep 3 2009 - Christophe Dumez <chris@qbittorrent.org> - v1.5.0
|
||||||
- FEATURE: Added Magnet URI support
|
- FEATURE: Added Magnet URI support
|
||||||
- FEATURE: Search engine supports category-based requests
|
- FEATURE: Search engine supports category-based requests
|
||||||
|
|||||||
52
INSTALL
@@ -1,28 +1,58 @@
|
|||||||
qBittorrent - A BitTorrent client in C++ / Qt4
|
qBittorrent - A BitTorrent client in C++ / Qt4
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
./configure
|
1) Compile and install qBittorrent with Qt4 Graphical Interface
|
||||||
make && make install
|
|
||||||
qbittorrent
|
|
||||||
|
|
||||||
will install and execute qBittorrent hopefully without any problems.
|
$ ./configure
|
||||||
|
$ make && make install
|
||||||
|
$ qbittorrent
|
||||||
|
|
||||||
Dependencies:
|
will install and execute qBittorrent hopefully without any problems.
|
||||||
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
|
||||||
|
|
||||||
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.0 REQUIRED, >= v0.15.0 ADVISED)
|
Dependencies:
|
||||||
-> http://www.qbittorrent.org/download.php (advised)
|
- Qt >= 4.4.0 (libqt-devel, libqtgui, libqtcore, libqtnetwork, libqtxml)
|
||||||
|
|
||||||
|
- pkg-config executable
|
||||||
|
|
||||||
|
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, compatible with v0.15.x)
|
||||||
-> http://www.libtorrent.net
|
-> http://www.libtorrent.net
|
||||||
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
|
- libboost 1.34.x (libboost-filesystem, libboost-thread, libboost-date-time) + libasio
|
||||||
|
or
|
||||||
|
- libboost >= 1.35.x (libboost-system, libboost-filesystem, libboost-thread, libboost-date-time)
|
||||||
|
|
||||||
- python >= 2.3 (needed by search engine)
|
- python >= 2.3 (needed by search engine)
|
||||||
|
* Run time only dependency
|
||||||
|
|
||||||
- libnotify >= 0.4.2, glib-2.0 (optional)
|
- libnotify >= 0.4.2, glib-2.0 (optional)
|
||||||
* Can be used for system notifications to replace standard Qt notifications
|
* Can be used for system notifications to replace standard Qt notifications
|
||||||
so that it integrates better into the Desktop
|
so that it integrates better into the Desktop
|
||||||
|
|
||||||
|
- geoip-database (optional)
|
||||||
|
* If qBittorrent cannot find this database, it will try to resolve countries using the Internet but it will be a lot slower.
|
||||||
|
* Run time only dependency
|
||||||
|
|
||||||
|
2) Compile and install qBittorrent without Qt4 Graphical interface
|
||||||
|
|
||||||
|
$ ./configure --disable-gui
|
||||||
|
$ make && make install
|
||||||
|
$ qbittorrent
|
||||||
|
|
||||||
|
will install and execute qBittorrent hopefully without any problems.
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- Qt >= 4.4.0 (libqt-devel, libqtcore, libqtnetwork)
|
||||||
|
|
||||||
|
- pkg-config executable
|
||||||
|
|
||||||
|
- libtorrent-rasterbar by Arvid Norberg (>= 0.14.4 REQUIRED, >= v0.15.0 ADVISED)
|
||||||
|
-> http://www.libtorrent.net
|
||||||
|
Be careful: another library (the one used by rTorrent) uses a similar name.
|
||||||
|
|
||||||
|
- libboost: libboost-filesystem, libboost-date-time, libboost-thread, libboost-serialization
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION:
|
DOCUMENTATION:
|
||||||
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
Please note that there is a documentation with a "compiling howto" at http://wiki.qbittorrent.org.
|
||||||
|
|
||||||
|
|||||||
62
README.os2
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
qBittorrent - A BitTorrent client in Qt4
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
This is the eComStation (OS/2) qBittorrent part of the readme. See also README for more general information.
|
||||||
|
|
||||||
|
|
||||||
|
Building qBittorrent
|
||||||
|
********************
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
============
|
||||||
|
|
||||||
|
- gcc based build env (recommended gcc v4.4.2 or greater)
|
||||||
|
|
||||||
|
- Qt4 for eCS (OS/2) dev package (see http://svn.netlabs.org/qt4 for more information)
|
||||||
|
|
||||||
|
- libtorrent-rasterbar for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
|
||||||
|
|
||||||
|
- boost for eCS (OS/2) port (see http://svn.netlabs.org/ports for more information)
|
||||||
|
|
||||||
|
|
||||||
|
How to build
|
||||||
|
============
|
||||||
|
|
||||||
|
First you need to create the conf.pri file in the same dir as this readme.os2 is.
|
||||||
|
the conf.pri file has the following content:
|
||||||
|
|
||||||
|
##### conf.pri content beginn #####
|
||||||
|
PREFIX = .
|
||||||
|
BINDIR = ./bin
|
||||||
|
INCDIR = ./include
|
||||||
|
LIBDIR = ./lib
|
||||||
|
DATADIR = ./share
|
||||||
|
|
||||||
|
CONFIG += staticlib
|
||||||
|
INCLUDEPATH += x:/trees/libtorrent/trunk/include
|
||||||
|
LIBS += -Lx:/trees/libtorrent/trunk/src/.libs \
|
||||||
|
-Lx:/trees/boost/trunk/stage/lib \
|
||||||
|
-Lx:/trees/openssl \
|
||||||
|
-Lx:/extras/lib
|
||||||
|
##### conf.pri content end #####
|
||||||
|
|
||||||
|
Of course all the above path references have to be adjusted to your build env.
|
||||||
|
|
||||||
|
It should now be easy to build qBittorrent:
|
||||||
|
|
||||||
|
Simply type:
|
||||||
|
$ qmake
|
||||||
|
|
||||||
|
Followed by:
|
||||||
|
$ make
|
||||||
|
|
||||||
|
If all works fine you should get a working qbittorrent executable.
|
||||||
|
|
||||||
|
If you have any question regarding the eCS (OS/2) port of qBittorrent you can meet me (_diver) on IRC:
|
||||||
|
#netlabs on irc.freenode.net
|
||||||
|
|
||||||
|
------------------------------------------
|
||||||
|
Silvan Scherrer <silvan.scherrer@aroa.ch>
|
||||||
|
|
||||||
|
|
||||||
252
configure
vendored
@@ -18,8 +18,24 @@ Main options:
|
|||||||
--help This help text.
|
--help This help text.
|
||||||
|
|
||||||
Dependency options:
|
Dependency options:
|
||||||
--with-libboost-inc=[path] Path to libboost include files
|
--disable-gui Disable qBittorrent
|
||||||
|
Graphical user interface for
|
||||||
|
headless running
|
||||||
|
--with-libboost-inc=[path] Path to libboost include
|
||||||
|
files
|
||||||
|
--with-libboost-lib=[path] Path to libboost library
|
||||||
|
files
|
||||||
--disable-libnotify Disable use of libnotify
|
--disable-libnotify Disable use of libnotify
|
||||||
|
--disable-geoip-database Disable use of geoip-database
|
||||||
|
--with-geoip-database-embedded Geoip Database will be
|
||||||
|
embedded in qBittorrent
|
||||||
|
executable (please follow
|
||||||
|
instructions in
|
||||||
|
src/geoip/README)
|
||||||
|
--disable-qtsingleapplication Disable use of libboost
|
||||||
|
--with-qtsingleapplication=[system|shipped] Use the shipped
|
||||||
|
qtsingleapplication library
|
||||||
|
or the system one
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
}
|
}
|
||||||
@@ -136,16 +152,46 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
--disable-gui)
|
||||||
|
QC_DISABLE_GUI="Y"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
--with-libboost-inc=*)
|
--with-libboost-inc=*)
|
||||||
QC_WITH_LIBBOOST_INC=$optarg
|
QC_WITH_LIBBOOST_INC=$optarg
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
--with-libboost-lib=*)
|
||||||
|
QC_WITH_LIBBOOST_LIB=$optarg
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
--disable-libnotify)
|
--disable-libnotify)
|
||||||
QC_DISABLE_libnotify="Y"
|
QC_DISABLE_libnotify="Y"
|
||||||
shift
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
--disable-geoip-database)
|
||||||
|
QC_DISABLE_geoip_database="Y"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--with-geoip-database-embedded)
|
||||||
|
QC_WITH_GEOIP_DATABASE_EMBEDDED="Y"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--disable-qtsingleapplication)
|
||||||
|
QC_DISABLE_qtsingleapplication="Y"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
|
--with-qtsingleapplication=*)
|
||||||
|
QC_WITH_QTSINGLEAPPLICATION=$optarg
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
|
||||||
--verbose)
|
--verbose)
|
||||||
QC_VERBOSE="Y"
|
QC_VERBOSE="Y"
|
||||||
shift
|
shift
|
||||||
@@ -167,8 +213,14 @@ echo PREFIX=$PREFIX
|
|||||||
echo BINDIR=$BINDIR
|
echo BINDIR=$BINDIR
|
||||||
echo DATADIR=$DATADIR
|
echo DATADIR=$DATADIR
|
||||||
echo EX_QTDIR=$EX_QTDIR
|
echo EX_QTDIR=$EX_QTDIR
|
||||||
|
echo QC_DISABLE_GUI=$QC_DISABLE_GUI
|
||||||
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
|
echo QC_WITH_LIBBOOST_INC=$QC_WITH_LIBBOOST_INC
|
||||||
|
echo QC_WITH_LIBBOOST_LIB=$QC_WITH_LIBBOOST_LIB
|
||||||
echo QC_DISABLE_libnotify=$QC_DISABLE_libnotify
|
echo QC_DISABLE_libnotify=$QC_DISABLE_libnotify
|
||||||
|
echo QC_DISABLE_geoip_database=$QC_DISABLE_geoip_database
|
||||||
|
echo QC_WITH_GEOIP_DATABASE_EMBEDDED=$QC_WITH_GEOIP_DATABASE_EMBEDDED
|
||||||
|
echo QC_DISABLE_qtsingleapplication=$QC_DISABLE_qtsingleapplication
|
||||||
|
echo QC_WITH_QTSINGLEAPPLICATION=$QC_WITH_QTSINGLEAPPLICATION
|
||||||
echo
|
echo
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -274,6 +326,7 @@ cat >$1/modules.cpp <<EOT
|
|||||||
/*
|
/*
|
||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: Qt >= 4.4
|
name: Qt >= 4.4
|
||||||
|
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
class qc_qt4 : public ConfObj
|
class qc_qt4 : public ConfObj
|
||||||
@@ -284,11 +337,27 @@ public:
|
|||||||
QString shortname() const { return "Qt 4.4"; }
|
QString shortname() const { return "Qt 4.4"; }
|
||||||
bool exec()
|
bool exec()
|
||||||
{
|
{
|
||||||
if(QT_VERSION >= 0x040500) {
|
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
conf->addDefine("QT_4_5");
|
conf->addDefine("DISABLE_GUI");
|
||||||
}
|
}
|
||||||
return(QT_VERSION >= 0x040400);
|
return(QT_VERSION >= 0x040400);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#line 1 "pkg-config.qcm"
|
||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: pkg-config
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
#include <QProcess>
|
||||||
|
class qc_pkg_config : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_pkg_config(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "pkg-config executable"; }
|
||||||
|
QString shortname() const { return "pkg-config"; }
|
||||||
|
bool exec(){
|
||||||
|
return !conf->findProgram("pkg-config").isEmpty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#line 1 "libtorrent-rasterbar.qcm"
|
#line 1 "libtorrent-rasterbar.qcm"
|
||||||
@@ -302,24 +371,20 @@ class qc_libtorrent_rasterbar : public ConfObj
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
|
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libtorrent-rasterbar >= 0.14.0 (>= 0.15.0 advised)"; }
|
QString name() const { return "libtorrent-rasterbar >= 0.14.4"; }
|
||||||
QString shortname() const { return "libtorrent-rasterbar"; }
|
QString shortname() const { return "libtorrent-rasterbar"; }
|
||||||
bool exec(){
|
bool exec(){
|
||||||
QStringList incs;
|
QStringList incs;
|
||||||
QString req_ver = "0.14.0";
|
QString req_ver = "0.14.4";
|
||||||
QString adv_ver = "0.15.0";
|
|
||||||
QString version, libs, other;
|
QString version, libs, other;
|
||||||
VersionMode mode = VersionMin;
|
VersionMode mode = VersionMin;
|
||||||
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
|
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
|
||||||
return false;
|
return false;
|
||||||
for(int n = 0; n < incs.count(); ++n)
|
for(int n = 0; n < incs.count(); ++n)
|
||||||
conf->addIncludePath(incs[n]);
|
conf->addIncludePath(incs[n]);
|
||||||
if(!libs.isEmpty())
|
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
conf->addLib(libs);
|
conf->addLib("-lcrypto");
|
||||||
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
|
}
|
||||||
printf("\nWarning: libtorrent-rasterbar v%s was detected.\nAlthough it will compile and run, you will be missing some features. Please consider updating to v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
|
|
||||||
else
|
|
||||||
conf->addDefine("LIBTORRENT_0_15");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -328,14 +393,39 @@ public:
|
|||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: libboost
|
name: libboost
|
||||||
arg: with-libboost-inc=[path], Path to libboost include files
|
arg: with-libboost-inc=[path], Path to libboost include files
|
||||||
|
arg: with-libboost-lib=[path], Path to libboost library files
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
|
#include <boost/version.hpp>
|
||||||
class qc_libboost : public ConfObj
|
class qc_libboost : public ConfObj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_libboost(Conf *c) : ConfObj(c) {}
|
qc_libboost(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libboost"; }
|
QString name() const { return "libboost"; }
|
||||||
QString shortname() const { return "libboost"; }
|
QString shortname() const { return "libboost"; }
|
||||||
|
QString findBoostLib(QString path, QString lib) const {
|
||||||
|
QString name;
|
||||||
|
QDir libDir(path);
|
||||||
|
QStringList filters;
|
||||||
|
filters << "libboost_"+lib+"*-mt*.so";
|
||||||
|
QStringList result = libDir.entryList(filters, QDir::Files);
|
||||||
|
if(!result.empty()) {
|
||||||
|
name = result.first().mid(3);
|
||||||
|
// Remove .so
|
||||||
|
name.chop(3);
|
||||||
|
} else {
|
||||||
|
// Fall back to non -mt boost lib
|
||||||
|
filters.clear();
|
||||||
|
filters << "libboost_"+lib+"*.so";
|
||||||
|
result = libDir.entryList(filters, QDir::Files);
|
||||||
|
if(!result.empty()) {
|
||||||
|
name = result.first().mid(3);
|
||||||
|
// Remove .so
|
||||||
|
name.chop(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
bool exec(){
|
bool exec(){
|
||||||
QString s;
|
QString s;
|
||||||
s = conf->getenv("QC_WITH_LIBBOOST_INC");
|
s = conf->getenv("QC_WITH_LIBBOOST_INC");
|
||||||
@@ -377,6 +467,43 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
conf->addIncludePath(s);
|
conf->addIncludePath(s);
|
||||||
|
// Find library
|
||||||
|
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
|
||||||
|
QStringList required_libs;
|
||||||
|
#if BOOST_VERSION >= 103500
|
||||||
|
required_libs << "system";
|
||||||
|
#endif
|
||||||
|
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
// Not required by nox
|
||||||
|
required_libs << "filesystem" << "thread";
|
||||||
|
}
|
||||||
|
QStringList libDirs;
|
||||||
|
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
|
||||||
|
foreach(const QString& lib, required_libs) {
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
QString detected_name = findBoostLib(s, lib);
|
||||||
|
if(detected_name.isEmpty()) {
|
||||||
|
printf("Could not find boost %s library!\n", qPrintable(lib));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
conf->addLib("-l"+detected_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool found = false;
|
||||||
|
foreach(const QString& libDir, libDirs) {
|
||||||
|
QString detected_name = findBoostLib(libDir, lib);
|
||||||
|
if(!detected_name.isEmpty()) {
|
||||||
|
conf->addLib("-l"+detected_name);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
printf("Could not find boost %s library!\n", qPrintable(lib));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -393,7 +520,15 @@ public:
|
|||||||
qc_libnotify(Conf *c) : ConfObj(c) {}
|
qc_libnotify(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libnotify >= 0.4.2 (optional)"; }
|
QString name() const { return "libnotify >= 0.4.2 (optional)"; }
|
||||||
QString shortname() const { return "libnotify"; }
|
QString shortname() const { return "libnotify"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
bool exec(){
|
bool exec(){
|
||||||
|
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
QStringList incs;
|
QStringList incs;
|
||||||
QString req_ver = "0.4.2";
|
QString req_ver = "0.4.2";
|
||||||
QString version, libs, other;
|
QString version, libs, other;
|
||||||
@@ -415,6 +550,17 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
QStringList incs3;
|
||||||
|
QString req_ver3 = "2.0";
|
||||||
|
QString version3, libs3, other3;
|
||||||
|
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
|
||||||
|
for(int n = 0; n < incs3.count(); ++n)
|
||||||
|
conf->addIncludePath(incs3[n]);
|
||||||
|
if(!libs3.isEmpty())
|
||||||
|
conf->addLib(libs3);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -422,12 +568,80 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
#line 1 "geoip-database.qcm"
|
||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: geoip-database
|
||||||
|
arg: with-geoip-database-embedded, Geoip Database will be embedded in qBittorrent executable (please follow instructions in src/geoip/README)
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
// see Conf::findPkgConfig
|
||||||
|
class qc_geoip_database : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_geoip_database(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "GeoIP Database (optional)"; }
|
||||||
|
QString shortname() const { return "GeoIP Database"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
|
bool exec() {
|
||||||
|
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) {
|
||||||
|
#endif
|
||||||
|
conf->addDefine("WITH_GEOIP_EMBEDDED");
|
||||||
|
printf(" embedded and");
|
||||||
|
return true;
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
}
|
||||||
|
if(QFile::exists("/usr/share/GeoIP/GeoIP.dat") || QFile::exists("/usr/local/share/GeoIP/GeoIP.dat") || QFile::exists("/var/lib/GeoIP/GeoIP.dat"))
|
||||||
|
return true;
|
||||||
|
printf("\nWarning: GeoIP database was not found at /usr/share/GeoIP/GeoIP.dat or /var/lib/GeoIP/GeoIP.dat\nCountry resolution will be slow.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#line 1 "qtsingleapplication.qcm"
|
||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: libboost
|
||||||
|
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
class qc_qtsingleapplication : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "qtsingleapplication library"; }
|
||||||
|
QString shortname() const { return "qtsingleapplication"; }
|
||||||
|
|
||||||
|
bool exec(){
|
||||||
|
QString s;
|
||||||
|
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
|
||||||
|
if(s.compare("system", Qt::CaseInsensitive) == 0) {
|
||||||
|
// System
|
||||||
|
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
|
||||||
|
printf(" [system] ");
|
||||||
|
} else {
|
||||||
|
printf(" [shipped] ");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
cat >$1/modules_new.cpp <<EOT
|
cat >$1/modules_new.cpp <<EOT
|
||||||
o = new qc_qt4(conf);
|
o = new qc_qt4(conf);
|
||||||
o->required = true;
|
o->required = true;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
|
o = new qc_pkg_config(conf);
|
||||||
|
o->required = true;
|
||||||
|
o->disabled = false;
|
||||||
o = new qc_libtorrent_rasterbar(conf);
|
o = new qc_libtorrent_rasterbar(conf);
|
||||||
o->required = true;
|
o->required = true;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
@@ -437,6 +651,12 @@ cat >$1/modules_new.cpp <<EOT
|
|||||||
o = new qc_libnotify(conf);
|
o = new qc_libnotify(conf);
|
||||||
o->required = false;
|
o->required = false;
|
||||||
o->disabled = false;
|
o->disabled = false;
|
||||||
|
o = new qc_geoip_database(conf);
|
||||||
|
o->required = false;
|
||||||
|
o->disabled = false;
|
||||||
|
o = new qc_qtsingleapplication(conf);
|
||||||
|
o->required = false;
|
||||||
|
o->disabled = false;
|
||||||
|
|
||||||
EOT
|
EOT
|
||||||
cat >$1/conf4.h <<EOT
|
cat >$1/conf4.h <<EOT
|
||||||
@@ -1382,8 +1602,14 @@ export PREFIX
|
|||||||
export BINDIR
|
export BINDIR
|
||||||
export DATADIR
|
export DATADIR
|
||||||
export EX_QTDIR
|
export EX_QTDIR
|
||||||
|
export QC_DISABLE_GUI
|
||||||
export QC_WITH_LIBBOOST_INC
|
export QC_WITH_LIBBOOST_INC
|
||||||
|
export QC_WITH_LIBBOOST_LIB
|
||||||
export QC_DISABLE_libnotify
|
export QC_DISABLE_libnotify
|
||||||
|
export QC_DISABLE_geoip_database
|
||||||
|
export QC_WITH_GEOIP_DATABASE_EMBEDDED
|
||||||
|
export QC_DISABLE_qtsingleapplication
|
||||||
|
export QC_WITH_QTSINGLEAPPLICATION
|
||||||
export QC_VERBOSE
|
export QC_VERBOSE
|
||||||
rm -rf .qconftemp
|
rm -rf .qconftemp
|
||||||
(
|
(
|
||||||
|
|||||||
44
doc/qbittorrent-nox.1
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
.\" This manpage has been automatically generated by docbook2man
|
||||||
|
.\" from a DocBook document. This tool can be found at:
|
||||||
|
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
|
||||||
|
.TH "QBITTORRENT\-NOX" "1" "January 16th 2010" "Command line Bittorrent client written in C++ / Qt4" ""
|
||||||
|
|
||||||
|
.SH "NAME"
|
||||||
|
qBittorrent\-nox \- a command line Bittorrent client written in C++ / Qt4
|
||||||
|
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
|
||||||
|
\fBqbittorrent\-nox\fR [\-\-webui-port=x] [TORRENT_FILE | URL]...
|
||||||
|
|
||||||
|
\fBqbittorrent\-nox\fR \-\-help
|
||||||
|
|
||||||
|
\fBqbittorrent\-nox\fR \-\-version
|
||||||
|
|
||||||
|
.PP
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
|
||||||
|
\fBqBittorrent-nox\fR is an advanced command-line Bittorrent client written in C++ / Qt4,
|
||||||
|
using the \fBlibtorrent-rasterbar\fR library by Arvid Norberg. qBittorrent\-nox aims
|
||||||
|
to be a good alternative to other command line bittorrent clients and provides features similar to popular graphical clients.
|
||||||
|
|
||||||
|
qBittorrent\-nox is fast, stable, light and it supports unicode.
|
||||||
|
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze compatible),
|
||||||
|
FAST extension (mainline) and PeX support (utorrent compatible).
|
||||||
|
|
||||||
|
qBittorrent\-nox is meant to be controlled via its feature-rich Web UI which is accessible as a default on http://localhost:8080. The Web UI access is secured and the default account user name is "admin" with "adminadmin" as a password.
|
||||||
|
|
||||||
|
.SH "OPTIONS"
|
||||||
|
|
||||||
|
\fB--help\fR Prints the command line options.
|
||||||
|
|
||||||
|
\fB--version\fR Prints qbittorrent program version number.
|
||||||
|
|
||||||
|
\fB--webui-port=x\fR Changes Web UI port to x (default: 8080).
|
||||||
|
|
||||||
|
.SH "BUGS"
|
||||||
|
|
||||||
|
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||||
|
|
||||||
|
.SH "AUTHOR"
|
||||||
|
|
||||||
|
Christophe Dumez <chris@qbittorrent.org>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
.\" This manpage has been automatically generated by docbook2man
|
.\" This manpage has been automatically generated by docbook2man
|
||||||
.\" from a DocBook document. This tool can be found at:
|
.\" from a DocBook document. This tool can be found at:
|
||||||
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
|
.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/>.
|
||||||
.TH "QBITTORRENT" "1" "September 30th 2009" "Bittorrent client written in C++ / Qt4" ""
|
.TH "QBITTORRENT" "1" "January 16th 2010" "Bittorrent client written in C++ / Qt4" ""
|
||||||
|
|
||||||
.SH "NAME"
|
.SH "NAME"
|
||||||
qBittorrent \- a Bittorrent client written in C++ / Qt4
|
qBittorrent \- a Bittorrent client written in C++ / Qt4
|
||||||
|
|
||||||
.SH "SYNOPSIS"
|
.SH "SYNOPSIS"
|
||||||
|
|
||||||
\fBqbittorrent\fR [\-\-no-splash] [TORRENT_FILE | URL]...
|
\fBqbittorrent\fR [\-\-no-splash] [\-\-webui-port=x] [TORRENT_FILE | URL]...
|
||||||
|
|
||||||
\fBqbittorrent\fR \-\-help
|
\fBqbittorrent\fR \-\-help
|
||||||
|
|
||||||
@@ -18,10 +18,9 @@ qBittorrent \- a Bittorrent client written in C++ / Qt4
|
|||||||
.SH "DESCRIPTION"
|
.SH "DESCRIPTION"
|
||||||
|
|
||||||
\fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4,
|
\fBqBittorrent\fR is an advanced Bittorrent client written in C++ / Qt4,
|
||||||
using the \fBrblibtorrent\fR library by Arvid Norberg. qBittorrent aims
|
using the \fBlibtorrent-rasterbar\fR library by Arvid Norberg. qBittorrent is similar to uTorrent. qBittorrent
|
||||||
to be a good alternative to all other bittorrent clients out there. qBittorrent
|
|
||||||
is fast, stable, light, it supports unicode and it provides a good integrated search engine.
|
is fast, stable, light, it supports unicode and it provides a good integrated search engine.
|
||||||
It also comes with UPnP port forwarding / NAT-PMP, encryption (Azureus compatible),
|
It also comes with UPnP port forwarding / NAT-PMP, encryption (Vuze compatible),
|
||||||
FAST extension (mainline) and PeX support (utorrent compatible).
|
FAST extension (mainline) and PeX support (utorrent compatible).
|
||||||
|
|
||||||
.SH "OPTIONS"
|
.SH "OPTIONS"
|
||||||
@@ -32,6 +31,8 @@ FAST extension (mainline) and PeX support (utorrent compatible).
|
|||||||
|
|
||||||
\fB--no-splash\fR Disables splash screen on startup.
|
\fB--no-splash\fR Disables splash screen on startup.
|
||||||
|
|
||||||
|
\fB--webui-port=x\fR Changes Web UI port to x (default: 8080).
|
||||||
|
|
||||||
.SH "BUGS"
|
.SH "BUGS"
|
||||||
|
|
||||||
If you find a bug, please report it at http://bugs.qbittorrent.org
|
If you find a bug, please report it at http://bugs.qbittorrent.org
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
TEMPLATE = subdirs
|
TEMPLATE = subdirs
|
||||||
|
|
||||||
include(conf.pri)
|
|
||||||
|
|
||||||
SUBDIRS += src
|
SUBDIRS += src
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
<dep type='qt4'>
|
<dep type='qt4'>
|
||||||
<required/>
|
<required/>
|
||||||
</dep>
|
</dep>
|
||||||
|
<dep type='pkg-config'>
|
||||||
|
<required/>
|
||||||
|
</dep>
|
||||||
<dep type='libtorrent-rasterbar'>
|
<dep type='libtorrent-rasterbar'>
|
||||||
<required/>
|
<required/>
|
||||||
</dep>
|
</dep>
|
||||||
@@ -14,4 +17,8 @@
|
|||||||
</dep>
|
</dep>
|
||||||
<dep type='libnotify'>
|
<dep type='libnotify'>
|
||||||
</dep>
|
</dep>
|
||||||
|
<dep type='geoip-database'>
|
||||||
|
</dep>
|
||||||
|
<dep type='qtsingleapplication'>
|
||||||
|
</dep>
|
||||||
</qconf>
|
</qconf>
|
||||||
|
|||||||
37
qcm/geoip-database.qcm
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: geoip-database
|
||||||
|
arg: with-geoip-database-embedded, Geoip Database will be embedded in qBittorrent executable (please follow instructions in src/geoip/README)
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
// see Conf::findPkgConfig
|
||||||
|
class qc_geoip_database : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_geoip_database(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "GeoIP Database (optional)"; }
|
||||||
|
QString shortname() const { return "GeoIP Database"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
|
bool exec() {
|
||||||
|
if(!conf->getenv("QC_DISABLE_geoip_database").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
if(!conf->getenv("QC_WITH_GEOIP_DATABASE_EMBEDDED").isEmpty()) {
|
||||||
|
#endif
|
||||||
|
conf->addDefine("WITH_GEOIP_EMBEDDED");
|
||||||
|
printf(" embedded and");
|
||||||
|
return true;
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
}
|
||||||
|
if(QFile::exists("/usr/share/GeoIP/GeoIP.dat") || QFile::exists("/usr/local/share/GeoIP/GeoIP.dat") || QFile::exists("/var/lib/GeoIP/GeoIP.dat"))
|
||||||
|
return true;
|
||||||
|
printf("\nWarning: GeoIP database was not found at /usr/share/GeoIP/GeoIP.dat or /var/lib/GeoIP/GeoIP.dat\nCountry resolution will be slow.");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,14 +2,39 @@
|
|||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: libboost
|
name: libboost
|
||||||
arg: with-libboost-inc=[path], Path to libboost include files
|
arg: with-libboost-inc=[path], Path to libboost include files
|
||||||
|
arg: with-libboost-lib=[path], Path to libboost library files
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
|
#include <boost/version.hpp>
|
||||||
class qc_libboost : public ConfObj
|
class qc_libboost : public ConfObj
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_libboost(Conf *c) : ConfObj(c) {}
|
qc_libboost(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libboost"; }
|
QString name() const { return "libboost"; }
|
||||||
QString shortname() const { return "libboost"; }
|
QString shortname() const { return "libboost"; }
|
||||||
|
QString findBoostLib(QString path, QString lib) const {
|
||||||
|
QString name;
|
||||||
|
QDir libDir(path);
|
||||||
|
QStringList filters;
|
||||||
|
filters << "libboost_"+lib+"*-mt*.so";
|
||||||
|
QStringList result = libDir.entryList(filters, QDir::Files);
|
||||||
|
if(!result.empty()) {
|
||||||
|
name = result.first().mid(3);
|
||||||
|
// Remove .so
|
||||||
|
name.chop(3);
|
||||||
|
} else {
|
||||||
|
// Fall back to non -mt boost lib
|
||||||
|
filters.clear();
|
||||||
|
filters << "libboost_"+lib+"*.so";
|
||||||
|
result = libDir.entryList(filters, QDir::Files);
|
||||||
|
if(!result.empty()) {
|
||||||
|
name = result.first().mid(3);
|
||||||
|
// Remove .so
|
||||||
|
name.chop(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
bool exec(){
|
bool exec(){
|
||||||
QString s;
|
QString s;
|
||||||
s = conf->getenv("QC_WITH_LIBBOOST_INC");
|
s = conf->getenv("QC_WITH_LIBBOOST_INC");
|
||||||
@@ -51,6 +76,43 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
conf->addIncludePath(s);
|
conf->addIncludePath(s);
|
||||||
|
// Find library
|
||||||
|
s = conf->getenv("QC_WITH_LIBBOOST_LIB");
|
||||||
|
QStringList required_libs;
|
||||||
|
#if BOOST_VERSION >= 103500
|
||||||
|
required_libs << "system";
|
||||||
|
#endif
|
||||||
|
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
// Not required by nox
|
||||||
|
required_libs << "filesystem" << "thread";
|
||||||
|
}
|
||||||
|
QStringList libDirs;
|
||||||
|
libDirs << "/usr/lib/" << "/usr/lib64/" << "/usr/local/lib/" << "/usr/local/lib64/";
|
||||||
|
foreach(const QString& lib, required_libs) {
|
||||||
|
if(!s.isEmpty()) {
|
||||||
|
QString detected_name = findBoostLib(s, lib);
|
||||||
|
if(detected_name.isEmpty()) {
|
||||||
|
printf("Could not find boost %s library!\n", qPrintable(lib));
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
conf->addLib("-l"+detected_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool found = false;
|
||||||
|
foreach(const QString& libDir, libDirs) {
|
||||||
|
QString detected_name = findBoostLib(libDir, lib);
|
||||||
|
if(!detected_name.isEmpty()) {
|
||||||
|
conf->addLib("-l"+detected_name);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!found) {
|
||||||
|
printf("Could not find boost %s library!\n", qPrintable(lib));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,15 @@ public:
|
|||||||
qc_libnotify(Conf *c) : ConfObj(c) {}
|
qc_libnotify(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libnotify >= 0.4.2 (optional)"; }
|
QString name() const { return "libnotify >= 0.4.2 (optional)"; }
|
||||||
QString shortname() const { return "libnotify"; }
|
QString shortname() const { return "libnotify"; }
|
||||||
|
QString checkString() const {
|
||||||
|
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty())
|
||||||
|
return "";
|
||||||
|
return ConfObj::checkString();
|
||||||
|
}
|
||||||
bool exec(){
|
bool exec(){
|
||||||
|
if(!conf->getenv("QC_DISABLE_libnotify").isEmpty() || !conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
QStringList incs;
|
QStringList incs;
|
||||||
QString req_ver = "0.4.2";
|
QString req_ver = "0.4.2";
|
||||||
QString version, libs, other;
|
QString version, libs, other;
|
||||||
@@ -32,6 +40,17 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
QStringList incs3;
|
||||||
|
QString req_ver3 = "2.0";
|
||||||
|
QString version3, libs3, other3;
|
||||||
|
if(conf->findPkgConfig("gtk+-2.0", mode, req_ver3, &version3, &incs3, &libs3, &other3)) {
|
||||||
|
for(int n = 0; n < incs3.count(); ++n)
|
||||||
|
conf->addIncludePath(incs3[n]);
|
||||||
|
if(!libs3.isEmpty())
|
||||||
|
conf->addLib(libs3);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,24 +8,20 @@ class qc_libtorrent_rasterbar : public ConfObj
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
|
qc_libtorrent_rasterbar(Conf *c) : ConfObj(c) {}
|
||||||
QString name() const { return "libtorrent-rasterbar >= 0.14.0 (>= 0.15.0 advised)"; }
|
QString name() const { return "libtorrent-rasterbar >= 0.14.4"; }
|
||||||
QString shortname() const { return "libtorrent-rasterbar"; }
|
QString shortname() const { return "libtorrent-rasterbar"; }
|
||||||
bool exec(){
|
bool exec(){
|
||||||
QStringList incs;
|
QStringList incs;
|
||||||
QString req_ver = "0.14.0";
|
QString req_ver = "0.14.4";
|
||||||
QString adv_ver = "0.15.0";
|
|
||||||
QString version, libs, other;
|
QString version, libs, other;
|
||||||
VersionMode mode = VersionMin;
|
VersionMode mode = VersionMin;
|
||||||
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
|
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, req_ver, &version, &incs, &libs, &other))
|
||||||
return false;
|
return false;
|
||||||
for(int n = 0; n < incs.count(); ++n)
|
for(int n = 0; n < incs.count(); ++n)
|
||||||
conf->addIncludePath(incs[n]);
|
conf->addIncludePath(incs[n]);
|
||||||
if(!libs.isEmpty())
|
if(conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
conf->addLib(libs);
|
conf->addLib("-lcrypto");
|
||||||
if(!conf->findPkgConfig("libtorrent-rasterbar", mode, adv_ver, &version, &incs, &libs, &other))
|
}
|
||||||
printf("\nWarning: libtorrent-rasterbar v%s was detected.\nAlthough it will compile and run, you will be missing some features. Please consider updating to v%s.\n", version.toLocal8Bit().data(), adv_ver.toUtf8().data());
|
|
||||||
else
|
|
||||||
conf->addDefine("LIBTORRENT_0_15");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
16
qcm/pkg-config.qcm
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: pkg-config
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
#include <QProcess>
|
||||||
|
class qc_pkg_config : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_pkg_config(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "pkg-config executable"; }
|
||||||
|
QString shortname() const { return "pkg-config"; }
|
||||||
|
bool exec(){
|
||||||
|
return !conf->findProgram("pkg-config").isEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
-----BEGIN QCMOD-----
|
-----BEGIN QCMOD-----
|
||||||
name: Qt >= 4.4
|
name: Qt >= 4.4
|
||||||
|
arg: disable-gui, Disable qBittorrent Graphical user interface for headless running
|
||||||
-----END QCMOD-----
|
-----END QCMOD-----
|
||||||
*/
|
*/
|
||||||
class qc_qt4 : public ConfObj
|
class qc_qt4 : public ConfObj
|
||||||
@@ -11,10 +12,9 @@ public:
|
|||||||
QString shortname() const { return "Qt 4.4"; }
|
QString shortname() const { return "Qt 4.4"; }
|
||||||
bool exec()
|
bool exec()
|
||||||
{
|
{
|
||||||
if(QT_VERSION >= 0x040500) {
|
if(!conf->getenv("QC_DISABLE_GUI").isEmpty()) {
|
||||||
conf->addDefine("QT_4_5");
|
conf->addDefine("DISABLE_GUI");
|
||||||
}
|
}
|
||||||
return(QT_VERSION >= 0x040400);
|
return(QT_VERSION >= 0x040400);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
26
qcm/qtsingleapplication.qcm
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
-----BEGIN QCMOD-----
|
||||||
|
name: libboost
|
||||||
|
arg: with-qtsingleapplication=[system|shipped], Use the shipped qtsingleapplication library or the system one
|
||||||
|
-----END QCMOD-----
|
||||||
|
*/
|
||||||
|
class qc_qtsingleapplication : public ConfObj
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
qc_qtsingleapplication(Conf *c) : ConfObj(c) {}
|
||||||
|
QString name() const { return "qtsingleapplication library"; }
|
||||||
|
QString shortname() const { return "qtsingleapplication"; }
|
||||||
|
|
||||||
|
bool exec(){
|
||||||
|
QString s;
|
||||||
|
s = conf->getenv("QC_WITH_QTSINGLEAPPLICATION");
|
||||||
|
if(s.compare("system", Qt::CaseInsensitive) == 0) {
|
||||||
|
// System
|
||||||
|
conf->addDefine("USE_SYSTEM_QTSINGLEAPPLICATION");
|
||||||
|
printf(" [system] ");
|
||||||
|
} else {
|
||||||
|
printf(" [shipped] ");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
648
src/GUI.cpp
136
src/GUI.h
@@ -37,13 +37,10 @@
|
|||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include "qtorrenthandle.h"
|
#include "qtorrenthandle.h"
|
||||||
|
|
||||||
enum TabIndex{TAB_TRANSFER, TAB_SEARCH, TAB_RSS};
|
|
||||||
|
|
||||||
class Bittorrent;
|
class Bittorrent;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class downloadFromURL;
|
class downloadFromURL;
|
||||||
class SearchEngine;
|
class SearchEngine;
|
||||||
class QLocalServer;
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
class RSSImp;
|
class RSSImp;
|
||||||
class QShortcut;
|
class QShortcut;
|
||||||
@@ -55,45 +52,37 @@ class TransferListFiltersWidget;
|
|||||||
class QSplitter;
|
class QSplitter;
|
||||||
class PropertiesWidget;
|
class PropertiesWidget;
|
||||||
class StatusBar;
|
class StatusBar;
|
||||||
|
class consoleDlg;
|
||||||
|
class about;
|
||||||
|
class createtorrent;
|
||||||
|
class downloadFromURL;
|
||||||
|
class HidableTabWidget;
|
||||||
|
class LineEdit;
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
class GUI : public QMainWindow, private Ui::MainWindow{
|
class GUI : public QMainWindow, private Ui::MainWindow{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
public:
|
||||||
// Bittorrent
|
// Construct / Destruct
|
||||||
Bittorrent *BTSession;
|
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
|
||||||
QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed?
|
~GUI();
|
||||||
// GUI related
|
// Methods
|
||||||
QTimer *guiUpdater;
|
QWidget* getCurrentTabWidget() const;
|
||||||
QTabWidget *tabs;
|
TransferListWidget* getTransferList() const { return transferList; }
|
||||||
StatusBar *status_bar;
|
QMenu* getTrayIconMenu();
|
||||||
QPointer<options_imp> options;
|
PropertiesWidget *getProperties() const { return properties; }
|
||||||
QPointer<QSystemTrayIcon> systrayIcon;
|
|
||||||
QPointer<QTimer> systrayCreator;
|
|
||||||
QMenu *myTrayIconMenu;
|
|
||||||
TransferListWidget *transferList;
|
|
||||||
TransferListFiltersWidget *transferListFilters;
|
|
||||||
PropertiesWidget *properties;
|
|
||||||
bool displaySpeedInTitle;
|
|
||||||
bool force_exit;
|
|
||||||
// Keyboard shortcuts
|
|
||||||
QShortcut *switchSearchShortcut;
|
|
||||||
QShortcut *switchSearchShortcut2;
|
|
||||||
QShortcut *switchTransferShortcut;
|
|
||||||
QShortcut *switchRSSShortcut;
|
|
||||||
// Widgets
|
|
||||||
QAction *prioSeparator;
|
|
||||||
QAction *prioSeparator2;
|
|
||||||
QSplitter *hSplitter;
|
|
||||||
QSplitter *vSplitter;
|
|
||||||
// Search
|
|
||||||
SearchEngine *searchEngine;
|
|
||||||
// RSS
|
|
||||||
QPointer<RSSImp> rssWidget;
|
|
||||||
// Misc
|
|
||||||
QLocalServer *localServer;
|
|
||||||
|
|
||||||
protected slots:
|
public slots:
|
||||||
|
void trackerAuthenticationRequired(QTorrentHandle& h);
|
||||||
|
void setTabText(int index, QString text) const;
|
||||||
|
void showNotificationBaloon(QString title, QString msg) const;
|
||||||
|
void downloadFromURLList(const QStringList& urls);
|
||||||
|
void updateAltSpeedsBtn(bool alternative);
|
||||||
|
void updateNbTorrents(unsigned int nb_downloading, unsigned int nb_seeding, unsigned int nb_active, unsigned int nb_inactive, unsigned int nb_paused);
|
||||||
|
void deleteBTSession();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
// GUI related slots
|
// GUI related slots
|
||||||
void dropEvent(QDropEvent *event);
|
void dropEvent(QDropEvent *event);
|
||||||
void dragEnterEvent(QDragEnterEvent *event);
|
void dragEnterEvent(QDragEnterEvent *event);
|
||||||
@@ -103,8 +92,6 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void on_actionWebsite_triggered() const;
|
void on_actionWebsite_triggered() const;
|
||||||
void on_actionBugReport_triggered() const;
|
void on_actionBugReport_triggered() const;
|
||||||
void on_actionShow_console_triggered();
|
void on_actionShow_console_triggered();
|
||||||
void readParamsOnSocket();
|
|
||||||
void acceptConnection();
|
|
||||||
void balloonClicked();
|
void balloonClicked();
|
||||||
void writeSettings();
|
void writeSettings();
|
||||||
void readSettings();
|
void readSettings();
|
||||||
@@ -114,6 +101,10 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void handleDownloadFromUrlFailure(QString, QString) const;
|
void handleDownloadFromUrlFailure(QString, QString) const;
|
||||||
void createSystrayDelayed();
|
void createSystrayDelayed();
|
||||||
void tab_changed(int);
|
void tab_changed(int);
|
||||||
|
void on_actionLock_qBittorrent_triggered();
|
||||||
|
void defineUILockPassword();
|
||||||
|
bool unlockUI();
|
||||||
|
void notifyOfUpdate(QString);
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
void createKeyboardShortcuts();
|
void createKeyboardShortcuts();
|
||||||
void displayTransferTab() const;
|
void displayTransferTab() const;
|
||||||
@@ -126,37 +117,72 @@ class GUI : public QMainWindow, private Ui::MainWindow{
|
|||||||
void on_actionOpen_triggered();
|
void on_actionOpen_triggered();
|
||||||
void updateGUI();
|
void updateGUI();
|
||||||
void loadPreferences(bool configure_session=true);
|
void loadPreferences(bool configure_session=true);
|
||||||
|
void processParams(const QString& params);
|
||||||
void processParams(const QStringList& params);
|
void processParams(const QStringList& params);
|
||||||
void addTorrent(QString path);
|
void addTorrent(QString path);
|
||||||
void addUnauthenticatedTracker(QPair<QTorrentHandle,QString> tracker);
|
void addUnauthenticatedTracker(const QPair<QTorrentHandle,QString> &tracker);
|
||||||
void processDownloadedFiles(QString path, QString url);
|
void processDownloadedFiles(QString path, QString url);
|
||||||
void downloadFromURLList(const QStringList& urls);
|
|
||||||
void finishedTorrent(QTorrentHandle& h) const;
|
void finishedTorrent(QTorrentHandle& h) const;
|
||||||
|
void askRecursiveTorrentDownloadConfirmation(QTorrentHandle &h);
|
||||||
// Options slots
|
// Options slots
|
||||||
void on_actionOptions_triggered();
|
void on_actionOptions_triggered();
|
||||||
void optionsSaved();
|
void optionsSaved();
|
||||||
// HTTP slots
|
// HTTP slots
|
||||||
void on_actionDownload_from_URL_triggered();
|
void on_actionDownload_from_URL_triggered();
|
||||||
|
|
||||||
|
protected:
|
||||||
public slots:
|
|
||||||
void trackerAuthenticationRequired(QTorrentHandle& h);
|
|
||||||
void setTabText(int index, QString text) const;
|
|
||||||
void showNotificationBaloon(QString title, QString msg) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void closeEvent(QCloseEvent *);
|
void closeEvent(QCloseEvent *);
|
||||||
void showEvent(QShowEvent *);
|
void showEvent(QShowEvent *);
|
||||||
bool event(QEvent * event);
|
bool event(QEvent * event);
|
||||||
void displayRSSTab(bool enable);
|
void displayRSSTab(bool enable);
|
||||||
|
void displaySearchTab(bool enable);
|
||||||
|
|
||||||
public:
|
private:
|
||||||
// Construct / Destruct
|
QFileSystemWatcher *executable_watcher;
|
||||||
GUI(QWidget *parent=0, QStringList torrentCmdLine=QStringList());
|
// Bittorrent
|
||||||
~GUI();
|
Bittorrent *BTSession;
|
||||||
// Methods
|
QList<QPair<QTorrentHandle,QString> > unauthenticated_trackers; // Still needed?
|
||||||
int getCurrentTabIndex() const;
|
// GUI related
|
||||||
QPoint screenCenter() const;
|
QTimer *guiUpdater;
|
||||||
|
HidableTabWidget *tabs;
|
||||||
|
StatusBar *status_bar;
|
||||||
|
QPointer<options_imp> options;
|
||||||
|
QPointer<consoleDlg> console;
|
||||||
|
QPointer<about> aboutDlg;
|
||||||
|
QPointer<createtorrent> createTorrentDlg;
|
||||||
|
QPointer<downloadFromURL> downloadFromURLDialog;
|
||||||
|
QPointer<QSystemTrayIcon> systrayIcon;
|
||||||
|
QPointer<QTimer> systrayCreator;
|
||||||
|
QPointer<QMenu> myTrayIconMenu;
|
||||||
|
TransferListWidget *transferList;
|
||||||
|
TransferListFiltersWidget *transferListFilters;
|
||||||
|
PropertiesWidget *properties;
|
||||||
|
bool displaySpeedInTitle;
|
||||||
|
bool force_exit;
|
||||||
|
bool ui_locked;
|
||||||
|
LineEdit *search_filter;
|
||||||
|
// Keyboard shortcuts
|
||||||
|
QShortcut *switchSearchShortcut;
|
||||||
|
QShortcut *switchSearchShortcut2;
|
||||||
|
QShortcut *switchTransferShortcut;
|
||||||
|
QShortcut *switchRSSShortcut;
|
||||||
|
// Widgets
|
||||||
|
QAction *prioSeparator;
|
||||||
|
QAction *prioSeparator2;
|
||||||
|
QSplitter *hSplitter;
|
||||||
|
QSplitter *vSplitter;
|
||||||
|
QMenu *lockMenu;
|
||||||
|
// Search
|
||||||
|
QPointer<SearchEngine> searchEngine;
|
||||||
|
// RSS
|
||||||
|
QPointer<RSSImp> rssWidget;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void on_actionSearch_engine_triggered();
|
||||||
|
void on_actionRSS_Reader_triggered();
|
||||||
|
void on_actionSpeed_in_title_bar_triggered();
|
||||||
|
void on_actionTop_tool_bar_triggered();
|
||||||
|
void on_actionShutdown_when_downloads_complete_triggered();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
BIN
src/Icons/3-state-checkbox.gif
Normal file
|
After Width: | Height: | Size: 322 B |
BIN
src/Icons/L.gif
Normal file
|
After Width: | Height: | Size: 66 B |
BIN
src/Icons/flags/saoudi_arabia.png
Normal file
|
After Width: | Height: | Size: 546 B |
|
Before Width: | Height: | Size: 185 B After Width: | Height: | Size: 459 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 743 B |
|
Before Width: | Height: | Size: 659 B |
BIN
src/Icons/oxygen/chronometer.png
Normal file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/Icons/oxygen/cookies.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
src/Icons/oxygen/encrypted32.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/Icons/oxygen/feed-subscribe.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/Icons/oxygen/folder-documents.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
src/Icons/oxygen/gear32.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/Icons/oxygen/go-bottom.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/Icons/oxygen/go-down.png
Normal file
|
After Width: | Height: | Size: 892 B |
BIN
src/Icons/oxygen/go-top.png
Normal file
|
After Width: | Height: | Size: 996 B |
BIN
src/Icons/oxygen/go-up.png
Normal file
|
After Width: | Height: | Size: 929 B |
|
Before Width: | Height: | Size: 860 B |
BIN
src/Icons/oxygen/services.png
Normal file
|
After Width: | Height: | Size: 722 B |
|
Before Width: | Height: | Size: 1.2 KiB |
BIN
src/Icons/qBitTorrentDocument.icns
Normal file
@@ -1,8 +1,9 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Categories=Qt;Network;P2P;
|
Categories=Qt;Network;P2P;
|
||||||
Comment=V2.0.0
|
Comment=V2.4.8
|
||||||
Exec=qbittorrent %f
|
Exec=qbittorrent %f
|
||||||
GenericName=Bittorrent client
|
GenericName=Bittorrent client
|
||||||
|
GenericName[ar]=العميل Bittorrent
|
||||||
GenericName[bg]=Торент клиент
|
GenericName[bg]=Торент клиент
|
||||||
GenericName[cs]=Bittorrent klient
|
GenericName[cs]=Bittorrent klient
|
||||||
GenericName[de]=Bittorren Client
|
GenericName[de]=Bittorren Client
|
||||||
@@ -10,6 +11,7 @@ GenericName[el]=Bittorrent πελάτης
|
|||||||
GenericName[es]=Cliente Bittorrent
|
GenericName[es]=Cliente Bittorrent
|
||||||
GenericName[fi]=Bittorrent-ohjelma
|
GenericName[fi]=Bittorrent-ohjelma
|
||||||
GenericName[fr]=Client Bittorrent
|
GenericName[fr]=Client Bittorrent
|
||||||
|
GenericName[hr]=Bittorrent klijent
|
||||||
GenericName[hu]=Bittorrent kliens
|
GenericName[hu]=Bittorrent kliens
|
||||||
GenericName[it]=Client Bittorrent
|
GenericName[it]=Client Bittorrent
|
||||||
GenericName[ja]=Bittorrent クライアント
|
GenericName[ja]=Bittorrent クライアント
|
||||||
@@ -21,6 +23,7 @@ GenericName[pt_BR]=Cliente Bittorrent
|
|||||||
GenericName[ro]=Client Bittorrent
|
GenericName[ro]=Client Bittorrent
|
||||||
GenericName[ru]=клиент Bittorrent
|
GenericName[ru]=клиент Bittorrent
|
||||||
GenericName[sk]=Klient siete Bittorrent
|
GenericName[sk]=Klient siete Bittorrent
|
||||||
|
GenericName[sr]=Bittorrent-клијент
|
||||||
GenericName[sv]=Bittorrent-klient
|
GenericName[sv]=Bittorrent-klient
|
||||||
GenericName[tr]=Bittorrent istemcisi
|
GenericName[tr]=Bittorrent istemcisi
|
||||||
GenericName[uk]=Bittorrent-клієнт
|
GenericName[uk]=Bittorrent-клієнт
|
||||||
|
|||||||
BIN
src/Icons/qbittorrent_mac.icns
Normal file
BIN
src/Icons/skin/arrow-right.gif
Normal file
|
After Width: | Height: | Size: 54 B |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
src/Icons/skin/disconnected.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 257 B After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
src/Icons/slow.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/Icons/slow_off.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
60
src/Info.plist
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>torrent</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeIconFile</key>
|
||||||
|
<string>qBitTorrentDocument</string>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>BitTorrent Document</string>
|
||||||
|
<key>CFBundleTypeMIMETypes</key>
|
||||||
|
<array>
|
||||||
|
<string>application/x-bittorrent</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>org.bittorrent.torrent</string>
|
||||||
|
</array>
|
||||||
|
<key>LSIsAppleDefaultForType</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>magnet</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleURLName</key>
|
||||||
|
<string>BitTorrent Magnet URL</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleIconFile</key>
|
||||||
|
<string>qbittorrent_mac.icns</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleGetInfoString</key>
|
||||||
|
<string>2.4.8</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>qbittorrent</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.qbittorrent</string>
|
||||||
|
<key>NOTE</key>
|
||||||
|
<string>This file was generated by Qt/QMake.</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
6
src/about.qrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource>
|
||||||
|
<file>gpl.html</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
|
|
||||||
430
src/about_imp.h
@@ -32,7 +32,7 @@
|
|||||||
#define ABOUT_H
|
#define ABOUT_H
|
||||||
|
|
||||||
#include "ui_about.h"
|
#include "ui_about.h"
|
||||||
#include <QScrollBar>
|
#include <QFile>
|
||||||
|
|
||||||
class about : public QDialog, private Ui::AboutDlg{
|
class about : public QDialog, private Ui::AboutDlg{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -47,397 +47,55 @@ class about : public QDialog, private Ui::AboutDlg{
|
|||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
// Set icons
|
// Set icons
|
||||||
logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png")));
|
logo->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/qbittorrent22.png")));
|
||||||
mascot_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/mascot.png")));
|
|
||||||
//Title
|
//Title
|
||||||
lb_name->setText(QString::fromUtf8("<b><h1>")+tr("qBittorrent")+QString::fromUtf8(" "VERSION"</h1></b>"));
|
lb_name->setText(QString::fromUtf8("<b><h1>")+tr("qBittorrent")+QString::fromUtf8(" "VERSION"</h1></b>"));
|
||||||
// Thanks
|
// Thanks
|
||||||
te_thanks->append(QString::fromUtf8("<a name='top'></a>"));
|
QString thanks_txt;
|
||||||
te_thanks->append(QString::fromUtf8("<ul><li>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</li>"));
|
thanks_txt += QString::fromUtf8("<p>I would first like to thank sourceforge.net for hosting qBittorrent project and for their support.</p>");
|
||||||
te_thanks->append(QString::fromUtf8("<li>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</li>"));
|
thanks_txt += QString::fromUtf8("<p>I am pleased that people from all over the world are contributing to qBittorrent: Ishan Arora (India), Arnaud Demaizière (France) and Stephanos Antaris (Greece). Their help is greatly appreciated</p>");
|
||||||
te_thanks->append(QString::fromUtf8("<li>I also want to thank Jeffery Fernandez (jeffery@qbittorrent.org), project consultant, for his help and support since the beginning of this project.</li>"));
|
thanks_txt += QString::fromUtf8("<p>I also want to thank Στέφανος Αντάρης (santaris@csd.auth.gr) and Mirco Chinelli (infinity89@fastwebmail.it) for working on Mac OS X packaging.</p>");
|
||||||
te_thanks->append(QString::fromUtf8("<li>I am grateful to Peter Koeleman (peter@qbittorrent.org) for working on qBittorrent port to Windows.</li>"));
|
thanks_txt += QString::fromUtf8("<p>I am grateful to Peter Koeleman (peter@qbittorrent.org) and Mohammad Dib (mdib@qbittorrent.org) for working on qBittorrent port to Windows.</p>");
|
||||||
te_thanks->append(QString::fromUtf8("<li>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</li></ul><br><br>"));
|
thanks_txt += QString::fromUtf8("<p>Thanks a lot to our graphist Mateusz Toboła (tobejodok@qbittorrent.org) for his great work.</p>");
|
||||||
te_thanks->scrollToAnchor(QString::fromUtf8("top"));
|
te_thanks->setHtml(thanks_txt);
|
||||||
// Translation
|
// Translation
|
||||||
te_translation->append(QString::fromUtf8("<a name='top'></a>"));
|
QString trans_txt = "<p>"+tr("I would like to thank the following people who volunteered to translate qBittorrent:")+"</p>";
|
||||||
te_translation->append(tr("I would like to thank the following people who volunteered to translate qBittorrent:")+QString::fromUtf8("<br>"));
|
trans_txt += QString::fromUtf8("<ul><li><u>Arabic:</u> SDERAWI (abz8868@msn.com) and sn51234 (nesseyan@gmail.com)</li>\
|
||||||
te_translation->append(QString::fromUtf8(
|
<li><u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)</li>\
|
||||||
"<i>- <u>Brazilian:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
|
<li><u>Bulgarian:</u> Tsvetan & Boyko Bankoff (emerge_life@users.sourceforge.net)</li>\
|
||||||
- <u>Bulgarian:</u> Tsvetan & Boiko Bankov (emerge_life@users.sourceforge.net)<br>\
|
<li><u>Catalan:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\
|
||||||
- <u>Catalan:</u> Gekko Dam Beer (gekko04@users.sourceforge.net)<br>\
|
<li><u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)</li>\
|
||||||
- <u>Chinese (Simplified):</u> Guo Yue (yue.guo0418@gmail.com)<br>\
|
<li><u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)</li>\
|
||||||
- <u>Chinese (Traditional):</u> Yi-Shun Wang (dnextstep@gmail.com)<br>\
|
<li><u>Croatian:</u> Oliver Mucafir (oliver.untwist@gmail.com)</li>\
|
||||||
- <u>Czech:</u> Jirka Vilim (web@tets.cz)<br>\
|
<li><u>Czech:</u> Jirka Vilim (web@tets.cz)</li>\
|
||||||
- <u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)<br>\
|
<li><u>Danish:</u> Mathias Nielsen (comoneo@gmail.com)</li>\
|
||||||
- <u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)<br>\
|
<li><u>Dutch:</u> Joost Schipper (heavyjoost@users.sourceforge.net) and Peter Koeleman (peter@peerweb.nl)</li>\
|
||||||
- <u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)<br>\
|
<li><u>Finnish:</u> Niklas Laxström (nikerabbit@users.sourceforge.net) and Pekka Niemi (pekka.niemi@iki.fi)</li>\
|
||||||
- <u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)<br>\
|
<li><u>German:</u> Niels Hoffmann (zentralmaschine@users.sourceforge.net)</li>\
|
||||||
- <u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)<br>\
|
<li><u>Greek:</u> Tsvetan Bankov (emerge_life@users.sourceforge.net)</li>\
|
||||||
- <u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)<br>\
|
<li><u>Hungarian:</u> Majoros Péter (majoros.peterj@gmail.com)</li>\
|
||||||
- <u>Italian:</u> Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)<br>\
|
<li><u>Italian:</u> Mirko Ferrari (mirkoferrari@gmail.com) and Ferraro Luciano (luciano.ferraro@gmail.com)</li>\
|
||||||
- <u>Japanese:</u> Nardog (nardog@e2umail.com)<br>\
|
<li><u>Japanese:</u> Nardog (alphisation@gmail.com)</li>\
|
||||||
- <u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)<br>\
|
<li><u>Korean:</u> Jin Woo Sin (jin828sin@users.sourceforge.net)</li>\
|
||||||
- <u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)<br>\
|
<li><u>Norwegian:</u> Lars-Erik Labori (hamil@users.sourceforge.net)</li>\
|
||||||
- <u>Polish:</u> Jarek Smieja (ajep9691@wp.pl)<br>\
|
<li><u>Polish:</u> Mariusz Fik (fisiu@opensuse.org)</li>\
|
||||||
- <u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)<br>\
|
<li><u>Portuguese:</u> Nick Marinho (nickmarinho@gmail.com)</li>\
|
||||||
- <u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)<br>\
|
<li><u>Romanian:</u> Obada Denis (obadadenis@users.sourceforge.net)</li>\
|
||||||
- <u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)<br>\
|
<li><u>Russian:</u> Nick Khazov (m2k3d0n@users.sourceforge.net) and Alexey Morsov (samurai@ricom.ru)</li>\
|
||||||
- <u>Slovak:</u> helix84<br>\
|
<li><u>Serbian:</u> Anaximandar Milet (anaximandar@operamail.com)</li>\
|
||||||
- <u>Spanish:</u> Vicente Raul Plata Fonseca (silverxnt@users.sourceforge.net) and Gabriel de Oliveira (deadloop@hotmail.com)<br>\
|
<li><u>Slovak:</u> helix84</li>\
|
||||||
- <u>Swedish:</u> Daniel Nylander (po@danielnylander.se)<br>\
|
<li><u>Spanish:</u> Francisco Luque Contreras (frannoe@ya.com)</li>\
|
||||||
- <u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)<br>\
|
<li><u>Swedish:</u> Daniel Nylander (po@danielnylander.se)</li>\
|
||||||
- <u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net)<br><br>"));
|
<li><u>Turkish:</u> Hasan YILMAZ (iletisim@hedefturkce.com) and Erdem Bingöl (erdem84@gmail.com)</li>\
|
||||||
te_translation->append(tr("Please contact me if you would like to translate qBittorrent into your own language."));
|
<li><u>Ukrainian:</u> Andrey Shpachenko (masterfix@users.sourceforge.net) and Oleh Prypin (blaxpirit@gmail.com)</li></ul>");
|
||||||
te_translation->scrollToAnchor(QString::fromUtf8("top"));
|
trans_txt += "<p>"+tr("Please contact me if you would like to translate qBittorrent into your own language.")+"</p>";
|
||||||
|
te_translation->setHtml(trans_txt);
|
||||||
// License
|
// License
|
||||||
te_license->append(QString::fromUtf8("<a name='top'></a>"));
|
te_license->append(QString::fromUtf8("<a name='top'></a>"));
|
||||||
te_license->append(QString::fromUtf8("qBittorrent is licensed under the GNU General Public License version 2 with the\
|
QFile licensefile(":/gpl.html");
|
||||||
addition of the following special exception:\
|
if(licensefile.open(QIODevice::ReadOnly|QIODevice::Text)) {
|
||||||
<br><br>\
|
te_license->setHtml(licensefile.readAll());
|
||||||
<i>In addition, as a special exception, the copyright holders give permission to\
|
licensefile.close();
|
||||||
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.</i>\
|
|
||||||
<br><br>\
|
|
||||||
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
|
|
||||||
<center>Version 2, June 1991</center><br>\
|
|
||||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.<br>\
|
|
||||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies<br>\
|
|
||||||
of this license document, but changing it is not allowed.<br>\
|
|
||||||
<br>\
|
|
||||||
<center><b>Preamble</b></center><br>\
|
|
||||||
The licenses for most software are designed to take away your<br>\
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public<br>\
|
|
||||||
License is intended to guarantee your freedom to share and change free<br>\
|
|
||||||
software--to make sure the software is free for all its users. This<br>\
|
|
||||||
General Public License applies to most of the Free Software<br>\
|
|
||||||
Foundation's software and to any other program whose authors commit to<br>\
|
|
||||||
using it. (Some other Free Software Foundation software is covered by<br>\
|
|
||||||
the GNU Library General Public License instead.) You can apply it to<br>\
|
|
||||||
your programs, too.<br>\
|
|
||||||
<br>\
|
|
||||||
When we speak of free software, we are referring to freedom, not<br>\
|
|
||||||
price. Our General Public Licenses are designed to make sure that you<br>\
|
|
||||||
have the freedom to distribute copies of free software (and charge for<br>\
|
|
||||||
this service if you wish), that you receive source code or can get it<br>\
|
|
||||||
if you want it, that you can change the software or use pieces of it<br>\
|
|
||||||
in new free programs; and that you know you can do these things.<br>\
|
|
||||||
<br>\
|
|
||||||
To protect your rights, we need to make restrictions that forbid<br>\
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.<br>\
|
|
||||||
These restrictions translate to certain responsibilities for you if you<br>\
|
|
||||||
distribute copies of the software, or if you modify it.<br>\
|
|
||||||
<br>\
|
|
||||||
For example, if you distribute copies of such a program, whether<br>\
|
|
||||||
gratis or for a fee, you must give the recipients all the rights that<br>\
|
|
||||||
you have. You must make sure that they, too, receive or can get the<br>\
|
|
||||||
source code. And you must show them these terms so they know their<br>\
|
|
||||||
rights.<br>\
|
|
||||||
<br>\
|
|
||||||
We protect your rights with two steps: (1) copyright the software, and<br>\
|
|
||||||
(2) offer you this license which gives you legal permission to copy,<br>\
|
|
||||||
distribute and/or modify the software.<br>\
|
|
||||||
<br>\
|
|
||||||
Also, for each author's protection and ours, we want to make certain<br>\
|
|
||||||
that everyone understands that there is no warranty for this free<br>\
|
|
||||||
software. If the software is modified by someone else and passed on, we<br>\
|
|
||||||
want its recipients to know that what they have is not the original, so<br>\
|
|
||||||
that any problems introduced by others will not reflect on the original<br>\
|
|
||||||
authors' reputations.<br>\
|
|
||||||
<br>\
|
|
||||||
Finally, any free program is threatened constantly by software<br>\
|
|
||||||
patents. We wish to avoid the danger that redistributors of a free<br>\
|
|
||||||
program will individually obtain patent licenses, in effect making the<br>\
|
|
||||||
program proprietary. To prevent this, we have made it clear that any<br>\
|
|
||||||
patent must be licensed for everyone's free use or not licensed at all.<br>\
|
|
||||||
<br>\
|
|
||||||
The precise terms and conditions for copying, distribution and<br>\
|
|
||||||
modification follow.<br>\
|
|
||||||
<br>\
|
|
||||||
<center><b>GNU GENERAL PUBLIC LICENSE</b></center><br>\
|
|
||||||
<center><b>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</b></center><br>\
|
|
||||||
0. This License applies to any program or other work which contains<br>\
|
|
||||||
a notice placed by the copyright holder saying it may be distributed<br>\
|
|
||||||
under the terms of this General Public License. The 'Program', below,<br>\
|
|
||||||
refers to any such program or work, and a 'work based on the Program'<br>\
|
|
||||||
means either the Program or any derivative work under copyright law:<br>\
|
|
||||||
that is to say, a work containing the Program or a portion of it,<br>\
|
|
||||||
either verbatim or with modifications and/or translated into another<br>\
|
|
||||||
language. (Hereinafter, translation is included without limitation in<br>\
|
|
||||||
the term 'modification'.) Each licensee is addressed as 'you'.<br>\
|
|
||||||
<br>\
|
|
||||||
Activities other than copying, distribution and modification are not<br>\
|
|
||||||
covered by this License; they are outside its scope. The act of<br>\
|
|
||||||
running the Program is not restricted, and the output from the Program<br>\
|
|
||||||
is covered only if its contents constitute a work based on the<br>\
|
|
||||||
Program (independent of having been made by running the Program).<br>\
|
|
||||||
Whether that is true depends on what the Program does.<br>\
|
|
||||||
<br>\
|
|
||||||
1. You may copy and distribute verbatim copies of the Program's<br>\
|
|
||||||
source code as you receive it, in any medium, provided that you<br>\
|
|
||||||
conspicuously and appropriately publish on each copy an appropriate<br>\
|
|
||||||
copyright notice and disclaimer of warranty; keep intact all the<br>\
|
|
||||||
notices that refer to this License and to the absence of any warranty;<br>\
|
|
||||||
and give any other recipients of the Program a copy of this License<br>\
|
|
||||||
along with the Program.<br>\
|
|
||||||
<br>\
|
|
||||||
You may charge a fee for the physical act of transferring a copy, and<br>\
|
|
||||||
you may at your option offer warranty protection in exchange for a fee.<br>\
|
|
||||||
<br>\
|
|
||||||
2. You may modify your copy or copies of the Program or any portion<br>\
|
|
||||||
of it, thus forming a work based on the Program, and copy and<br>\
|
|
||||||
distribute such modifications or work under the terms of Section 1<br>\
|
|
||||||
above, provided that you also meet all of these conditions:<br>\
|
|
||||||
<br>\
|
|
||||||
a) You must cause the modified files to carry prominent notices<br>\
|
|
||||||
stating that you changed the files and the date of any change.<br>\
|
|
||||||
<br>\
|
|
||||||
b) You must cause any work that you distribute or publish, that in<br>\
|
|
||||||
whole or in part contains or is derived from the Program or any<br>\
|
|
||||||
part thereof, to be licensed as a whole at no charge to all third<br>\
|
|
||||||
parties under the terms of this License.<br>\
|
|
||||||
<br>\
|
|
||||||
c) If the modified program normally reads commands interactively<br>\
|
|
||||||
when run, you must cause it, when started running for such<br>\
|
|
||||||
interactive use in the most ordinary way, to print or display an<br>\
|
|
||||||
announcement including an appropriate copyright notice and a<br>\
|
|
||||||
notice that there is no warranty (or else, saying that you provide<br>\
|
|
||||||
a warranty) and that users may redistribute the program under<br>\
|
|
||||||
these conditions, and telling the user how to view a copy of this<br>\
|
|
||||||
License. (Exception: if the Program itself is interactive but<br>\
|
|
||||||
does not normally print such an announcement, your work based on<br>\
|
|
||||||
the Program is not required to print an announcement.)<br>\
|
|
||||||
<br>\
|
|
||||||
These requirements apply to the modified work as a whole. If<br>\
|
|
||||||
identifiable sections of that work are not derived from the Program,<br>\
|
|
||||||
and can be reasonably considered independent and separate works in<br>\
|
|
||||||
themselves, then this License, and its terms, do not apply to those<br>\
|
|
||||||
sections when you distribute them as separate works. But when you<br>\
|
|
||||||
distribute the same sections as part of a whole which is a work based<br>\
|
|
||||||
on the Program, the distribution of the whole must be on the terms of<br>\
|
|
||||||
this License, whose permissions for other licensees extend to the<br>\
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote it.<br>\
|
|
||||||
<br>\
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest<br>\
|
|
||||||
your rights to work written entirely by you; rather, the intent is to<br>\
|
|
||||||
exercise the right to control the distribution of derivative or<br>\
|
|
||||||
collective works based on the Program.<br>\
|
|
||||||
<br>\
|
|
||||||
In addition, mere aggregation of another work not based on the Program<br>\
|
|
||||||
with the Program (or with a work based on the Program) on a volume of<br>\
|
|
||||||
a storage or distribution medium does not bring the other work under<br>\
|
|
||||||
the scope of this License.<br>\
|
|
||||||
<br>\
|
|
||||||
3. You may copy and distribute the Program (or a work based on it,<br>\
|
|
||||||
under Section 2) in object code or executable form under the terms of<br>\
|
|
||||||
Sections 1 and 2 above provided that you also do one of the following:<br>\
|
|
||||||
<br>\
|
|
||||||
a) Accompany it with the complete corresponding machine-readable<br>\
|
|
||||||
source code, which must be distributed under the terms of Sections<br>\
|
|
||||||
1 and 2 above on a medium customarily used for software interchange; or,<br>\
|
|
||||||
<br>\
|
|
||||||
b) Accompany it with a written offer, valid for at least three<br>\
|
|
||||||
years, to give any third party, for a charge no more than your<br>\
|
|
||||||
cost of physically performing source distribution, a complete<br>\
|
|
||||||
machine-readable copy of the corresponding source code, to be<br>\
|
|
||||||
distributed under the terms of Sections 1 and 2 above on a medium<br>\
|
|
||||||
customarily used for software interchange; or,<br>\
|
|
||||||
<br>\
|
|
||||||
c) Accompany it with the information you received as to the offer<br>\
|
|
||||||
to distribute corresponding source code. (This alternative is<br>\
|
|
||||||
allowed only for noncommercial distribution and only if you<br>\
|
|
||||||
received the program in object code or executable form with such<br>\
|
|
||||||
an offer, in accord with Subsection b above.)<br>\
|
|
||||||
<br>\
|
|
||||||
The source code for a work means the preferred form of the work for<br>\
|
|
||||||
making modifications to it. For an executable work, complete source<br>\
|
|
||||||
code means all the source code for all modules it contains, plus any<br>\
|
|
||||||
associated interface definition files, plus the scripts used to<br>\
|
|
||||||
control compilation and installation of the executable. However, as a<br>\
|
|
||||||
special exception, the source code distributed need not include<br>\
|
|
||||||
anything that is normally distributed (in either source or binary<br>\
|
|
||||||
form) with the major components (compiler, kernel, and so on) of the<br>\
|
|
||||||
operating system on which the executable runs, unless that component<br>\
|
|
||||||
itself accompanies the executable.<br>\
|
|
||||||
<br>\
|
|
||||||
If distribution of executable or object code is made by offering<br>\
|
|
||||||
access to copy from a designated place, then offering equivalent<br>\
|
|
||||||
access to copy the source code from the same place counts as<br>\
|
|
||||||
distribution of the source code, even though third parties are not<br>\
|
|
||||||
compelled to copy the source along with the object code.<br>\
|
|
||||||
<br>\
|
|
||||||
4. You may not copy, modify, sublicense, or distribute the Program<br>\
|
|
||||||
except as expressly provided under this License. Any attempt<br>\
|
|
||||||
otherwise to copy, modify, sublicense or distribute the Program is<br>\
|
|
||||||
void, and will automatically terminate your rights under this License.<br>\
|
|
||||||
However, parties who have received copies, or rights, from you under<br>\
|
|
||||||
this License will not have their licenses terminated so long as such<br>\
|
|
||||||
parties remain in full compliance.<br>\
|
|
||||||
<br>\
|
|
||||||
5. You are not required to accept this License, since you have not<br>\
|
|
||||||
signed it. However, nothing else grants you permission to modify or<br>\
|
|
||||||
distribute the Program or its derivative works. These actions are<br>\
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by<br>\
|
|
||||||
modifying or distributing the Program (or any work based on the<br>\
|
|
||||||
Program), you indicate your acceptance of this License to do so, and<br>\
|
|
||||||
all its terms and conditions for copying, distributing or modifying<br>\
|
|
||||||
the Program or works based on it.<br>\
|
|
||||||
<br>\
|
|
||||||
6. Each time you redistribute the Program (or any work based on the<br>\
|
|
||||||
Program), the recipient automatically receives a license from the<br>\
|
|
||||||
original licensor to copy, distribute or modify the Program subject to<br>\
|
|
||||||
these terms and conditions. You may not impose any further<br>\
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.<br>\
|
|
||||||
You are not responsible for enforcing compliance by third parties to<br>\
|
|
||||||
this License.<br>\
|
|
||||||
<br>\
|
|
||||||
7. If, as a consequence of a court judgment or allegation of patent<br>\
|
|
||||||
infringement or for any other reason (not limited to patent issues),<br>\
|
|
||||||
conditions are imposed on you (whether by court order, agreement or<br>\
|
|
||||||
otherwise) that contradict the conditions of this License, they do not<br>\
|
|
||||||
excuse you from the conditions of this License. If you cannot<br>\
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this<br>\
|
|
||||||
License and any other pertinent obligations, then as a consequence you<br>\
|
|
||||||
may not distribute the Program at all. For example, if a patent<br>\
|
|
||||||
license would not permit royalty-free redistribution of the Program by<br>\
|
|
||||||
all those who receive copies directly or indirectly through you, then<br>\
|
|
||||||
the only way you could satisfy both it and this License would be to<br>\
|
|
||||||
refrain entirely from distribution of the Program.<br>\
|
|
||||||
<br>\
|
|
||||||
If any portion of this section is held invalid or unenforceable under<br>\
|
|
||||||
any particular circumstance, the balance of the section is intended to<br>\
|
|
||||||
apply and the section as a whole is intended to apply in other<br>\
|
|
||||||
circumstances.<br>\
|
|
||||||
<br>\
|
|
||||||
It is not the purpose of this section to induce you to infringe any<br>\
|
|
||||||
patents or other property right claims or to contest validity of any<br>\
|
|
||||||
such claims; this section has the sole purpose of protecting the<br>\
|
|
||||||
integrity of the free software distribution system, which is<br>\
|
|
||||||
implemented by public license practices. Many people have made<br>\
|
|
||||||
generous contributions to the wide range of software distributed<br>\
|
|
||||||
through that system in reliance on consistent application of that<br>\
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing<br>\
|
|
||||||
to distribute software through any other system and a licensee cannot<br>\
|
|
||||||
impose that choice.<br>\
|
|
||||||
<br>\
|
|
||||||
This section is intended to make thoroughly clear what is believed to<br>\
|
|
||||||
be a consequence of the rest of this License.<br>\
|
|
||||||
<br>\
|
|
||||||
8. If the distribution and/or use of the Program is restricted in<br>\
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the<br>\
|
|
||||||
original copyright holder who places the Program under this License<br>\
|
|
||||||
may add an explicit geographical distribution limitation excluding<br>\
|
|
||||||
those countries, so that distribution is permitted only in or among<br>\
|
|
||||||
countries not thus excluded. In such case, this License incorporates<br>\
|
|
||||||
the limitation as if written in the body of this License.<br>\
|
|
||||||
<br>\
|
|
||||||
9. The Free Software Foundation may publish revised and/or new versions<br>\
|
|
||||||
of the General Public License from time to time. Such new versions will<br>\
|
|
||||||
be similar in spirit to the present version, but may differ in detail to<br>\
|
|
||||||
address new problems or concerns.<br>\
|
|
||||||
<br>\
|
|
||||||
Each version is given a distinguishing version number. If the Program<br>\
|
|
||||||
specifies a version number of this License which applies to it and 'any<br>\
|
|
||||||
later version', you have the option of following the terms and conditions<br>\
|
|
||||||
either of that version or of any later version published by the Free<br>\
|
|
||||||
Software Foundation. If the Program does not specify a version number of<br>\
|
|
||||||
this License, you may choose any version ever published by the Free Software<br>\
|
|
||||||
Foundation.<br>\
|
|
||||||
<br>\
|
|
||||||
10. If you wish to incorporate parts of the Program into other free<br>\
|
|
||||||
programs whose distribution conditions are different, write to the author<br>\
|
|
||||||
to ask for permission. For software which is copyrighted by the Free<br>\
|
|
||||||
Software Foundation, write to the Free Software Foundation; we sometimes<br>\
|
|
||||||
make exceptions for this. Our decision will be guided by the two goals<br>\
|
|
||||||
of preserving the free status of all derivatives of our free software and<br>\
|
|
||||||
of promoting the sharing and reuse of software generally.<br>\
|
|
||||||
<br>\
|
|
||||||
<center><b>NO WARRANTY</b></center><br>\
|
|
||||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY<br>\
|
|
||||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN<br>\
|
|
||||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES<br>\
|
|
||||||
PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED<br>\
|
|
||||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF<br>\
|
|
||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS<br>\
|
|
||||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE<br>\
|
|
||||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,<br>\
|
|
||||||
REPAIR OR CORRECTION.<br>\
|
|
||||||
<br>\
|
|
||||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING<br>\
|
|
||||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR<br>\
|
|
||||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,<br>\
|
|
||||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING<br>\
|
|
||||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED<br>\
|
|
||||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY<br>\
|
|
||||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER<br>\
|
|
||||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<br>\
|
|
||||||
POSSIBILITY OF SUCH DAMAGES.<br>\
|
|
||||||
<br>\
|
|
||||||
<center><b>END OF TERMS AND CONDITIONS</b></center><br>\
|
|
||||||
<center>How to Apply These Terms to Your New Programs</center><br>\
|
|
||||||
If you develop a new program, and you want it to be of the greatest<br>\
|
|
||||||
possible use to the public, the best way to achieve this is to make it<br>\
|
|
||||||
free software which everyone can redistribute and change under these terms.<br>\
|
|
||||||
<br>\
|
|
||||||
To do so, attach the following notices to the program. It is safest<br>\
|
|
||||||
to attach them to the start of each source file to most effectively<br>\
|
|
||||||
convey the exclusion of warranty; and each file should have at least<br>\
|
|
||||||
the 'copyright' line and a pointer to where the full notice is found.<br>\
|
|
||||||
<br>\
|
|
||||||
<one line to give the program's name and a brief idea of what it does.><br>\
|
|
||||||
Copyright (C) <year> <name of author><br>\
|
|
||||||
<br>\
|
|
||||||
This program is free software; you can redistribute it and/or modify<br>\
|
|
||||||
it under the terms of the GNU General Public License as published by<br>\
|
|
||||||
the Free Software Foundation; either version 2 of the License, or<br>\
|
|
||||||
(at your option) any later version.<br>\
|
|
||||||
<br>\
|
|
||||||
This program is distributed in the hope that it will be useful,<br>\
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of<br>\
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>\
|
|
||||||
GNU General Public License for more details.<br>\
|
|
||||||
<br>\
|
|
||||||
You should have received a copy of the GNU General Public License<br>\
|
|
||||||
along with this program; if not, write to the Free Software<br>\
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA<br>\
|
|
||||||
<br>\
|
|
||||||
<br>\
|
|
||||||
Also add information on how to contact you by electronic and paper mail.<br>\
|
|
||||||
<br>\
|
|
||||||
If the program is interactive, make it output a short notice like this<br>\
|
|
||||||
when it starts in an interactive mode:<br>\
|
|
||||||
<br>\
|
|
||||||
Gnomovision version 69, Copyright (C) year name of author<br>\
|
|
||||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.<br>\
|
|
||||||
This is free software, and you are welcome to redistribute it<br>\
|
|
||||||
under certain conditions; type `show c' for details.<br>\
|
|
||||||
<br>\
|
|
||||||
The hypothetical commands `show w' and `show c' should show the appropriate<br>\
|
|
||||||
parts of the General Public License. Of course, the commands you use may<br>\
|
|
||||||
be called something other than `show w' and `show c'; they could even be<br>\
|
|
||||||
mouse-clicks or menu items--whatever suits your program.<br>\
|
|
||||||
<br>\
|
|
||||||
You should also get your employer (if you work as a programmer) or your<br>\
|
|
||||||
school, if any, to sign a 'copyright disclaimer' for the program, if<br>\
|
|
||||||
necessary. Here is a sample; alter the names:<br>\
|
|
||||||
<br>\
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program<br>\
|
|
||||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.<br>\
|
|
||||||
<br>\
|
|
||||||
'signature of Ty Coon', 1 April 1989<br>\
|
|
||||||
Ty Coon, President of Vice<br>\
|
|
||||||
<br>\
|
|
||||||
This General Public License does not permit incorporating your program into<br>\
|
|
||||||
proprietary programs. If your program is a subroutine library, you may<br>\
|
|
||||||
consider it more useful to permit linking proprietary applications with the<br>\
|
|
||||||
library. If this is what you want to do, use the GNU Library General<br>\
|
|
||||||
Public License instead of this License.<br>"));
|
|
||||||
te_license->scrollToAnchor(QString::fromUtf8("top"));
|
|
||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
208
src/advancedsettings.h
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
#ifndef ADVANCEDSETTINGS_H
|
||||||
|
#define ADVANCEDSETTINGS_H
|
||||||
|
|
||||||
|
#include <QTableWidget>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QComboBox>
|
||||||
|
#include <QNetworkInterface>
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
|
#include "preferences.h"
|
||||||
|
|
||||||
|
enum AdvSettingsCols {PROPERTY, VALUE};
|
||||||
|
enum AdvSettingsRows {DISK_CACHE, OUTGOING_PORT_MIN, OUTGOING_PORT_MAX, IGNORE_LIMIT_LAN, COUNT_OVERHEAD, RECHECK_COMPLETED, LIST_REFRESH, RESOLVE_COUNTRIES, RESOLVE_HOSTS, MAX_HALF_OPEN, SUPER_SEEDING, NETWORK_IFACE, PROGRAM_NOTIFICATIONS };
|
||||||
|
#define ROW_COUNT 13
|
||||||
|
|
||||||
|
class AdvancedSettings: public QTableWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSpinBox *spin_cache, *outgoing_ports_min, *outgoing_ports_max, *spin_list_refresh, *spin_maxhalfopen;
|
||||||
|
QCheckBox *cb_ignore_limits_lan, *cb_count_overhead, *cb_recheck_completed, *cb_resolve_countries, *cb_resolve_hosts, *cb_super_seeding, *cb_program_notifications;
|
||||||
|
QComboBox *combo_iface;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AdvancedSettings(QWidget *parent=0): QTableWidget(parent) {
|
||||||
|
// Set visual appearance
|
||||||
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
setAlternatingRowColors(true);
|
||||||
|
setColumnCount(2);
|
||||||
|
QStringList header;
|
||||||
|
header << tr("Property") << tr("Value");
|
||||||
|
setHorizontalHeaderLabels(header);
|
||||||
|
setColumnWidth(0, width()/2);
|
||||||
|
horizontalHeader()->setStretchLastSection(true);
|
||||||
|
verticalHeader()->setVisible(false);
|
||||||
|
setRowCount(ROW_COUNT);
|
||||||
|
// Load settings
|
||||||
|
loadAdvancedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AdvancedSettings() {
|
||||||
|
delete spin_cache;
|
||||||
|
delete outgoing_ports_min;
|
||||||
|
delete outgoing_ports_max;
|
||||||
|
delete cb_ignore_limits_lan;
|
||||||
|
delete cb_count_overhead;
|
||||||
|
delete cb_recheck_completed;
|
||||||
|
delete spin_list_refresh;
|
||||||
|
delete cb_resolve_countries;
|
||||||
|
delete cb_resolve_hosts;
|
||||||
|
delete spin_maxhalfopen;
|
||||||
|
delete cb_super_seeding;
|
||||||
|
delete combo_iface;
|
||||||
|
delete cb_program_notifications;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void saveAdvancedSettings() {
|
||||||
|
// Disk write cache
|
||||||
|
Preferences::setDiskCacheSize(spin_cache->value());
|
||||||
|
// Outgoing ports
|
||||||
|
Preferences::setOutgoingPortsMin(outgoing_ports_min->value());
|
||||||
|
Preferences::setOutgoingPortsMax(outgoing_ports_max->value());
|
||||||
|
// Ignore limits on LAN
|
||||||
|
Preferences::ignoreLimitsOnLAN(cb_ignore_limits_lan->isChecked());
|
||||||
|
// Include protocol overhead in transfer limits
|
||||||
|
Preferences::includeOverheadInLimits(cb_count_overhead->isChecked());
|
||||||
|
// Recheck torrents on completion
|
||||||
|
Preferences::recheckTorrentsOnCompletion(cb_recheck_completed->isChecked());
|
||||||
|
// Transfer list refresh interval
|
||||||
|
Preferences::setRefreshInterval(spin_list_refresh->value());
|
||||||
|
// Peer resolution
|
||||||
|
Preferences::resolvePeerCountries(cb_resolve_countries->isChecked());
|
||||||
|
Preferences::resolvePeerHostNames(cb_resolve_hosts->isChecked());
|
||||||
|
// Max Half-Open connections
|
||||||
|
Preferences::setMaxHalfOpenConnections(spin_maxhalfopen->value());
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
// Super seeding
|
||||||
|
Preferences::enableSuperSeeding(cb_super_seeding->isChecked());
|
||||||
|
#endif
|
||||||
|
// Network interface
|
||||||
|
if(combo_iface->currentIndex() == 0) {
|
||||||
|
// All interfaces (default)
|
||||||
|
Preferences::setNetworkInterface(QString::null);
|
||||||
|
} else {
|
||||||
|
Preferences::setNetworkInterface(combo_iface->currentText());
|
||||||
|
}
|
||||||
|
// Program notification
|
||||||
|
Preferences::useProgramNotification(cb_program_notifications->isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void loadAdvancedSettings() {
|
||||||
|
// Disk write cache
|
||||||
|
setItem(DISK_CACHE, PROPERTY, new QTableWidgetItem(tr("Disk write cache size")));
|
||||||
|
spin_cache = new QSpinBox();
|
||||||
|
connect(spin_cache, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
spin_cache->setMinimum(1);
|
||||||
|
spin_cache->setMaximum(200);
|
||||||
|
spin_cache->setValue(Preferences::diskCacheSize());
|
||||||
|
spin_cache->setSuffix(tr(" MiB"));
|
||||||
|
setCellWidget(DISK_CACHE, VALUE, spin_cache);
|
||||||
|
// Outgoing port Min
|
||||||
|
setItem(OUTGOING_PORT_MIN, PROPERTY, new QTableWidgetItem(tr("Outgoing ports (Min) [0: Disabled]")));
|
||||||
|
outgoing_ports_min = new QSpinBox();
|
||||||
|
connect(outgoing_ports_min, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
outgoing_ports_min->setMinimum(0);
|
||||||
|
outgoing_ports_min->setMaximum(65535);
|
||||||
|
outgoing_ports_min->setValue(Preferences::outgoingPortsMin());
|
||||||
|
setCellWidget(OUTGOING_PORT_MIN, VALUE, outgoing_ports_min);
|
||||||
|
// Outgoing port Min
|
||||||
|
setItem(OUTGOING_PORT_MAX, PROPERTY, new QTableWidgetItem(tr("Outgoing ports (Max) [0: Disabled]")));
|
||||||
|
outgoing_ports_max = new QSpinBox();
|
||||||
|
connect(outgoing_ports_max, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
outgoing_ports_max->setMinimum(0);
|
||||||
|
outgoing_ports_max->setMaximum(65535);
|
||||||
|
outgoing_ports_max->setValue(Preferences::outgoingPortsMax());
|
||||||
|
setCellWidget(OUTGOING_PORT_MAX, VALUE, outgoing_ports_max);
|
||||||
|
// Ignore transfer limits on local network
|
||||||
|
setItem(IGNORE_LIMIT_LAN, PROPERTY, new QTableWidgetItem(tr("Ignore transfer limits on local network")));
|
||||||
|
cb_ignore_limits_lan = new QCheckBox();
|
||||||
|
connect(cb_ignore_limits_lan, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_ignore_limits_lan->setChecked(Preferences::ignoreLimitsOnLAN());
|
||||||
|
setCellWidget(IGNORE_LIMIT_LAN, VALUE, cb_ignore_limits_lan);
|
||||||
|
// Consider protocol overhead in transfer limits
|
||||||
|
setItem(COUNT_OVERHEAD, PROPERTY, new QTableWidgetItem(tr("Include TCP/IP overhead in transfer limits")));
|
||||||
|
cb_count_overhead = new QCheckBox();
|
||||||
|
connect(cb_count_overhead, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_count_overhead->setChecked(Preferences::includeOverheadInLimits());
|
||||||
|
setCellWidget(COUNT_OVERHEAD, VALUE, cb_count_overhead);
|
||||||
|
// Recheck completed torrents
|
||||||
|
setItem(RECHECK_COMPLETED, PROPERTY, new QTableWidgetItem(tr("Recheck torrents on completion")));
|
||||||
|
cb_recheck_completed = new QCheckBox();
|
||||||
|
connect(cb_recheck_completed, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_recheck_completed->setChecked(Preferences::recheckTorrentsOnCompletion());
|
||||||
|
setCellWidget(RECHECK_COMPLETED, VALUE, cb_recheck_completed);
|
||||||
|
// Transfer list refresh interval
|
||||||
|
setItem(LIST_REFRESH, PROPERTY, new QTableWidgetItem(tr("Transfer list refresh interval")));
|
||||||
|
spin_list_refresh = new QSpinBox();
|
||||||
|
connect(spin_list_refresh, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
spin_list_refresh->setMinimum(30);
|
||||||
|
spin_list_refresh->setMaximum(99999);
|
||||||
|
spin_list_refresh->setValue(Preferences::getRefreshInterval());
|
||||||
|
spin_list_refresh->setSuffix(tr(" ms", " milliseconds"));
|
||||||
|
setCellWidget(LIST_REFRESH, VALUE, spin_list_refresh);
|
||||||
|
// Resolve Peer countries
|
||||||
|
setItem(RESOLVE_COUNTRIES, PROPERTY, new QTableWidgetItem(tr("Resolve peer countries (GeoIP)")));
|
||||||
|
cb_resolve_countries = new QCheckBox();
|
||||||
|
connect(cb_resolve_countries, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_resolve_countries->setChecked(Preferences::resolvePeerCountries());
|
||||||
|
setCellWidget(RESOLVE_COUNTRIES, VALUE, cb_resolve_countries);
|
||||||
|
// Resolve peer hosts
|
||||||
|
setItem(RESOLVE_HOSTS, PROPERTY, new QTableWidgetItem(tr("Resolve peer host names")));
|
||||||
|
cb_resolve_hosts = new QCheckBox();
|
||||||
|
connect(cb_resolve_hosts, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_resolve_hosts->setChecked(Preferences::resolvePeerHostNames());
|
||||||
|
setCellWidget(RESOLVE_HOSTS, VALUE, cb_resolve_hosts);
|
||||||
|
// Max Half Open connections
|
||||||
|
setItem(MAX_HALF_OPEN, PROPERTY, new QTableWidgetItem(tr("Maximum number of half-open connections [0: Disabled]")));
|
||||||
|
spin_maxhalfopen = new QSpinBox();
|
||||||
|
connect(spin_maxhalfopen, SIGNAL(valueChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
spin_maxhalfopen->setMinimum(0);
|
||||||
|
spin_maxhalfopen->setMaximum(99999);
|
||||||
|
spin_maxhalfopen->setValue(Preferences::getMaxHalfOpenConnections());
|
||||||
|
setCellWidget(MAX_HALF_OPEN, VALUE, spin_maxhalfopen);
|
||||||
|
// Super seeding
|
||||||
|
setItem(SUPER_SEEDING, PROPERTY, new QTableWidgetItem(tr("Strict super seeding")));
|
||||||
|
cb_super_seeding = new QCheckBox();
|
||||||
|
connect(cb_super_seeding, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
cb_super_seeding->setChecked(Preferences::isSuperSeedingEnabled());
|
||||||
|
#else
|
||||||
|
cb_super_seeding->setEnabled(false);
|
||||||
|
#endif
|
||||||
|
setCellWidget(SUPER_SEEDING, VALUE, cb_super_seeding);
|
||||||
|
// Network interface
|
||||||
|
setItem(NETWORK_IFACE, PROPERTY, new QTableWidgetItem(tr("Network Interface (requires restart)")));
|
||||||
|
combo_iface = new QComboBox;
|
||||||
|
combo_iface->addItem(tr("Any interface", "i.e. Any network interface"));
|
||||||
|
const QString current_iface = Preferences::getNetworkInterface();
|
||||||
|
int i = 1;
|
||||||
|
foreach(const QNetworkInterface& iface, QNetworkInterface::allInterfaces()) {
|
||||||
|
if(iface.name() == "lo") continue;
|
||||||
|
combo_iface->addItem(iface.name());
|
||||||
|
if(!current_iface.isEmpty() && iface.name() == current_iface)
|
||||||
|
combo_iface->setCurrentIndex(i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
connect(combo_iface, SIGNAL(currentIndexChanged(int)), this, SLOT(emitSettingsChanged()));
|
||||||
|
setCellWidget(NETWORK_IFACE, VALUE, combo_iface);
|
||||||
|
// Program notifications
|
||||||
|
setItem(PROGRAM_NOTIFICATIONS, PROPERTY, new QTableWidgetItem(tr("Display program notification baloons")));
|
||||||
|
cb_program_notifications = new QCheckBox();
|
||||||
|
connect(cb_program_notifications, SIGNAL(toggled(bool)), this, SLOT(emitSettingsChanged()));
|
||||||
|
cb_program_notifications->setChecked(Preferences::useProgramNotification());
|
||||||
|
setCellWidget(PROGRAM_NOTIFICATIONS, VALUE, cb_program_notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitSettingsChanged() {
|
||||||
|
emit settingsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void settingsChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ADVANCEDSETTINGS_H
|
||||||
111
src/bandwidthscheduler.h
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#ifndef BANDWIDTHSCHEDULER_H
|
||||||
|
#define BANDWIDTHSCHEDULER_H
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include "preferences.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class BandwidthScheduler: public QTimer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool in_alternative_mode;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BandwidthScheduler(QObject *parent): QTimer(parent), in_alternative_mode(false) {
|
||||||
|
Q_ASSERT(Preferences::isSchedulerEnabled());
|
||||||
|
// Signal shot, we call start() again manually
|
||||||
|
setSingleShot(true);
|
||||||
|
// Connect Signals/Slots
|
||||||
|
connect(this, SIGNAL(timeout()), this, SLOT(switchMode()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void start() {
|
||||||
|
Q_ASSERT(Preferences::isSchedulerEnabled());
|
||||||
|
|
||||||
|
QTime startAltSpeeds = Preferences::getSchedulerStartTime();
|
||||||
|
QTime endAltSpeeds = Preferences::getSchedulerEndTime();
|
||||||
|
if(startAltSpeeds == endAltSpeeds) {
|
||||||
|
std::cerr << "Error: bandwidth scheduler have the same start time and end time." << std::endl;
|
||||||
|
std::cerr << "The bandwidth scheduler will be disabled" << std::endl;
|
||||||
|
stop();
|
||||||
|
emit switchToAlternativeMode(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine what the closest QTime is
|
||||||
|
QTime now = QTime::currentTime();
|
||||||
|
uint time_to_start = secsTo(now, startAltSpeeds);
|
||||||
|
uint time_to_end = secsTo(now, endAltSpeeds);
|
||||||
|
if(time_to_end < time_to_start) {
|
||||||
|
// We should be in alternative mode
|
||||||
|
in_alternative_mode = true;
|
||||||
|
// Start counting
|
||||||
|
QTimer::start(time_to_end*1000);
|
||||||
|
} else {
|
||||||
|
// We should be in normal mode
|
||||||
|
in_alternative_mode = false;
|
||||||
|
// Start counting
|
||||||
|
QTimer::start(time_to_start*1000);
|
||||||
|
}
|
||||||
|
// Send signal to notify BTSession
|
||||||
|
emit switchToAlternativeMode(in_alternative_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void switchMode() {
|
||||||
|
// Get the day this mode was started (either today or yesterday)
|
||||||
|
QDate current_date = QDateTime::currentDateTime().toLocalTime().date();
|
||||||
|
int day = current_date.dayOfWeek();
|
||||||
|
if(in_alternative_mode) {
|
||||||
|
// It is possible that starttime was yesterday
|
||||||
|
if(QTime::currentTime().secsTo(Preferences::getSchedulerStartTime()) > 0) {
|
||||||
|
current_date.addDays(-1); // Go to yesterday
|
||||||
|
day = current_date.day();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the day is in scheduler days
|
||||||
|
// Notify BTSession only if necessary
|
||||||
|
switch(Preferences::getSchedulerDays()) {
|
||||||
|
case EVERY_DAY:
|
||||||
|
emit switchToAlternativeMode(!in_alternative_mode);
|
||||||
|
break;
|
||||||
|
case WEEK_ENDS:
|
||||||
|
if(day == Qt::Saturday || day == Qt::Sunday)
|
||||||
|
emit switchToAlternativeMode(!in_alternative_mode);
|
||||||
|
break;
|
||||||
|
case WEEK_DAYS:
|
||||||
|
if(day != Qt::Saturday && day != Qt::Sunday)
|
||||||
|
emit switchToAlternativeMode(!in_alternative_mode);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Convert our enum index to Qt enum index
|
||||||
|
int scheduler_day = ((int)Preferences::getSchedulerDays()) - 2;
|
||||||
|
if(day == scheduler_day)
|
||||||
|
emit switchToAlternativeMode(!in_alternative_mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Call start again
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void switchToAlternativeMode(bool alternative);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Qt function can return negative values and we
|
||||||
|
// don't want that
|
||||||
|
uint secsTo(QTime now, QTime t) {
|
||||||
|
int diff = now.secsTo(t);
|
||||||
|
if(diff < 0) {
|
||||||
|
// 86400 seconds in a day
|
||||||
|
diff += 86400;
|
||||||
|
}
|
||||||
|
Q_ASSERT(diff >= 0);
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BANDWIDTHSCHEDULER_H
|
||||||
2125
src/bittorrent.cpp
166
src/bittorrent.h
@@ -31,11 +31,19 @@
|
|||||||
#define __BITTORRENT_H__
|
#define __BITTORRENT_H__
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QUrl>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#ifdef DISABLE_GUI
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#else
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QPalette>
|
#include <QPalette>
|
||||||
|
#endif
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#include <libtorrent/ip_filter.hpp>
|
#include <libtorrent/ip_filter.hpp>
|
||||||
#include "qtorrenthandle.h"
|
#include "qtorrenthandle.h"
|
||||||
@@ -46,16 +54,17 @@ using namespace libtorrent;
|
|||||||
|
|
||||||
class downloadThread;
|
class downloadThread;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class FileSystemWatcher;
|
|
||||||
class FilterParserThread;
|
class FilterParserThread;
|
||||||
class HttpServer;
|
class HttpServer;
|
||||||
|
class BandwidthScheduler;
|
||||||
|
class ScanFoldersModel;
|
||||||
|
|
||||||
class TrackerInfos {
|
class TrackerInfos {
|
||||||
public:
|
public:
|
||||||
QString name_or_url;
|
QString name_or_url;
|
||||||
QString last_message;
|
QString last_message;
|
||||||
unsigned long num_peers;
|
unsigned long num_peers;
|
||||||
#ifndef LIBTORRENT_0_15
|
#if LIBTORRENT_VERSION_MINOR < 15
|
||||||
bool verified;
|
bool verified;
|
||||||
uint fail_count;
|
uint fail_count;
|
||||||
#endif
|
#endif
|
||||||
@@ -66,13 +75,13 @@ public:
|
|||||||
Q_ASSERT(!name_or_url.isEmpty());
|
Q_ASSERT(!name_or_url.isEmpty());
|
||||||
last_message = b.last_message;
|
last_message = b.last_message;
|
||||||
num_peers = b.num_peers;
|
num_peers = b.num_peers;
|
||||||
#ifndef LIBTORRENT_0_15
|
#if LIBTORRENT_VERSION_MINOR < 15
|
||||||
verified = b.verified;
|
verified = b.verified;
|
||||||
fail_count = b.fail_count;
|
fail_count = b.fail_count;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
TrackerInfos(QString name_or_url): name_or_url(name_or_url), last_message(""), num_peers(0) {
|
TrackerInfos(QString name_or_url): name_or_url(name_or_url), last_message(""), num_peers(0) {
|
||||||
#ifndef LIBTORRENT_0_15
|
#if LIBTORRENT_VERSION_MINOR < 15
|
||||||
fail_count = 0;
|
fail_count = 0;
|
||||||
verified = false;
|
verified = false;
|
||||||
#endif
|
#endif
|
||||||
@@ -82,49 +91,6 @@ public:
|
|||||||
class Bittorrent : public QObject {
|
class Bittorrent : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
|
||||||
// Bittorrent
|
|
||||||
session *s;
|
|
||||||
QPointer<QTimer> timerAlerts;
|
|
||||||
QHash<QString, QString> savepath_fromurl;
|
|
||||||
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
|
||||||
// Ratio
|
|
||||||
QPointer<QTimer> BigRatioTimer;
|
|
||||||
// HTTP
|
|
||||||
QPointer<downloadThread> downloader;
|
|
||||||
// File System
|
|
||||||
QPointer<FileSystemWatcher> FSWatcher;
|
|
||||||
// Console / Log
|
|
||||||
QStringList consoleMessages;
|
|
||||||
QStringList peerBanMessages;
|
|
||||||
// Settings
|
|
||||||
bool preAllocateAll;
|
|
||||||
bool addInPause;
|
|
||||||
float ratio_limit;
|
|
||||||
bool UPnPEnabled;
|
|
||||||
bool NATPMPEnabled;
|
|
||||||
bool LSDEnabled;
|
|
||||||
bool DHTEnabled;
|
|
||||||
bool queueingEnabled;
|
|
||||||
QString defaultSavePath;
|
|
||||||
QString defaultTempPath;
|
|
||||||
// GeoIP
|
|
||||||
bool resolve_countries;
|
|
||||||
bool geoipDBLoaded;
|
|
||||||
// ETA Computation
|
|
||||||
QPointer<QTimer> timerETA;
|
|
||||||
QHash<QString, QList<int> > ETA_samples;
|
|
||||||
// IP filtering
|
|
||||||
QPointer<FilterParserThread> filterParser;
|
|
||||||
QString filterPath;
|
|
||||||
// Web UI
|
|
||||||
QPointer<HttpServer> httpServer;
|
|
||||||
QStringList url_skippingDlg;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QString getSavePath(QString hash);
|
|
||||||
bool initWebUi(QString username, QString password, int port);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructor / Destructor
|
// Constructor / Destructor
|
||||||
Bittorrent();
|
Bittorrent();
|
||||||
@@ -142,6 +108,7 @@ public:
|
|||||||
session* getSession() const;
|
session* getSession() const;
|
||||||
QHash<QString, TrackerInfos> getTrackersInfo(QString hash) const;
|
QHash<QString, TrackerInfos> getTrackersInfo(QString hash) const;
|
||||||
bool hasActiveTorrents() const;
|
bool hasActiveTorrents() const;
|
||||||
|
bool hasDownloadingTorrents() const;
|
||||||
bool isQueueingEnabled() const;
|
bool isQueueingEnabled() const;
|
||||||
int getMaximumActiveDownloads() const;
|
int getMaximumActiveDownloads() const;
|
||||||
int getMaximumActiveTorrents() const;
|
int getMaximumActiveTorrents() const;
|
||||||
@@ -151,29 +118,31 @@ public:
|
|||||||
qlonglong getETA(QString hash);
|
qlonglong getETA(QString hash);
|
||||||
bool useTemporaryFolder() const;
|
bool useTemporaryFolder() const;
|
||||||
QString getDefaultSavePath() const;
|
QString getDefaultSavePath() const;
|
||||||
|
ScanFoldersModel* getScanFoldersModel() const;
|
||||||
|
bool isPexEnabled() const;
|
||||||
|
#if LIBTORRENT_VERSION_MINOR < 15
|
||||||
|
void saveDHTEntry();
|
||||||
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
|
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
|
||||||
QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false);
|
QTorrentHandle addMagnetUri(QString magnet_uri, bool resumed=false);
|
||||||
void importOldTorrents();
|
|
||||||
void applyFormerAttributeFiles(QTorrentHandle h);
|
|
||||||
void importOldTempData(QString torrent_path);
|
|
||||||
void loadSessionState();
|
void loadSessionState();
|
||||||
void saveSessionState();
|
void saveSessionState();
|
||||||
void downloadFromUrl(QString url);
|
void downloadFromUrl(QString url);
|
||||||
void deleteTorrent(QString hash, bool delete_local_files = false);
|
void deleteTorrent(QString hash, bool delete_local_files = false);
|
||||||
void startUpTorrents();
|
void startUpTorrents();
|
||||||
|
session_proxy asyncDeletion();
|
||||||
|
void recheckTorrent(QString hash);
|
||||||
|
void useAlternativeSpeedsLimit(bool alternative);
|
||||||
/* Needed by Web UI */
|
/* Needed by Web UI */
|
||||||
void pauseAllTorrents();
|
void pauseAllTorrents();
|
||||||
void pauseTorrent(QString hash);
|
void pauseTorrent(QString hash);
|
||||||
void resumeTorrent(QString hash);
|
void resumeTorrent(QString hash);
|
||||||
void resumeAllTorrents();
|
void resumeAllTorrents();
|
||||||
/* End Web UI */
|
/* End Web UI */
|
||||||
void saveDHTEntry();
|
|
||||||
void preAllocateAllFiles(bool b);
|
void preAllocateAllFiles(bool b);
|
||||||
void saveFastResumeData();
|
void saveFastResumeData();
|
||||||
void enableDirectoryScanning(QString scan_dir);
|
|
||||||
void disableDirectoryScanning();
|
|
||||||
void enableIPFilter(QString filter);
|
void enableIPFilter(QString filter);
|
||||||
void disableIPFilter();
|
void disableIPFilter();
|
||||||
void setQueueingEnabled(bool enable);
|
void setQueueingEnabled(bool enable);
|
||||||
@@ -186,13 +155,20 @@ public slots:
|
|||||||
void setMaxUploadsPerTorrent(int max);
|
void setMaxUploadsPerTorrent(int max);
|
||||||
void setDownloadRateLimit(long rate);
|
void setDownloadRateLimit(long rate);
|
||||||
void setUploadRateLimit(long rate);
|
void setUploadRateLimit(long rate);
|
||||||
void setGlobalRatio(float ratio);
|
void setMaxRatio(float ratio);
|
||||||
void setDeleteRatio(float ratio);
|
|
||||||
void setDHTPort(int dht_port);
|
void setDHTPort(int dht_port);
|
||||||
void setProxySettings(proxy_settings proxySettings, bool trackers=true, bool peers=true, bool web_seeds=true, bool dht=true);
|
void setPeerProxySettings(const proxy_settings &proxySettings);
|
||||||
void setSessionSettings(session_settings sessionSettings);
|
void setHTTPProxySettings(const proxy_settings &proxySettings);
|
||||||
|
void setSessionSettings(const session_settings &sessionSettings);
|
||||||
void startTorrentsInPause(bool b);
|
void startTorrentsInPause(bool b);
|
||||||
void setDefaultTempPath(QString temppath);
|
void setDefaultTempPath(QString temppath);
|
||||||
|
void setAppendLabelToSavePath(bool append);
|
||||||
|
void appendLabelToTorrentSavePath(QTorrentHandle &h);
|
||||||
|
void changeLabelInTorrentSavePath(QTorrentHandle &h, QString old_label, QString new_label);
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
void appendqBextensionToTorrent(QTorrentHandle &h, bool append);
|
||||||
|
void setAppendqBExtension(bool append);
|
||||||
|
#endif
|
||||||
void applyEncryptionSettings(pe_settings se);
|
void applyEncryptionSettings(pe_settings se);
|
||||||
void setDownloadLimit(QString hash, long val);
|
void setDownloadLimit(QString hash, long val);
|
||||||
void setUploadLimit(QString hash, long val);
|
void setUploadLimit(QString hash, long val);
|
||||||
@@ -200,19 +176,33 @@ public slots:
|
|||||||
void enableNATPMP(bool b);
|
void enableNATPMP(bool b);
|
||||||
void enableLSD(bool b);
|
void enableLSD(bool b);
|
||||||
bool enableDHT(bool b);
|
bool enableDHT(bool b);
|
||||||
|
#ifdef DISABLE_GUI
|
||||||
|
void addConsoleMessage(QString msg, QString color=QString::null);
|
||||||
|
#else
|
||||||
void addConsoleMessage(QString msg, QColor color=QApplication::palette().color(QPalette::WindowText));
|
void addConsoleMessage(QString msg, QColor color=QApplication::palette().color(QPalette::WindowText));
|
||||||
|
#endif
|
||||||
void addPeerBanMessage(QString msg, bool from_ipfilter);
|
void addPeerBanMessage(QString msg, bool from_ipfilter);
|
||||||
void processDownloadedFile(QString, QString);
|
void processDownloadedFile(QString, QString);
|
||||||
void addMagnetSkipAddDlg(QString uri);
|
void addMagnetSkipAddDlg(QString uri);
|
||||||
void downloadFromURLList(const QStringList& urls);
|
void downloadFromURLList(const QStringList& urls);
|
||||||
void configureSession();
|
void configureSession();
|
||||||
void banIP(QString ip);
|
void banIP(QString ip);
|
||||||
|
void recursiveTorrentDownload(const QTorrentHandle &h);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString getSavePath(QString hash, bool fromScanDir = false, QString filePath = QString::null, QString root_folder=QString::null);
|
||||||
|
bool initWebUi(QString username, QString password, int port);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void addTorrentsFromScanFolder(QStringList&);
|
void addTorrentsFromScanFolder(QStringList&);
|
||||||
void readAlerts();
|
void readAlerts();
|
||||||
void deleteBigRatios();
|
void processBigRatios();
|
||||||
void takeETASamples();
|
void takeETASamples();
|
||||||
|
void exportTorrentFiles(QString path);
|
||||||
|
void saveTempFastResumeData();
|
||||||
|
void sendNotificationEmail(QTorrentHandle h);
|
||||||
|
void autoRunExternalProgram(QTorrentHandle h, bool async=true);
|
||||||
|
void cleanUpAutoRunProcess(int);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void addedTorrent(QTorrentHandle& h);
|
void addedTorrent(QTorrentHandle& h);
|
||||||
@@ -228,6 +218,66 @@ signals:
|
|||||||
void downloadFromUrlFailure(QString url, QString reason);
|
void downloadFromUrlFailure(QString url, QString reason);
|
||||||
void torrentFinishedChecking(QTorrentHandle& h);
|
void torrentFinishedChecking(QTorrentHandle& h);
|
||||||
void metadataReceived(QTorrentHandle &h);
|
void metadataReceived(QTorrentHandle &h);
|
||||||
|
void savePathChanged(QTorrentHandle &h);
|
||||||
|
void newConsoleMessage(QString msg);
|
||||||
|
void alternativeSpeedsModeChanged(bool alternative);
|
||||||
|
void recursiveTorrentDownloadPossible(QTorrentHandle &h);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Bittorrent
|
||||||
|
session *s;
|
||||||
|
QPointer<QTimer> timerAlerts;
|
||||||
|
QPointer<BandwidthScheduler> bd_scheduler;
|
||||||
|
QMap<QUrl, QString> savepath_fromurl;
|
||||||
|
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
|
||||||
|
QHash<QString, QString> savePathsToRemove;
|
||||||
|
QStringList torrentsToPausedAfterChecking;
|
||||||
|
QTimer resumeDataTimer;
|
||||||
|
// Ratio
|
||||||
|
QPointer<QTimer> BigRatioTimer;
|
||||||
|
// HTTP
|
||||||
|
QPointer<downloadThread> downloader;
|
||||||
|
// File System
|
||||||
|
ScanFoldersModel *m_scanFolders;
|
||||||
|
// Console / Log
|
||||||
|
QStringList consoleMessages;
|
||||||
|
QStringList peerBanMessages;
|
||||||
|
// Settings
|
||||||
|
bool preAllocateAll;
|
||||||
|
bool addInPause;
|
||||||
|
float ratio_limit;
|
||||||
|
int high_ratio_action;
|
||||||
|
bool UPnPEnabled;
|
||||||
|
bool NATPMPEnabled;
|
||||||
|
bool LSDEnabled;
|
||||||
|
bool DHTEnabled;
|
||||||
|
int current_dht_port;
|
||||||
|
bool PeXEnabled;
|
||||||
|
bool queueingEnabled;
|
||||||
|
bool appendLabelToSavePath;
|
||||||
|
bool torrentExport;
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
bool appendqBExtension;
|
||||||
|
#endif
|
||||||
|
QString defaultSavePath;
|
||||||
|
QString defaultTempPath;
|
||||||
|
// ETA Computation
|
||||||
|
QPointer<QTimer> timerETA;
|
||||||
|
QHash<QString, QList<int> > ETA_samples;
|
||||||
|
// IP filtering
|
||||||
|
QPointer<FilterParserThread> filterParser;
|
||||||
|
QString filterPath;
|
||||||
|
// Web UI
|
||||||
|
QPointer<HttpServer> httpServer;
|
||||||
|
QList<QUrl> url_skippingDlg;
|
||||||
|
// Fast exit (async)
|
||||||
|
bool exiting;
|
||||||
|
// GeoIP
|
||||||
|
#ifndef DISABLE_GUI
|
||||||
|
bool geoipDBLoaded;
|
||||||
|
bool resolve_countries;
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class consoleDlg : public QDialog, private Ui_ConsoleDlg{
|
|||||||
consoleDlg(QWidget *parent, Bittorrent* _BTSession) : QDialog(parent) {
|
consoleDlg(QWidget *parent, Bittorrent* _BTSession) : QDialog(parent) {
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setModal(true);
|
||||||
BTSession = _BTSession;
|
BTSession = _BTSession;
|
||||||
textConsole->setHtml(BTSession->getConsoleMessages().join("<br>"));
|
textConsole->setHtml(BTSession->getConsoleMessages().join("<br>"));
|
||||||
textBannedPeers->setHtml(BTSession->getPeerBanMessages().join("<br>"));
|
textBannedPeers->setHtml(BTSession->getPeerBanMessages().join("<br>"));
|
||||||
|
|||||||
95
src/cookiesdlg.cpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cookiesdlg.h"
|
||||||
|
#include "ui_cookiesdlg.h"
|
||||||
|
|
||||||
|
#include <QNetworkCookie>
|
||||||
|
|
||||||
|
enum CookiesCols { COOKIE_KEY, COOKIE_VALUE};
|
||||||
|
|
||||||
|
CookiesDlg::CookiesDlg(QWidget *parent, const QList<QByteArray> &raw_cookies) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::CookiesDlg)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
ui->infos_lbl->setText(tr("Common keys for cookies are : '%1', '%2'.\nYou should get this information from your Web browser preferences.").arg("uid").arg("pass"));
|
||||||
|
foreach(const QByteArray &raw_cookie, raw_cookies) {
|
||||||
|
QList<QByteArray> cookie_parts = raw_cookie.split('=');
|
||||||
|
if(cookie_parts.size() != 2) continue;
|
||||||
|
const int i = ui->cookiesTable->rowCount();
|
||||||
|
ui->cookiesTable->setRowCount(i+1);
|
||||||
|
ui->cookiesTable->setItem(i, COOKIE_KEY, new QTableWidgetItem(cookie_parts.first().data()));
|
||||||
|
ui->cookiesTable->setItem(i, COOKIE_VALUE, new QTableWidgetItem(cookie_parts.last().data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CookiesDlg::~CookiesDlg()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookiesDlg::on_add_btn_clicked() {
|
||||||
|
ui->cookiesTable->setRowCount(ui->cookiesTable->rowCount()+1);
|
||||||
|
// Edit first column
|
||||||
|
ui->cookiesTable->editItem(ui->cookiesTable->item(ui->cookiesTable->rowCount()-1, COOKIE_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CookiesDlg::on_del_btn_clicked() {
|
||||||
|
// Get selected cookie
|
||||||
|
QList<QTableWidgetItem*> selection = ui->cookiesTable->selectedItems();
|
||||||
|
if(!selection.isEmpty()) {
|
||||||
|
ui->cookiesTable->removeRow(selection.first()->row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> CookiesDlg::getCookies() const {
|
||||||
|
QList<QByteArray> ret;
|
||||||
|
for(int i=0; i<ui->cookiesTable->rowCount(); ++i) {
|
||||||
|
QString key = ui->cookiesTable->item(i, COOKIE_KEY)->text().trimmed();
|
||||||
|
QString value = ui->cookiesTable->item(i, COOKIE_VALUE)->text().trimmed();
|
||||||
|
if(!key.isEmpty() && !value.isEmpty()) {
|
||||||
|
const QString raw_cookie = key+"="+value;
|
||||||
|
qDebug("Cookie: %s", qPrintable(raw_cookie));
|
||||||
|
ret << raw_cookie.toLocal8Bit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> CookiesDlg::askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok) {
|
||||||
|
CookiesDlg dlg(parent, raw_cookies);
|
||||||
|
if(dlg.exec()) {
|
||||||
|
*ok = true;
|
||||||
|
return dlg.getCookies();
|
||||||
|
}
|
||||||
|
*ok = false;
|
||||||
|
return QList<QByteArray>();
|
||||||
|
}
|
||||||
58
src/cookiesdlg.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2010 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org arnaud@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COOKIESDLG_H
|
||||||
|
#define COOKIESDLG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class CookiesDlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CookiesDlg : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CookiesDlg(QWidget *parent = 0, const QList<QByteArray> &raw_cookies = QList<QByteArray>());
|
||||||
|
~CookiesDlg();
|
||||||
|
QList<QByteArray> getCookies() const;
|
||||||
|
static QList<QByteArray> askForCookies(QWidget *parent, const QList<QByteArray> &raw_cookies, bool *ok);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void on_add_btn_clicked();
|
||||||
|
void on_del_btn_clicked();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::CookiesDlg *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // COOKIESDLG_H
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/bind.hpp>
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
#include <libtorrent/entry.hpp>
|
#include <libtorrent/entry.hpp>
|
||||||
#include <libtorrent/bencode.hpp>
|
#include <libtorrent/bencode.hpp>
|
||||||
#include <libtorrent/torrent_info.hpp>
|
#include <libtorrent/torrent_info.hpp>
|
||||||
@@ -46,8 +47,10 @@
|
|||||||
#include <libtorrent/file_pool.hpp>
|
#include <libtorrent/file_pool.hpp>
|
||||||
#include <libtorrent/create_torrent.hpp>
|
#include <libtorrent/create_torrent.hpp>
|
||||||
|
|
||||||
|
#include "torrentpersistentdata.h"
|
||||||
#include "createtorrent_imp.h"
|
#include "createtorrent_imp.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "qinisettings.h"
|
||||||
|
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
@@ -64,8 +67,9 @@ bool file_filter(boost::filesystem::path const& filename)
|
|||||||
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
|
createtorrent::createtorrent(QWidget *parent): QDialog(parent){
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setModal(true);
|
||||||
creatorThread = new torrentCreatorThread(this);
|
creatorThread = new torrentCreatorThread(this);
|
||||||
connect(creatorThread, SIGNAL(creationSuccess(QString, const char*)), this, SLOT(handleCreationSuccess(QString, const char*)));
|
connect(creatorThread, SIGNAL(creationSuccess(QString, QString)), this, SLOT(handleCreationSuccess(QString, QString)));
|
||||||
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
|
connect(creatorThread, SIGNAL(creationFailure(QString)), this, SLOT(handleCreationFailure(QString)));
|
||||||
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
|
connect(creatorThread, SIGNAL(updateProgress(int)), this, SLOT(updateProgressBar(int)));
|
||||||
path::default_name_check(no_check);
|
path::default_name_check(no_check);
|
||||||
@@ -77,15 +81,29 @@ createtorrent::~createtorrent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_addFolder_button_clicked(){
|
void createtorrent::on_addFolder_button_clicked(){
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), QDir::homePath(), QFileDialog::ShowDirsOnly);
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
if(!dir.isEmpty())
|
QString last_path = settings.value("CreateTorrent/last_add_path", QDir::homePath()).toString();
|
||||||
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder to add to the torrent"), last_path, QFileDialog::ShowDirsOnly);
|
||||||
|
if(!dir.isEmpty()) {
|
||||||
|
settings.setValue("CreateTorrent/last_add_path", dir);
|
||||||
|
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
|
||||||
|
dir = dir.replace("/", "\\");
|
||||||
|
#endif
|
||||||
textInputPath->setText(dir);
|
textInputPath->setText(dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_addFile_button_clicked(){
|
void createtorrent::on_addFile_button_clicked(){
|
||||||
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), QDir::homePath(), QString(), 0, QFileDialog::ShowDirsOnly);
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
if(!file.isEmpty())
|
QString last_path = settings.value("CreateTorrent/last_add_path", QDir::homePath()).toString();
|
||||||
|
QString file = QFileDialog::getOpenFileName(this, tr("Select a file to add to the torrent"), last_path);
|
||||||
|
if(!file.isEmpty()) {
|
||||||
|
settings.setValue("CreateTorrent/last_add_path", misc::removeLastPathPart(file));
|
||||||
|
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
|
||||||
|
file = file.replace("/", "\\");
|
||||||
|
#endif
|
||||||
textInputPath->setText(file);
|
textInputPath->setText(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::on_removeTracker_button_clicked() {
|
void createtorrent::on_removeTracker_button_clicked() {
|
||||||
@@ -166,47 +184,80 @@ void createtorrent::on_createButton_clicked(){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QStringList trackers = allItems(trackers_list);
|
QStringList trackers = allItems(trackers_list);
|
||||||
/*if(!trackers.size()){
|
|
||||||
QMessageBox::critical(0, tr("No tracker path set"), tr("Please set at least one tracker"));
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
return;
|
QString last_path = settings.value("CreateTorrent/last_save_path", QDir::homePath()).toString();
|
||||||
}*/
|
|
||||||
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), QDir::homePath(), tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
QString destination = QFileDialog::getSaveFileName(this, tr("Select destination torrent file"), last_path, tr("Torrent Files")+QString::fromUtf8(" (*.torrent)"));
|
||||||
if(!destination.isEmpty()) {
|
if(!destination.isEmpty()) {
|
||||||
if(!destination.endsWith(QString::fromUtf8(".torrent")))
|
settings.setValue("CreateTorrent/last_save_path", misc::removeLastPathPart(destination));
|
||||||
|
if(!destination.toUpper().endsWith(".TORRENT"))
|
||||||
destination += QString::fromUtf8(".torrent");
|
destination += QString::fromUtf8(".torrent");
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Disable dialog
|
||||||
|
setEnabled(false);
|
||||||
|
// Set busy cursor
|
||||||
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
|
// Actually create the torrent
|
||||||
QStringList url_seeds = allItems(URLSeeds_list);
|
QStringList url_seeds = allItems(URLSeeds_list);
|
||||||
QString comment = txt_comment->toPlainText();
|
QString comment = txt_comment->toPlainText();
|
||||||
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
|
creatorThread->create(input, destination, trackers, url_seeds, comment, check_private->isChecked(), getPieceSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::handleCreationFailure(QString msg) {
|
void createtorrent::handleCreationFailure(QString msg) {
|
||||||
|
// Enable dialog
|
||||||
|
setEnabled(true);
|
||||||
|
// Remove busy cursor
|
||||||
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
|
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent creation was unsuccessful, reason: %1").arg(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
void createtorrent::handleCreationSuccess(QString path, const char* branch_path) {
|
void createtorrent::handleCreationSuccess(QString path, QString branch_path) {
|
||||||
|
// Enable Dialog
|
||||||
|
setEnabled(true);
|
||||||
|
// Remove busy cursor
|
||||||
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
if(checkStartSeeding->isChecked()) {
|
if(checkStartSeeding->isChecked()) {
|
||||||
// Create save path file
|
QString root_folder;
|
||||||
|
// Create save path temp data
|
||||||
boost::intrusive_ptr<torrent_info> t;
|
boost::intrusive_ptr<torrent_info> t;
|
||||||
try {
|
try {
|
||||||
t = new torrent_info(path.toLocal8Bit().data());
|
t = new torrent_info(path.toUtf8().data());
|
||||||
|
root_folder = misc::truncateRootFolder(t);
|
||||||
} catch(std::exception&) {
|
} catch(std::exception&) {
|
||||||
QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list."));
|
QMessageBox::critical(0, tr("Torrent creation"), tr("Created torrent file is invalid. It won't be added to download list."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString hash = misc::toQString(t->info_hash());
|
QString hash = misc::toQString(t->info_hash());
|
||||||
QFile savepath_file(misc::qBittorrentPath()+QString::fromUtf8("BT_backup")+QDir::separator()+hash+QString::fromUtf8(".savepath"));
|
QString save_path = branch_path;
|
||||||
savepath_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
if(!root_folder.isEmpty()) {
|
||||||
savepath_file.write(branch_path);
|
save_path = QDir(save_path).absoluteFilePath(root_folder);
|
||||||
savepath_file.close();
|
}
|
||||||
|
TorrentTempData::setSavePath(hash, save_path);
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
// Enable seeding mode (do not recheck the files)
|
||||||
|
TorrentTempData::setSeedingMode(hash, true);
|
||||||
|
#endif
|
||||||
emit torrent_to_seed(path);
|
emit torrent_to_seed(path);
|
||||||
}
|
}
|
||||||
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+path);
|
QMessageBox::information(0, tr("Torrent creation"), tr("Torrent was created successfully:")+" "+path);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void createtorrent::on_cancelButton_clicked() {
|
||||||
|
// End torrent creation thread
|
||||||
|
if(creatorThread->isRunning()) {
|
||||||
|
creatorThread->abortCreation();
|
||||||
|
creatorThread->terminate();
|
||||||
|
// Wait for termination
|
||||||
|
creatorThread->wait();
|
||||||
|
}
|
||||||
|
// Close the dialog
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
|
||||||
void createtorrent::updateProgressBar(int progress) {
|
void createtorrent::updateProgressBar(int progress) {
|
||||||
progressBar->setValue(progress);
|
progressBar->setValue(progress);
|
||||||
}
|
}
|
||||||
@@ -240,8 +291,7 @@ void torrentCreatorThread::run() {
|
|||||||
char const* creator_str = "qBittorrent "VERSION;
|
char const* creator_str = "qBittorrent "VERSION;
|
||||||
try {
|
try {
|
||||||
file_storage fs;
|
file_storage fs;
|
||||||
file_pool fp;
|
path full_path = complete(path(input_path.toUtf8().constData()));
|
||||||
path full_path = complete(path(input_path.toLocal8Bit().data()));
|
|
||||||
// Adding files to the torrent
|
// Adding files to the torrent
|
||||||
add_files(fs, full_path, file_filter);
|
add_files(fs, full_path, file_filter);
|
||||||
if(abort) return;
|
if(abort) return;
|
||||||
@@ -261,15 +311,15 @@ void torrentCreatorThread::run() {
|
|||||||
// Set qBittorrent as creator and add user comment to
|
// Set qBittorrent as creator and add user comment to
|
||||||
// torrent_info structure
|
// torrent_info structure
|
||||||
t.set_creator(creator_str);
|
t.set_creator(creator_str);
|
||||||
t.set_comment((const char*)comment.toLocal8Bit());
|
t.set_comment((const char*)comment.toUtf8());
|
||||||
// Is private ?
|
// Is private ?
|
||||||
t.set_priv(is_private);
|
t.set_priv(is_private);
|
||||||
if(abort) return;
|
if(abort) return;
|
||||||
// create the torrent and print it to out
|
// create the torrent and print it to out
|
||||||
ofstream out(complete(path((const char*)save_path.toLocal8Bit())), std::ios_base::binary);
|
ofstream out(complete(path((const char*)save_path.toUtf8())), std::ios_base::binary);
|
||||||
bencode(std::ostream_iterator<char>(out), t.generate());
|
bencode(std::ostream_iterator<char>(out), t.generate());
|
||||||
emit updateProgress(100);
|
emit updateProgress(100);
|
||||||
emit creationSuccess(save_path, full_path.branch_path().string().c_str());
|
emit creationSuccess(save_path, QString::fromUtf8(full_path.branch_path().string().c_str()));
|
||||||
}
|
}
|
||||||
catch (std::exception& e){
|
catch (std::exception& e){
|
||||||
emit creationFailure(QString::fromUtf8(e.what()));
|
emit creationFailure(QString::fromUtf8(e.what()));
|
||||||
|
|||||||
@@ -58,13 +58,14 @@ class torrentCreatorThread : public QThread {
|
|||||||
}
|
}
|
||||||
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
|
void create(QString _input_path, QString _save_path, QStringList _trackers, QStringList _url_seeds, QString _comment, bool _is_private, int _piece_size);
|
||||||
void sendProgressSignal(int progress);
|
void sendProgressSignal(int progress);
|
||||||
|
void abortCreation() { abort = true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void creationFailure(QString msg);
|
void creationFailure(QString msg);
|
||||||
void creationSuccess(QString path, const char* branch_path);
|
void creationSuccess(QString path, QString branch_path);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateProgress(int progress);
|
void updateProgress(int progress);
|
||||||
@@ -87,6 +88,7 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateProgressBar(int progress);
|
void updateProgressBar(int progress);
|
||||||
|
void on_cancelButton_clicked();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void on_createButton_clicked();
|
void on_createButton_clicked();
|
||||||
@@ -97,7 +99,7 @@ class createtorrent : public QDialog, private Ui::createTorrentDialog{
|
|||||||
void on_addURLSeed_button_clicked();
|
void on_addURLSeed_button_clicked();
|
||||||
void on_removeURLSeed_button_clicked();
|
void on_removeURLSeed_button_clicked();
|
||||||
void handleCreationFailure(QString msg);
|
void handleCreationFailure(QString msg);
|
||||||
void handleCreationSuccess(QString path, const char* branch_path);
|
void handleCreationSuccess(QString path, QString branch_path);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include "ui_confirmdeletiondlg.h"
|
#include "ui_confirmdeletiondlg.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
|
class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -40,6 +42,10 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
|
|||||||
public:
|
public:
|
||||||
DeletionConfirmationDlg(QWidget *parent=0): QDialog(parent) {
|
DeletionConfirmationDlg(QWidget *parent=0): QDialog(parent) {
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
move(misc::screenCenter(this));
|
||||||
|
checkPermDelete->setChecked(Preferences::deleteTorrentFilesAsDefault());
|
||||||
|
connect(checkPermDelete, SIGNAL(clicked()), this, SLOT(updateRememberButtonState()));
|
||||||
|
buttonBox->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool shouldDeleteLocalFiles() const {
|
bool shouldDeleteLocalFiles() const {
|
||||||
@@ -55,6 +61,15 @@ class DeletionConfirmationDlg : public QDialog, private Ui::confirmDeletionDlg {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateRememberButtonState() {
|
||||||
|
rememberBtn->setEnabled(checkPermDelete->isChecked() != Preferences::deleteTorrentFilesAsDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_rememberBtn_clicked() {
|
||||||
|
Preferences::setDeleteTorrentFilesAsDefault(checkPermDelete->isChecked());
|
||||||
|
rememberBtn->setEnabled(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DELETIONCONFIRMATIONDLG_H
|
#endif // DELETIONCONFIRMATIONDLG_H
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <libtorrent/bitfield.hpp>
|
#include <libtorrent/bitfield.hpp>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
#define BAR_HEIGHT 18
|
#define BAR_HEIGHT 18
|
||||||
@@ -52,23 +53,73 @@ public:
|
|||||||
setFixedHeight(BAR_HEIGHT);
|
setFixedHeight(BAR_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProgress(bitfield pieces) {
|
void setProgress(const bitfield &pieces, const bitfield &downloading_pieces) {
|
||||||
if(pieces.empty()) {
|
if(pieces.empty()) {
|
||||||
// Empty bar
|
// Empty bar
|
||||||
pixmap = QPixmap(1, 1);
|
QPixmap pix = QPixmap(1, 1);
|
||||||
QPainter painter(&pixmap);
|
pix.fill();
|
||||||
painter.setPen(Qt::white);
|
pixmap = pix;
|
||||||
painter.drawPoint(0,0);
|
|
||||||
} else {
|
} else {
|
||||||
pixmap = QPixmap(pieces.size(), 1);
|
const qulonglong nb_pieces = pieces.size();
|
||||||
QPainter painter(&pixmap);
|
// Reduce the number of pieces before creating the pixmap
|
||||||
for(uint i=0; i<pieces.size(); ++i) {
|
// otherwise it can crash when there are too many pieces
|
||||||
if(pieces[i])
|
const uint w = width();
|
||||||
|
if(nb_pieces > w) {
|
||||||
|
const uint ratio = floor(nb_pieces/(double)w);
|
||||||
|
bitfield scaled_pieces(ceil(nb_pieces/(double)ratio), false);
|
||||||
|
bitfield scaled_downloading(ceil(nb_pieces/(double)ratio), false);
|
||||||
|
uint scaled_index = 0;
|
||||||
|
for(qulonglong i=0; i<nb_pieces; i+= ratio) {
|
||||||
|
bool have = true;
|
||||||
|
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
|
||||||
|
if(!pieces[i]) { have = false; break; }
|
||||||
|
}
|
||||||
|
if(have) {
|
||||||
|
scaled_pieces.set_bit(scaled_index);
|
||||||
|
} else {
|
||||||
|
bool downloading = false;
|
||||||
|
for(qulonglong j=i; j<qMin(i+ratio, nb_pieces); ++j) {
|
||||||
|
if(downloading_pieces[i]) { downloading = true; break; }
|
||||||
|
}
|
||||||
|
if(downloading)
|
||||||
|
scaled_downloading.set_bit(scaled_index);
|
||||||
|
}
|
||||||
|
++scaled_index;
|
||||||
|
}
|
||||||
|
QPixmap pix = QPixmap(scaled_pieces.size(), 1);
|
||||||
|
//pix.fill();
|
||||||
|
QPainter painter(&pix);
|
||||||
|
for(uint i=0; i<scaled_pieces.size(); ++i) {
|
||||||
|
if(scaled_pieces[i]) {
|
||||||
painter.setPen(Qt::blue);
|
painter.setPen(Qt::blue);
|
||||||
else
|
} else {
|
||||||
|
if(scaled_downloading[i]) {
|
||||||
|
painter.setPen(Qt::yellow);
|
||||||
|
} else {
|
||||||
painter.setPen(Qt::white);
|
painter.setPen(Qt::white);
|
||||||
|
}
|
||||||
|
}
|
||||||
painter.drawPoint(i,0);
|
painter.drawPoint(i,0);
|
||||||
}
|
}
|
||||||
|
pixmap = pix;
|
||||||
|
} else {
|
||||||
|
QPixmap pix = QPixmap(pieces.size(), 1);
|
||||||
|
//pix.fill();
|
||||||
|
QPainter painter(&pix);
|
||||||
|
for(uint i=0; i<pieces.size(); ++i) {
|
||||||
|
if(pieces[i]) {
|
||||||
|
painter.setPen(Qt::blue);
|
||||||
|
} else {
|
||||||
|
if(downloading_pieces[i]) {
|
||||||
|
painter.setPen(Qt::yellow);
|
||||||
|
} else {
|
||||||
|
painter.setPen(Qt::white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
painter.drawPoint(i,0);
|
||||||
|
}
|
||||||
|
pixmap = pix;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,10 +47,11 @@ class downloadFromURL : public QDialog, private Ui::downloadFromURL{
|
|||||||
setupUi(this);
|
setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
icon_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/url.png")));
|
icon_lbl->setPixmap(QPixmap(QString::fromUtf8(":/Icons/skin/url.png")));
|
||||||
|
setModal(true);
|
||||||
show();
|
show();
|
||||||
// Paste clipboard if there is an URL in it
|
// Paste clipboard if there is an URL in it
|
||||||
QString clip_txt = qApp->clipboard()->text();
|
QString clip_txt = qApp->clipboard()->text();
|
||||||
if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive)) {
|
if(clip_txt.startsWith("http://", Qt::CaseInsensitive) || clip_txt.startsWith("https://", Qt::CaseInsensitive) || clip_txt.startsWith("ftp://", Qt::CaseInsensitive) || clip_txt.startsWith("magnet:", Qt::CaseInsensitive) || clip_txt.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||||
textUrls->setText(clip_txt);
|
textUrls->setText(clip_txt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,51 +28,71 @@
|
|||||||
* Contact : chris@qbittorrent.org
|
* Contact : chris@qbittorrent.org
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "downloadthread.h"
|
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QSettings>
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
|
#include <QNetworkCookie>
|
||||||
|
#include <QNetworkCookieJar>
|
||||||
|
|
||||||
|
#include "downloadthread.h"
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "qinisettings.h"
|
||||||
|
|
||||||
|
enum ProxyType {HTTP=1, SOCKS5=2, HTTP_PW=3, SOCKS5_PW=4, SOCKS4=5};
|
||||||
|
|
||||||
/** Download Thread **/
|
/** Download Thread **/
|
||||||
|
|
||||||
downloadThread::downloadThread(QObject* parent) : QObject(parent) {
|
downloadThread::downloadThread(QObject* parent) : QObject(parent) {
|
||||||
networkManager = new QNetworkAccessManager(this);
|
connect(&networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
|
||||||
connect(networkManager, SIGNAL(finished (QNetworkReply*)), this, SLOT(processDlFinished(QNetworkReply*)));
|
#ifndef QT_NO_OPENSSL
|
||||||
}
|
connect(&networkManager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)), this, SLOT(ignoreSslErrors(QNetworkReply*,QList<QSslError>)));
|
||||||
|
#endif
|
||||||
downloadThread::~downloadThread(){
|
|
||||||
qDebug("Deleting network manager");
|
|
||||||
delete networkManager;
|
|
||||||
qDebug("Deleted network manager");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void downloadThread::processDlFinished(QNetworkReply* reply) {
|
void downloadThread::processDlFinished(QNetworkReply* reply) {
|
||||||
QString url = reply->url().toString();
|
QString url = reply->url().toEncoded().data();
|
||||||
|
qDebug("Download finished: %s", qPrintable(url));
|
||||||
if(reply->error() != QNetworkReply::NoError) {
|
if(reply->error() != QNetworkReply::NoError) {
|
||||||
// Failure
|
// Failure
|
||||||
|
qDebug("Download failure (%s), reason: %s", qPrintable(url), qPrintable(errorCodeToString(reply->error())));
|
||||||
emit downloadFailure(url, errorCodeToString(reply->error()));
|
emit downloadFailure(url, errorCodeToString(reply->error()));
|
||||||
} else {
|
} else {
|
||||||
|
QVariant redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||||
|
if(redirection.isValid()) {
|
||||||
|
// We should redirect
|
||||||
|
qDebug("Redirecting from %s to %s", qPrintable(url), qPrintable(redirection.toUrl().toString()));
|
||||||
|
redirect_mapping.insert(redirection.toUrl().toString(), url);
|
||||||
|
downloadUrl(redirection.toUrl().toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Checking if it was redirecting, restoring initial URL
|
||||||
|
if(redirect_mapping.contains(url)) {
|
||||||
|
url = redirect_mapping.take(url);
|
||||||
|
}
|
||||||
// Success
|
// Success
|
||||||
QString filePath;
|
QString filePath;
|
||||||
QTemporaryFile tmpfile;
|
QTemporaryFile *tmpfile = new QTemporaryFile;
|
||||||
tmpfile.setAutoRemove(false);
|
tmpfile->setAutoRemove(false);
|
||||||
if (tmpfile.open()) {
|
if (tmpfile->open()) {
|
||||||
filePath = tmpfile.fileName();
|
filePath = tmpfile->fileName();
|
||||||
qDebug("Temporary filename is: %s", filePath.toLocal8Bit().data());
|
qDebug("Temporary filename is: %s", qPrintable(filePath));
|
||||||
if(reply->open(QIODevice::ReadOnly)) {
|
if(reply->open(QIODevice::ReadOnly)) {
|
||||||
tmpfile.write(reply->readAll());
|
// TODO: Support GZIP compression
|
||||||
|
tmpfile->write(reply->readAll());
|
||||||
reply->close();
|
reply->close();
|
||||||
tmpfile.close();
|
tmpfile->close();
|
||||||
|
delete tmpfile;
|
||||||
// Send finished signal
|
// Send finished signal
|
||||||
emit downloadFinished(url, filePath);
|
emit downloadFinished(url, filePath);
|
||||||
} else {
|
} else {
|
||||||
// Error when reading the request
|
// Error when reading the request
|
||||||
tmpfile.close();
|
tmpfile->close();
|
||||||
|
delete tmpfile;
|
||||||
emit downloadFailure(url, tr("I/O Error"));
|
emit downloadFailure(url, tr("I/O Error"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
delete tmpfile;
|
||||||
emit downloadFailure(url, tr("I/O Error"));
|
emit downloadFailure(url, tr("I/O Error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,26 +100,87 @@ void downloadThread::processDlFinished(QNetworkReply* reply) {
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
void downloadThread::downloadUrl(QString url){
|
void downloadThread::loadCookies(const QString &host_name, QString url) {
|
||||||
|
const QList<QByteArray> raw_cookies = Preferences::getHostNameCookies(host_name);
|
||||||
|
QNetworkCookieJar *cookie_jar = networkManager.cookieJar();
|
||||||
|
QList<QNetworkCookie> cookies;
|
||||||
|
qDebug("Loading cookies for host name: %s", qPrintable(host_name));
|
||||||
|
foreach(const QByteArray& raw_cookie, raw_cookies) {
|
||||||
|
QList<QByteArray> cookie_parts = raw_cookie.split('=');
|
||||||
|
if(cookie_parts.size() == 2) {
|
||||||
|
qDebug("Loading cookie: %s", raw_cookie.constData());
|
||||||
|
cookies << QNetworkCookie(cookie_parts.first(), cookie_parts.last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cookie_jar->setCookiesFromUrl(cookies, url);
|
||||||
|
networkManager.setCookieJar(cookie_jar);
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::downloadTorrentUrl(QString url){
|
||||||
|
// Load cookies
|
||||||
|
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
|
||||||
|
if(!host_name.isEmpty())
|
||||||
|
loadCookies(host_name, url);
|
||||||
|
// Process request
|
||||||
|
QNetworkReply *reply = downloadUrl(url);
|
||||||
|
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkReply* downloadThread::downloadUrl(QString url){
|
||||||
// Update proxy settings
|
// Update proxy settings
|
||||||
applyProxySettings();
|
applyProxySettings();
|
||||||
|
// Load cookies
|
||||||
|
QString host_name = QUrl::fromEncoded(url.toLocal8Bit()).host();
|
||||||
|
if(!host_name.isEmpty())
|
||||||
|
loadCookies(host_name, url);
|
||||||
// Process download request
|
// Process download request
|
||||||
networkManager->get(QNetworkRequest(QUrl(url)));
|
qDebug("url is %s", qPrintable(url));
|
||||||
|
const QUrl qurl = QUrl::fromEncoded(url.toLocal8Bit());
|
||||||
|
QNetworkRequest request(qurl);
|
||||||
|
// Spoof Firefox 3.5 user agent to avoid
|
||||||
|
// Web server banning
|
||||||
|
request.setRawHeader("User-Agent", "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5");
|
||||||
|
qDebug("Downloading %s...", request.url().toEncoded().data());
|
||||||
|
qDebug("%d cookies for this URL", networkManager.cookieJar()->cookiesForUrl(url).size());
|
||||||
|
for(int i=0; i<networkManager.cookieJar()->cookiesForUrl(url).size(); ++i) {
|
||||||
|
qDebug("%s=%s", networkManager.cookieJar()->cookiesForUrl(url).at(i).name().data(), networkManager.cookieJar()->cookiesForUrl(url).at(i).value().data());
|
||||||
|
qDebug("Domain: %s, Path: %s", qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).domain()), qPrintable(networkManager.cookieJar()->cookiesForUrl(url).at(i).path()));
|
||||||
|
}
|
||||||
|
return networkManager.get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void downloadThread::checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
|
if(bytesTotal > 0) {
|
||||||
|
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
|
||||||
|
// Total number of bytes is available
|
||||||
|
if(bytesTotal > 1048576) {
|
||||||
|
// More than 1MB, this is probably not a torrent file, aborting...
|
||||||
|
reply->abort();
|
||||||
|
} else {
|
||||||
|
disconnect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(checkDownloadSize(qint64,qint64)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(bytesReceived > 1048576) {
|
||||||
|
// More than 1MB, this is probably not a torrent file, aborting...
|
||||||
|
QNetworkReply *reply = static_cast<QNetworkReply*>(sender());
|
||||||
|
reply->abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void downloadThread::applyProxySettings() {
|
void downloadThread::applyProxySettings() {
|
||||||
QNetworkProxy proxy;
|
QNetworkProxy proxy;
|
||||||
QSettings settings("qBittorrent", "qBittorrent");
|
QIniSettings settings("qBittorrent", "qBittorrent");
|
||||||
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt();
|
int intValue = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxyType"), 0).toInt();
|
||||||
if(intValue > 0) {
|
if(intValue > 0) {
|
||||||
// Proxy enabled
|
// Proxy enabled
|
||||||
QString IP = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString();
|
QString IP = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/IP"), "0.0.0.0").toString();
|
||||||
proxy.setHostName(IP);
|
proxy.setHostName(IP);
|
||||||
QString port = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toString();
|
QString port = settings.value(QString::fromUtf8("Preferences/Connection/HTTPProxy/Port"), 8080).toString();
|
||||||
qDebug("Using proxy: %s", (IP+QString(":")+port).toLocal8Bit().data());
|
qDebug("Using proxy: %s", qPrintable(IP));
|
||||||
proxy.setPort(port.toUShort());
|
proxy.setPort(port.toUShort());
|
||||||
// Default proxy type is HTTP, we must change if it is SOCKS5
|
// Default proxy type is HTTP, we must change if it is SOCKS5
|
||||||
if(intValue%2==0) {
|
if(intValue == SOCKS5 || intValue == SOCKS5_PW) {
|
||||||
qDebug("Proxy is SOCKS5, not HTTP");
|
qDebug("Proxy is SOCKS5, not HTTP");
|
||||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||||
} else {
|
} else {
|
||||||
@@ -117,7 +198,7 @@ void downloadThread::applyProxySettings() {
|
|||||||
} else {
|
} else {
|
||||||
proxy.setType(QNetworkProxy::NoProxy);
|
proxy.setType(QNetworkProxy::NoProxy);
|
||||||
}
|
}
|
||||||
networkManager->setProxy(proxy);
|
networkManager.setProxy(proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
|
QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
|
||||||
@@ -168,3 +249,11 @@ QString downloadThread::errorCodeToString(QNetworkReply::NetworkError status) {
|
|||||||
return tr("Unknown error");
|
return tr("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
void downloadThread::ignoreSslErrors(QNetworkReply* reply,QList<QSslError> errors) {
|
||||||
|
Q_UNUSED(errors)
|
||||||
|
// Ignore all SSL errors
|
||||||
|
reply->ignoreSslErrors();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QSslError>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
|
|
||||||
@@ -40,7 +42,8 @@ class downloadThread : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkAccessManager *networkManager;
|
QNetworkAccessManager networkManager;
|
||||||
|
QHash<QString, QString> redirect_mapping;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void downloadFinished(QString url, QString file_path);
|
void downloadFinished(QString url, QString file_path);
|
||||||
@@ -48,16 +51,21 @@ signals:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
downloadThread(QObject* parent);
|
downloadThread(QObject* parent);
|
||||||
~downloadThread();
|
QNetworkReply* downloadUrl(QString url);
|
||||||
void downloadUrl(QString url);
|
void downloadTorrentUrl(QString url);
|
||||||
//void setProxy(QString IP, int port, QString username, QString password);
|
//void setProxy(QString IP, int port, QString username, QString password);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString errorCodeToString(QNetworkReply::NetworkError status);
|
QString errorCodeToString(QNetworkReply::NetworkError status);
|
||||||
void applyProxySettings();
|
void applyProxySettings();
|
||||||
|
void loadCookies(const QString &host_name, QString url);
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void processDlFinished(QNetworkReply* reply);
|
void processDlFinished(QNetworkReply* reply);
|
||||||
|
void checkDownloadSize(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
#ifndef QT_NO_OPENSSL
|
||||||
|
void ignoreSslErrors(QNetworkReply*,QList<QSslError>);
|
||||||
|
#endif
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include "pluginsource.h"
|
#include "pluginsource.h"
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QSettings>
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@@ -45,7 +44,7 @@
|
|||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID};
|
enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID};
|
||||||
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/trunk/src/search_engine/engines/"
|
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/branches/v2_4_x/src/search_engine/engines/"
|
||||||
|
|
||||||
engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) {
|
engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) {
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
@@ -81,9 +80,14 @@ void engineSelectDlg::dropEvent(QDropEvent *event) {
|
|||||||
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
|
QStringList files=event->mimeData()->text().split(QString::fromUtf8("\n"));
|
||||||
QString file;
|
QString file;
|
||||||
foreach(file, files) {
|
foreach(file, files) {
|
||||||
qDebug("dropped %s", file.toLocal8Bit().data());
|
qDebug("dropped %s", qPrintable(file));
|
||||||
|
#ifdef Q_WS_WIN
|
||||||
|
file = file.replace("file:///", "");
|
||||||
|
#else
|
||||||
file = file.replace("file://", "");
|
file = file.replace("file://", "");
|
||||||
|
#endif
|
||||||
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
|
if(file.startsWith("http://", Qt::CaseInsensitive) || file.startsWith("https://", Qt::CaseInsensitive) || file.startsWith("ftp://", Qt::CaseInsensitive)) {
|
||||||
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
downloader->downloadUrl(file);
|
downloader->downloadUrl(file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -99,7 +103,7 @@ void engineSelectDlg::dropEvent(QDropEvent *event) {
|
|||||||
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
|
void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
|
||||||
QString mime;
|
QString mime;
|
||||||
foreach(mime, event->mimeData()->formats()){
|
foreach(mime, event->mimeData()->formats()){
|
||||||
qDebug("mimeData: %s", mime.toLocal8Bit().data());
|
qDebug("mimeData: %s", qPrintable(mime));
|
||||||
}
|
}
|
||||||
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
|
if (event->mimeData()->hasFormat(QString::fromUtf8("text/plain")) || event->mimeData()->hasFormat(QString::fromUtf8("text/uri-list"))) {
|
||||||
event->acceptProposedAction();
|
event->acceptProposedAction();
|
||||||
@@ -108,6 +112,7 @@ void engineSelectDlg::dragEnterEvent(QDragEnterEvent *event) {
|
|||||||
|
|
||||||
void engineSelectDlg::on_updateButton_clicked() {
|
void engineSelectDlg::on_updateButton_clicked() {
|
||||||
// Download version file from update server on sourceforge
|
// Download version file from update server on sourceforge
|
||||||
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt");
|
downloader->downloadUrl(QString(UPDATE_URL)+"versions.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,18 +130,17 @@ void engineSelectDlg::toggleEngineState(QTreeWidgetItem *item, int) {
|
|||||||
|
|
||||||
void engineSelectDlg::displayContextMenu(const QPoint&) {
|
void engineSelectDlg::displayContextMenu(const QPoint&) {
|
||||||
QMenu myContextMenu(this);
|
QMenu myContextMenu(this);
|
||||||
QModelIndex index;
|
|
||||||
// Enable/disable pause/start action given the DL state
|
// Enable/disable pause/start action given the DL state
|
||||||
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
QList<QTreeWidgetItem *> items = pluginsTree->selectedItems();
|
||||||
bool has_enable = false, has_disable = false;
|
bool has_enable = false, has_disable = false;
|
||||||
QTreeWidgetItem *item;
|
QTreeWidgetItem *item;
|
||||||
foreach(item, items) {
|
foreach(item, items) {
|
||||||
QString id = item->text(ENGINE_ID);
|
QString id = item->text(ENGINE_ID);
|
||||||
if(supported_engines->value(id)->isEnabled() and !has_disable) {
|
if(supported_engines->value(id)->isEnabled() && !has_disable) {
|
||||||
myContextMenu.addAction(actionDisable);
|
myContextMenu.addAction(actionDisable);
|
||||||
has_disable = true;
|
has_disable = true;
|
||||||
}
|
}
|
||||||
if(!supported_engines->value(id)->isEnabled() and !has_enable) {
|
if(!supported_engines->value(id)->isEnabled() && !has_enable) {
|
||||||
myContextMenu.addAction(actionEnable);
|
myContextMenu.addAction(actionEnable);
|
||||||
has_enable = true;
|
has_enable = true;
|
||||||
}
|
}
|
||||||
@@ -170,7 +174,7 @@ void engineSelectDlg::on_actionUninstall_triggered() {
|
|||||||
}else {
|
}else {
|
||||||
// Proceed with uninstall
|
// Proceed with uninstall
|
||||||
// remove it from hard drive
|
// remove it from hard drive
|
||||||
QDir enginesFolder(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines");
|
QDir enginesFolder(misc::searchEngineLocation()+QDir::separator()+"engines");
|
||||||
QStringList filters;
|
QStringList filters;
|
||||||
filters << id+".*";
|
filters << id+".*";
|
||||||
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
QStringList files = enginesFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||||
@@ -245,27 +249,28 @@ QTreeWidgetItem* engineSelectDlg::findItemWithID(QString id){
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
|
bool engineSelectDlg::isUpdateNeeded(QString plugin_name, float new_version) const {
|
||||||
float old_version = SearchEngine::getPluginVersion(misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
|
float old_version = SearchEngine::getPluginVersion(misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py");
|
||||||
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
|
qDebug("IsUpdate needed? tobeinstalled: %.2f, alreadyinstalled: %.2f", new_version, old_version);
|
||||||
return (new_version > old_version);
|
return (new_version > old_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
|
void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
|
||||||
qDebug("Asked to install plugin at %s", path.toLocal8Bit().data());
|
qDebug("Asked to install plugin at %s", qPrintable(path));
|
||||||
float new_version = SearchEngine::getPluginVersion(path);
|
float new_version = SearchEngine::getPluginVersion(path);
|
||||||
qDebug("Version to be installed: %.2f", new_version);
|
qDebug("Version to be installed: %.2f", new_version);
|
||||||
if(!isUpdateNeeded(plugin_name, new_version)) {
|
if(!isUpdateNeeded(plugin_name, new_version)) {
|
||||||
qDebug("Apparently update is not needed, we have a more recent version");
|
qDebug("Apparently update is not needed, we have a more recent version");
|
||||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("A more recent version of %1 search engine plugin is already installed.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Process with install
|
// Process with install
|
||||||
QString dest_path = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
|
QString dest_path = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+plugin_name+".py";
|
||||||
bool update = false;
|
bool update = false;
|
||||||
if(QFile::exists(dest_path)) {
|
if(QFile::exists(dest_path)) {
|
||||||
// Backup in case install fails
|
// Backup in case install fails
|
||||||
QFile::copy(dest_path, dest_path+".bak");
|
QFile::copy(dest_path, dest_path+".bak");
|
||||||
QFile::remove(dest_path);
|
misc::safeRemove(dest_path);
|
||||||
|
misc::safeRemove(dest_path+"c");
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
// Copy the plugin
|
// Copy the plugin
|
||||||
@@ -276,28 +281,28 @@ void engineSelectDlg::installPlugin(QString path, QString plugin_name) {
|
|||||||
if(!supported_engines->contains(plugin_name)) {
|
if(!supported_engines->contains(plugin_name)) {
|
||||||
if(update) {
|
if(update) {
|
||||||
// Remove broken file
|
// Remove broken file
|
||||||
QFile::remove(dest_path);
|
misc::safeRemove(dest_path);
|
||||||
// restore backup
|
// restore backup
|
||||||
QFile::copy(dest_path+".bak", dest_path);
|
QFile::copy(dest_path+".bak", dest_path);
|
||||||
QFile::remove(dest_path+".bak");
|
misc::safeRemove(dest_path+".bak");
|
||||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be updated, keeping old version.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Remove broken file
|
// Remove broken file
|
||||||
QFile::remove(dest_path);
|
misc::safeRemove(dest_path);
|
||||||
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::warning(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin could not be installed.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Install was successful, remove backup
|
// Install was successful, remove backup
|
||||||
if(update) {
|
if(update) {
|
||||||
QFile::remove(dest_path+".bak");
|
misc::safeRemove(dest_path+".bak");
|
||||||
}
|
}
|
||||||
if(update) {
|
if(update) {
|
||||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully updated.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::information(this, tr("Search plugin install")+" -- "+tr("qBittorrent"), tr("%1 search engine plugin was successfully installed.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,12 +329,12 @@ void engineSelectDlg::addNewEngine(QString engine_name) {
|
|||||||
setRowColor(pluginsTree->indexOfTopLevelItem(item), "red");
|
setRowColor(pluginsTree->indexOfTopLevelItem(item), "red");
|
||||||
}
|
}
|
||||||
// Handle icon
|
// Handle icon
|
||||||
QString iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".png";
|
QString iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".png";
|
||||||
if(QFile::exists(iconPath)) {
|
if(QFile::exists(iconPath)) {
|
||||||
// Good, we already have the icon
|
// Good, we already have the icon
|
||||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
} else {
|
} else {
|
||||||
iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".ico";
|
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+engine->getName()+".ico";
|
||||||
if(QFile::exists(iconPath)) { // ICO support
|
if(QFile::exists(iconPath)) { // ICO support
|
||||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
} else {
|
} else {
|
||||||
@@ -350,8 +355,10 @@ void engineSelectDlg::askForPluginUrl() {
|
|||||||
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
|
QString url = QInputDialog::getText(this, tr("New search engine plugin URL"),
|
||||||
tr("URL:"), QLineEdit::Normal,
|
tr("URL:"), QLineEdit::Normal,
|
||||||
"http://", &ok);
|
"http://", &ok);
|
||||||
if (ok && !url.isEmpty())
|
if (ok && !url.isEmpty()) {
|
||||||
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
downloader->downloadUrl(url);
|
downloader->downloadUrl(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void engineSelectDlg::askForLocalPlugin() {
|
void engineSelectDlg::askForLocalPlugin() {
|
||||||
@@ -390,23 +397,24 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
|
|||||||
plugin_name.chop(1); // remove trailing ':'
|
plugin_name.chop(1); // remove trailing ':'
|
||||||
bool ok;
|
bool ok;
|
||||||
float version = list.last().toFloat(&ok);
|
float version = list.last().toFloat(&ok);
|
||||||
qDebug("read line %s: %.2f", plugin_name.toLocal8Bit().data(), version);
|
qDebug("read line %s: %.2f", qPrintable(plugin_name), version);
|
||||||
if(!ok) continue;
|
if(!ok) continue;
|
||||||
file_correct = true;
|
file_correct = true;
|
||||||
if(isUpdateNeeded(plugin_name, version)) {
|
if(isUpdateNeeded(plugin_name, version)) {
|
||||||
qDebug("Plugin: %s is outdated", plugin_name.toLocal8Bit().data());
|
qDebug("Plugin: %s is outdated", qPrintable(plugin_name));
|
||||||
// Downloading update
|
// Downloading update
|
||||||
|
setCursor(QCursor(Qt::WaitCursor));
|
||||||
downloader->downloadUrl(UPDATE_URL+plugin_name+".py");
|
downloader->downloadUrl(UPDATE_URL+plugin_name+".py");
|
||||||
//downloader->downloadUrl(UPDATE_URL+plugin_name+".png");
|
//downloader->downloadUrl(UPDATE_URL+plugin_name+".png");
|
||||||
updated = true;
|
updated = true;
|
||||||
}else {
|
}else {
|
||||||
qDebug("Plugin: %s is up to date", plugin_name.toLocal8Bit().data());
|
qDebug("Plugin: %s is up to date", qPrintable(plugin_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Close file
|
// Close file
|
||||||
versions.close();
|
versions.close();
|
||||||
// Clean up tmp file
|
// Clean up tmp file
|
||||||
QFile::remove(versions_file);
|
misc::safeRemove(versions_file);
|
||||||
if(file_correct && !updated) {
|
if(file_correct && !updated) {
|
||||||
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
|
QMessageBox::information(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("All your plugins are already up to date."));
|
||||||
}
|
}
|
||||||
@@ -414,7 +422,8 @@ bool engineSelectDlg::parseVersionsFile(QString versions_file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
|
void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
|
||||||
qDebug("engineSelectDlg received %s", url.toLocal8Bit().data());
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
|
qDebug("engineSelectDlg received %s", qPrintable(url));
|
||||||
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||||
// Icon downloaded
|
// Icon downloaded
|
||||||
QImage fileIcon;
|
QImage fileIcon;
|
||||||
@@ -427,36 +436,37 @@ void engineSelectDlg::processDownloadedFile(QString url, QString filePath) {
|
|||||||
QFile icon(filePath);
|
QFile icon(filePath);
|
||||||
icon.open(QIODevice::ReadOnly);
|
icon.open(QIODevice::ReadOnly);
|
||||||
if(ICOHandler::canRead(&icon))
|
if(ICOHandler::canRead(&icon))
|
||||||
iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".ico";
|
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+id+".ico";
|
||||||
else
|
else
|
||||||
iconPath = misc::qBittorrentPath()+"search_engine"+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
iconPath = misc::searchEngineLocation()+QDir::separator()+"engines"+QDir::separator()+id+".png";
|
||||||
QFile::copy(filePath, iconPath);
|
QFile::copy(filePath, iconPath);
|
||||||
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
item->setData(ENGINE_NAME, Qt::DecorationRole, QVariant(QIcon(iconPath)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Delete tmp file
|
// Delete tmp file
|
||||||
QFile::remove(filePath);
|
misc::safeRemove(filePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(url.endsWith("versions.txt")) {
|
if(url.endsWith("versions.txt")) {
|
||||||
if(!parseVersionsFile(filePath)) {
|
if(!parseVersionsFile(filePath)) {
|
||||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, update server is temporarily unavailable."));
|
||||||
}
|
}
|
||||||
QFile::remove(filePath);
|
misc::safeRemove(filePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(url.endsWith(".py", Qt::CaseInsensitive)) {
|
if(url.endsWith(".py", Qt::CaseInsensitive)) {
|
||||||
QString plugin_name = url.split('/').last();
|
QString plugin_name = url.split('/').last();
|
||||||
plugin_name.replace(".py", "");
|
plugin_name.replace(".py", "");
|
||||||
installPlugin(filePath, plugin_name);
|
installPlugin(filePath, plugin_name);
|
||||||
QFile::remove(filePath);
|
misc::safeRemove(filePath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
|
void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
|
||||||
|
setCursor(QCursor(Qt::ArrowCursor));
|
||||||
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
if(url.endsWith("favicon.ico", Qt::CaseInsensitive)){
|
||||||
qDebug("Could not download favicon: %s, reason: %s", url.toLocal8Bit().data(), reason.toLocal8Bit().data());
|
qDebug("Could not download favicon: %s, reason: %s", qPrintable(url), qPrintable(reason));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(url.endsWith("versions.txt")) {
|
if(url.endsWith("versions.txt")) {
|
||||||
@@ -467,6 +477,6 @@ void engineSelectDlg::handleDownloadFailure(QString url, QString reason) {
|
|||||||
// a plugin update download has been failed
|
// a plugin update download has been failed
|
||||||
QString plugin_name = url.split('/').last();
|
QString plugin_name = url.split('/').last();
|
||||||
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
plugin_name.replace(".py", "", Qt::CaseInsensitive);
|
||||||
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name.toLocal8Bit().data()));
|
QMessageBox::warning(this, tr("Search plugin update")+" -- "+tr("qBittorrent"), tr("Sorry, %1 search plugin install failed.", "%1 is the name of the search engine").arg(plugin_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,13 +29,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <libtorrent/version.hpp>
|
||||||
#include "eventmanager.h"
|
#include "eventmanager.h"
|
||||||
#include "bittorrent.h"
|
#include "bittorrent.h"
|
||||||
|
#include "scannedfoldersmodel.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "proplistdelegate.h"
|
//#include "proplistdelegate.h"
|
||||||
#include "torrentpersistentdata.h"
|
#include "torrentpersistentdata.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QTranslator>
|
||||||
|
|
||||||
EventManager::EventManager(QObject *parent, Bittorrent *BTSession)
|
EventManager::EventManager(QObject *parent, Bittorrent *BTSession)
|
||||||
: QObject(parent), BTSession(BTSession)
|
: QObject(parent), BTSession(BTSession)
|
||||||
@@ -59,7 +62,7 @@ QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
|
|||||||
tracker["url"] = tracker_url;
|
tracker["url"] = tracker_url;
|
||||||
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
|
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
|
||||||
QString error_message = data.last_message.trimmed();
|
QString error_message = data.last_message.trimmed();
|
||||||
#ifdef LIBTORRENT_0_15
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
if(it->verified) {
|
if(it->verified) {
|
||||||
tracker["status"] = tr("Working");
|
tracker["status"] = tr("Working");
|
||||||
} else {
|
} else {
|
||||||
@@ -94,7 +97,7 @@ QList<QVariantMap> EventManager::getPropTrackersInfo(QString hash) const {
|
|||||||
QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
|
QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
|
||||||
QList<QVariantMap> files;
|
QList<QVariantMap> files;
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
if(!h.is_valid()) return files;
|
if(!h.is_valid() || !h.has_metadata()) return files;
|
||||||
std::vector<int> priorities = h.file_priorities();
|
std::vector<int> priorities = h.file_priorities();
|
||||||
std::vector<size_type> fp;
|
std::vector<size_type> fp;
|
||||||
h.file_progress(fp);
|
h.file_progress(fp);
|
||||||
@@ -103,73 +106,274 @@ QList<QVariantMap> EventManager::getPropFilesInfo(QString hash) const {
|
|||||||
int i=0;
|
int i=0;
|
||||||
for(fi=t.begin_files(); fi != t.end_files(); fi++) {
|
for(fi=t.begin_files(); fi != t.end_files(); fi++) {
|
||||||
QVariantMap file;
|
QVariantMap file;
|
||||||
if(h.num_files() == 1) {
|
QString path = QDir::cleanPath(misc::toQStringU(fi->path.string()));
|
||||||
file["name"] = h.name();
|
|
||||||
} else {
|
|
||||||
QString path = QDir::cleanPath(misc::toQString(fi->path.string()));
|
|
||||||
QString name = path.split('/').last();
|
QString name = path.split('/').last();
|
||||||
file["name"] = name;
|
file["name"] = name;
|
||||||
}
|
|
||||||
file["size"] = misc::friendlyUnit((double)fi->size);
|
file["size"] = misc::friendlyUnit((double)fi->size);
|
||||||
|
if(fi->size > 0)
|
||||||
file["progress"] = fp[i]/(double)fi->size;
|
file["progress"] = fp[i]/(double)fi->size;
|
||||||
|
else
|
||||||
|
file["progress"] = 1.; // Empty file...
|
||||||
file["priority"] = priorities[i];
|
file["priority"] = priorities[i];
|
||||||
|
if(i == 0)
|
||||||
|
file["is_seed"] = h.is_seed();
|
||||||
files << file;
|
files << file;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EventManager::setGlobalPreferences(QVariantMap m) const {
|
||||||
|
// UI
|
||||||
|
if(m.contains("locale")) {
|
||||||
|
QString locale = m["locale"].toString();
|
||||||
|
if(Preferences::getLocale() != locale) {
|
||||||
|
QTranslator *translator = new QTranslator;
|
||||||
|
if(translator->load(QString::fromUtf8(":/lang/qbittorrent_") + locale)){
|
||||||
|
qDebug("%s locale recognized, using translation.", qPrintable(locale));
|
||||||
|
}else{
|
||||||
|
qDebug("%s locale unrecognized, using default (en_GB).", qPrintable(locale));
|
||||||
|
}
|
||||||
|
qApp->installTranslator(translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences::setLocale(locale);
|
||||||
|
}
|
||||||
|
// Downloads
|
||||||
|
if(m.contains("save_path"))
|
||||||
|
Preferences::setSavePath(m["save_path"].toString());
|
||||||
|
if(m.contains("temp_path_enabled"))
|
||||||
|
Preferences::setTempPathEnabled(m["temp_path_enabled"].toBool());
|
||||||
|
if(m.contains("temp_path"))
|
||||||
|
Preferences::setTempPath(m["temp_path"].toString());
|
||||||
|
if(m.contains("scan_dirs") && m.contains("download_in_scan_dirs")) {
|
||||||
|
QVariantList download_at_path_tmp = m["download_in_scan_dirs"].toList();
|
||||||
|
QList<bool> download_at_path;
|
||||||
|
foreach(QVariant var, download_at_path_tmp) {
|
||||||
|
download_at_path << var.toBool();
|
||||||
|
}
|
||||||
|
QStringList old_folders = Preferences::getScanDirs();
|
||||||
|
QStringList new_folders = m["scan_dirs"].toStringList();
|
||||||
|
if(download_at_path.size() == new_folders.size()) {
|
||||||
|
Preferences::setScanDirs(new_folders);
|
||||||
|
Preferences::setDownloadInScanDirs(download_at_path);
|
||||||
|
foreach(const QString &old_folder, old_folders) {
|
||||||
|
// Update deleted folders
|
||||||
|
if(!new_folders.contains(old_folder)) {
|
||||||
|
BTSession->getScanFoldersModel()->removePath(old_folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
foreach(const QString &new_folder, new_folders) {
|
||||||
|
qDebug("New watched folder: %s", qPrintable(new_folder));
|
||||||
|
// Update new folders
|
||||||
|
if(!old_folders.contains(new_folder)) {
|
||||||
|
BTSession->getScanFoldersModel()->addPath(new_folder, download_at_path.at(i));
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(m.contains("export_dir"))
|
||||||
|
Preferences::setExportDir(m["export_dir"].toString());
|
||||||
|
if(m.contains("mail_notification_enabled"))
|
||||||
|
Preferences::setMailNotificationEnabled(m["mail_notification_enabled"].toBool());
|
||||||
|
if(m.contains("mail_notification_email"))
|
||||||
|
Preferences::setMailNotificationEmail(m["mail_notification_email"].toString());
|
||||||
|
if(m.contains("mail_notification_smtp"))
|
||||||
|
Preferences::setMailNotificationSMTP(m["mail_notification_smtp"].toString());
|
||||||
|
if(m.contains("autorun_enabled"))
|
||||||
|
Preferences::setAutoRunEnabled(m["autorun_enabled"].toBool());
|
||||||
|
if(m.contains("autorun_program"))
|
||||||
|
Preferences::setAutoRunProgram(m["autorun_program"].toString());
|
||||||
|
if(m.contains("preallocate_all"))
|
||||||
|
Preferences::preAllocateAllFiles(m["preallocate_all"].toBool());
|
||||||
|
if(m.contains("queueing_enabled"))
|
||||||
|
Preferences::setQueueingSystemEnabled(m["queueing_enabled"].toBool());
|
||||||
|
if(m.contains("max_active_downloads"))
|
||||||
|
Preferences::setMaxActiveDownloads(m["max_active_downloads"].toInt());
|
||||||
|
if(m.contains("max_active_torrents"))
|
||||||
|
Preferences::setMaxActiveTorrents(m["max_active_torrents"].toInt());
|
||||||
|
if(m.contains("max_active_uploads"))
|
||||||
|
Preferences::setMaxActiveUploads(m["max_active_uploads"].toInt());
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
if(m.contains("incomplete_files_ext"))
|
||||||
|
Preferences::useIncompleteFilesExtension(m["incomplete_files_ext"].toBool());
|
||||||
|
#endif
|
||||||
|
// Connection
|
||||||
|
if(m.contains("listen_port"))
|
||||||
|
Preferences::setSessionPort(m["listen_port"].toInt());
|
||||||
|
if(m.contains("upnp"))
|
||||||
|
Preferences::setUPnPEnabled(m["upnp"].toBool());
|
||||||
|
if(m.contains("natpmp"))
|
||||||
|
Preferences::setNATPMPEnabled(m["natpmp"].toBool());
|
||||||
|
if(m.contains("dl_limit"))
|
||||||
|
Preferences::setGlobalDownloadLimit(m["dl_limit"].toInt());
|
||||||
|
if(m.contains("up_limit"))
|
||||||
|
Preferences::setGlobalUploadLimit(m["up_limit"].toInt());
|
||||||
|
if(m.contains("max_connec"))
|
||||||
|
Preferences::setMaxConnecs(m["max_connec"].toInt());
|
||||||
|
if(m.contains("max_connec_per_torrent"))
|
||||||
|
Preferences::setMaxConnecsPerTorrent(m["max_connec_per_torrent"].toInt());
|
||||||
|
if(m.contains("max_uploads_per_torrent"))
|
||||||
|
Preferences::setMaxUploadsPerTorrent(m["max_uploads_per_torrent"].toInt());
|
||||||
|
// Bittorrent
|
||||||
|
if(m.contains("dht"))
|
||||||
|
Preferences::setDHTEnabled(m["dht"].toBool());
|
||||||
|
if(m.contains("dhtSameAsBT"))
|
||||||
|
Preferences::setDHTPortSameAsBT(m["dhtSameAsBT"].toBool());
|
||||||
|
if(m.contains("dht_port"))
|
||||||
|
Preferences::setDHTPort(m["dht_port"].toInt());
|
||||||
|
if(m.contains("pex"))
|
||||||
|
Preferences::setPeXEnabled(m["pex"].toBool());
|
||||||
|
qDebug("Pex support: %d", (int)m["pex"].toBool());
|
||||||
|
if(m.contains("lsd"))
|
||||||
|
Preferences::setLSDEnabled(m["lsd"].toBool());
|
||||||
|
if(m.contains("encryption"))
|
||||||
|
Preferences::setEncryptionSetting(m["encryption"].toInt());
|
||||||
|
// Proxy
|
||||||
|
if(m.contains("proxy_type"))
|
||||||
|
Preferences::setPeerProxyType(m["proxy_type"].toInt());
|
||||||
|
if(m.contains("proxy_ip"))
|
||||||
|
Preferences::setPeerProxyIp(m["proxy_ip"].toString());
|
||||||
|
if(m.contains("proxy_port"))
|
||||||
|
Preferences::setPeerProxyPort(m["proxy_port"].toUInt());
|
||||||
|
if(m.contains("proxy_auth_enabled"))
|
||||||
|
Preferences::setPeerProxyAuthEnabled(m["proxy_auth_enabled"].toBool());
|
||||||
|
if(m.contains("proxy_username"))
|
||||||
|
Preferences::setPeerProxyUsername(m["proxy_username"].toString());
|
||||||
|
if(m.contains("proxy_password"))
|
||||||
|
Preferences::setPeerProxyPassword(m["proxy_password"].toString());
|
||||||
|
if(m.contains("http_proxy_type"))
|
||||||
|
Preferences::setHTTPProxyType(m["http_proxy_type"].toInt());
|
||||||
|
if(m.contains("http_proxy_ip"))
|
||||||
|
Preferences::setHTTPProxyIp(m["http_proxy_ip"].toString());
|
||||||
|
if(m.contains("http_proxy_port"))
|
||||||
|
Preferences::setHTTPProxyPort(m["http_proxy_port"].toUInt());
|
||||||
|
if(m.contains("http_proxy_auth_enabled"))
|
||||||
|
Preferences::setHTTPProxyAuthEnabled(m["http_proxy_auth_enabled"].toBool());
|
||||||
|
if(m.contains("http_proxy_username"))
|
||||||
|
Preferences::setHTTPProxyUsername(m["http_proxy_username"].toString());
|
||||||
|
if(m.contains("http_proxy_password"))
|
||||||
|
Preferences::setHTTPProxyPassword(m["http_proxy_password"].toString());
|
||||||
|
// IP Filter
|
||||||
|
if(m.contains("ip_filter_enabled"))
|
||||||
|
Preferences::setFilteringEnabled(m["ip_filter_enabled"].toBool());
|
||||||
|
if(m.contains("ip_filter_path"))
|
||||||
|
Preferences::setFilter(m["ip_filter_path"].toString());
|
||||||
|
// Web UI
|
||||||
|
if(m.contains("web_ui_port"))
|
||||||
|
Preferences::setWebUiPort(m["web_ui_port"].toUInt());
|
||||||
|
if(m.contains("web_ui_username"))
|
||||||
|
Preferences::setWebUiUsername(m["web_ui_username"].toString());
|
||||||
|
if(m.contains("web_ui_password"))
|
||||||
|
Preferences::setWebUiPassword(m["web_ui_password"].toString());
|
||||||
|
// Reload preferences
|
||||||
|
BTSession->configureSession();
|
||||||
|
}
|
||||||
|
|
||||||
QVariantMap EventManager::getGlobalPreferences() const {
|
QVariantMap EventManager::getGlobalPreferences() const {
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
|
// UI
|
||||||
|
data["locale"] = Preferences::getLocale();
|
||||||
|
// Downloads
|
||||||
|
data["save_path"] = Preferences::getSavePath();
|
||||||
|
data["temp_path_enabled"] = Preferences::isTempPathEnabled();
|
||||||
|
data["temp_path"] = Preferences::getTempPath();
|
||||||
|
data["scan_dirs"] = Preferences::getScanDirs();
|
||||||
|
QVariantList var_list;
|
||||||
|
foreach(bool b, Preferences::getDownloadInScanDirs()) {
|
||||||
|
var_list << b;
|
||||||
|
}
|
||||||
|
data["download_in_scan_dirs"] = var_list;
|
||||||
|
data["export_dir_enabled"] = Preferences::isTorrentExportEnabled();
|
||||||
|
data["export_dir"] = Preferences::getExportDir();
|
||||||
|
data["mail_notification_enabled"] = Preferences::isMailNotificationEnabled();
|
||||||
|
data["mail_notification_email"] = Preferences::getMailNotificationEmail();
|
||||||
|
data["mail_notification_smtp"] = Preferences::getMailNotificationSMTP();
|
||||||
|
data["autorun_enabled"] = Preferences::isAutoRunEnabled();
|
||||||
|
data["autorun_program"] = Preferences::getAutoRunProgram();
|
||||||
|
data["preallocate_all"] = Preferences::preAllocateAllFiles();
|
||||||
|
data["queueing_enabled"] = Preferences::isQueueingSystemEnabled();
|
||||||
|
data["max_active_downloads"] = Preferences::getMaxActiveDownloads();
|
||||||
|
data["max_active_torrents"] = Preferences::getMaxActiveTorrents();
|
||||||
|
data["max_active_uploads"] = Preferences::getMaxActiveUploads();
|
||||||
|
#if LIBTORRENT_VERSION_MINOR > 14
|
||||||
|
data["incomplete_files_ext"] = Preferences::useIncompleteFilesExtension();
|
||||||
|
#endif
|
||||||
|
// Connection
|
||||||
|
data["listen_port"] = Preferences::getSessionPort();
|
||||||
|
data["upnp"] = Preferences::isUPnPEnabled();
|
||||||
|
data["natpmp"] = Preferences::isNATPMPEnabled();
|
||||||
data["dl_limit"] = Preferences::getGlobalDownloadLimit();
|
data["dl_limit"] = Preferences::getGlobalDownloadLimit();
|
||||||
data["up_limit"] = Preferences::getGlobalUploadLimit();
|
data["up_limit"] = Preferences::getGlobalUploadLimit();
|
||||||
data["dht"] = Preferences::isDHTEnabled();
|
|
||||||
data["max_connec"] = Preferences::getMaxConnecs();
|
data["max_connec"] = Preferences::getMaxConnecs();
|
||||||
data["max_connec_per_torrent"] = Preferences::getMaxConnecsPerTorrent();
|
data["max_connec_per_torrent"] = Preferences::getMaxConnecsPerTorrent();
|
||||||
data["max_uploads_per_torrent"] = Preferences::getMaxUploadsPerTorrent();
|
data["max_uploads_per_torrent"] = Preferences::getMaxUploadsPerTorrent();
|
||||||
|
// Bittorrent
|
||||||
|
data["dht"] = Preferences::isDHTEnabled();
|
||||||
|
data["dhtSameAsBT"] = Preferences::isDHTPortSameAsBT();
|
||||||
|
data["dht_port"] = Preferences::getDHTPort();
|
||||||
|
data["pex"] = Preferences::isPeXEnabled();
|
||||||
|
data["lsd"] = Preferences::isLSDEnabled();
|
||||||
|
data["encryption"] = Preferences::getEncryptionSetting();
|
||||||
|
// Proxy
|
||||||
|
data["proxy_type"] = Preferences::getPeerProxyType();
|
||||||
|
data["proxy_ip"] = Preferences::getPeerProxyIp();
|
||||||
|
data["proxy_port"] = Preferences::getPeerProxyPort();
|
||||||
|
data["proxy_auth_enabled"] = Preferences::isPeerProxyAuthEnabled();
|
||||||
|
data["proxy_username"] = Preferences::getPeerProxyUsername();
|
||||||
|
data["proxy_password"] = Preferences::getPeerProxyPassword();
|
||||||
|
data["http_proxy_type"] = Preferences::getHTTPProxyType();
|
||||||
|
data["http_proxy_ip"] = Preferences::getHTTPProxyIp();
|
||||||
|
data["http_proxy_port"] = Preferences::getHTTPProxyPort();
|
||||||
|
data["http_proxy_auth_enabled"] = Preferences::isHTTPProxyAuthEnabled();
|
||||||
|
data["http_proxy_username"] = Preferences::getHTTPProxyUsername();
|
||||||
|
data["http_proxy_password"] = Preferences::getHTTPProxyPassword();
|
||||||
|
// IP Filter
|
||||||
|
data["ip_filter_enabled"] = Preferences::isFilteringEnabled();
|
||||||
|
data["ip_filter_path"] = Preferences::getFilter();
|
||||||
|
// Web UI
|
||||||
|
data["web_ui_port"] = Preferences::getWebUiPort();
|
||||||
|
data["web_ui_username"] = Preferences::getWebUiUsername();
|
||||||
|
data["web_ui_password"] = Preferences::getWebUiPassword();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
|
QVariantMap EventManager::getPropGeneralInfo(QString hash) const {
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
if(h.is_valid()) {
|
if(h.is_valid() && h.has_metadata()) {
|
||||||
// Save path
|
// Save path
|
||||||
data["save_path"] = TorrentPersistentData::getSavePath(hash);
|
QString p = TorrentPersistentData::getSavePath(hash);
|
||||||
|
if(p.isEmpty()) p = h.save_path();
|
||||||
|
data["save_path"] = p;
|
||||||
// Creation date
|
// Creation date
|
||||||
data["creation_date"] = h.creation_date();
|
data["creation_date"] = h.creation_date();
|
||||||
// Comment
|
// Comment
|
||||||
data["comment"] = h.comment();
|
data["comment"] = h.comment();
|
||||||
data["total_wasted"] = misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes());
|
data["total_wasted"] = QVariant(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes()));
|
||||||
data["total_uploaded"] = misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")";
|
data["total_uploaded"] = QVariant(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")");
|
||||||
data["total_downloaded"] = misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")";
|
data["total_downloaded"] = QVariant(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")");
|
||||||
if(h.upload_limit() <= 0)
|
if(h.upload_limit() <= 0)
|
||||||
data["up_limit"] = QString::fromUtf8("∞");
|
data["up_limit"] = QString::fromUtf8("∞");
|
||||||
else
|
else
|
||||||
data["up_limit"] = misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)");
|
data["up_limit"] = QVariant(misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
|
||||||
if(h.download_limit() <= 0)
|
if(h.download_limit() <= 0)
|
||||||
data["dl_limit"] = QString::fromUtf8("∞");
|
data["dl_limit"] = QString::fromUtf8("∞");
|
||||||
else
|
else
|
||||||
data["dl_limit"] = misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)");
|
data["dl_limit"] = QVariant(misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
|
||||||
QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
|
QString elapsed_txt = misc::userFriendlyDuration(h.active_time());
|
||||||
if(h.is_seed()) {
|
if(h.is_seed()) {
|
||||||
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
|
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")";
|
||||||
}
|
}
|
||||||
data["time_elapsed"] = elapsed_txt;
|
data["time_elapsed"] = elapsed_txt;
|
||||||
data["nb_connections"] = QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")";
|
data["nb_connections"] = QVariant(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")");
|
||||||
// Update ratio info
|
// Update ratio info
|
||||||
float ratio;
|
double ratio = BTSession->getRealRatio(h.hash());
|
||||||
if(h.total_payload_download() == 0){
|
if(ratio > 100.)
|
||||||
if(h.total_payload_upload() == 0)
|
data["share_ratio"] = QString::fromUtf8("∞");
|
||||||
ratio = 1.;
|
|
||||||
else
|
else
|
||||||
ratio = 10.; // Max ratio
|
|
||||||
}else{
|
|
||||||
ratio = (double)h.total_payload_upload()/(double)h.total_payload_download();
|
|
||||||
if(ratio > 10.){
|
|
||||||
ratio = 10.;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1));
|
data["share_ratio"] = QString(QByteArray::number(ratio, 'f', 1));
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
@@ -191,10 +395,14 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
|
|||||||
QVariantMap event;
|
QVariantMap event;
|
||||||
event["eta"] = QVariant(QString::fromUtf8("∞"));
|
event["eta"] = QVariant(QString::fromUtf8("∞"));
|
||||||
if(h.is_paused()) {
|
if(h.is_paused()) {
|
||||||
|
if(h.has_error()) {
|
||||||
|
event["state"] = QVariant("error");
|
||||||
|
} else {
|
||||||
if(h.is_seed())
|
if(h.is_seed())
|
||||||
event["state"] = QVariant("pausedUP");
|
event["state"] = QVariant("pausedUP");
|
||||||
else
|
else
|
||||||
event["state"] = QVariant("pausedDL");
|
event["state"] = QVariant("pausedDL");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if(BTSession->isQueueingEnabled() && h.is_queued()) {
|
if(BTSession->isQueueingEnabled() && h.is_queued()) {
|
||||||
if(h.is_seed())
|
if(h.is_seed())
|
||||||
@@ -207,7 +415,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
|
|||||||
case torrent_status::finished:
|
case torrent_status::finished:
|
||||||
case torrent_status::seeding:
|
case torrent_status::seeding:
|
||||||
if(h.upload_payload_rate() > 0) {
|
if(h.upload_payload_rate() > 0) {
|
||||||
event["state"] = QVariant("seeding");
|
event["state"] = QVariant("uploading");
|
||||||
} else {
|
} else {
|
||||||
event["state"] = QVariant("stalledUP");
|
event["state"] = QVariant("stalledUP");
|
||||||
}
|
}
|
||||||
@@ -238,7 +446,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
|
|||||||
}
|
}
|
||||||
event["name"] = QVariant(h.name());
|
event["name"] = QVariant(h.name());
|
||||||
event["size"] = QVariant(misc::friendlyUnit(h.actual_size()));
|
event["size"] = QVariant(misc::friendlyUnit(h.actual_size()));
|
||||||
event["progress"] = QVariant(h.progress());
|
event["progress"] = QVariant((double)h.progress());
|
||||||
event["dlspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.download_payload_rate())));
|
event["dlspeed"] = QVariant(tr("%1/s", "e.g. 120 KiB/s").arg(misc::friendlyUnit(h.download_payload_rate())));
|
||||||
if(BTSession->isQueueingEnabled()) {
|
if(BTSession->isQueueingEnabled()) {
|
||||||
if(h.queue_position() >= 0)
|
if(h.queue_position() >= 0)
|
||||||
@@ -260,7 +468,7 @@ void EventManager::modifiedTorrent(QTorrentHandle h)
|
|||||||
event["seed"] = QVariant(h.is_seed());
|
event["seed"] = QVariant(h.is_seed());
|
||||||
double ratio = BTSession->getRealRatio(hash);
|
double ratio = BTSession->getRealRatio(hash);
|
||||||
if(ratio > 100.)
|
if(ratio > 100.)
|
||||||
QString::fromUtf8("∞");
|
event["ratio"] = QString::fromUtf8("∞");
|
||||||
else
|
else
|
||||||
event["ratio"] = QVariant(QString::number(ratio, 'f', 1));
|
event["ratio"] = QVariant(QString::number(ratio, 'f', 1));
|
||||||
event["hash"] = QVariant(hash);
|
event["hash"] = QVariant(hash);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class EventManager : public QObject
|
|||||||
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
|
QList<QVariantMap> getPropTrackersInfo(QString hash) const;
|
||||||
QList<QVariantMap> getPropFilesInfo(QString hash) const;
|
QList<QVariantMap> getPropFilesInfo(QString hash) const;
|
||||||
QVariantMap getGlobalPreferences() const;
|
QVariantMap getGlobalPreferences() const;
|
||||||
|
void setGlobalPreferences(QVariantMap m) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addedTorrent(QTorrentHandle& h);
|
void addedTorrent(QTorrentHandle& h);
|
||||||
|
|||||||
@@ -28,14 +28,19 @@ public:
|
|||||||
setColumnCount(1);
|
setColumnCount(1);
|
||||||
QTreeWidgetItem *___qtreewidgetitem = headerItem();
|
QTreeWidgetItem *___qtreewidgetitem = headerItem();
|
||||||
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
|
___qtreewidgetitem->setText(0, QApplication::translate("RSS", "RSS feeds", 0, QApplication::UnicodeUTF8));
|
||||||
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
|
|
||||||
unread_item = new QTreeWidgetItem(this);
|
unread_item = new QTreeWidgetItem(this);
|
||||||
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
|
unread_item->setText(0, tr("Unread") + QString::fromUtf8(" (") + QString::number(rssmanager->getNbUnRead(), 10)+ QString(")"));
|
||||||
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
|
unread_item->setData(0,Qt::DecorationRole, QVariant(QIcon(":/Icons/oxygen/mail-folder-inbox.png")));
|
||||||
itemAdded(unread_item, rssmanager);
|
itemAdded(unread_item, rssmanager);
|
||||||
|
connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
|
||||||
setCurrentItem(unread_item);
|
setCurrentItem(unread_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~FeedList() {
|
||||||
|
disconnect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(updateCurrentFeed(QTreeWidgetItem*)));
|
||||||
|
delete unread_item;
|
||||||
|
}
|
||||||
|
|
||||||
void itemAdded(QTreeWidgetItem *item, RssFile* file) {
|
void itemAdded(QTreeWidgetItem *item, RssFile* file) {
|
||||||
mapping[item] = file;
|
mapping[item] = file;
|
||||||
if(file->getType() == RssFile::STREAM) {
|
if(file->getType() == RssFile::STREAM) {
|
||||||
@@ -43,10 +48,16 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void itemRemoved(QTreeWidgetItem *item) {
|
void itemAboutToBeRemoved(QTreeWidgetItem *item) {
|
||||||
RssFile* file = mapping.take(item);
|
RssFile* file = mapping.take(item);
|
||||||
if(file->getType() == RssFile::STREAM)
|
if(file->getType() == RssFile::STREAM) {
|
||||||
feeds_items.remove(file->getID());
|
feeds_items.remove(file->getID());
|
||||||
|
} else {
|
||||||
|
QList<RssStream*> feeds = ((RssFolder*)file)->getAllFeeds();
|
||||||
|
foreach(RssStream* feed, feeds) {
|
||||||
|
feeds_items.remove(feed->getID());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasFeed(QString url) const {
|
bool hasFeed(QString url) const {
|
||||||
@@ -65,7 +76,7 @@ public:
|
|||||||
QStringList path;
|
QStringList path;
|
||||||
if(item) {
|
if(item) {
|
||||||
if(item->parent())
|
if(item->parent())
|
||||||
path.append(getItemPath(item->parent()));
|
path << getItemPath(item->parent());
|
||||||
path.append(getRSSItem(item)->getID());
|
path.append(getRSSItem(item)->getID());
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
@@ -87,7 +98,7 @@ public:
|
|||||||
if(getItemType(item) == RssFile::FOLDER && item->isExpanded()) {
|
if(getItemType(item) == RssFile::FOLDER && item->isExpanded()) {
|
||||||
QList<QTreeWidgetItem*> open_subfolders = getAllOpenFolders(item);
|
QList<QTreeWidgetItem*> open_subfolders = getAllOpenFolders(item);
|
||||||
if(!open_subfolders.empty()) {
|
if(!open_subfolders.empty()) {
|
||||||
open_folders.append(open_subfolders);
|
open_folders << open_subfolders;
|
||||||
} else {
|
} else {
|
||||||
open_folders << item;
|
open_folders << item;
|
||||||
}
|
}
|
||||||
@@ -104,26 +115,26 @@ public:
|
|||||||
if(getItemType(item) == RssFile::STREAM) {
|
if(getItemType(item) == RssFile::STREAM) {
|
||||||
feeds << item;
|
feeds << item;
|
||||||
} else {
|
} else {
|
||||||
feeds.append(getAllFeedItems(item));
|
feeds << getAllFeedItems(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return feeds;
|
return feeds;
|
||||||
}
|
}
|
||||||
|
|
||||||
RssFile* getRSSItem(QTreeWidgetItem *item) const {
|
RssFile* getRSSItem(QTreeWidgetItem *item) const {
|
||||||
return mapping[item];
|
return mapping.value(item, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
RssFile::FileType getItemType(QTreeWidgetItem *item) const {
|
RssFile::FileType getItemType(QTreeWidgetItem *item) const {
|
||||||
return mapping[item]->getType();
|
return mapping.value(item)->getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getItemID(QTreeWidgetItem *item) const {
|
QString getItemID(QTreeWidgetItem *item) const {
|
||||||
return mapping[item]->getID();
|
return mapping.value(item)->getID();
|
||||||
}
|
}
|
||||||
|
|
||||||
QTreeWidgetItem* getTreeItemFromUrl(QString url) const{
|
QTreeWidgetItem* getTreeItemFromUrl(QString url) const{
|
||||||
return feeds_items[url];
|
return feeds_items.value(url, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
RssStream* getRSSItemFromUrl(QString url) const {
|
RssStream* getRSSItemFromUrl(QString url) const {
|
||||||
@@ -144,7 +155,9 @@ signals:
|
|||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void updateCurrentFeed(QTreeWidgetItem* new_item) {
|
void updateCurrentFeed(QTreeWidgetItem* new_item) {
|
||||||
if((new_item && getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
|
if(!new_item) return;
|
||||||
|
if(!mapping.contains(new_item)) return;
|
||||||
|
if((getItemType(new_item) == RssFile::STREAM) || new_item == unread_item)
|
||||||
current_feed = new_item;
|
current_feed = new_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,6 @@
|
|||||||
#define FEEDDOWNLOADER_H
|
#define FEEDDOWNLOADER_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QHash>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QListWidgetItem>
|
#include <QListWidgetItem>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
@@ -46,6 +44,15 @@
|
|||||||
|
|
||||||
#include "bittorrent.h"
|
#include "bittorrent.h"
|
||||||
#include "ui_feeddownloader.h"
|
#include "ui_feeddownloader.h"
|
||||||
|
#include "qinisettings.h"
|
||||||
|
|
||||||
|
#if QT_VERSION >= 0x040500
|
||||||
|
#include <QHash>
|
||||||
|
#else
|
||||||
|
#include <QMap>
|
||||||
|
#define QHash QMap
|
||||||
|
#define toHash toMap
|
||||||
|
#endif
|
||||||
|
|
||||||
class FeedFilter: public QHash<QString, QVariant> {
|
class FeedFilter: public QHash<QString, QVariant> {
|
||||||
private:
|
private:
|
||||||
@@ -60,14 +67,16 @@ public:
|
|||||||
foreach(const QString& token, match_tokens) {
|
foreach(const QString& token, match_tokens) {
|
||||||
if(token.isEmpty() || token == "")
|
if(token.isEmpty() || token == "")
|
||||||
continue;
|
continue;
|
||||||
QRegExp reg(token, Qt::CaseInsensitive);
|
QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard);
|
||||||
|
//reg.setMinimal(false);
|
||||||
if(reg.indexIn(s) < 0) return false;
|
if(reg.indexIn(s) < 0) return false;
|
||||||
}
|
}
|
||||||
|
qDebug("Checking not matching tokens");
|
||||||
// Checking not matching
|
// Checking not matching
|
||||||
QStringList notmatch_tokens = getNotMatchingTokens();
|
QStringList notmatch_tokens = getNotMatchingTokens();
|
||||||
foreach(const QString& token, notmatch_tokens) {
|
foreach(const QString& token, notmatch_tokens) {
|
||||||
if(token.isEmpty()) continue;
|
if(token.isEmpty()) continue;
|
||||||
QRegExp reg(token, Qt::CaseInsensitive);
|
QRegExp reg(token, Qt::CaseInsensitive, QRegExp::Wildcard);
|
||||||
if(reg.indexIn(s) > -1) return false;
|
if(reg.indexIn(s) > -1) return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -96,7 +105,7 @@ public:
|
|||||||
|
|
||||||
QStringList getNotMatchingTokens() const {
|
QStringList getNotMatchingTokens() const {
|
||||||
QString notmatching = this->value("not", "").toString();
|
QString notmatching = this->value("not", "").toString();
|
||||||
return notmatching.split(" ");
|
return notmatching.split(QRegExp("[\\s|]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getNotMatchingTokens_str() const {
|
QString getNotMatchingTokens_str() const {
|
||||||
@@ -159,20 +168,20 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isDownloadingEnabled() const {
|
bool isDownloadingEnabled() const {
|
||||||
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
||||||
return feeds_w_downloader.value(feed_url, false).toBool();
|
return feeds_w_downloader.value(feed_url, false).toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDownloadingEnabled(bool enabled) {
|
void setDownloadingEnabled(bool enabled) {
|
||||||
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
QHash<QString, QVariant> feeds_w_downloader = qBTRSS.value("downloader_on", QHash<QString, QVariant>()).toHash();
|
||||||
feeds_w_downloader[feed_url] = enabled;
|
feeds_w_downloader[feed_url] = enabled;
|
||||||
qBTRSS.setValue("downloader_on", feeds_w_downloader);
|
qBTRSS.setValue("downloader_on", feeds_w_downloader);
|
||||||
}
|
}
|
||||||
|
|
||||||
static FeedFilters getFeedFilters(QString url) {
|
static FeedFilters getFeedFilters(QString url) {
|
||||||
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
||||||
return FeedFilters(url, all_feeds_filters.value(url, QHash<QString, QVariant>()).toHash());
|
return FeedFilters(url, all_feeds_filters.value(url, QHash<QString, QVariant>()).toHash());
|
||||||
}
|
}
|
||||||
@@ -213,9 +222,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void save() {
|
void save() {
|
||||||
QSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
QIniSettings qBTRSS("qBittorrent", "qBittorrent-rss");
|
||||||
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
QHash<QString, QVariant> all_feeds_filters = qBTRSS.value("feed_filters", QHash<QString, QVariant>()).toHash();
|
||||||
qDebug("Saving filters for feed: %s (%d filters)", feed_url.toLocal8Bit().data(), (*this).size());
|
qDebug("Saving filters for feed: %s (%d filters)", qPrintable(feed_url), (*this).size());
|
||||||
all_feeds_filters[feed_url] = *this;
|
all_feeds_filters[feed_url] = *this;
|
||||||
qBTRSS.setValue("feed_filters", all_feeds_filters);
|
qBTRSS.setValue("feed_filters", all_feeds_filters);
|
||||||
}
|
}
|
||||||
@@ -260,6 +269,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
~FeedDownloaderDlg() {
|
~FeedDownloaderDlg() {
|
||||||
|
if(enableDl_cb->isChecked())
|
||||||
|
emit filteringEnabled();
|
||||||
// Make sure we save everything
|
// Make sure we save everything
|
||||||
saveCurrentFilterSettings();
|
saveCurrentFilterSettings();
|
||||||
filters.save();
|
filters.save();
|
||||||
@@ -280,6 +291,17 @@ protected slots:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_browse_button_clicked() {
|
||||||
|
QString default_path = savepath_line->text();
|
||||||
|
if(default_path.isEmpty() || !QDir(default_path).exists()) {
|
||||||
|
default_path = QDir::homePath();
|
||||||
|
}
|
||||||
|
QString dir = QFileDialog::getExistingDirectory(this, tr("Choose save path"), QDir::homePath());
|
||||||
|
if(!dir.isNull() && QDir(dir).exists()) {
|
||||||
|
savepath_line->setText(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void fillFiltersList() {
|
void fillFiltersList() {
|
||||||
// Fill filter list
|
// Fill filter list
|
||||||
foreach(QString filter_name, filters.names()) {
|
foreach(QString filter_name, filters.names()) {
|
||||||
@@ -356,6 +378,8 @@ protected slots:
|
|||||||
QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use."));
|
QMessageBox::warning(0, tr("Invalid filter name"), tr("This filter name is already in use."));
|
||||||
}
|
}
|
||||||
}while(!validated);
|
}while(!validated);
|
||||||
|
// Save the current filter
|
||||||
|
saveCurrentFilterSettings();
|
||||||
// Rename the filter
|
// Rename the filter
|
||||||
filters.rename(current_name, new_name);
|
filters.rename(current_name, new_name);
|
||||||
if(selected_filter == current_name)
|
if(selected_filter == current_name)
|
||||||
@@ -427,7 +451,11 @@ protected slots:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void on_testButton_clicked(bool) {
|
void on_testButton_clicked(bool) {
|
||||||
if(selected_filter.isEmpty()) return;
|
test_res_lbl->clear();
|
||||||
|
if(selected_filter.isEmpty()) {
|
||||||
|
qDebug("No filter is selected!!!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
QString s = test_line->text().trimmed();
|
QString s = test_line->text().trimmed();
|
||||||
if(s.isEmpty()) {
|
if(s.isEmpty()) {
|
||||||
QMessageBox::warning(0, tr("Filter testing error"), tr("Please specify a test torrent name."));
|
QMessageBox::warning(0, tr("Filter testing error"), tr("Please specify a test torrent name."));
|
||||||
@@ -465,16 +493,22 @@ protected slots:
|
|||||||
// Append file extension
|
// Append file extension
|
||||||
if(!destination.endsWith(".filters"))
|
if(!destination.endsWith(".filters"))
|
||||||
destination += ".filters";
|
destination += ".filters";
|
||||||
if(QFile::exists(destination)) {
|
/*if(QFile::exists(destination)) {
|
||||||
int ret = QMessageBox::question(0, tr("Overwriting confirmation"), tr("Are you sure you want to overwrite existing file?"), QMessageBox::Yes|QMessageBox::No);
|
int ret = QMessageBox::question(0, tr("Overwriting confirmation"), tr("Are you sure you want to overwrite existing file?"), QMessageBox::Yes|QMessageBox::No);
|
||||||
if(ret != QMessageBox::Yes) return;
|
if(ret != QMessageBox::Yes) return;
|
||||||
}
|
}*/
|
||||||
if(filters.serialize(destination))
|
if(filters.serialize(destination))
|
||||||
QMessageBox::information(0, tr("Export successful"), tr("Filters export was successful."));
|
QMessageBox::information(0, tr("Export successful"), tr("Filters export was successful."));
|
||||||
else
|
else
|
||||||
QMessageBox::warning(0, tr("Export failure"), tr("Filters could not be exported due to an I/O error."));
|
QMessageBox::warning(0, tr("Export failure"), tr("Filters could not be exported due to an I/O error."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void filteringEnabled();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#undef QHash
|
||||||
|
#undef toHash
|
||||||
|
|
||||||
#endif // FEEDDOWNLOADER_H
|
#endif // FEEDDOWNLOADER_H
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
#define FILESYSTEMWATCHER_H
|
#define FILESYSTEMWATCHER_H
|
||||||
|
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
#ifndef Q_WS_WIN
|
#ifndef Q_WS_WIN
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDir>
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
@@ -34,12 +34,14 @@
|
|||||||
class FileSystemWatcher: public QFileSystemWatcher {
|
class FileSystemWatcher: public QFileSystemWatcher {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
#ifndef Q_WS_WIN
|
|
||||||
private:
|
private:
|
||||||
QDir watched_folder;
|
#ifndef Q_WS_WIN
|
||||||
|
QList<QDir> watched_folders;
|
||||||
QPointer<QTimer> watch_timer;
|
QPointer<QTimer> watch_timer;
|
||||||
|
#endif
|
||||||
QStringList filters;
|
QStringList filters;
|
||||||
|
|
||||||
|
#ifndef Q_WS_WIN
|
||||||
protected:
|
protected:
|
||||||
bool isNetworkFileSystem(QString path) {
|
bool isNetworkFileSystem(QString path) {
|
||||||
QString file = path;
|
QString file = path;
|
||||||
@@ -47,10 +49,10 @@ protected:
|
|||||||
file += QDir::separator();
|
file += QDir::separator();
|
||||||
file += ".";
|
file += ".";
|
||||||
struct statfs buf;
|
struct statfs buf;
|
||||||
if(!statfs(file.toLocal8Bit().data(), &buf)) {
|
if(!statfs(file.toLocal8Bit().constData(), &buf)) {
|
||||||
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC);
|
return (buf.f_type == (long)CIFS_MAGIC_NUMBER || buf.f_type == (long)NFS_SUPER_MAGIC);
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Error: statfs() call failed for " << file.toLocal8Bit().data() << ". Supposing it is a local folder..." << std::endl;
|
std::cerr << "Error: statfs() call failed for " << qPrintable(file) << ". Supposing it is a local folder..." << std::endl;
|
||||||
switch(errno) {
|
switch(errno) {
|
||||||
case EACCES:
|
case EACCES:
|
||||||
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
std::cerr << "Search permission is denied for a component of the path prefix of the path" << std::endl;
|
||||||
@@ -98,13 +100,7 @@ protected:
|
|||||||
public:
|
public:
|
||||||
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
|
FileSystemWatcher(QObject *parent): QFileSystemWatcher(parent) {
|
||||||
filters << "*.torrent";
|
filters << "*.torrent";
|
||||||
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder()));
|
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanLocalFolder(QString)));
|
||||||
}
|
|
||||||
|
|
||||||
FileSystemWatcher(QString path, QObject *parent): QFileSystemWatcher(parent) {
|
|
||||||
filters << "*.torrent";
|
|
||||||
connect(this, SIGNAL(directoryChanged(QString)), this, SLOT(scanFolder()));
|
|
||||||
addPath(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~FileSystemWatcher() {
|
~FileSystemWatcher() {
|
||||||
@@ -115,33 +111,40 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
QStringList directories() const {
|
QStringList directories() const {
|
||||||
|
QStringList dirs;
|
||||||
#ifndef Q_WS_WIN
|
#ifndef Q_WS_WIN
|
||||||
if(watch_timer)
|
if(watch_timer) {
|
||||||
return QStringList(watched_folder.path());
|
foreach (const QDir &dir, watched_folders)
|
||||||
|
dirs << dir.canonicalPath();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return QFileSystemWatcher::directories();
|
dirs << QFileSystemWatcher::directories();
|
||||||
|
return dirs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPath(const QString & path) {
|
void addPath(const QString & path) {
|
||||||
#ifndef Q_WS_WIN
|
#ifndef Q_WS_WIN
|
||||||
watched_folder = QDir(path);
|
QDir dir(path);
|
||||||
if(!watched_folder.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(isNetworkFileSystem(path)) {
|
||||||
// Network mode
|
// Network mode
|
||||||
Q_ASSERT(!watch_timer);
|
qDebug("Network folder detected: %s", qPrintable(path));
|
||||||
qDebug("Network folder detected: %s", path.toLocal8Bit().data());
|
|
||||||
qDebug("Using file polling mode instead of inotify...");
|
qDebug("Using file polling mode instead of inotify...");
|
||||||
|
watched_folders << dir;
|
||||||
// Set up the watch timer
|
// Set up the watch timer
|
||||||
|
if (!watch_timer) {
|
||||||
watch_timer = new QTimer(this);
|
watch_timer = new QTimer(this);
|
||||||
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanFolder()));
|
connect(watch_timer, SIGNAL(timeout()), this, SLOT(scanNetworkFolders()));
|
||||||
watch_timer->start(5000); // 5 sec
|
watch_timer->start(5000); // 5 sec
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
#endif
|
#endif
|
||||||
// Normal mode
|
// Normal mode
|
||||||
qDebug("FS Watching is watching %s in normal mode", path.toLocal8Bit().data());
|
qDebug("FS Watching is watching %s in normal mode", qPrintable(path));
|
||||||
QFileSystemWatcher::addPath(path);
|
QFileSystemWatcher::addPath(path);
|
||||||
scanFolder();
|
scanLocalFolder(path);
|
||||||
#ifndef Q_WS_WIN
|
#ifndef Q_WS_WIN
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -149,38 +152,65 @@ public:
|
|||||||
|
|
||||||
void removePath(const QString & path) {
|
void removePath(const QString & path) {
|
||||||
#ifndef Q_WS_WIN
|
#ifndef Q_WS_WIN
|
||||||
if(watch_timer) {
|
QDir dir(path);
|
||||||
// Network mode
|
for (int i = 0; i < watched_folders.count(); ++i) {
|
||||||
if(QDir(path) == watched_folder) {
|
if (QDir(watched_folders.at(i)) == dir) {
|
||||||
|
watched_folders.removeAt(i);
|
||||||
|
if (watched_folders.isEmpty())
|
||||||
delete watch_timer;
|
delete watch_timer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
#endif
|
#endif
|
||||||
// Normal mode
|
// Normal mode
|
||||||
QFileSystemWatcher::removePath(path);
|
QFileSystemWatcher::removePath(path);
|
||||||
#ifndef Q_WS_WIN
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
// XXX: Does not detect file size changes to improve performance.
|
void scanLocalFolder(QString path) {
|
||||||
void scanFolder() {
|
qDebug("scanLocalFolder(%s) called", qPrintable(path));
|
||||||
qDebug("Scan folder was called");
|
|
||||||
QStringList torrents;
|
QStringList torrents;
|
||||||
if(watch_timer) {
|
// Local folders scan
|
||||||
torrents = watched_folder.entryList(filters, QDir::Files, QDir::Unsorted);
|
addTorrentsFromDir(QDir(path), torrents);
|
||||||
} else {
|
// Report detected torrent files
|
||||||
torrents = QDir(QFileSystemWatcher::directories().first()).entryList(filters, QDir::Files, QDir::Unsorted);
|
if(!torrents.empty()) {
|
||||||
qDebug("FSWatcher: Polling manually folder %s", QFileSystemWatcher::directories().first().toLocal8Bit().data());
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
}
|
|
||||||
if(!torrents.empty())
|
|
||||||
emit torrentsAdded(torrents);
|
emit torrentsAdded(torrents);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scanNetworkFolders() {
|
||||||
|
#ifndef Q_WS_WIN
|
||||||
|
qDebug("scanNetworkFolders() called");
|
||||||
|
QStringList torrents;
|
||||||
|
// Network folders scan
|
||||||
|
foreach (const QDir &dir, watched_folders) {
|
||||||
|
//qDebug("FSWatcher: Polling manually folder %s", qPrintable(dir.path()));
|
||||||
|
addTorrentsFromDir(dir, torrents);
|
||||||
|
}
|
||||||
|
// Report detected torrent files
|
||||||
|
if(!torrents.empty()) {
|
||||||
|
qDebug("The following files are being reported: %s", qPrintable(torrents.join("\n")));
|
||||||
|
emit torrentsAdded(torrents);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void torrentsAdded(QStringList &pathList);
|
void torrentsAdded(QStringList &pathList);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addTorrentsFromDir(const QDir &dir, QStringList &torrents) {
|
||||||
|
const QStringList files = dir.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||||
|
foreach(const QString &file, files) {
|
||||||
|
#if defined(Q_WS_WIN) || defined(Q_OS_OS2)
|
||||||
|
torrents << dir.absoluteFilePath(file).replace("/", "\\");
|
||||||
|
#else
|
||||||
|
torrents << dir.absoluteFilePath(file);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILESYSTEMWATCHER_H
|
#endif // FILESYSTEMWATCHER_H
|
||||||
|
|||||||
@@ -34,7 +34,8 @@
|
|||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
#include <QMessageBox>
|
#include <QRegExp>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
#include <libtorrent/ip_filter.hpp>
|
#include <libtorrent/ip_filter.hpp>
|
||||||
@@ -71,7 +72,7 @@ class FilterParserThread : public QThread {
|
|||||||
// PeerGuardian p2p file
|
// PeerGuardian p2p file
|
||||||
parseP2PFilterFile(filePath);
|
parseP2PFilterFile(filePath);
|
||||||
} else {
|
} else {
|
||||||
if(filePath.endsWith(".p2p", Qt::CaseInsensitive)) {
|
if(filePath.endsWith(".p2b", Qt::CaseInsensitive)) {
|
||||||
// PeerGuardian p2b file
|
// PeerGuardian p2b file
|
||||||
parseP2BFilterFile(filePath);
|
parseP2BFilterFile(filePath);
|
||||||
} else {
|
} else {
|
||||||
@@ -103,7 +104,7 @@ class FilterParserThread : public QThread {
|
|||||||
QFile file(filePath);
|
QFile file(filePath);
|
||||||
if (file.exists()){
|
if (file.exists()){
|
||||||
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned int nbLine = 0;
|
unsigned int nbLine = 0;
|
||||||
@@ -224,7 +225,7 @@ class FilterParserThread : public QThread {
|
|||||||
QStringList IP;
|
QStringList IP;
|
||||||
if (file.exists()){
|
if (file.exists()){
|
||||||
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned int nbLine = 0;
|
unsigned int nbLine = 0;
|
||||||
@@ -292,7 +293,7 @@ class FilterParserThread : public QThread {
|
|||||||
QFile file(filePath);
|
QFile file(filePath);
|
||||||
if (file.exists()){
|
if (file.exists()){
|
||||||
if(!file.open(QIODevice::ReadOnly)){
|
if(!file.open(QIODevice::ReadOnly)){
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("Couldn't open %1 in read mode.").arg(filePath));
|
std::cerr << "I/O Error: Could not open ip filer file in read mode." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QDataStream stream(&file);
|
QDataStream stream(&file);
|
||||||
@@ -304,7 +305,7 @@ class FilterParserThread : public QThread {
|
|||||||
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
|
memcmp(buf, "\xFF\xFF\xFF\xFFP2B", 7) ||
|
||||||
!stream.readRawData((char*)&version, sizeof(version))
|
!stream.readRawData((char*)&version, sizeof(version))
|
||||||
) {
|
) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +319,7 @@ class FilterParserThread : public QThread {
|
|||||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||||
!stream.readRawData((char*)&end, sizeof(end))
|
!stream.readRawData((char*)&end, sizeof(end))
|
||||||
) {
|
) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Network byte order to Host byte order
|
// Network byte order to Host byte order
|
||||||
@@ -334,7 +335,7 @@ class FilterParserThread : public QThread {
|
|||||||
qDebug ("p2b version 3");
|
qDebug ("p2b version 3");
|
||||||
unsigned int namecount;
|
unsigned int namecount;
|
||||||
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
if(!stream.readRawData((char*)&namecount, sizeof(namecount))) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
namecount=ntohl(namecount);
|
namecount=ntohl(namecount);
|
||||||
@@ -342,7 +343,7 @@ class FilterParserThread : public QThread {
|
|||||||
for(unsigned int i=0; i<namecount; i++) {
|
for(unsigned int i=0; i<namecount; i++) {
|
||||||
string name;
|
string name;
|
||||||
if(!getlineInStream(stream, name, '\0')) {
|
if(!getlineInStream(stream, name, '\0')) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(abort) return;
|
if(abort) return;
|
||||||
@@ -350,7 +351,7 @@ class FilterParserThread : public QThread {
|
|||||||
// Reading the ranges
|
// Reading the ranges
|
||||||
unsigned int rangecount;
|
unsigned int rangecount;
|
||||||
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
if(!stream.readRawData((char*)&rangecount, sizeof(rangecount))) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
rangecount=ntohl(rangecount);
|
rangecount=ntohl(rangecount);
|
||||||
@@ -363,7 +364,7 @@ class FilterParserThread : public QThread {
|
|||||||
!stream.readRawData((char*)&start, sizeof(start)) ||
|
!stream.readRawData((char*)&start, sizeof(start)) ||
|
||||||
!stream.readRawData((char*)&end, sizeof(end))
|
!stream.readRawData((char*)&end, sizeof(end))
|
||||||
) {
|
) {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Network byte order to Host byte order
|
// Network byte order to Host byte order
|
||||||
@@ -376,7 +377,7 @@ class FilterParserThread : public QThread {
|
|||||||
if(abort) return;
|
if(abort) return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMessageBox::critical(0, tr("I/O Error", "Input/Output Error"), tr("%1 is not a valid PeerGuardian P2B file.").arg(filePath));
|
std::cerr << "Parsing Error: The filter file is not a valid PeerGuardian P2B file." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
@@ -406,8 +407,11 @@ class FilterParserThread : public QThread {
|
|||||||
// First, import current filter
|
// First, import current filter
|
||||||
ip_filter filter = s->get_ip_filter();
|
ip_filter filter = s->get_ip_filter();
|
||||||
foreach(const QString &ip, IPs) {
|
foreach(const QString &ip, IPs) {
|
||||||
qDebug("Manual ban of peer %s", ip.toLocal8Bit().data());
|
qDebug("Manual ban of peer %s", ip.toLocal8Bit().constData());
|
||||||
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().data());
|
boost::system::error_code ec;
|
||||||
|
address_v4 addr = address_v4::from_string(ip.toLocal8Bit().constData(), ec);
|
||||||
|
Q_ASSERT(!ec);
|
||||||
|
if(!ec)
|
||||||
filter.add_rule(addr, addr, ip_filter::blocked);
|
filter.add_rule(addr, addr, ip_filter::blocked);
|
||||||
}
|
}
|
||||||
s->set_ip_filter(filter);
|
s->set_ip_filter(filter);
|
||||||
|
|||||||
195
src/geoip.h
@@ -40,68 +40,62 @@
|
|||||||
|
|
||||||
using namespace libtorrent;
|
using namespace libtorrent;
|
||||||
|
|
||||||
// TODO: Update from Internet
|
class GeoIP : public QObject {
|
||||||
class GeoIP {
|
Q_OBJECT
|
||||||
protected:
|
protected:
|
||||||
|
#ifdef WITH_GEOIP_EMBEDDED
|
||||||
static QString geoipFolder(bool embedded=false) {
|
static QString geoipFolder(bool embedded=false) {
|
||||||
if(embedded)
|
if(embedded)
|
||||||
return ":/geoip/";
|
return ":/geoip/";
|
||||||
return misc::qBittorrentPath()+"geoip"+QDir::separator();
|
return misc::QDesktopServicesDataLocation()+"geoip"+QDir::separator();
|
||||||
|
#else
|
||||||
|
static QString geoipFolder(bool) {
|
||||||
|
if(QFile::exists("/usr/local/share/GeoIP/GeoIP.dat"))
|
||||||
|
return "/usr/local/share/GeoIP/";
|
||||||
|
if(QFile::exists("/var/lib/GeoIP/GeoIP.dat"))
|
||||||
|
return "/var/lib/GeoIP/";
|
||||||
|
return "/usr/share/GeoIP/";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString geoipDBpath(bool embedded=false) {
|
static QString geoipDBpath(bool embedded=false) {
|
||||||
return geoipFolder(embedded)+"GeoIP.dat";
|
return geoipFolder(embedded)+"GeoIP.dat";
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString geoipVersionPath(bool embedded=false) {
|
#ifdef WITH_GEOIP_EMBEDDED
|
||||||
return geoipFolder(embedded)+"VERSION";
|
static void exportEmbeddedDb() {
|
||||||
}
|
if(!QFile::exists(geoipDBpath(false)) || QFile(geoipDBpath(false)).size() != QFile(geoipDBpath(true)).size()) { // Export is required
|
||||||
|
|
||||||
static int getDBVersion(bool embedded = false) {
|
|
||||||
QFile vFile(geoipVersionPath(embedded));
|
|
||||||
qDebug("Reading file at %s", geoipVersionPath(embedded).toLocal8Bit().data());
|
|
||||||
if(vFile.exists() && vFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
qDebug("File exists and was opened");
|
|
||||||
QByteArray v = vFile.readAll().trimmed();
|
|
||||||
/*while(!v.isEmpty() && v[0] == '0') {
|
|
||||||
v = v.mid(1);
|
|
||||||
}*/
|
|
||||||
qDebug("Read version: %s", v.data());
|
|
||||||
bool ok = false;
|
|
||||||
int version = v.toInt(&ok);
|
|
||||||
qDebug("Read version %d (Error? %d)", version, (int) !ok);
|
|
||||||
if(ok) return version;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void updateLocalDb() {
|
|
||||||
if(getDBVersion(true) > getDBVersion(false)) { // Update required
|
|
||||||
qDebug("A local Geoip database update is required, proceeding...");
|
qDebug("A local Geoip database update is required, proceeding...");
|
||||||
// Create geoip folder is necessary
|
// Create geoip folder is necessary
|
||||||
QDir gfolder(geoipFolder(false));
|
QDir gfolder(geoipFolder(false));
|
||||||
if(!gfolder.exists()) {
|
if(!gfolder.exists()) {
|
||||||
if(!gfolder.mkpath(geoipFolder(false))) return;
|
if(!gfolder.mkpath(geoipFolder(false))) {
|
||||||
|
std::cerr << "Failed to create geoip folder at " << qPrintable(geoipFolder(false)) << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Remove destination files
|
// Remove destination files
|
||||||
if(QFile::exists(geoipDBpath(false)))
|
if(QFile::exists(geoipDBpath(false)))
|
||||||
QFile::remove(geoipDBpath(false));
|
misc::safeRemove(geoipDBpath(false));
|
||||||
if(QFile::exists(geoipVersionPath(false)))
|
|
||||||
QFile::remove(geoipVersionPath(false));
|
|
||||||
// Copy from executable to hard disk
|
// Copy from executable to hard disk
|
||||||
QFile::copy(geoipDBpath(true), geoipDBpath(false));
|
qDebug("%s -> %s", qPrintable(geoipDBpath(true)), qPrintable(geoipDBpath(false)));
|
||||||
QFile::copy(geoipVersionPath(true), geoipVersionPath(false));
|
if(!QFile::copy(geoipDBpath(true), geoipDBpath(false))) {
|
||||||
|
std::cerr << "ERROR: Failed to copy geoip.dat from executable to hard disk" << std::endl;
|
||||||
|
}
|
||||||
qDebug("Local Geoip database was updated");
|
qDebug("Local Geoip database was updated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void loadDatabase(session *s) {
|
static void loadDatabase(session *s) {
|
||||||
updateLocalDb();
|
#ifdef WITH_GEOIP_EMBEDDED
|
||||||
|
exportEmbeddedDb();
|
||||||
|
#endif
|
||||||
if(QFile::exists(geoipDBpath(false))) {
|
if(QFile::exists(geoipDBpath(false))) {
|
||||||
qDebug("Loading GeoIP database from %s...", geoipDBpath(false).toLocal8Bit().data());
|
qDebug("Loading GeoIP database from %s...", qPrintable(geoipDBpath(false)));
|
||||||
if(!s->load_country_db(geoipDBpath(false).toLocal8Bit().data())) {
|
if(!s->load_country_db(geoipDBpath(false).toLocal8Bit().constData())) {
|
||||||
std::cerr << "Failed to load Geoip Database at " << geoipDBpath(false).toLocal8Bit().data() << std::endl;
|
std::cerr << "Failed to load Geoip Database at " << qPrintable(geoipDBpath(false)) << std::endl;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qDebug("ERROR: Impossible to find local Geoip Database");
|
qDebug("ERROR: Impossible to find local Geoip Database");
|
||||||
@@ -110,7 +104,7 @@ public:
|
|||||||
|
|
||||||
// TODO: Support more countries
|
// TODO: Support more countries
|
||||||
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
|
// http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
|
||||||
static QIcon CountryISOCodeToIcon(char* iso) {
|
static QIcon CountryISOCodeToIcon(const char* iso, QString &country_name) {
|
||||||
switch(iso[0]) {
|
switch(iso[0]) {
|
||||||
case 0:
|
case 0:
|
||||||
case '-':
|
case '-':
|
||||||
@@ -118,106 +112,107 @@ public:
|
|||||||
//qDebug("Not returning any icon because iso is invalid: %s", iso);
|
//qDebug("Not returning any icon because iso is invalid: %s", iso);
|
||||||
return QIcon();
|
return QIcon();
|
||||||
case 'A':
|
case 'A':
|
||||||
if(iso[1] == 'U') return QIcon(":/Icons/flags/australia.png");
|
if(iso[1] == 'U') { country_name = tr("Australia"); return QIcon(":/Icons/flags/australia.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/argentina.png");
|
if(iso[1] == 'R') { country_name = tr("Argentina"); return QIcon(":/Icons/flags/argentina.png"); }
|
||||||
if(iso[1] == 'T') return QIcon(":/Icons/flags/austria.png");
|
if(iso[1] == 'T') { country_name = tr("Austria"); return QIcon(":/Icons/flags/austria.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/united_arab_emirates.png");
|
if(iso[1] == 'E') { country_name = tr("United Arab Emirates"); return QIcon(":/Icons/flags/united_arab_emirates.png"); }
|
||||||
break;
|
break;
|
||||||
case 'B':
|
case 'B':
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/brazil.png");
|
if(iso[1] == 'R') { country_name = tr("Brazil"); return QIcon(":/Icons/flags/brazil.png"); }
|
||||||
if(iso[1] == 'G') return QIcon(":/Icons/flags/bulgaria.png");
|
if(iso[1] == 'G') { country_name = tr("Bulgaria"); return QIcon(":/Icons/flags/bulgaria.png"); }
|
||||||
if(iso[1] == 'Y') return QIcon(":/Icons/flags/belarus.png");
|
if(iso[1] == 'Y') { country_name = tr("Belarus"); return QIcon(":/Icons/flags/belarus.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/belgium.png");
|
if(iso[1] == 'E') { country_name = tr("Belgium"); return QIcon(":/Icons/flags/belgium.png"); }
|
||||||
if(iso[1] == 'A') return QIcon(":/Icons/flags/bosnia.png");
|
if(iso[1] == 'A') { country_name = tr("Bosnia"); return QIcon(":/Icons/flags/bosnia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
if(iso[1] == 'A') return QIcon(":/Icons/flags/canada.png");
|
if(iso[1] == 'A') { country_name = tr("Canada"); return QIcon(":/Icons/flags/canada.png"); }
|
||||||
if(iso[1] == 'Z') return QIcon(":/Icons/flags/czech.png");
|
if(iso[1] == 'Z') { country_name = tr("Czech Republic"); return QIcon(":/Icons/flags/czech.png"); }
|
||||||
if(iso[1] == 'N') return QIcon(":/Icons/flags/china.png");
|
if(iso[1] == 'N') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/costa_rica.png");
|
if(iso[1] == 'R') { country_name = tr("Costa Rica"); return QIcon(":/Icons/flags/costa_rica.png"); }
|
||||||
if(iso[1] == 'H') return QIcon(":/Icons/flags/suisse.png");
|
if(iso[1] == 'H') { country_name = tr("Switzerland"); return QIcon(":/Icons/flags/suisse.png"); }
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/germany.png");
|
if(iso[1] == 'E') { country_name = tr("Germany"); return QIcon(":/Icons/flags/germany.png"); }
|
||||||
if(iso[1] == 'K') return QIcon(":/Icons/flags/denmark.png");
|
if(iso[1] == 'K') { country_name = tr("Denmark"); return QIcon(":/Icons/flags/denmark.png"); }
|
||||||
if(iso[1] == 'Z') return QIcon(":/Icons/flags/algeria.png");
|
if(iso[1] == 'Z') { country_name = tr("Algeria"); return QIcon(":/Icons/flags/algeria.png"); }
|
||||||
break;
|
break;
|
||||||
case 'E':
|
case 'E':
|
||||||
if(iso[1] == 'S') return QIcon(":/Icons/flags/spain.png");
|
if(iso[1] == 'S') { country_name = tr("Spain"); return QIcon(":/Icons/flags/spain.png"); }
|
||||||
if(iso[1] == 'G') return QIcon(":/Icons/flags/egypt.png");
|
if(iso[1] == 'G') { country_name = tr("Egypt"); return QIcon(":/Icons/flags/egypt.png"); }
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
if(iso[1] == 'I') return QIcon(":/Icons/flags/finland.png");
|
if(iso[1] == 'I') { country_name = tr("Finland"); return QIcon(":/Icons/flags/finland.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/france.png");
|
if(iso[1] == 'R') { country_name = tr("France"); return QIcon(":/Icons/flags/france.png"); }
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
if(iso[1] == 'B') return QIcon(":/Icons/flags/united_kingdom.png");
|
if(iso[1] == 'B') { country_name = tr("United Kingdom"); return QIcon(":/Icons/flags/united_kingdom.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/greece.png");
|
if(iso[1] == 'R') { country_name = tr("Greece"); return QIcon(":/Icons/flags/greece.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/georgia.png");
|
if(iso[1] == 'E') { country_name = tr("Georgia"); return QIcon(":/Icons/flags/georgia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'H':
|
case 'H':
|
||||||
if(iso[1] == 'U') return QIcon(":/Icons/flags/hungary.png");
|
if(iso[1] == 'U') { country_name = tr("Hungary"); return QIcon(":/Icons/flags/hungary.png"); }
|
||||||
if(iso[1] == 'K') return QIcon(":/Icons/flags/china.png");
|
if(iso[1] == 'K') { country_name = tr("China"); return QIcon(":/Icons/flags/china.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/croatia.png");
|
if(iso[1] == 'R') { country_name = tr("Croatia"); return QIcon(":/Icons/flags/croatia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
if(iso[1] == 'T') return QIcon(":/Icons/flags/italy.png");
|
if(iso[1] == 'T') { country_name = tr("Italy"); return QIcon(":/Icons/flags/italy.png"); }
|
||||||
if(iso[1] == 'N') return QIcon(":/Icons/flags/india.png");
|
if(iso[1] == 'N') { country_name = tr("India"); return QIcon(":/Icons/flags/india.png"); }
|
||||||
if(iso[1] == 'L') return QIcon(":/Icons/flags/israel.png");
|
if(iso[1] == 'L') { country_name = tr("Israel"); return QIcon(":/Icons/flags/israel.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/ireland.png");
|
if(iso[1] == 'E') { country_name = tr("Ireland"); return QIcon(":/Icons/flags/ireland.png"); }
|
||||||
if(iso[1] == 'S') return QIcon(":/Icons/flags/iceland.png");
|
if(iso[1] == 'S') { country_name = tr("Iceland"); return QIcon(":/Icons/flags/iceland.png"); }
|
||||||
if(iso[1] == 'D') return QIcon(":/Icons/flags/indonesia.png");
|
if(iso[1] == 'D') { country_name = tr("Indonesia"); return QIcon(":/Icons/flags/indonesia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'J':
|
case 'J':
|
||||||
if(iso[1] == 'P') return QIcon(":/Icons/flags/japan.png");
|
if(iso[1] == 'P') { country_name = tr("Japan"); return QIcon(":/Icons/flags/japan.png"); }
|
||||||
break;
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/south_korea.png");
|
if(iso[1] == 'R') { country_name = tr("South Korea"); return QIcon(":/Icons/flags/south_korea.png"); }
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
if(iso[1] == 'U') return QIcon(":/Icons/flags/luxembourg.png");
|
if(iso[1] == 'U') { country_name = tr("Luxembourg"); return QIcon(":/Icons/flags/luxembourg.png"); }
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
if(iso[1] == 'Y') return QIcon(":/Icons/flags/malaysia.png");
|
if(iso[1] == 'Y') { country_name = tr("Malaysia"); return QIcon(":/Icons/flags/malaysia.png"); }
|
||||||
if(iso[1] == 'X') return QIcon(":/Icons/flags/mexico.png");
|
if(iso[1] == 'X') { country_name = tr("Mexico"); return QIcon(":/Icons/flags/mexico.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/serbia.png");
|
if(iso[1] == 'E') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); }
|
||||||
if(iso[1] == 'A') return QIcon(":/Icons/flags/morocco.png");
|
if(iso[1] == 'A') { country_name = tr("Morocco"); return QIcon(":/Icons/flags/morocco.png"); }
|
||||||
break;
|
break;
|
||||||
case 'N':
|
case 'N':
|
||||||
if(iso[1] == 'L') return QIcon(":/Icons/flags/netherlands.png");
|
if(iso[1] == 'L') { country_name = tr("Netherlands"); return QIcon(":/Icons/flags/netherlands.png"); }
|
||||||
if(iso[1] == 'O') return QIcon(":/Icons/flags/norway.png");
|
if(iso[1] == 'O') { country_name = tr("Norway"); return QIcon(":/Icons/flags/norway.png"); }
|
||||||
if(iso[1] == 'Z') return QIcon(":/Icons/flags/newzealand.png");
|
if(iso[1] == 'Z') { country_name = tr("New Zealand"); return QIcon(":/Icons/flags/newzealand.png"); }
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
if(iso[1] == 'T') return QIcon(":/Icons/flags/portugal.png");
|
if(iso[1] == 'T') { country_name = tr("Portugal"); return QIcon(":/Icons/flags/portugal.png"); }
|
||||||
if(iso[1] == 'L') return QIcon(":/Icons/flags/poland.png");
|
if(iso[1] == 'L') { country_name = tr("Poland"); return QIcon(":/Icons/flags/poland.png"); }
|
||||||
if(iso[1] == 'K') return QIcon(":/Icons/flags/pakistan.png");
|
if(iso[1] == 'K') { country_name = tr("Pakistan"); return QIcon(":/Icons/flags/pakistan.png"); }
|
||||||
if(iso[1] == 'H') return QIcon(":/Icons/flags/philippines.png");
|
if(iso[1] == 'H') { country_name = tr("Philippines"); return QIcon(":/Icons/flags/philippines.png"); }
|
||||||
break;
|
break;
|
||||||
case 'R':
|
case 'R':
|
||||||
if(iso[1] == 'U') return QIcon(":/Icons/flags/russia.png");
|
if(iso[1] == 'U') { country_name = tr("Russia"); return QIcon(":/Icons/flags/russia.png"); }
|
||||||
if(iso[1] == 'O') return QIcon(":/Icons/flags/romania.png");
|
if(iso[1] == 'O') { country_name = tr("Romania"); return QIcon(":/Icons/flags/romania.png"); }
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/france.png");
|
if(iso[1] == 'E') { country_name = tr("France (Reunion Island)"); return QIcon(":/Icons/flags/france.png"); }
|
||||||
if(iso[1] == 'S') return QIcon(":/Icons/flags/serbia.png");
|
if(iso[1] == 'S') { country_name = tr("Serbia"); return QIcon(":/Icons/flags/serbia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if(iso[1] == 'E') return QIcon(":/Icons/flags/sweden.png");
|
if(iso[1] == 'A') { country_name = tr("Saudi Arabia"); return QIcon(":/Icons/flags/saoudi_arabia.png"); }
|
||||||
if(iso[1] == 'K') return QIcon(":/Icons/flags/slovakia.png");
|
if(iso[1] == 'E') { country_name = tr("Sweden"); return QIcon(":/Icons/flags/sweden.png"); }
|
||||||
if(iso[1] == 'G') return QIcon(":/Icons/flags/singapore.png");
|
if(iso[1] == 'K') { country_name = tr("Slovakia"); return QIcon(":/Icons/flags/slovakia.png"); }
|
||||||
if(iso[1] == 'I') return QIcon(":/Icons/flags/slovenia.png");
|
if(iso[1] == 'G') { country_name = tr("Singapore"); return QIcon(":/Icons/flags/singapore.png"); }
|
||||||
|
if(iso[1] == 'I') { country_name = tr("Slovenia"); return QIcon(":/Icons/flags/slovenia.png"); }
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
if(iso[1] == 'W') return QIcon(":/Icons/flags/china.png");
|
if(iso[1] == 'W') { country_name = tr("Taiwan"); return QIcon(":/Icons/flags/china.png"); }
|
||||||
if(iso[1] == 'R') return QIcon(":/Icons/flags/turkey.png");
|
if(iso[1] == 'R') { country_name = tr("Turkey"); return QIcon(":/Icons/flags/turkey.png"); }
|
||||||
if(iso[1] == 'H') return QIcon(":/Icons/flags/thailand.png");
|
if(iso[1] == 'H') { country_name = tr("Thailand"); return QIcon(":/Icons/flags/thailand.png"); }
|
||||||
break;
|
break;
|
||||||
case 'U':
|
case 'U':
|
||||||
if(iso[1] == 'S') return QIcon(":/Icons/flags/usa.png");
|
if(iso[1] == 'S') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); }
|
||||||
if(iso[1] == 'M') return QIcon(":/Icons/flags/usa.png");
|
if(iso[1] == 'M') { country_name = tr("USA"); return QIcon(":/Icons/flags/usa.png"); }
|
||||||
if(iso[1] == 'A') return QIcon(":/Icons/flags/ukraine.png");
|
if(iso[1] == 'A') { country_name = tr("Ukraine"); return QIcon(":/Icons/flags/ukraine.png"); }
|
||||||
break;
|
break;
|
||||||
case 'Z':
|
case 'Z':
|
||||||
if(iso[1] == 'A') return QIcon(":/Icons/flags/south_africa.png");
|
if(iso[1] == 'A') { country_name = tr("South Africa"); return QIcon(":/Icons/flags/south_africa.png"); }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
qDebug("Unrecognized country code: %c%c", iso[0], iso[1]);
|
qDebug("Unrecognized country code: %c%c", iso[0], iso[1]);
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<RCC>
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>geoip/GeoIP.dat</file>
|
<file>geoip/GeoIP.dat</file>
|
||||||
<file>geoip/VERSION</file>
|
</qresource>
|
||||||
</qresource>
|
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
There are two licenses, one for the C library software, and one for
|
|
||||||
the database.
|
|
||||||
|
|
||||||
SOFTWARE LICENSE (C library)
|
|
||||||
|
|
||||||
The GeoIP C Library is licensed under the LGPL. For details see
|
|
||||||
the COPYING file.
|
|
||||||
|
|
||||||
OPEN DATA LICENSE (GeoLite Country and GeoLite City databases)
|
|
||||||
|
|
||||||
Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
|
|
||||||
|
|
||||||
All advertising materials and documentation mentioning features or use of
|
|
||||||
this database must display the following acknowledgment:
|
|
||||||
"This product includes GeoLite data created by MaxMind, available from
|
|
||||||
http://maxmind.com/"
|
|
||||||
|
|
||||||
Redistribution and use with or without modification, are permitted provided
|
|
||||||
that the following conditions are met:
|
|
||||||
1. Redistributions must retain the above copyright notice, this list of
|
|
||||||
conditions and the following disclaimer in the documentation and/or other
|
|
||||||
materials provided with the distribution.
|
|
||||||
2. All advertising materials and documentation mentioning features or use of
|
|
||||||
this database must display the following acknowledgement:
|
|
||||||
"This product includes GeoLite data created by MaxMind, available from
|
|
||||||
http://maxmind.com/"
|
|
||||||
3. "MaxMind" may not be used to endorse or promote products derived from this
|
|
||||||
database without specific prior written permission.
|
|
||||||
|
|
||||||
THIS DATABASE IS PROVIDED BY MAXMIND, INC ``AS IS'' AND ANY
|
|
||||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL MAXMIND BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
12
src/geoip/README
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
If you wish to embed GeoIP database into qBittorrent executable, please download put GeoIP.dat in this folder.
|
||||||
|
|
||||||
|
GeoIP Database can be downloaded from here:
|
||||||
|
* http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
|
||||||
|
Note that the database should be uncompressed.
|
||||||
|
|
||||||
|
Embedding GeoIP database into qBittorrent executable is advised for:
|
||||||
|
* Windows
|
||||||
|
* Mac OS X
|
||||||
|
* Linux distributions that don't provide GeoIP database in a separate package
|
||||||
|
|
||||||
|
On Linux operating system, since this is not the default behavior, you also need to pass --with-geoip-database-embedded parameter to the configure file.
|
||||||
@@ -1 +0,0 @@
|
|||||||
0911
|
|
||||||
508
src/gpl.html
Normal file
@@ -0,0 +1,508 @@
|
|||||||
|
<p>qBittorrent is licensed under the GNU General Public License version 2 with the
|
||||||
|
addition of the following special exception:</p>
|
||||||
|
<p>
|
||||||
|
In addition, as a special exception, the copyright holders give permission to
|
||||||
|
link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
exception statement from your version.</p>
|
||||||
|
----------
|
||||||
|
<h2>GNU General Public License, version 2</h2>
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h3>Table of Contents</h3>
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<li><a name="TOC1" href="#SEC1">GNU GENERAL PUBLIC
|
||||||
|
LICENSE<!--TRANSLATORS: Don't translate the license; copy msgid's
|
||||||
|
verbatim!--></a>
|
||||||
|
<ul>
|
||||||
|
<li><a name="TOC2" href="#SEC2">Preamble</a></li>
|
||||||
|
<li><a name="TOC3" href="#SEC3">TERMS AND CONDITIONS
|
||||||
|
FOR COPYING, DISTRIBUTION AND MODIFICATION</a></li>
|
||||||
|
<li><a name="TOC4" href="#SEC4">How to Apply These
|
||||||
|
Terms to Your New Programs</a></li>
|
||||||
|
</ul></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h3><a name="SEC1" href="#TOC1">GNU GENERAL PUBLIC LICENSE</a></h3>
|
||||||
|
<p>
|
||||||
|
Version 2, June 1991
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3><a name="preamble"></a><a name="SEC2" href="#TOC2">Preamble</a></h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<h3><a name="terms"></a><a name="SEC3" href="#TOC3">TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</a></h3>
|
||||||
|
|
||||||
|
|
||||||
|
<a name="section0"></a><p>
|
||||||
|
<strong>0.</strong>
|
||||||
|
This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section1"></a><p>
|
||||||
|
<strong>1.</strong>
|
||||||
|
You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section2"></a><p>
|
||||||
|
<strong>2.</strong>
|
||||||
|
You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>a)</strong>
|
||||||
|
You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
</dd>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>b)</strong>
|
||||||
|
You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
</dd>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>c)</strong>
|
||||||
|
If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section3"></a><p>
|
||||||
|
<strong>3.</strong>
|
||||||
|
You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- we use this doubled UL to get the sub-sections indented, -->
|
||||||
|
<!-- while making the bullets as unobvious as possible. -->
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>a)</strong>
|
||||||
|
Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
</dd>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>b)</strong>
|
||||||
|
Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
</dd>
|
||||||
|
<dt></dt>
|
||||||
|
<dd>
|
||||||
|
<strong>c)</strong>
|
||||||
|
Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section4"></a><p>
|
||||||
|
<strong>4.</strong>
|
||||||
|
You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section5"></a><p>
|
||||||
|
<strong>5.</strong>
|
||||||
|
You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section6"></a><p>
|
||||||
|
<strong>6.</strong>
|
||||||
|
Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section7"></a><p>
|
||||||
|
<strong>7.</strong>
|
||||||
|
If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section8"></a><p>
|
||||||
|
<strong>8.</strong>
|
||||||
|
If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section9"></a><p>
|
||||||
|
<strong>9.</strong>
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section10"></a><p>
|
||||||
|
<strong>10.</strong>
|
||||||
|
If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section11"></a><p><strong>NO WARRANTY</strong></p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>11.</strong>
|
||||||
|
BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<a name="section12"></a><p>
|
||||||
|
<strong>12.</strong>
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>END OF TERMS AND CONDITIONS</h3>
|
||||||
|
|
||||||
|
<h3><a name="howto"></a><a name="SEC4" href="#TOC4">How to Apply These Terms to Your New Programs</a></h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<var>one line to give the program's name and an idea of what it does.</var>
|
||||||
|
Copyright (C) <var>yyyy</var> <var>name of author</var>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
MA 02110-1301, USA.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
Gnomovision version 69, Copyright (C) <var>year</var> <var>name of author</var>
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
|
||||||
|
type `show w'. This is free software, and you are welcome
|
||||||
|
to redistribute it under certain conditions; type `show c'
|
||||||
|
for details.
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The hypothetical commands <samp>`show w'</samp> and <samp>`show c'</samp> should show
|
||||||
|
the appropriate parts of the General Public License. Of course, the
|
||||||
|
commands you use may be called something other than <samp>`show w'</samp> and
|
||||||
|
<samp>`show c'</samp>; they could even be mouse-clicks or menu items--whatever
|
||||||
|
suits your program.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright
|
||||||
|
interest in the program `Gnomovision'
|
||||||
|
(which makes passes at compilers) written
|
||||||
|
by James Hacker.
|
||||||
|
|
||||||
|
<var>signature of Ty Coon</var>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the
|
||||||
|
<a href="http://www.gnu.org/licenses/lgpl.html">GNU Lesser General Public License</a>
|
||||||
|
instead of this License.
|
||||||
|
</p>
|
||||||
110
src/headlessloader.h
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez, Frédéric Lassabe
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HEADLESSLOADER_H
|
||||||
|
#define HEADLESSLOADER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include "preferences.h"
|
||||||
|
#include "bittorrent.h"
|
||||||
|
|
||||||
|
class HeadlessLoader: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
HeadlessLoader(QStringList torrentCmdLine) {
|
||||||
|
// Enable Web UI
|
||||||
|
Preferences::setWebUiEnabled(true);
|
||||||
|
// Instanciate Bittorrent Object
|
||||||
|
BTSession = new Bittorrent();
|
||||||
|
connect(BTSession, SIGNAL(newConsoleMessage(QString)), this, SLOT(displayConsoleMessage(QString)));
|
||||||
|
// Resume unfinished torrents
|
||||||
|
BTSession->startUpTorrents();
|
||||||
|
// Process command line parameters
|
||||||
|
processParams(torrentCmdLine);
|
||||||
|
// Display some information to the user
|
||||||
|
std::cout << std::endl << "******** " << qPrintable(tr("Information")) << " ********" << std::endl;
|
||||||
|
std::cout << qPrintable(tr("To control qBittorrent, access the Web UI at http://localhost:%1").arg(QString::number(Preferences::getWebUiPort()))) << std::endl;
|
||||||
|
std::cout << qPrintable(tr("The Web UI administrator user name is: %1").arg(Preferences::getWebUiUsername())) << std::endl;
|
||||||
|
if(Preferences::getWebUiPassword() == "f6fdffe48c908deb0f4c3bd36c032e72") {
|
||||||
|
std::cout << qPrintable(tr("The Web UI administrator password is still the default one: %1").arg("adminadmin")) << std::endl;
|
||||||
|
std::cout << qPrintable(tr("This is a security risk, please consider changing your password from program preferences.")) << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~HeadlessLoader() {
|
||||||
|
delete BTSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
// Call this function to exit qBittorrent headless loader
|
||||||
|
// and return to prompt (object will be deleted by main)
|
||||||
|
void exit() {
|
||||||
|
qApp->quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void displayConsoleMessage(QString msg) {
|
||||||
|
std::cout << qPrintable(msg) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processParams(const QString& params_str) {
|
||||||
|
processParams(params_str.split(" ", QString::SkipEmptyParts));
|
||||||
|
}
|
||||||
|
|
||||||
|
// As program parameters, we can get paths or urls.
|
||||||
|
// This function parse the parameters and call
|
||||||
|
// the right addTorrent function, considering
|
||||||
|
// the parameter type.
|
||||||
|
void processParams(const QStringList& params) {
|
||||||
|
foreach(QString param, params) {
|
||||||
|
param = param.trimmed();
|
||||||
|
if(param.startsWith(QString::fromUtf8("http://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("ftp://"), Qt::CaseInsensitive) || param.startsWith(QString::fromUtf8("https://"), Qt::CaseInsensitive)) {
|
||||||
|
BTSession->downloadFromUrl(param);
|
||||||
|
}else{
|
||||||
|
if(param.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||||
|
qDebug("Converting bc link to magnet link");
|
||||||
|
param = misc::bcLinkToMagnet(param);
|
||||||
|
}
|
||||||
|
if(param.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||||
|
BTSession->addMagnetUri(param);
|
||||||
|
} else {
|
||||||
|
BTSession->addTorrent(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Bittorrent *BTSession;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
44
src/hidabletabwidget.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt4 and libtorrent.
|
||||||
|
* Copyright (C) 2006 Christophe Dumez
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*
|
||||||
|
* Contact : chris@qbittorrent.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HIDABLETABWIDGET_H
|
||||||
|
#define HIDABLETABWIDGET_H
|
||||||
|
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include <QTabBar>
|
||||||
|
|
||||||
|
class HidableTabWidget : public QTabWidget {
|
||||||
|
public:
|
||||||
|
void showTabBar(bool show) {
|
||||||
|
tabBar()->setVisible(show);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HIDABLETABWIDGET_H
|
||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include "bittorrent.h"
|
#include "bittorrent.h"
|
||||||
|
#include "misc.h"
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
@@ -103,12 +104,12 @@ void HttpConnection::write()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString HttpConnection::translateDocument(QString data) {
|
QString HttpConnection::translateDocument(QString data) {
|
||||||
std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", "options_imp"};
|
std::string contexts[] = {"TransferListFiltersWidget", "TransferListWidget", "PropertiesWidget", "GUI", "MainWindow", "HttpServer", "confirmDeletionDlg", "TrackerList", "TorrentFilesModel", "options_imp", "Preferences", "TrackersAdditionDlg", "ScanFoldersModel"};
|
||||||
int i=0;
|
int i=0;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
do {
|
do {
|
||||||
found = false;
|
found = false;
|
||||||
QRegExp regex("_\\(([\\w\\s?!:\\/\\(\\)\\.]+)\\)");
|
QRegExp regex(QString::fromUtf8("_\\(([\\w\\s?!:\\/\\(\\),%µ&\\-\\.]+)\\)"));
|
||||||
i = regex.indexIn(data, i);
|
i = regex.indexIn(data, i);
|
||||||
if(i >= 0) {
|
if(i >= 0) {
|
||||||
//qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
|
//qDebug("Found translatable string: %s", regex.cap(1).toUtf8().data());
|
||||||
@@ -116,9 +117,11 @@ QString HttpConnection::translateDocument(QString data) {
|
|||||||
QString translation = word;
|
QString translation = word;
|
||||||
int context_index= 0;
|
int context_index= 0;
|
||||||
do {
|
do {
|
||||||
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().data(), 0, QCoreApplication::UnicodeUTF8, 1);
|
translation = qApp->translate(contexts[context_index].c_str(), word.toLocal8Bit().constData(), 0, QCoreApplication::UnicodeUTF8, 1);
|
||||||
++context_index;
|
++context_index;
|
||||||
}while(translation == word && context_index < 10);
|
}while(translation == word && context_index < 13);
|
||||||
|
// Remove keyboard shortcuts
|
||||||
|
translation = translation.replace("&", "");
|
||||||
//qDebug("Translation is %s", translation.toUtf8().data());
|
//qDebug("Translation is %s", translation.toUtf8().data());
|
||||||
data = data.replace(i, regex.matchedLength(), translation);
|
data = data.replace(i, regex.matchedLength(), translation);
|
||||||
i += translation.length();
|
i += translation.length();
|
||||||
@@ -128,18 +131,54 @@ QString HttpConnection::translateDocument(QString data) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpConnection::respond()
|
void HttpConnection::respond() {
|
||||||
{
|
|
||||||
//qDebug("Respond called");
|
//qDebug("Respond called");
|
||||||
QStringList auth = parser.value("Authorization").split(" ", QString::SkipEmptyParts);
|
const QString peer_ip = socket->peerAddress().toString();
|
||||||
if (auth.size() != 2 || QString::compare(auth[0], "Basic", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth[1].toLocal8Bit()))
|
const int nb_fail = parent->NbFailedAttemptsForIp(peer_ip);
|
||||||
{
|
if(nb_fail >= MAX_AUTH_FAILED_ATTEMPTS) {
|
||||||
generator.setStatusLine(401, "Unauthorized");
|
generator.setStatusLine(403, "Forbidden");
|
||||||
generator.setValue("WWW-Authenticate", "Basic realm=\"you know what\"");
|
generator.setMessage(tr("Your IP address has been banned after too many failed authentication attempts."));
|
||||||
write();
|
write();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
QString auth = parser.value("Authorization");
|
||||||
|
if(auth.isEmpty()) {
|
||||||
|
// Return unauthorized header
|
||||||
|
qDebug("Auth is Empty...");
|
||||||
|
generator.setStatusLine(401, "Unauthorized");
|
||||||
|
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
|
||||||
|
write();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//qDebug("Auth: %s", qPrintable(auth.split(" ").first()));
|
||||||
|
if (QString::compare(auth.split(" ").first(), "Digest", Qt::CaseInsensitive) != 0 || !parent->isAuthorized(auth.toLocal8Bit(), parser.method())) {
|
||||||
|
// Update failed attempt counter
|
||||||
|
parent->increaseNbFailedAttemptsForIp(peer_ip);
|
||||||
|
qDebug("client IP: %s (%d failed attempts)", qPrintable(peer_ip), nb_fail+1);
|
||||||
|
// Return unauthorized header
|
||||||
|
generator.setStatusLine(401, "Unauthorized");
|
||||||
|
generator.setValue("WWW-Authenticate", "Digest realm=\""+QString(QBT_REALM)+"\", nonce=\""+parent->generateNonce()+"\", opaque=\""+parent->generateNonce()+"\", stale=\"false\", algorithm=\"MD5\", qop=\"auth\"");
|
||||||
|
write();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Client successfully authenticated, reset number of failed attempts
|
||||||
|
parent->resetNbFailedAttemptsForIp(peer_ip);
|
||||||
QString url = parser.url();
|
QString url = parser.url();
|
||||||
|
// Favicon
|
||||||
|
if(url.endsWith("favicon.ico")) {
|
||||||
|
qDebug("Returning favicon");
|
||||||
|
QFile favicon(":/Icons/skin/qbittorrent16.png");
|
||||||
|
if(favicon.open(QIODevice::ReadOnly)) {
|
||||||
|
QByteArray data = favicon.readAll();
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("png");
|
||||||
|
generator.setMessage(data);
|
||||||
|
write();
|
||||||
|
} else {
|
||||||
|
respondNotFound();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
QStringList list = url.split('/', QString::SkipEmptyParts);
|
QStringList list = url.split('/', QString::SkipEmptyParts);
|
||||||
if (list.contains(".") || list.contains(".."))
|
if (list.contains(".") || list.contains(".."))
|
||||||
{
|
{
|
||||||
@@ -176,6 +215,10 @@ void HttpConnection::respond()
|
|||||||
} else {
|
} else {
|
||||||
if(list[1] == "preferences") {
|
if(list[1] == "preferences") {
|
||||||
respondPreferencesJson();
|
respondPreferencesJson();
|
||||||
|
} else {
|
||||||
|
if(list[1] == "transferInfo") {
|
||||||
|
respondGlobalTransferInfoJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,6 +239,7 @@ void HttpConnection::respond()
|
|||||||
QFile file(url);
|
QFile file(url);
|
||||||
if(!file.open(QIODevice::ReadOnly))
|
if(!file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
|
qDebug("File %s was not found!", qPrintable(url));
|
||||||
respondNotFound();
|
respondNotFound();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -207,7 +251,7 @@ void HttpConnection::respond()
|
|||||||
ext.clear();
|
ext.clear();
|
||||||
QByteArray data = file.readAll();
|
QByteArray data = file.readAll();
|
||||||
// Translate the page
|
// Translate the page
|
||||||
if(ext == "html" || ext == "js") {
|
if(ext == "html" || (ext == "js" && !list.last().startsWith("excanvas"))) {
|
||||||
data = translateDocument(QString::fromUtf8(data.data())).toUtf8();
|
data = translateDocument(QString::fromUtf8(data.data())).toUtf8();
|
||||||
}
|
}
|
||||||
generator.setStatusLine(200, "OK");
|
generator.setStatusLine(200, "OK");
|
||||||
@@ -268,6 +312,18 @@ void HttpConnection::respondPreferencesJson() {
|
|||||||
write();
|
write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HttpConnection::respondGlobalTransferInfoJson() {
|
||||||
|
QVariantMap info;
|
||||||
|
session_status sessionStatus = BTSession->getSessionStatus();
|
||||||
|
info["DlInfos"] = tr("D: %1/s - T: %2", "Download speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_download_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_download));
|
||||||
|
info["UpInfos"] = tr("U: %1/s - T: %2", "Upload speed: x KiB/s - Transferred: x MiB").arg(misc::friendlyUnit(sessionStatus.payload_upload_rate)).arg(misc::friendlyUnit(sessionStatus.total_payload_upload));
|
||||||
|
QString string = json::toJson(info);
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("js");
|
||||||
|
generator.setMessage(string);
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
|
||||||
void HttpConnection::respondCommand(QString command)
|
void HttpConnection::respondCommand(QString command)
|
||||||
{
|
{
|
||||||
if(command == "download")
|
if(command == "download")
|
||||||
@@ -277,6 +333,10 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
foreach(QString url, list){
|
foreach(QString url, list){
|
||||||
url = url.trimmed();
|
url = url.trimmed();
|
||||||
if(!url.isEmpty()){
|
if(!url.isEmpty()){
|
||||||
|
if(url.startsWith("bc://bt/", Qt::CaseInsensitive)) {
|
||||||
|
qDebug("Converting bc link to magnet link");
|
||||||
|
url = misc::bcLinkToMagnet(url);
|
||||||
|
}
|
||||||
if(url.startsWith("magnet:", Qt::CaseInsensitive)) {
|
if(url.startsWith("magnet:", Qt::CaseInsensitive)) {
|
||||||
emit MagnetReadyToBeDownloaded(url);
|
emit MagnetReadyToBeDownloaded(url);
|
||||||
} else {
|
} else {
|
||||||
@@ -287,6 +347,20 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(command == "addTrackers") {
|
||||||
|
QString hash = parser.post("hash");
|
||||||
|
if(!hash.isEmpty()) {
|
||||||
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
|
if(h.is_valid() && h.has_metadata()) {
|
||||||
|
QString urls = parser.post("urls");
|
||||||
|
QStringList list = urls.split('\n');
|
||||||
|
foreach(QString url, list) {
|
||||||
|
announce_entry e(url.toStdString());
|
||||||
|
h.add_tracker(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if(command == "upload")
|
if(command == "upload")
|
||||||
{
|
{
|
||||||
QByteArray torrentfile = parser.torrent();
|
QByteArray torrentfile = parser.torrent();
|
||||||
@@ -309,6 +383,11 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
torrent.close();
|
torrent.close();
|
||||||
}
|
}
|
||||||
emit torrentReadyToBeDownloaded(filePath, false, QString(), false);
|
emit torrentReadyToBeDownloaded(filePath, false, QString(), false);
|
||||||
|
// Prepare response
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("html");
|
||||||
|
generator.setMessage(QString("<script type=\"text/javascript\">window.parent.hideAll();</script>"));
|
||||||
|
write();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(command == "resumeall") {
|
if(command == "resumeall") {
|
||||||
@@ -324,47 +403,31 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(command == "setPreferences") {
|
if(command == "setPreferences") {
|
||||||
bool ok = false;
|
QString json_str = parser.post("json");
|
||||||
int dl_limit = parser.post("dl_limit").toInt(&ok);
|
EventManager* manager = parent->eventManager();
|
||||||
if(ok) {
|
manager->setGlobalPreferences(json::fromJson(json_str));
|
||||||
BTSession->setDownloadRateLimit(dl_limit*1024);
|
|
||||||
Preferences::setGlobalDownloadLimit(dl_limit);
|
|
||||||
}
|
|
||||||
int up_limit = parser.post("up_limit").toInt(&ok);
|
|
||||||
if(ok) {
|
|
||||||
BTSession->setUploadRateLimit(up_limit*1024);
|
|
||||||
Preferences::setGlobalUploadLimit(up_limit);
|
|
||||||
}
|
|
||||||
int dht_state = parser.post("dht").toInt(&ok);
|
|
||||||
if(ok) {
|
|
||||||
BTSession->enableDHT(dht_state == 1);
|
|
||||||
Preferences::setDHTEnabled(dht_state == 1);
|
|
||||||
}
|
|
||||||
int max_connec = parser.post("max_connec").toInt(&ok);
|
|
||||||
if(ok) {
|
|
||||||
BTSession->setMaxConnections(max_connec);
|
|
||||||
Preferences::setMaxConnecs(max_connec);
|
|
||||||
}
|
|
||||||
int max_connec_per_torrent = parser.post("max_connec_per_torrent").toInt(&ok);
|
|
||||||
if(ok) {
|
|
||||||
BTSession->setMaxConnectionsPerTorrent(max_connec_per_torrent);
|
|
||||||
Preferences::setMaxConnecsPerTorrent(max_connec_per_torrent);
|
|
||||||
}
|
|
||||||
int max_uploads_per_torrent = parser.post("max_uploads_per_torrent").toInt(&ok);
|
|
||||||
if(ok) {
|
|
||||||
BTSession->setMaxUploadsPerTorrent(max_uploads_per_torrent);
|
|
||||||
Preferences::setMaxUploadsPerTorrent(max_uploads_per_torrent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if(command == "setFilePrio") {
|
if(command == "setFilePrio") {
|
||||||
QString hash = parser.post("hash");
|
QString hash = parser.post("hash");
|
||||||
int file_id = parser.post("id").toInt();
|
int file_id = parser.post("id").toInt();
|
||||||
int priority = parser.post("priority").toInt();
|
int priority = parser.post("priority").toInt();
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
if(h.is_valid()) {
|
if(h.is_valid() && h.has_metadata()) {
|
||||||
h.file_priority(file_id, priority);
|
h.file_priority(file_id, priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(command == "getGlobalUpLimit") {
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("html");
|
||||||
|
generator.setMessage(QString::number(BTSession->getSession()->upload_rate_limit()));
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
if(command == "getGlobalDlLimit") {
|
||||||
|
generator.setStatusLine(200, "OK");
|
||||||
|
generator.setContentTypeByExt("html");
|
||||||
|
generator.setMessage(QString::number(BTSession->getSession()->download_rate_limit()));
|
||||||
|
write();
|
||||||
|
}
|
||||||
if(command == "getTorrentUpLimit") {
|
if(command == "getTorrentUpLimit") {
|
||||||
QString hash = parser.post("hash");
|
QString hash = parser.post("hash");
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
@@ -403,6 +466,18 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
h.set_download_limit(limit);
|
h.set_download_limit(limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(command == "setGlobalUpLimit") {
|
||||||
|
qlonglong limit = parser.post("limit").toLongLong();
|
||||||
|
if(limit == 0) limit = -1;
|
||||||
|
BTSession->getSession()->set_upload_rate_limit(limit);
|
||||||
|
Preferences::setGlobalUploadLimit(limit/1024.);
|
||||||
|
}
|
||||||
|
if(command == "setGlobalDlLimit") {
|
||||||
|
qlonglong limit = parser.post("limit").toLongLong();
|
||||||
|
if(limit == 0) limit = -1;
|
||||||
|
BTSession->getSession()->set_download_rate_limit(limit);
|
||||||
|
Preferences::setGlobalDownloadLimit(limit/1024.);
|
||||||
|
}
|
||||||
if(command == "pause") {
|
if(command == "pause") {
|
||||||
emit pauseTorrent(parser.post("hash"));
|
emit pauseTorrent(parser.post("hash"));
|
||||||
return;
|
return;
|
||||||
@@ -425,6 +500,16 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
if(h.is_valid()) h.queue_position_down();
|
if(h.is_valid()) h.queue_position_down();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(command == "topPrio") {
|
||||||
|
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
|
||||||
|
if(h.is_valid()) h.queue_position_top();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(command == "bottomPrio") {
|
||||||
|
QTorrentHandle h = BTSession->getTorrentHandle(parser.post("hash"));
|
||||||
|
if(h.is_valid()) h.queue_position_bottom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(command == "recheck"){
|
if(command == "recheck"){
|
||||||
recheckTorrent(parser.post("hash"));
|
recheckTorrent(parser.post("hash"));
|
||||||
return;
|
return;
|
||||||
@@ -437,8 +522,8 @@ void HttpConnection::respondCommand(QString command)
|
|||||||
|
|
||||||
void HttpConnection::recheckTorrent(QString hash) {
|
void HttpConnection::recheckTorrent(QString hash) {
|
||||||
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
QTorrentHandle h = BTSession->getTorrentHandle(hash);
|
||||||
if(h.is_valid() && !h.is_paused()){
|
if(h.is_valid()){
|
||||||
h.force_recheck();
|
BTSession->recheckTorrent(h.hash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,7 +532,7 @@ void HttpConnection::recheckAllTorrents() {
|
|||||||
std::vector<torrent_handle>::iterator torrentIT;
|
std::vector<torrent_handle>::iterator torrentIT;
|
||||||
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
|
for(torrentIT = torrents.begin(); torrentIT != torrents.end(); torrentIT++) {
|
||||||
QTorrentHandle h = QTorrentHandle(*torrentIT);
|
QTorrentHandle h = QTorrentHandle(*torrentIT);
|
||||||
if(h.is_valid() && !h.is_paused())
|
if(h.is_valid())
|
||||||
h.force_recheck();
|
BTSession->recheckTorrent(h.hash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class HttpConnection : public QObject
|
|||||||
void respondTrackersPropertiesJson(QString hash);
|
void respondTrackersPropertiesJson(QString hash);
|
||||||
void respondFilesPropertiesJson(QString hash);
|
void respondFilesPropertiesJson(QString hash);
|
||||||
void respondPreferencesJson();
|
void respondPreferencesJson();
|
||||||
|
void respondGlobalTransferInfoJson();
|
||||||
void respondCommand(QString command);
|
void respondCommand(QString command);
|
||||||
void respondNotFound();
|
void respondNotFound();
|
||||||
void processDownloadedFile(QString, QString);
|
void processDownloadedFile(QString, QString);
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ void HttpResponseGenerator::setMessage(const QByteArray message)
|
|||||||
|
|
||||||
void HttpResponseGenerator::setMessage(const QString message)
|
void HttpResponseGenerator::setMessage(const QString message)
|
||||||
{
|
{
|
||||||
setMessage(message.QString::toLocal8Bit());
|
// This must be UTF-8!
|
||||||
|
setMessage(message.toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpResponseGenerator::stripMessage()
|
void HttpResponseGenerator::stripMessage()
|
||||||
|
|||||||
@@ -34,10 +34,55 @@
|
|||||||
#include "eventmanager.h"
|
#include "eventmanager.h"
|
||||||
#include "bittorrent.h"
|
#include "bittorrent.h"
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QRegExp>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent)
|
const int BAN_TIME = 3600000; // 1 hour
|
||||||
{
|
|
||||||
base64 = QByteArray(":").toBase64();
|
class UnbanTimer: public QTimer {
|
||||||
|
public:
|
||||||
|
UnbanTimer(QObject *parent, QString peer_ip): QTimer(parent), peer_ip(peer_ip){
|
||||||
|
setSingleShot(true);
|
||||||
|
setInterval(BAN_TIME);
|
||||||
|
}
|
||||||
|
~UnbanTimer() {
|
||||||
|
qDebug("||||||||||||Deleting ban timer|||||||||||||||");
|
||||||
|
}
|
||||||
|
QString peer_ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
void HttpServer::UnbanTimerEvent() {
|
||||||
|
UnbanTimer* ubantimer = static_cast<UnbanTimer*>(sender());
|
||||||
|
qDebug("Ban period has expired for %s", qPrintable(ubantimer->peer_ip));
|
||||||
|
client_failed_attempts.remove(ubantimer->peer_ip);
|
||||||
|
ubantimer->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpServer::NbFailedAttemptsForIp(QString ip) const {
|
||||||
|
return client_failed_attempts.value(ip, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::increaseNbFailedAttemptsForIp(QString ip) {
|
||||||
|
const int nb_fail = client_failed_attempts.value(ip, 0);
|
||||||
|
client_failed_attempts.insert(ip, nb_fail+1);
|
||||||
|
if(nb_fail == MAX_AUTH_FAILED_ATTEMPTS-1) {
|
||||||
|
// Max number of failed attempts reached
|
||||||
|
// Start ban period
|
||||||
|
UnbanTimer* ubantimer = new UnbanTimer(this, ip);
|
||||||
|
connect(ubantimer, SIGNAL(timeout()), this, SLOT(UnbanTimerEvent()));
|
||||||
|
ubantimer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::resetNbFailedAttemptsForIp(QString ip) {
|
||||||
|
client_failed_attempts.remove(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcpServer(parent) {
|
||||||
|
username = Preferences::getWebUiUsername().toLocal8Bit();
|
||||||
|
password_ha1 = Preferences::getWebUiPassword().toLocal8Bit();
|
||||||
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
|
connect(this, SIGNAL(newConnection()), this, SLOT(newHttpConnection()));
|
||||||
BTSession = _BTSession;
|
BTSession = _BTSession;
|
||||||
manager = new EventManager(this, BTSession);
|
manager = new EventManager(this, BTSession);
|
||||||
@@ -74,6 +119,12 @@ HttpServer::HttpServer(Bittorrent *_BTSession, int msec, QObject* parent) : QTcp
|
|||||||
a = tr("Maximum number of connections per torrent limit must be greater than 0 or disabled.");
|
a = tr("Maximum number of connections per torrent limit must be greater than 0 or disabled.");
|
||||||
a = tr("Maximum number of upload slots per torrent limit must be greater than 0 or disabled.");
|
a = tr("Maximum number of upload slots per torrent limit must be greater than 0 or disabled.");
|
||||||
a = tr("Unable to save program preferences, qBittorrent is probably unreachable.");
|
a = tr("Unable to save program preferences, qBittorrent is probably unreachable.");
|
||||||
|
a = tr("Language");
|
||||||
|
a = tr("Downloaded", "Is the file downloaded or not?");
|
||||||
|
a = tr("The port used for incoming connections must be greater than 1024 and less than 65535.");
|
||||||
|
a = tr("The port used for the Web UI must be greater than 1024 and less than 65535.");
|
||||||
|
a = tr("The Web UI username must be at least 3 characters long.");
|
||||||
|
a = tr("The Web UI password must be at least 3 characters long.");
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServer::~HttpServer()
|
HttpServer::~HttpServer()
|
||||||
@@ -110,15 +161,106 @@ void HttpServer::onTimer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpServer::setAuthorization(QString username, QString password)
|
QString HttpServer::generateNonce() const {
|
||||||
{
|
QCryptographicHash md5(QCryptographicHash::Md5);
|
||||||
QString cat = username + ":" + password;
|
md5.addData(QTime::currentTime().toString("hhmmsszzz").toLocal8Bit());
|
||||||
base64 = QByteArray(cat.toLocal8Bit()).toBase64();
|
md5.addData(":");
|
||||||
|
md5.addData(QBT_REALM);
|
||||||
|
return md5.result().toHex();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpServer::isAuthorized(QByteArray auth) const
|
void HttpServer::setAuthorization(QString _username, QString _password_ha1) {
|
||||||
{
|
username = _username.toLocal8Bit();
|
||||||
return (auth == base64);
|
password_ha1 = _password_ha1.toLocal8Bit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse HTTP AUTH string
|
||||||
|
// http://tools.ietf.org/html/rfc2617
|
||||||
|
bool HttpServer::isAuthorized(QByteArray auth, QString method) const {
|
||||||
|
//qDebug("AUTH string is %s", auth.data());
|
||||||
|
// Get user name
|
||||||
|
QRegExp regex_user(".*username=\"([^\"]+)\".*"); // Must be a quoted string
|
||||||
|
if(regex_user.indexIn(auth) < 0) return false;
|
||||||
|
QString prop_user = regex_user.cap(1);
|
||||||
|
//qDebug("AUTH: Proposed username is %s, real username is %s", prop_user.toLocal8Bit().data(), username.data());
|
||||||
|
if(prop_user != username) {
|
||||||
|
// User name is invalid, we can reject already
|
||||||
|
qDebug("AUTH-PROB: Username is invalid");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Get realm
|
||||||
|
QRegExp regex_realm(".*realm=\"([^\"]+)\".*"); // Must be a quoted string
|
||||||
|
if(regex_realm.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: Missing realm");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_realm = regex_realm.cap(1).toLocal8Bit();
|
||||||
|
if(prop_realm != QBT_REALM) {
|
||||||
|
qDebug("AUTH-PROB: Wrong realm");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// get nonce
|
||||||
|
QRegExp regex_nonce(".*nonce=[\"]?([\\w=]+)[\"]?.*");
|
||||||
|
if(regex_nonce.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: missing nonce");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_nonce = regex_nonce.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop nonce is: %s", prop_nonce.data());
|
||||||
|
// get uri
|
||||||
|
QRegExp regex_uri(".*uri=\"([^\"]+)\".*");
|
||||||
|
if(regex_uri.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: Missing uri");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_uri = regex_uri.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop uri is: %s", prop_uri.data());
|
||||||
|
// get response
|
||||||
|
QRegExp regex_response(".*response=[\"]?([\\w=]+)[\"]?.*");
|
||||||
|
if(regex_response.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: Missing response");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_response = regex_response.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop response is: %s", prop_response.data());
|
||||||
|
// Compute correct reponse
|
||||||
|
QCryptographicHash md5_ha2(QCryptographicHash::Md5);
|
||||||
|
md5_ha2.addData(method.toLocal8Bit() + ":" + prop_uri);
|
||||||
|
QByteArray ha2 = md5_ha2.result().toHex();
|
||||||
|
QByteArray response = "";
|
||||||
|
if(auth.contains("qop=")) {
|
||||||
|
QCryptographicHash md5_ha(QCryptographicHash::Md5);
|
||||||
|
// Get nc
|
||||||
|
QRegExp regex_nc(".*nc=[\"]?([\\w=]+)[\"]?.*");
|
||||||
|
if(regex_nc.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: qop but missing nc");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_nc = regex_nc.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop nc is: %s", prop_nc.data());
|
||||||
|
QRegExp regex_cnonce(".*cnonce=[\"]?([\\w=]+)[\"]?.*");
|
||||||
|
if(regex_cnonce.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: qop but missing cnonce");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_cnonce = regex_cnonce.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop cnonce is: %s", prop_cnonce.data());
|
||||||
|
QRegExp regex_qop(".*qop=[\"]?(\\w+)[\"]?.*");
|
||||||
|
if(regex_qop.indexIn(auth) < 0) {
|
||||||
|
qDebug("AUTH-PROB: missing qop");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QByteArray prop_qop = regex_qop.cap(1).toLocal8Bit();
|
||||||
|
qDebug("prop qop is: %s", prop_qop.data());
|
||||||
|
md5_ha.addData(password_ha1+":"+prop_nonce+":"+prop_nc+":"+prop_cnonce+":"+prop_qop+":"+ha2);
|
||||||
|
response = md5_ha.result().toHex();
|
||||||
|
} else {
|
||||||
|
QCryptographicHash md5_ha(QCryptographicHash::Md5);
|
||||||
|
md5_ha.addData(password_ha1+":"+prop_nonce+":"+ha2);
|
||||||
|
response = md5_ha.result().toHex();
|
||||||
|
}
|
||||||
|
qDebug("AUTH: comparing reponses: (%d)", static_cast<int>(prop_response == response));
|
||||||
|
return prop_response == response;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventManager* HttpServer::eventManager() const
|
EventManager* HttpServer::eventManager() const
|
||||||
|
|||||||
@@ -35,30 +35,41 @@
|
|||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QTcpServer>
|
#include <QTcpServer>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
#include <QHash>
|
||||||
|
#include "preferences.h"
|
||||||
|
|
||||||
class Bittorrent;
|
class Bittorrent;
|
||||||
class QTimer;
|
class QTimer;
|
||||||
class EventManager;
|
class EventManager;
|
||||||
|
|
||||||
|
const int MAX_AUTH_FAILED_ATTEMPTS = 5;
|
||||||
|
|
||||||
class HttpServer : public QTcpServer {
|
class HttpServer : public QTcpServer {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
public:
|
||||||
QByteArray base64;
|
HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
|
||||||
|
~HttpServer();
|
||||||
|
void setAuthorization(QString username, QString password_ha1);
|
||||||
|
bool isAuthorized(QByteArray auth, QString method) const;
|
||||||
|
EventManager *eventManager() const;
|
||||||
|
QString generateNonce() const;
|
||||||
|
int NbFailedAttemptsForIp(QString ip) const;
|
||||||
|
void increaseNbFailedAttemptsForIp(QString ip);
|
||||||
|
void resetNbFailedAttemptsForIp(QString ip);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void newHttpConnection();
|
||||||
|
void onTimer();
|
||||||
|
void UnbanTimerEvent();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray username;
|
||||||
|
QByteArray password_ha1;
|
||||||
Bittorrent *BTSession;
|
Bittorrent *BTSession;
|
||||||
EventManager *manager;
|
EventManager *manager;
|
||||||
QTimer *timer;
|
QTimer *timer;
|
||||||
|
QHash<QString, int> client_failed_attempts;
|
||||||
public:
|
|
||||||
HttpServer(Bittorrent *BTSession, int msec, QObject* parent = 0);
|
|
||||||
~HttpServer();
|
|
||||||
void setAuthorization(QString username, QString password);
|
|
||||||
bool isAuthorized(QByteArray auth) const;
|
|
||||||
EventManager *eventManager() const;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void newHttpConnection();
|
|
||||||
void onTimer();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
306
src/icons.qrc
@@ -1,175 +1,183 @@
|
|||||||
<!DOCTYPE RCC><RCC version="1.0">
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
<file>Icons/rss32.png</file>
|
|
||||||
<file>Icons/sphere2.png</file>
|
|
||||||
<file>Icons/downarrow.png</file>
|
|
||||||
<file>Icons/url.png</file>
|
<file>Icons/url.png</file>
|
||||||
<file>Icons/locale.png</file>
|
|
||||||
<file>Icons/loading.png</file>
|
<file>Icons/loading.png</file>
|
||||||
<file>Icons/magnet.png</file>
|
|
||||||
<file>Icons/sphere.png</file>
|
|
||||||
<file>Icons/uparrow.png</file>
|
|
||||||
<file>Icons/rss16.png</file>
|
<file>Icons/rss16.png</file>
|
||||||
<file>Icons/skin/checkingUP.png</file>
|
<file>Icons/rss32.png</file>
|
||||||
<file>Icons/skin/bg-handle-horizontal.gif</file>
|
<file>Icons/3-state-checkbox.gif</file>
|
||||||
<file>Icons/skin/play.png</file>
|
<file>Icons/uparrow.png</file>
|
||||||
<file>Icons/skin/qbittorrent22.png</file>
|
<file>Icons/sphere.png</file>
|
||||||
<file>Icons/skin/queuedDL.png</file>
|
<file>Icons/downarrow.png</file>
|
||||||
<file>Icons/skin/new.png</file>
|
<file>Icons/slow_off.png</file>
|
||||||
<file>Icons/skin/queuedUP.png</file>
|
<file>Icons/sphere2.png</file>
|
||||||
<file>Icons/skin/preview.png</file>
|
<file>Icons/magnet.png</file>
|
||||||
<file>Icons/skin/stalled.png</file>
|
<file>Icons/slow.png</file>
|
||||||
<file>Icons/skin/delete.png</file>
|
<file>Icons/L.gif</file>
|
||||||
<file>Icons/skin/handle-icon-horizontal.gif</file>
|
|
||||||
<file>Icons/skin/handle-icon.gif</file>
|
|
||||||
<file>Icons/skin/knob.gif</file>
|
|
||||||
<file>Icons/skin/url.png</file>
|
|
||||||
<file>Icons/skin/stalledUP.png</file>
|
|
||||||
<file>Icons/skin/delete_perm22.png</file>
|
<file>Icons/skin/delete_perm22.png</file>
|
||||||
<file>Icons/skin/filteractive.png</file>
|
|
||||||
<file>Icons/skin/connected.png</file>
|
|
||||||
<file>Icons/skin/pausedDL.png</file>
|
|
||||||
<file>Icons/skin/mascot.png</file>
|
|
||||||
<file>Icons/skin/pausedUP.png</file>
|
|
||||||
<file>Icons/skin/seeding.png</file>
|
<file>Icons/skin/seeding.png</file>
|
||||||
<file>Icons/skin/increase.png</file>
|
|
||||||
<file>Icons/skin/qbittorrent32.png</file>
|
|
||||||
<file>Icons/skin/paused.png</file>
|
|
||||||
<file>Icons/skin/toolbox-divider.gif</file>
|
|
||||||
<file>Icons/skin/stalledDL.png</file>
|
|
||||||
<file>Icons/skin/qb_question.png</file>
|
|
||||||
<file>Icons/skin/download.png</file>
|
|
||||||
<file>Icons/skin/open.png</file>
|
|
||||||
<file>Icons/skin/play22.png</file>
|
|
||||||
<file>Icons/skin/qbittorrent16.png</file>
|
|
||||||
<file>Icons/skin/slider-area.gif</file>
|
|
||||||
<file>Icons/skin/downloading.png</file>
|
|
||||||
<file>Icons/skin/filterinactive.png</file>
|
|
||||||
<file>Icons/skin/pause22.png</file>
|
|
||||||
<file>Icons/skin/pause_all.png</file>
|
|
||||||
<file>Icons/skin/delete22.png</file>
|
|
||||||
<file>Icons/skin/play_all.png</file>
|
|
||||||
<file>Icons/skin/pause.png</file>
|
|
||||||
<file>Icons/skin/firewalled.png</file>
|
|
||||||
<file>Icons/skin/properties.png</file>
|
|
||||||
<file>Icons/skin/info.png</file>
|
|
||||||
<file>Icons/skin/tabs.gif</file>
|
|
||||||
<file>Icons/skin/delete_perm.png</file>
|
|
||||||
<file>Icons/skin/checkingDL.png</file>
|
|
||||||
<file>Icons/skin/settings.png</file>
|
|
||||||
<file>Icons/skin/exit.png</file>
|
|
||||||
<file>Icons/skin/delete_all.png</file>
|
|
||||||
<file>Icons/skin/splash.png</file>
|
<file>Icons/skin/splash.png</file>
|
||||||
<file>Icons/skin/decrease.png</file>
|
<file>Icons/skin/preview.png</file>
|
||||||
<file>Icons/skin/uploading.png</file>
|
<file>Icons/skin/tabs.gif</file>
|
||||||
|
<file>Icons/skin/delete.png</file>
|
||||||
|
<file>Icons/skin/increase.png</file>
|
||||||
|
<file>Icons/skin/qbittorrent16.png</file>
|
||||||
|
<file>Icons/skin/connected.png</file>
|
||||||
|
<file>Icons/skin/url.png</file>
|
||||||
|
<file>Icons/skin/qbittorrent22.png</file>
|
||||||
|
<file>Icons/skin/disconnected.png</file>
|
||||||
|
<file>Icons/skin/mascot.png</file>
|
||||||
|
<file>Icons/skin/info.png</file>
|
||||||
|
<file>Icons/skin/paused.png</file>
|
||||||
|
<file>Icons/skin/slider-area.gif</file>
|
||||||
|
<file>Icons/skin/pause22.png</file>
|
||||||
|
<file>Icons/skin/stalledDL.png</file>
|
||||||
|
<file>Icons/skin/play.png</file>
|
||||||
|
<file>Icons/skin/delete_all.png</file>
|
||||||
|
<file>Icons/skin/delete_perm.png</file>
|
||||||
|
<file>Icons/skin/qb_question.png</file>
|
||||||
|
<file>Icons/skin/error.png</file>
|
||||||
|
<file>Icons/skin/settings.png</file>
|
||||||
|
<file>Icons/skin/handle-icon-horizontal.gif</file>
|
||||||
|
<file>Icons/skin/pause.png</file>
|
||||||
|
<file>Icons/skin/qbittorrent32.png</file>
|
||||||
|
<file>Icons/skin/exit.png</file>
|
||||||
|
<file>Icons/skin/knob.gif</file>
|
||||||
|
<file>Icons/skin/open.png</file>
|
||||||
|
<file>Icons/skin/pause_all.png</file>
|
||||||
<file>Icons/skin/filterall.png</file>
|
<file>Icons/skin/filterall.png</file>
|
||||||
<file>Icons/flags/czech.png</file>
|
<file>Icons/skin/uploading.png</file>
|
||||||
<file>Icons/flags/serbia.png</file>
|
<file>Icons/skin/queued.png</file>
|
||||||
<file>Icons/flags/iceland.png</file>
|
<file>Icons/skin/checking.png</file>
|
||||||
<file>Icons/flags/china.png</file>
|
<file>Icons/skin/handle-icon.gif</file>
|
||||||
<file>Icons/flags/luxembourg.png</file>
|
<file>Icons/skin/arrow-right.gif</file>
|
||||||
<file>Icons/flags/newzealand.png</file>
|
<file>Icons/skin/filterinactive.png</file>
|
||||||
<file>Icons/flags/austria.png</file>
|
<file>Icons/skin/decrease.png</file>
|
||||||
<file>Icons/flags/indonesia.png</file>
|
<file>Icons/skin/play22.png</file>
|
||||||
<file>Icons/flags/united_arab_emirates.png</file>
|
<file>Icons/skin/firewalled.png</file>
|
||||||
<file>Icons/flags/norway.png</file>
|
<file>Icons/skin/delete22.png</file>
|
||||||
<file>Icons/flags/india.png</file>
|
<file>Icons/skin/new.png</file>
|
||||||
<file>Icons/flags/finland.png</file>
|
<file>Icons/skin/downloading.png</file>
|
||||||
<file>Icons/flags/australia.png</file>
|
<file>Icons/skin/play_all.png</file>
|
||||||
|
<file>Icons/skin/toolbox-divider.gif</file>
|
||||||
|
<file>Icons/skin/stalledUP.png</file>
|
||||||
|
<file>Icons/skin/filteractive.png</file>
|
||||||
|
<file>Icons/skin/bg-handle-horizontal.gif</file>
|
||||||
|
<file>Icons/skin/download.png</file>
|
||||||
|
<file>Icons/flags/pakistan.png</file>
|
||||||
|
<file>Icons/flags/argentina.png</file>
|
||||||
<file>Icons/flags/netherlands.png</file>
|
<file>Icons/flags/netherlands.png</file>
|
||||||
<file>Icons/flags/south_africa.png</file>
|
<file>Icons/flags/australia.png</file>
|
||||||
<file>Icons/flags/belarus.png</file>
|
<file>Icons/flags/finland.png</file>
|
||||||
<file>Icons/flags/georgia.png</file>
|
|
||||||
<file>Icons/flags/taiwan.png</file>
|
|
||||||
<file>Icons/flags/sweden.png</file>
|
|
||||||
<file>Icons/flags/spain_catalunya.png</file>
|
|
||||||
<file>Icons/flags/ireland.png</file>
|
|
||||||
<file>Icons/flags/singapore.png</file>
|
|
||||||
<file>Icons/flags/israel.png</file>
|
|
||||||
<file>Icons/flags/belgium.png</file>
|
|
||||||
<file>Icons/flags/usa.png</file>
|
|
||||||
<file>Icons/flags/costa_rica.png</file>
|
|
||||||
<file>Icons/flags/romania.png</file>
|
|
||||||
<file>Icons/flags/suisse.png</file>
|
|
||||||
<file>Icons/flags/croatia.png</file>
|
<file>Icons/flags/croatia.png</file>
|
||||||
<file>Icons/flags/mexico.png</file>
|
<file>Icons/flags/indonesia.png</file>
|
||||||
|
<file>Icons/flags/greece.png</file>
|
||||||
|
<file>Icons/flags/belarus.png</file>
|
||||||
|
<file>Icons/flags/morocco.png</file>
|
||||||
|
<file>Icons/flags/portugal.png</file>
|
||||||
|
<file>Icons/flags/egypt.png</file>
|
||||||
|
<file>Icons/flags/georgia.png</file>
|
||||||
|
<file>Icons/flags/costa_rica.png</file>
|
||||||
<file>Icons/flags/denmark.png</file>
|
<file>Icons/flags/denmark.png</file>
|
||||||
<file>Icons/flags/hungary.png</file>
|
<file>Icons/flags/bosnia.png</file>
|
||||||
<file>Icons/flags/slovenia.png</file>
|
<file>Icons/flags/newzealand.png</file>
|
||||||
<file>Icons/flags/ukraine.png</file>
|
<file>Icons/flags/serbia.png</file>
|
||||||
|
<file>Icons/flags/singapore.png</file>
|
||||||
|
<file>Icons/flags/italy.png</file>
|
||||||
|
<file>Icons/flags/brazil.png</file>
|
||||||
|
<file>Icons/flags/taiwan.png</file>
|
||||||
|
<file>Icons/flags/south_korea.png</file>
|
||||||
<file>Icons/flags/turkey.png</file>
|
<file>Icons/flags/turkey.png</file>
|
||||||
<file>Icons/flags/algeria.png</file>
|
<file>Icons/flags/algeria.png</file>
|
||||||
<file>Icons/flags/greece.png</file>
|
|
||||||
<file>Icons/flags/morocco.png</file>
|
|
||||||
<file>Icons/flags/argentina.png</file>
|
|
||||||
<file>Icons/flags/spain.png</file>
|
|
||||||
<file>Icons/flags/portugal.png</file>
|
|
||||||
<file>Icons/flags/russia.png</file>
|
|
||||||
<file>Icons/flags/united_kingdom.png</file>
|
|
||||||
<file>Icons/flags/thailand.png</file>
|
|
||||||
<file>Icons/flags/poland.png</file>
|
|
||||||
<file>Icons/flags/germany.png</file>
|
|
||||||
<file>Icons/flags/bulgaria.png</file>
|
|
||||||
<file>Icons/flags/canada.png</file>
|
|
||||||
<file>Icons/flags/brazil.png</file>
|
|
||||||
<file>Icons/flags/france.png</file>
|
|
||||||
<file>Icons/flags/slovakia.png</file>
|
|
||||||
<file>Icons/flags/pakistan.png</file>
|
|
||||||
<file>Icons/flags/egypt.png</file>
|
|
||||||
<file>Icons/flags/italy.png</file>
|
|
||||||
<file>Icons/flags/south_korea.png</file>
|
|
||||||
<file>Icons/flags/bosnia.png</file>
|
|
||||||
<file>Icons/flags/japan.png</file>
|
<file>Icons/flags/japan.png</file>
|
||||||
<file>Icons/flags/malaysia.png</file>
|
<file>Icons/flags/luxembourg.png</file>
|
||||||
|
<file>Icons/flags/poland.png</file>
|
||||||
|
<file>Icons/flags/iceland.png</file>
|
||||||
|
<file>Icons/flags/spain.png</file>
|
||||||
|
<file>Icons/flags/saoudi_arabia.png</file>
|
||||||
|
<file>Icons/flags/norway.png</file>
|
||||||
|
<file>Icons/flags/russia.png</file>
|
||||||
|
<file>Icons/flags/slovakia.png</file>
|
||||||
<file>Icons/flags/philippines.png</file>
|
<file>Icons/flags/philippines.png</file>
|
||||||
<file>Icons/oxygen/mail-queue.png</file>
|
<file>Icons/flags/thailand.png</file>
|
||||||
<file>Icons/oxygen/view-refresh.png</file>
|
<file>Icons/flags/israel.png</file>
|
||||||
<file>Icons/oxygen/file.png</file>
|
<file>Icons/flags/bulgaria.png</file>
|
||||||
<file>Icons/oxygen/folder-remote16.png</file>
|
<file>Icons/flags/czech.png</file>
|
||||||
<file>Icons/oxygen/mail-folder-inbox.png</file>
|
<file>Icons/flags/usa.png</file>
|
||||||
<file>Icons/oxygen/edit-find.png</file>
|
<file>Icons/flags/malaysia.png</file>
|
||||||
<file>Icons/oxygen/folder-new.png</file>
|
<file>Icons/flags/belgium.png</file>
|
||||||
<file>Icons/oxygen/folder-remote.png</file>
|
<file>Icons/flags/south_africa.png</file>
|
||||||
<file>Icons/oxygen/edit-paste.png</file>
|
<file>Icons/flags/slovenia.png</file>
|
||||||
<file>Icons/oxygen/run-build.png</file>
|
<file>Icons/flags/china.png</file>
|
||||||
<file>Icons/oxygen/proxy.png</file>
|
<file>Icons/flags/germany.png</file>
|
||||||
<file>Icons/oxygen/user-group-delete.png</file>
|
<file>Icons/flags/ukraine.png</file>
|
||||||
<file>Icons/oxygen/user-group-new.png</file>
|
<file>Icons/flags/india.png</file>
|
||||||
<file>Icons/oxygen/log.png</file>
|
<file>Icons/flags/spain_catalunya.png</file>
|
||||||
<file>Icons/oxygen/unavailable.png</file>
|
<file>Icons/flags/romania.png</file>
|
||||||
<file>Icons/oxygen/button_ok.png</file>
|
<file>Icons/flags/united_kingdom.png</file>
|
||||||
<file>Icons/oxygen/button_cancel.png</file>
|
<file>Icons/flags/united_arab_emirates.png</file>
|
||||||
<file>Icons/oxygen/edit-clear.png</file>
|
<file>Icons/flags/sweden.png</file>
|
||||||
<file>Icons/oxygen/filter.png</file>
|
<file>Icons/flags/canada.png</file>
|
||||||
<file>Icons/oxygen/encrypted.png</file>
|
<file>Icons/flags/mexico.png</file>
|
||||||
<file>Icons/oxygen/edit_clear.png</file>
|
<file>Icons/flags/hungary.png</file>
|
||||||
<file>Icons/oxygen/download.png</file>
|
<file>Icons/flags/france.png</file>
|
||||||
<file>Icons/oxygen/application-x-kgetlist-no.png</file>
|
<file>Icons/flags/ireland.png</file>
|
||||||
<file>Icons/oxygen/gear.png</file>
|
<file>Icons/flags/austria.png</file>
|
||||||
<file>Icons/oxygen/remove.png</file>
|
<file>Icons/flags/suisse.png</file>
|
||||||
<file>Icons/oxygen/dialog-warning.png</file>
|
|
||||||
<file>Icons/oxygen/peer.png</file>
|
<file>Icons/oxygen/peer.png</file>
|
||||||
<file>Icons/oxygen/browse.png</file>
|
<file>Icons/oxygen/unavailable.png</file>
|
||||||
<file>Icons/oxygen/unsubscribe16.png</file>
|
|
||||||
<file>Icons/oxygen/subscribe.png</file>
|
<file>Icons/oxygen/subscribe.png</file>
|
||||||
<file>Icons/oxygen/edit-copy.png</file>
|
|
||||||
<file>Icons/oxygen/bt_settings.png</file>
|
|
||||||
<file>Icons/oxygen/document-new.png</file>
|
|
||||||
<file>Icons/oxygen/preferences-desktop.png</file>
|
|
||||||
<file>Icons/oxygen/tab-close.png</file>
|
|
||||||
<file>Icons/oxygen/wallet.png</file>
|
|
||||||
<file>Icons/oxygen/webui.png</file>
|
|
||||||
<file>Icons/oxygen/list-remove.png</file>
|
<file>Icons/oxygen/list-remove.png</file>
|
||||||
<file>Icons/oxygen/connection.png</file>
|
<file>Icons/oxygen/dialog-warning.png</file>
|
||||||
<file>Icons/oxygen/bug.png</file>
|
<file>Icons/oxygen/mail-folder-inbox.png</file>
|
||||||
<file>Icons/oxygen/help-about.png</file>
|
|
||||||
<file>Icons/oxygen/list-add.png</file>
|
|
||||||
<file>Icons/oxygen/network-server.png</file>
|
|
||||||
<file>Icons/oxygen/application-x-kgetlist.png</file>
|
|
||||||
<file>Icons/oxygen/folder.png</file>
|
<file>Icons/oxygen/folder.png</file>
|
||||||
|
<file>Icons/oxygen/edit-copy.png</file>
|
||||||
|
<file>Icons/oxygen/folder-documents.png</file>
|
||||||
<file>Icons/oxygen/urlseed.png</file>
|
<file>Icons/oxygen/urlseed.png</file>
|
||||||
|
<file>Icons/oxygen/go-up.png</file>
|
||||||
<file>Icons/oxygen/edit-cut.png</file>
|
<file>Icons/oxygen/edit-cut.png</file>
|
||||||
|
<file>Icons/oxygen/gear32.png</file>
|
||||||
|
<file>Icons/oxygen/go-bottom.png</file>
|
||||||
|
<file>Icons/oxygen/user-group-delete.png</file>
|
||||||
<file>Icons/oxygen/unsubscribe.png</file>
|
<file>Icons/oxygen/unsubscribe.png</file>
|
||||||
|
<file>Icons/oxygen/tab-close.png</file>
|
||||||
|
<file>Icons/oxygen/file.png</file>
|
||||||
|
<file>Icons/oxygen/services.png</file>
|
||||||
|
<file>Icons/oxygen/view-refresh.png</file>
|
||||||
|
<file>Icons/oxygen/feed-subscribe.png</file>
|
||||||
|
<file>Icons/oxygen/remove.png</file>
|
||||||
|
<file>Icons/oxygen/chronometer.png</file>
|
||||||
|
<file>Icons/oxygen/filter.png</file>
|
||||||
|
<file>Icons/oxygen/run-build.png</file>
|
||||||
|
<file>Icons/oxygen/button_ok.png</file>
|
||||||
|
<file>Icons/oxygen/user-group-new.png</file>
|
||||||
|
<file>Icons/oxygen/cookies.png</file>
|
||||||
|
<file>Icons/oxygen/network-server.png</file>
|
||||||
|
<file>Icons/oxygen/unsubscribe16.png</file>
|
||||||
|
<file>Icons/oxygen/encrypted32.png</file>
|
||||||
|
<file>Icons/oxygen/list-add.png</file>
|
||||||
|
<file>Icons/oxygen/edit-paste.png</file>
|
||||||
|
<file>Icons/oxygen/folder-remote.png</file>
|
||||||
|
<file>Icons/oxygen/help-about.png</file>
|
||||||
|
<file>Icons/oxygen/encrypted.png</file>
|
||||||
|
<file>Icons/oxygen/folder-remote16.png</file>
|
||||||
|
<file>Icons/oxygen/go-top.png</file>
|
||||||
|
<file>Icons/oxygen/edit_clear.png</file>
|
||||||
|
<file>Icons/oxygen/bug.png</file>
|
||||||
|
<file>Icons/oxygen/gear.png</file>
|
||||||
|
<file>Icons/oxygen/connection.png</file>
|
||||||
|
<file>Icons/oxygen/document-new.png</file>
|
||||||
|
<file>Icons/oxygen/browse.png</file>
|
||||||
|
<file>Icons/oxygen/proxy.png</file>
|
||||||
|
<file>Icons/oxygen/button_cancel.png</file>
|
||||||
|
<file>Icons/oxygen/preferences-desktop.png</file>
|
||||||
|
<file>Icons/oxygen/bt_settings.png</file>
|
||||||
|
<file>Icons/oxygen/go-down.png</file>
|
||||||
<file>Icons/oxygen/subscribe16.png</file>
|
<file>Icons/oxygen/subscribe16.png</file>
|
||||||
|
<file>Icons/oxygen/download.png</file>
|
||||||
|
<file>Icons/oxygen/log.png</file>
|
||||||
|
<file>Icons/oxygen/edit-find.png</file>
|
||||||
|
<file>Icons/oxygen/edit-clear.png</file>
|
||||||
|
<file>Icons/oxygen/webui.png</file>
|
||||||
|
<file>Icons/oxygen/folder-new.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
76
src/json.h
@@ -47,7 +47,16 @@ namespace json {
|
|||||||
case QVariant::LongLong:
|
case QVariant::LongLong:
|
||||||
case QVariant::UInt:
|
case QVariant::UInt:
|
||||||
case QVariant::ULongLong:
|
case QVariant::ULongLong:
|
||||||
|
//case QMetaType::Float:
|
||||||
return v.value<QString>();
|
return v.value<QString>();
|
||||||
|
case QVariant::StringList:
|
||||||
|
case QVariant::List: {
|
||||||
|
QStringList strList;
|
||||||
|
foreach(const QVariant &var, v.toList()) {
|
||||||
|
strList << toJson(var);
|
||||||
|
}
|
||||||
|
return "["+strList.join(",")+"]";
|
||||||
|
}
|
||||||
case QVariant::String:
|
case QVariant::String:
|
||||||
{
|
{
|
||||||
QString s = v.value<QString>();
|
QString s = v.value<QString>();
|
||||||
@@ -86,6 +95,7 @@ namespace json {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
qDebug("Unknown QVariantType: %d", (int)v.type());
|
||||||
return "undefined";
|
return "undefined";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,6 +108,72 @@ namespace json {
|
|||||||
return "{"+vlist.join(",")+"}";
|
return "{"+vlist.join(",")+"}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QVariantMap fromJson(QString json) {
|
||||||
|
qDebug("JSON is %s", qPrintable(json));
|
||||||
|
QVariantMap m;
|
||||||
|
if(json.startsWith("{") && json.endsWith("}")) {
|
||||||
|
json.chop(1);
|
||||||
|
json = json.replace(0, 1, "");
|
||||||
|
QStringList couples;
|
||||||
|
QString tmp = "";
|
||||||
|
bool in_list = false;
|
||||||
|
foreach(QChar c, json) {
|
||||||
|
if(c == ',' && !in_list) {
|
||||||
|
couples << tmp;
|
||||||
|
tmp = "";
|
||||||
|
} else {
|
||||||
|
if(c == '[') {
|
||||||
|
in_list = true;
|
||||||
|
} else {
|
||||||
|
if(c == ']') {
|
||||||
|
in_list = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!tmp.isEmpty()) couples << tmp;
|
||||||
|
foreach(QString couple, couples) {
|
||||||
|
QStringList parts = couple.split(":");
|
||||||
|
if(parts.size() != 2) continue;
|
||||||
|
QString key = parts.first();
|
||||||
|
if(key.startsWith("\"") && key.endsWith("\"")) {
|
||||||
|
key.chop(1);
|
||||||
|
key = key.replace(0, 1, "");
|
||||||
|
}
|
||||||
|
QString value_str = parts.last();
|
||||||
|
QVariant value;
|
||||||
|
if(value_str.startsWith("[") && value_str.endsWith("]")) {
|
||||||
|
value_str.chop(1);
|
||||||
|
value_str.replace(0, 1, "");
|
||||||
|
QStringList list_elems = value_str.split(",", QString::SkipEmptyParts);
|
||||||
|
QVariantList varlist;
|
||||||
|
foreach(QString list_val, list_elems) {
|
||||||
|
if(list_val.startsWith("\"") && list_val.endsWith("\"")) {
|
||||||
|
list_val.chop(1);
|
||||||
|
list_val = list_val.replace(0, 1, "");
|
||||||
|
varlist << list_val;
|
||||||
|
} else {
|
||||||
|
varlist << list_val.toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value = varlist;
|
||||||
|
} else {
|
||||||
|
if(value_str.startsWith("\"") && value_str.endsWith("\"")) {
|
||||||
|
value_str.chop(1);
|
||||||
|
value_str = value_str.replace(0, 1, "");
|
||||||
|
value = value_str;
|
||||||
|
} else {
|
||||||
|
value = value_str.toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.insert(key,value);
|
||||||
|
qDebug("%s:%s", key.toLocal8Bit().data(), value_str.toLocal8Bit().data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
QString toJson(QList<QVariantMap> v) {
|
QString toJson(QList<QVariantMap> v) {
|
||||||
QStringList res;
|
QStringList res;
|
||||||
foreach(QVariantMap m, v) {
|
foreach(QVariantMap m, v) {
|
||||||
|
|||||||