mirror of
https://github.com/nzbget/nzbget.git
synced 2025-12-24 06:37:44 -05:00
Compare commits
2249 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e26d52d70 | ||
|
|
ae81c9403d | ||
|
|
b0d35f9a09 | ||
|
|
ce7cd631c2 | ||
|
|
0432cf13d3 | ||
|
|
799de88b3e | ||
|
|
7ff3251dcf | ||
|
|
97ae03bbd3 | ||
|
|
6bbfb6b7b7 | ||
|
|
f02bbbefd7 | ||
|
|
4d19c899bd | ||
|
|
1d008bd1f5 | ||
|
|
8c1e62ef49 | ||
|
|
e18c25c231 | ||
|
|
6dbe6edbab | ||
|
|
1ee8e02586 | ||
|
|
f8f9dd2b6d | ||
|
|
414ffcbc35 | ||
|
|
0522b5f49d | ||
|
|
575b823758 | ||
|
|
a124a91a84 | ||
|
|
4546b0f368 | ||
|
|
625e7a61e1 | ||
|
|
f1c1373c7d | ||
|
|
5dda6b2e49 | ||
|
|
81aa56324f | ||
|
|
a8533e7f0a | ||
|
|
bbfcf07689 | ||
|
|
fd35e05b61 | ||
|
|
3e0be12cb3 | ||
|
|
d6e8f67927 | ||
|
|
a159a1ff5a | ||
|
|
15f4955f38 | ||
|
|
aac98b53ee | ||
|
|
fa4a5bb261 | ||
|
|
d19c9b80e7 | ||
|
|
c7716ae9b7 | ||
|
|
e07a6b9443 | ||
|
|
fa57474d78 | ||
|
|
4299ac1354 | ||
|
|
82dfec471b | ||
|
|
3a5bc85962 | ||
|
|
25dc60e71f | ||
|
|
855f3e8649 | ||
|
|
bdc7ba38db | ||
|
|
89427f42ce | ||
|
|
a665dc5375 | ||
|
|
adf3e05e1d | ||
|
|
e3bd94189a | ||
|
|
bb1cb68653 | ||
|
|
e91f37d566 | ||
|
|
57f4d2864b | ||
|
|
05c841880f | ||
|
|
92828acab0 | ||
|
|
137c936830 | ||
|
|
4826f04778 | ||
|
|
15b4f55310 | ||
|
|
0461f2ad55 | ||
|
|
67ca371c6b | ||
|
|
a97a6d7c7f | ||
|
|
b6927e992e | ||
|
|
59cae49344 | ||
|
|
6bf097f1c3 | ||
|
|
ad0592843c | ||
|
|
8a09de775f | ||
|
|
adf7ec225b | ||
|
|
d15722c72d | ||
|
|
0776c6b057 | ||
|
|
8f63eef312 | ||
|
|
8a59079627 | ||
|
|
491d816bff | ||
|
|
6dfe17c1d8 | ||
|
|
f3cf9317a6 | ||
|
|
b0356d88d6 | ||
|
|
c0d7a15afa | ||
|
|
fbfa793b20 | ||
|
|
a329c65eb3 | ||
|
|
b9c4c5b19e | ||
|
|
a5f2c1c7c5 | ||
|
|
e2ea481799 | ||
|
|
c2b93c588b | ||
|
|
3934244a70 | ||
|
|
62ba9a5609 | ||
|
|
e7d4556f8b | ||
|
|
43c9bb78f3 | ||
|
|
e824c5b940 | ||
|
|
32a6bf18ad | ||
|
|
2cb419691d | ||
|
|
a74722d8cc | ||
|
|
0602e9d2f1 | ||
|
|
9713cbad5e | ||
|
|
009cf9eee2 | ||
|
|
85995ad56f | ||
|
|
1f3067c1e3 | ||
|
|
794f240f48 | ||
|
|
49e8fea0e2 | ||
|
|
fa8f8855f9 | ||
|
|
2c85def959 | ||
|
|
fb3a27fde9 | ||
|
|
b29131ffb8 | ||
|
|
31a34b58ea | ||
|
|
4a10fdb2df | ||
|
|
541a695e2f | ||
|
|
07b7a766a2 | ||
|
|
1057e9194c | ||
|
|
9eaf9fae9a | ||
|
|
34d157990d | ||
|
|
c93eb2087f | ||
|
|
1f89c037b9 | ||
|
|
20036b73b8 | ||
|
|
da3425af3f | ||
|
|
4c482a91da | ||
|
|
458a1afb13 | ||
|
|
3339a2c520 | ||
|
|
17c5a9cbc8 | ||
|
|
4db9ef2535 | ||
|
|
5a0eae7bf4 | ||
|
|
86ac23b6aa | ||
|
|
fa1aa45fa7 | ||
|
|
f842a19544 | ||
|
|
f3cb44e7b2 | ||
|
|
e54ffbaaaa | ||
|
|
2d049f1904 | ||
|
|
5106979d5d | ||
|
|
75d05bce4a | ||
|
|
ea4ea2c901 | ||
|
|
5e15677218 | ||
|
|
93ad31b9d8 | ||
|
|
14c5a1caf7 | ||
|
|
f52f5b5de9 | ||
|
|
0916c2a908 | ||
|
|
ab1238dde4 | ||
|
|
758ce4047b | ||
|
|
c24bf0e8ce | ||
|
|
1264878a97 | ||
|
|
a85ff314f3 | ||
|
|
a349ab08f7 | ||
|
|
064de49edf | ||
|
|
ae79c56c07 | ||
|
|
d6353e9cee | ||
|
|
d9d824631e | ||
|
|
2bd765b06f | ||
|
|
f51c216417 | ||
|
|
78b270d23e | ||
|
|
a4252a1e79 | ||
|
|
1ac2be47d5 | ||
|
|
9437a227ee | ||
|
|
0d19722881 | ||
|
|
adfe5eef26 | ||
|
|
321cddeeba | ||
|
|
44f08325f9 | ||
|
|
e601e77e5e | ||
|
|
8e6ccfa8a7 | ||
|
|
3eebee20aa | ||
|
|
b83a9b9aff | ||
|
|
05d7a8ede2 | ||
|
|
4d771036e2 | ||
|
|
7e659d8d97 | ||
|
|
137ac1a3ee | ||
|
|
3a4e6623db | ||
|
|
c2669b359e | ||
|
|
8bffb51974 | ||
|
|
81be21b540 | ||
|
|
222d6a1f6d | ||
|
|
cd6bf682f9 | ||
|
|
d93769021a | ||
|
|
cf0d086b57 | ||
|
|
bf53c6eaa6 | ||
|
|
9b50760006 | ||
|
|
b7102894d7 | ||
|
|
db102f5a15 | ||
|
|
d9cb0026bd | ||
|
|
5893d03f1b | ||
|
|
18d138648b | ||
|
|
93a43e711f | ||
|
|
2b52dc5bfe | ||
|
|
ce844367e7 | ||
|
|
64a5a78866 | ||
|
|
6f9fb29595 | ||
|
|
0ee60ab844 | ||
|
|
8dfca2a542 | ||
|
|
76bdd63e60 | ||
|
|
ef78cbfc74 | ||
|
|
74768b2183 | ||
|
|
801bf1ae7c | ||
|
|
a901deff03 | ||
|
|
67245d6ca8 | ||
|
|
2d70e1de21 | ||
|
|
7deb3c1b68 | ||
|
|
d4886ac7d1 | ||
|
|
5b3372107d | ||
|
|
07c54740a7 | ||
|
|
af111adbde | ||
|
|
d31a734a5c | ||
|
|
54f14f5efa | ||
|
|
18fbd12f2c | ||
|
|
ff671e722d | ||
|
|
15c292653e | ||
|
|
c0aed9af48 | ||
|
|
597e4fd034 | ||
|
|
3c2575bc26 | ||
|
|
50c1ca588c | ||
|
|
da9c8b1138 | ||
|
|
c59ab2d9dc | ||
|
|
35fca1479c | ||
|
|
54c5a061c8 | ||
|
|
3a0489a4a9 | ||
|
|
a31fb733a2 | ||
|
|
2691eff535 | ||
|
|
37b04c593a | ||
|
|
b9b1c76ada | ||
|
|
69a0db63f6 | ||
|
|
e9926d92e0 | ||
|
|
f5aa27979c | ||
|
|
24a4542c14 | ||
|
|
bb95e1f274 | ||
|
|
186da63056 | ||
|
|
1facedb694 | ||
|
|
54eb8e1291 | ||
|
|
80b67383e3 | ||
|
|
406a78218a | ||
|
|
262df77f74 | ||
|
|
71505340d0 | ||
|
|
bddb0bb26d | ||
|
|
d90a40909b | ||
|
|
2bdc87c198 | ||
|
|
e97a0fde11 | ||
|
|
eb18608522 | ||
|
|
481e7b3d2b | ||
|
|
8545cb3581 | ||
|
|
e422fea746 | ||
|
|
2ce9f0df38 | ||
|
|
36de095e51 | ||
|
|
9b05f779f6 | ||
|
|
38efd4a4de | ||
|
|
80b8ee8dfb | ||
|
|
47b1c1a2dd | ||
|
|
7417160da9 | ||
|
|
a41e010165 | ||
|
|
cbe7b1e051 | ||
|
|
00a5b68d84 | ||
|
|
561713dbed | ||
|
|
cce9338909 | ||
|
|
515fd9298d | ||
|
|
0ee72d2dd7 | ||
|
|
57f932cfab | ||
|
|
8f803c2f21 | ||
|
|
89bd5d6dfe | ||
|
|
49a0292053 | ||
|
|
a60d8d1273 | ||
|
|
44ea3d02ab | ||
|
|
fe9f208f20 | ||
|
|
20e8bb6ebc | ||
|
|
0709f248ee | ||
|
|
35d8aa5fa7 | ||
|
|
9f80f45fb9 | ||
|
|
763fe425d6 | ||
|
|
9c86dc70bd | ||
|
|
1f6a360de5 | ||
|
|
dcdc41ca9a | ||
|
|
6d307a05f8 | ||
|
|
83c15b1f05 | ||
|
|
43fc121219 | ||
|
|
4b729eb0f0 | ||
|
|
0158614da2 | ||
|
|
ca7807fa92 | ||
|
|
97018ae102 | ||
|
|
cbe6c6a340 | ||
|
|
d84ec5685b | ||
|
|
557e0580a7 | ||
|
|
43c0bdd9d3 | ||
|
|
86bcb7073c | ||
|
|
6cf0edd278 | ||
|
|
b4bcc82abe | ||
|
|
6fb1ea1cff | ||
|
|
3ee9125100 | ||
|
|
fad2be0e0f | ||
|
|
2763f1a522 | ||
|
|
1214c79eab | ||
|
|
7ee0b60361 | ||
|
|
0135e605a8 | ||
|
|
546324d891 | ||
|
|
43563e8dfb | ||
|
|
4f5d357e3c | ||
|
|
a6c120bc82 | ||
|
|
18f673e6b3 | ||
|
|
5ac7c0398e | ||
|
|
0008f040b3 | ||
|
|
45b5727374 | ||
|
|
f001b0744b | ||
|
|
2124a886f8 | ||
|
|
7f4b15b4de | ||
|
|
68c74a5a30 | ||
|
|
01d4ebb800 | ||
|
|
f56e01d200 | ||
|
|
cdc5c5515f | ||
|
|
67195e7683 | ||
|
|
499e3d5d8f | ||
|
|
0ee9083627 | ||
|
|
726a6154be | ||
|
|
c0eedc342b | ||
|
|
eb4b8b30e1 | ||
|
|
593e29f163 | ||
|
|
c4d29bc57f | ||
|
|
92db424ce0 | ||
|
|
17fbb795c8 | ||
|
|
35e65e792b | ||
|
|
486b9d7d2b | ||
|
|
3abaa0fb3f | ||
|
|
810ddc8356 | ||
|
|
1a840f894e | ||
|
|
928e0a6006 | ||
|
|
a4cca673dc | ||
|
|
fb9b84a23b | ||
|
|
bc2b9de6a9 | ||
|
|
7793f64b77 | ||
|
|
cad3116b5b | ||
|
|
dd714355c4 | ||
|
|
0a73a0c31f | ||
|
|
7f393d050c | ||
|
|
5dcca7294f | ||
|
|
53da5725c2 | ||
|
|
77cabd7bce | ||
|
|
2336d4bcfe | ||
|
|
580e1974bc | ||
|
|
8790ee685f | ||
|
|
32f0bbae58 | ||
|
|
8ffd6c24fe | ||
|
|
98cc4817fe | ||
|
|
14b40d6712 | ||
|
|
629640898d | ||
|
|
5df06a2626 | ||
|
|
4ca95b2989 | ||
|
|
b3cc316092 | ||
|
|
298fde6cd4 | ||
|
|
015b3be461 | ||
|
|
f6adbe848d | ||
|
|
08de827d7b | ||
|
|
d1bda91954 | ||
|
|
e40e3178da | ||
|
|
cf3985f228 | ||
|
|
f2329dada4 | ||
|
|
a73d1ba56e | ||
|
|
8d5ce3ddc3 | ||
|
|
d3362f9280 | ||
|
|
85dcde36c5 | ||
|
|
716bb3130f | ||
|
|
725e2c7376 | ||
|
|
3a07dd378a | ||
|
|
ff02b53ed0 | ||
|
|
39089b6f2f | ||
|
|
61af2b3446 | ||
|
|
20f4f3020b | ||
|
|
df5a1fe959 | ||
|
|
6b546394b2 | ||
|
|
160b274ce8 | ||
|
|
4104a2357b | ||
|
|
ba1e51a8d8 | ||
|
|
9f7d6655b2 | ||
|
|
f6a9253a53 | ||
|
|
48020e8901 | ||
|
|
5afa20d655 | ||
|
|
197baf066a | ||
|
|
c873647aae | ||
|
|
fc1847588d | ||
|
|
62b3d47b43 | ||
|
|
ddb9333ca6 | ||
|
|
830e0e4858 | ||
|
|
085c612f97 | ||
|
|
dea9fb2090 | ||
|
|
5813c903eb | ||
|
|
4f499e2c2e | ||
|
|
3884328251 | ||
|
|
7e9e2471ef | ||
|
|
2a433ee7fb | ||
|
|
118f835385 | ||
|
|
9a6a42bd44 | ||
|
|
9434149842 | ||
|
|
4107536c03 | ||
|
|
2aec782f58 | ||
|
|
eaaa943af3 | ||
|
|
964e8311a9 | ||
|
|
895dd12e4d | ||
|
|
d16036aa78 | ||
|
|
8b79a81eaf | ||
|
|
231e94dd2e | ||
|
|
f7be22893d | ||
|
|
3ac91a4bb6 | ||
|
|
43441a8d55 | ||
|
|
b02059f196 | ||
|
|
f107802f0e | ||
|
|
7faf1fe64b | ||
|
|
bf2fea64e7 | ||
|
|
de3eb3de9d | ||
|
|
55d080ac96 | ||
|
|
795bacb4fe | ||
|
|
f7930b56a6 | ||
|
|
b2bf488d59 | ||
|
|
8e4b75b21e | ||
|
|
5b17bebbd6 | ||
|
|
bda1eaa192 | ||
|
|
4bcdbbeb09 | ||
|
|
f0ee12f92e | ||
|
|
0b14a2f869 | ||
|
|
2579ce65b9 | ||
|
|
77f86988cb | ||
|
|
b9b62dcd75 | ||
|
|
bfee7c55cd | ||
|
|
34efa87699 | ||
|
|
e839db7107 | ||
|
|
ffb16aa7bb | ||
|
|
a3b0b7675e | ||
|
|
602f62c17c | ||
|
|
ace7a1968d | ||
|
|
6efefe3780 | ||
|
|
c02f708d74 | ||
|
|
e7c47f523a | ||
|
|
5c8be152f4 | ||
|
|
4cdbfc84dd | ||
|
|
75ec856af3 | ||
|
|
88b5e16597 | ||
|
|
fff6fdd4eb | ||
|
|
c4ff544459 | ||
|
|
9c87942b43 | ||
|
|
0e4c9275b0 | ||
|
|
f11dce4269 | ||
|
|
f1340eb542 | ||
|
|
e89f5cbd8a | ||
|
|
975016700e | ||
|
|
a9017be606 | ||
|
|
d81d6831dc | ||
|
|
1b94373c7e | ||
|
|
d5e881ce1a | ||
|
|
a3f84aca0e | ||
|
|
0ab86b90f0 | ||
|
|
b6a606db35 | ||
|
|
578731f239 | ||
|
|
71db4ffe9c | ||
|
|
7421ec7b1a | ||
|
|
958712663e | ||
|
|
d96fa66487 | ||
|
|
36ac548842 | ||
|
|
fc44ab6128 | ||
|
|
f0da3936e5 | ||
|
|
04e694799d | ||
|
|
712cedb84f | ||
|
|
4fb3ddcf84 | ||
|
|
69d40c11fd | ||
|
|
58893710d8 | ||
|
|
6c6f781510 | ||
|
|
569ec22ee8 | ||
|
|
b06d3eca86 | ||
|
|
dcf63b4db7 | ||
|
|
06e7573572 | ||
|
|
639e0b6bfb | ||
|
|
f2073ff920 | ||
|
|
4ba2e07d94 | ||
|
|
91515b20e5 | ||
|
|
7226e1c186 | ||
|
|
98c2dd46b7 | ||
|
|
f0b08dbd63 | ||
|
|
25ddfd5659 | ||
|
|
7450b97871 | ||
|
|
9f7e0ee972 | ||
|
|
ae7719e948 | ||
|
|
9a92896678 | ||
|
|
ab0723adda | ||
|
|
13b98fca83 | ||
|
|
5901998cb5 | ||
|
|
9ddd73368e | ||
|
|
12daa683e5 | ||
|
|
5f71c4a5a7 | ||
|
|
13db817395 | ||
|
|
84f4cf2f33 | ||
|
|
0d67e322a3 | ||
|
|
374f706354 | ||
|
|
f7cefadf33 | ||
|
|
c111a114b9 | ||
|
|
2cd3f0fc68 | ||
|
|
92db59116e | ||
|
|
760aed68fb | ||
|
|
e612257c28 | ||
|
|
31208a5816 | ||
|
|
a03e7cd550 | ||
|
|
02b33fb559 | ||
|
|
a9ed53daa8 | ||
|
|
a3c460ed40 | ||
|
|
df11d1acb4 | ||
|
|
e49d4c59af | ||
|
|
5dc9c07a58 | ||
|
|
3bb0751c86 | ||
|
|
a3b3921bea | ||
|
|
b19d26aee8 | ||
|
|
7fae337360 | ||
|
|
80debf521a | ||
|
|
528133482e | ||
|
|
a98bbd7d0d | ||
|
|
2681fe187b | ||
|
|
e16cab67fb | ||
|
|
50e2e0cf37 | ||
|
|
d2d20f29c1 | ||
|
|
53f4992eeb | ||
|
|
3ae1be1ca4 | ||
|
|
ce1e1b61e7 | ||
|
|
cac25ed290 | ||
|
|
8ed0b70df5 | ||
|
|
d72071c8ed | ||
|
|
ba05dfc202 | ||
|
|
f3adab5690 | ||
|
|
b1b5405809 | ||
|
|
99fcd164ac | ||
|
|
eacfacc5a2 | ||
|
|
3404b544de | ||
|
|
3604534850 | ||
|
|
2e18021e08 | ||
|
|
2e19382ccc | ||
|
|
a6dc20dc8e | ||
|
|
a1bef9146e | ||
|
|
61f18f81b7 | ||
|
|
129125faa1 | ||
|
|
b97c987f66 | ||
|
|
53e504d974 | ||
|
|
5885258c35 | ||
|
|
2034ea97d2 | ||
|
|
2cc38a85df | ||
|
|
ccd509b70c | ||
|
|
bab43d8d30 | ||
|
|
ed4761db37 | ||
|
|
db7bc4314f | ||
|
|
8a21626bf6 | ||
|
|
804f8ab085 | ||
|
|
3593990dd6 | ||
|
|
3a38a2c7c6 | ||
|
|
16aef2e7a8 | ||
|
|
bf992195e8 | ||
|
|
2fb0fd1113 | ||
|
|
709c9856c9 | ||
|
|
9e9ef3120f | ||
|
|
a7525ae6f9 | ||
|
|
5e0e91f671 | ||
|
|
09f863f3af | ||
|
|
87e8d479d8 | ||
|
|
fb8c6b9cd3 | ||
|
|
bc98b0582c | ||
|
|
84388785e7 | ||
|
|
4c519cf646 | ||
|
|
f28c049c12 | ||
|
|
33dbbcb0b5 | ||
|
|
7f6cbd137f | ||
|
|
facd8cd41f | ||
|
|
e269d8f062 | ||
|
|
dd141e4cf5 | ||
|
|
f18ee92a23 | ||
|
|
b25a88683b | ||
|
|
9dc2b8c71b | ||
|
|
2ef393531a | ||
|
|
0c1fce27a2 | ||
|
|
606cf56150 | ||
|
|
af8451e16e | ||
|
|
203a828bbd | ||
|
|
a990078884 | ||
|
|
3e5bd203f3 | ||
|
|
042979d122 | ||
|
|
11c464ed46 | ||
|
|
070054a814 | ||
|
|
5841cf5002 | ||
|
|
6dda360986 | ||
|
|
12ff14b397 | ||
|
|
20aa83e0f7 | ||
|
|
4c8c7ef46b | ||
|
|
43a6717394 | ||
|
|
b4fc57977b | ||
|
|
4f9dbdadc3 | ||
|
|
418acb052f | ||
|
|
c741297d84 | ||
|
|
55fb4df411 | ||
|
|
35643b0207 | ||
|
|
45753410df | ||
|
|
b58de541b3 | ||
|
|
3d577777bb | ||
|
|
a0e9c537a3 | ||
|
|
a2d87de7b9 | ||
|
|
032a7a4770 | ||
|
|
b2dcc59845 | ||
|
|
2ad86ee738 | ||
|
|
dcf0318bea | ||
|
|
9973ec5f6c | ||
|
|
2b43695a01 | ||
|
|
19dd39d5e6 | ||
|
|
e5eddbe7ce | ||
|
|
0f279aaf6e | ||
|
|
62cd5c776c | ||
|
|
8c8f41ac33 | ||
|
|
51880a60e9 | ||
|
|
7dffe6f502 | ||
|
|
f591e1680d | ||
|
|
0e0d7be16b | ||
|
|
8ad12646fb | ||
|
|
92ae10e338 | ||
|
|
b2b3a2e281 | ||
|
|
355e7a5ab1 | ||
|
|
6f7be71a93 | ||
|
|
4d5a4bb428 | ||
|
|
717db2486a | ||
|
|
43d450b8ae | ||
|
|
4501d129c2 | ||
|
|
d14dc599d9 | ||
|
|
18d89435e4 | ||
|
|
a1e945661b | ||
|
|
150857816d | ||
|
|
2190eec25a | ||
|
|
b6dc2b6be9 | ||
|
|
1809fa9228 | ||
|
|
864fb92bc7 | ||
|
|
43f25587d2 | ||
|
|
f08d3918dc | ||
|
|
89143bc19f | ||
|
|
7c2cac135d | ||
|
|
11fc72e763 | ||
|
|
8c4d8cef1a | ||
|
|
788d8d1f42 | ||
|
|
a59b29c731 | ||
|
|
65398ac55f | ||
|
|
8c3e70b1de | ||
|
|
8de5461759 | ||
|
|
cb20dfb547 | ||
|
|
b82cc06789 | ||
|
|
5376abc717 | ||
|
|
6826410c6a | ||
|
|
85340831cc | ||
|
|
dd4df8b734 | ||
|
|
a4a3d1bc7a | ||
|
|
4f29804602 | ||
|
|
a97cb4c61e | ||
|
|
680171bd88 | ||
|
|
e3c976406d | ||
|
|
17dda0b0fc | ||
|
|
298178b2dc | ||
|
|
3e919c0cb4 | ||
|
|
f3814e4e48 | ||
|
|
3278544d46 | ||
|
|
44cf69b093 | ||
|
|
e6344d36a7 | ||
|
|
6b879e0c75 | ||
|
|
0b5168ed23 | ||
|
|
698edf1185 | ||
|
|
737f059b3a | ||
|
|
cb8df8495b | ||
|
|
d348929ff0 | ||
|
|
c0d29d88b9 | ||
|
|
181a395515 | ||
|
|
4f7849fbc1 | ||
|
|
857ada54ea | ||
|
|
01ef1c4024 | ||
|
|
1e59b9976a | ||
|
|
7d36881b29 | ||
|
|
8378ef8437 | ||
|
|
1f83fb8a39 | ||
|
|
67612d8dd5 | ||
|
|
a42df761fb | ||
|
|
d37a9364e0 | ||
|
|
caca0abb75 | ||
|
|
b71c990c2e | ||
|
|
5948729b87 | ||
|
|
f973388879 | ||
|
|
65e0238aa2 | ||
|
|
ca088ef2e6 | ||
|
|
1a48feba72 | ||
|
|
1d728996b2 | ||
|
|
22b1ae55e5 | ||
|
|
d7f5d8dea2 | ||
|
|
39cf412938 | ||
|
|
a5ac6d594f | ||
|
|
1a74695126 | ||
|
|
35049d436e | ||
|
|
069350eaa3 | ||
|
|
e3cce71329 | ||
|
|
6bae67412a | ||
|
|
9461678a19 | ||
|
|
c22c5f6c7b | ||
|
|
e5501cfa15 | ||
|
|
14d1906320 | ||
|
|
82ca298902 | ||
|
|
27fc3b594b | ||
|
|
b4a6b3954d | ||
|
|
b6f4c9f704 | ||
|
|
ee5cec4b16 | ||
|
|
f3f7fbd0de | ||
|
|
8a344c97c9 | ||
|
|
831c73d28d | ||
|
|
bfa5027bf9 | ||
|
|
194a4a66e5 | ||
|
|
ceb66387eb | ||
|
|
b18bc04606 | ||
|
|
b7f01fc58c | ||
|
|
7fc6d9e99e | ||
|
|
232c1a5597 | ||
|
|
1414a4976e | ||
|
|
048add1dcf | ||
|
|
1ae8132be2 | ||
|
|
72cd89d5f4 | ||
|
|
b414526d02 | ||
|
|
27eb78faab | ||
|
|
6bd5223b92 | ||
|
|
e8afb4e331 | ||
|
|
390481f814 | ||
|
|
4eb0e82f75 | ||
|
|
548753aa69 | ||
|
|
cdf6068db5 | ||
|
|
6aead41e6f | ||
|
|
e48cc0257b | ||
|
|
1d05477729 | ||
|
|
2b840315b0 | ||
|
|
177be1571e | ||
|
|
b9076fc21d | ||
|
|
9dfa66ac3e | ||
|
|
b5dae56c7e | ||
|
|
4a08fdb586 | ||
|
|
b910d123eb | ||
|
|
9da07e1e54 | ||
|
|
c96d2259ce | ||
|
|
36fe905bd9 | ||
|
|
40eb5c4e1e | ||
|
|
c9b883f909 | ||
|
|
d803330e3f | ||
|
|
8f4718fb9d | ||
|
|
1162c89e77 | ||
|
|
b55190e52f | ||
|
|
74f2ab8963 | ||
|
|
4fbff8cd25 | ||
|
|
c1f58220c2 | ||
|
|
624944b82a | ||
|
|
00d81795e9 | ||
|
|
ec87c3c1f8 | ||
|
|
c1ac38d10b | ||
|
|
b7d785797f | ||
|
|
2b2b1e1539 | ||
|
|
3ae8b5ac95 | ||
|
|
05a1926ebc | ||
|
|
ce64eeeb1d | ||
|
|
6e763f3573 | ||
|
|
b448fddfe6 | ||
|
|
69eb079851 | ||
|
|
ff69fbbeb9 | ||
|
|
26fea301e0 | ||
|
|
d95e389583 | ||
|
|
72370c9c71 | ||
|
|
d04e6a53da | ||
|
|
1aa59ded15 | ||
|
|
080bd22d77 | ||
|
|
8f84132218 | ||
|
|
34771792ac | ||
|
|
17024eb0e5 | ||
|
|
da0a0f3105 | ||
|
|
98cd5a8dbc | ||
|
|
421de1013f | ||
|
|
0ee644d252 | ||
|
|
ac1bd3d07c | ||
|
|
ef4a72d383 | ||
|
|
b32b4c0691 | ||
|
|
0732cb4b8b | ||
|
|
362ee700c5 | ||
|
|
93835ea2af | ||
|
|
32400a810f | ||
|
|
17999fb96d | ||
|
|
4f8370400f | ||
|
|
09dde2b82f | ||
|
|
f6587d3299 | ||
|
|
a70b86abd0 | ||
|
|
6885082299 | ||
|
|
37c126e24d | ||
|
|
476dae8c1e | ||
|
|
c949b27468 | ||
|
|
28897e4e79 | ||
|
|
81f2c7825d | ||
|
|
5d4b56b40b | ||
|
|
dc8803d6a3 | ||
|
|
be852d0a9b | ||
|
|
c8c86f00ef | ||
|
|
13fef5ce96 | ||
|
|
69c995359b | ||
|
|
3e89638b39 | ||
|
|
8dc9b4e396 | ||
|
|
28ec8a8ec3 | ||
|
|
1b457bceb7 | ||
|
|
2c152b51b6 | ||
|
|
9771d7f53f | ||
|
|
94b7ef8a37 | ||
|
|
04c3e0d263 | ||
|
|
74c0e8804f | ||
|
|
3aa97e44e7 | ||
|
|
5df8f73b20 | ||
|
|
a9181d8a56 | ||
|
|
df68fd52c8 | ||
|
|
65f2a0afe3 | ||
|
|
6292ad5394 | ||
|
|
b8e14faefe | ||
|
|
d8a2d79240 | ||
|
|
449a048304 | ||
|
|
f347f1aed1 | ||
|
|
03ba670d36 | ||
|
|
a9847831a5 | ||
|
|
920507c163 | ||
|
|
e11dfb62d0 | ||
|
|
c68ba306fe | ||
|
|
6840ef4439 | ||
|
|
dce6f0c9a3 | ||
|
|
8fb9fb7b2d | ||
|
|
eae6fd5d91 | ||
|
|
321c7efa41 | ||
|
|
19ce3bf69b | ||
|
|
c170d6a180 | ||
|
|
80f614a54c | ||
|
|
99d8cee0db | ||
|
|
d87d6ac2ac | ||
|
|
4f4a289309 | ||
|
|
44aaf77e5d | ||
|
|
9e2d8544da | ||
|
|
8394759527 | ||
|
|
4ba432dee3 | ||
|
|
5ea439c8a0 | ||
|
|
6d33d83d20 | ||
|
|
17d3e05e16 | ||
|
|
ecc7fc9695 | ||
|
|
558fce9b47 | ||
|
|
6b0fdc881e | ||
|
|
94e55e1b5a | ||
|
|
d6e05430bf | ||
|
|
fc50dc871a | ||
|
|
7e6c3f435a | ||
|
|
9512ec7d74 | ||
|
|
202200c74a | ||
|
|
fe4e9d1a94 | ||
|
|
c330704c9f | ||
|
|
1d77852bea | ||
|
|
15a5d056ed | ||
|
|
32009c5691 | ||
|
|
41c62dc413 | ||
|
|
9c3d6fadca | ||
|
|
726542b698 | ||
|
|
51172e1c96 | ||
|
|
f8049a81e1 | ||
|
|
80653a8dad | ||
|
|
4e4816c3c8 | ||
|
|
b5dd49dbc4 | ||
|
|
1f406a391a | ||
|
|
87d2c1a7f4 | ||
|
|
ec17d119a1 | ||
|
|
8630f1365c | ||
|
|
322b08e4b2 | ||
|
|
fc3c90605c | ||
|
|
afb310ab9b | ||
|
|
580df4d361 | ||
|
|
70ccfd9802 | ||
|
|
98bc1ebd37 | ||
|
|
a9a6f1e2d4 | ||
|
|
bf49f16d7c | ||
|
|
c46e6953e1 | ||
|
|
a72f787a40 | ||
|
|
1fb21b330e | ||
|
|
af85bb91fa | ||
|
|
57bee2447e | ||
|
|
050dc8d55f | ||
|
|
4a5063c14e | ||
|
|
5adb50274e | ||
|
|
665645b510 | ||
|
|
eb87111204 | ||
|
|
739925ecc8 | ||
|
|
c8f4712e77 | ||
|
|
13f5ab7388 | ||
|
|
7d2ee607a1 | ||
|
|
68a87c775c | ||
|
|
50d344cb91 | ||
|
|
94aa547a85 | ||
|
|
dfa18b50a4 | ||
|
|
2820ee4bc5 | ||
|
|
c9ff56cc7e | ||
|
|
9ea9da8d33 | ||
|
|
297a966da3 | ||
|
|
0c5dc22a93 | ||
|
|
e81b42f8dc | ||
|
|
2a302b3f0d | ||
|
|
eb78bdcf06 | ||
|
|
e7562b6470 | ||
|
|
01e672c7e2 | ||
|
|
3c638c5ca2 | ||
|
|
5356f5aafc | ||
|
|
dafb956e6e | ||
|
|
263f669873 | ||
|
|
ceec19c6fd | ||
|
|
a513dc0c01 | ||
|
|
1948dd2420 | ||
|
|
87f9cc68a0 | ||
|
|
d0897c2e09 | ||
|
|
5e392e98b6 | ||
|
|
caf2d919b4 | ||
|
|
111630b6b7 | ||
|
|
5418865a1b | ||
|
|
752d27ee08 | ||
|
|
afa32676ac | ||
|
|
e4d3773c22 | ||
|
|
04558bc25e | ||
|
|
12b6a2602a | ||
|
|
9e493c6c4d | ||
|
|
fdebae5cc2 | ||
|
|
ff8f2472a0 | ||
|
|
f732a0edc1 | ||
|
|
d48a16598f | ||
|
|
fb5a254b83 | ||
|
|
e01f1a37e5 | ||
|
|
4887941170 | ||
|
|
d57c895127 | ||
|
|
7385a1744b | ||
|
|
dc678b37f5 | ||
|
|
cdcbd783cd | ||
|
|
20b43ec736 | ||
|
|
cb41e3314c | ||
|
|
a9edcdf4fd | ||
|
|
f5daf39bb8 | ||
|
|
aeab407dc0 | ||
|
|
d2770d7a33 | ||
|
|
ab86d0efe2 | ||
|
|
5b4d3d8038 | ||
|
|
95fffe619d | ||
|
|
ea95a819c1 | ||
|
|
44197148d0 | ||
|
|
434ded22e2 | ||
|
|
46c8398942 | ||
|
|
1369b23974 | ||
|
|
d21badb6d5 | ||
|
|
4c51d2dc28 | ||
|
|
c0b3058f7c | ||
|
|
e5eb29be05 | ||
|
|
3d3fa4980e | ||
|
|
405ed766f4 | ||
|
|
cc62387292 | ||
|
|
1594345775 | ||
|
|
2d3d5af25e | ||
|
|
c6656cffbf | ||
|
|
e4d62ebbc8 | ||
|
|
af422050b6 | ||
|
|
5f52512a5f | ||
|
|
ca0af70d53 | ||
|
|
6e0a3e0ceb | ||
|
|
24fd4e8c15 | ||
|
|
1e64a7d453 | ||
|
|
8a03c64021 | ||
|
|
f8c1df1856 | ||
|
|
5b460b7512 | ||
|
|
45277c85e0 | ||
|
|
c38069443d | ||
|
|
6da5730356 | ||
|
|
2a9f9f8e98 | ||
|
|
a334a32b35 | ||
|
|
1583e84927 | ||
|
|
8a2fef7c46 | ||
|
|
6c3f2a9871 | ||
|
|
3ac2ccbc80 | ||
|
|
a11d22d3e0 | ||
|
|
d07b9af366 | ||
|
|
1a65409d0e | ||
|
|
941c2efb52 | ||
|
|
e99a0c5975 | ||
|
|
6b8c27fdcc | ||
|
|
820260cb6c | ||
|
|
96c73f05af | ||
|
|
c674405b44 | ||
|
|
6ac82f03d8 | ||
|
|
1a206457a2 | ||
|
|
6fd407b200 | ||
|
|
d8c6be9f52 | ||
|
|
46039698ca | ||
|
|
002547fde5 | ||
|
|
547b430c91 | ||
|
|
860f05eb70 | ||
|
|
4cc2beaaca | ||
|
|
d49ab2a087 | ||
|
|
c208eec5c3 | ||
|
|
c7047b1e33 | ||
|
|
659ed48652 | ||
|
|
a9a73b635c | ||
|
|
fc484ba0dd | ||
|
|
c6ad5523c7 | ||
|
|
0a8edc2388 | ||
|
|
3e5bb54ca4 | ||
|
|
7fc8238b23 | ||
|
|
06454eddcc | ||
|
|
c93c0e9dce | ||
|
|
4ec9f947c6 | ||
|
|
6436c3657d | ||
|
|
8d1ffa4947 | ||
|
|
5d6dab779e | ||
|
|
36d1378881 | ||
|
|
187679443f | ||
|
|
97e2776480 | ||
|
|
d7ab37ad31 | ||
|
|
1f7c15628a | ||
|
|
15e8a853fb | ||
|
|
1b248721e9 | ||
|
|
fde5f7e744 | ||
|
|
f28e1e76ff | ||
|
|
4daf01e683 | ||
|
|
3752a78fa1 | ||
|
|
bbc86a15a1 | ||
|
|
9864184606 | ||
|
|
a0730475f1 | ||
|
|
d2f6350fab | ||
|
|
d535ac781e | ||
|
|
332647c296 | ||
|
|
30f0051976 | ||
|
|
1efb67b60c | ||
|
|
4c3bec2a3f | ||
|
|
39063e4bcf | ||
|
|
da67342419 | ||
|
|
50155a0838 | ||
|
|
25f773efa8 | ||
|
|
c1fcf0b075 | ||
|
|
063d5a22ba | ||
|
|
c92c1c9a3d | ||
|
|
213eb2c7c1 | ||
|
|
b284dcc7ef | ||
|
|
9822eae8ff | ||
|
|
c3dd57abc6 | ||
|
|
3c65d13c00 | ||
|
|
b84bab52e0 | ||
|
|
059bd2b54e | ||
|
|
ec29f55f53 | ||
|
|
547e0e73de | ||
|
|
d4f1660a1a | ||
|
|
6c81365ee7 | ||
|
|
f0e779c9ea | ||
|
|
71bf3815c3 | ||
|
|
d7c14201ac | ||
|
|
e963ccefe5 | ||
|
|
37252faedc | ||
|
|
5144f8ba02 | ||
|
|
16f83417fe | ||
|
|
17a506009e | ||
|
|
0b0d7784e0 | ||
|
|
151204c8d1 | ||
|
|
ced1536195 | ||
|
|
cce367b83c | ||
|
|
8101780fa1 | ||
|
|
20fcd3436c | ||
|
|
215521b800 | ||
|
|
34f8b9f82c | ||
|
|
23fd1fb35e | ||
|
|
91e362ca15 | ||
|
|
0027df28a3 | ||
|
|
9f9fcaedee | ||
|
|
a91f296562 | ||
|
|
708b9d93ff | ||
|
|
08c9c8f5fb | ||
|
|
b12b51d17a | ||
|
|
7b99aadb3f | ||
|
|
c5b551d68e | ||
|
|
fdfb7ce628 | ||
|
|
eceb7bd14b | ||
|
|
1e527c900a | ||
|
|
517f860c6b | ||
|
|
0e4da5719c | ||
|
|
7f1f9d6394 | ||
|
|
d25f723ae2 | ||
|
|
dd81ffeb00 | ||
|
|
f225ea8905 | ||
|
|
5b6999232f | ||
|
|
17d2b0da7c | ||
|
|
7768035da2 | ||
|
|
d4b53ac007 | ||
|
|
67e07c8fcd | ||
|
|
2737c63903 | ||
|
|
a367698670 | ||
|
|
eb904280e9 | ||
|
|
2ec4ad331c | ||
|
|
393c9af054 | ||
|
|
0f28e3e689 | ||
|
|
c072ff570b | ||
|
|
ec47da608f | ||
|
|
734dbfc98b | ||
|
|
a78649773f | ||
|
|
dc2429d9b7 | ||
|
|
965dabc415 | ||
|
|
b04bdb0b9f | ||
|
|
9ce304ea52 | ||
|
|
77a351c94c | ||
|
|
5d24697b0c | ||
|
|
0cbeb2fc52 | ||
|
|
934c9e23ba | ||
|
|
2441cc208f | ||
|
|
27fd8989d8 | ||
|
|
a641bd8bd1 | ||
|
|
366cb2e456 | ||
|
|
31a7e133fa | ||
|
|
c808b38778 | ||
|
|
ac04dc248c | ||
|
|
15eb927137 | ||
|
|
91ab53a471 | ||
|
|
9656d5274c | ||
|
|
01fd805ee6 | ||
|
|
846b3b81ff | ||
|
|
5c921c2bff | ||
|
|
d3317a48c0 | ||
|
|
ac8b7610cf | ||
|
|
a06edd4c4e | ||
|
|
b9d6c11e63 | ||
|
|
ce9519c9cb | ||
|
|
dbda657e6e | ||
|
|
7d2b895bfb | ||
|
|
ad719a2c07 | ||
|
|
ae31139ffe | ||
|
|
bea0806a47 | ||
|
|
7394595abb | ||
|
|
d2ce6f826a | ||
|
|
2544ff5902 | ||
|
|
113306ac23 | ||
|
|
e2cedb5594 | ||
|
|
63cf2ec616 | ||
|
|
c99cc22138 | ||
|
|
b76f7bd3ba | ||
|
|
d37e9ea1c3 | ||
|
|
1d008961ab | ||
|
|
8a7224d4b5 | ||
|
|
bd95a7fc01 | ||
|
|
6a9cdc9e18 | ||
|
|
3a9fbf88bd | ||
|
|
d9ca826095 | ||
|
|
1d36fbba53 | ||
|
|
7d230aeebc | ||
|
|
4e573c46cb | ||
|
|
0c649c3959 | ||
|
|
a14a99b681 | ||
|
|
aefd87e3e5 | ||
|
|
109d56b55e | ||
|
|
4eed0b38f8 | ||
|
|
424ae68621 | ||
|
|
101b2e7bdf | ||
|
|
049dba4b06 | ||
|
|
3024d32257 | ||
|
|
784ed7f21b | ||
|
|
2de44bfd99 | ||
|
|
65e391a4a7 | ||
|
|
16057247c2 | ||
|
|
04506c1e1e | ||
|
|
7038e2a18e | ||
|
|
3429444c0c | ||
|
|
3c46953d47 | ||
|
|
6c7a1b5697 | ||
|
|
cc35644e24 | ||
|
|
ccf2b3bc5b | ||
|
|
698a46eb7b | ||
|
|
9e5de62841 | ||
|
|
70ac9029c6 | ||
|
|
b66b989de0 | ||
|
|
9c82b2ea34 | ||
|
|
d033088113 | ||
|
|
240ccdc65e | ||
|
|
975d632007 | ||
|
|
53371344ae | ||
|
|
e9356ebe79 | ||
|
|
bc0c8fc84a | ||
|
|
6252f2c8c1 | ||
|
|
708f819182 | ||
|
|
bad4c7ed34 | ||
|
|
7e6f8f19eb | ||
|
|
a6fd969ead | ||
|
|
7b5443d680 | ||
|
|
8de723d2aa | ||
|
|
82b252ce2e | ||
|
|
a2dfb26b36 | ||
|
|
f1b6492d1c | ||
|
|
19d297f934 | ||
|
|
a23128f25f | ||
|
|
567f7c3028 | ||
|
|
30af4cfc3d | ||
|
|
019fcf519a | ||
|
|
1645562d78 | ||
|
|
fab726482c | ||
|
|
351cb9835f | ||
|
|
d0d59469bc | ||
|
|
577d934ccd | ||
|
|
4438131d56 | ||
|
|
7b13c9a9ba | ||
|
|
7d60566f3c | ||
|
|
e9a7c2f184 | ||
|
|
3e07873575 | ||
|
|
2f17584ab4 | ||
|
|
02f87b23fb | ||
|
|
31032e29f5 | ||
|
|
11bfb57809 | ||
|
|
0e83ef32bb | ||
|
|
86ae9e94cd | ||
|
|
2388250dfa | ||
|
|
d947ea65a2 | ||
|
|
4a11c04742 | ||
|
|
14b24e6050 | ||
|
|
4402d6fbd6 | ||
|
|
c69b81404c | ||
|
|
241a3efacf | ||
|
|
185d52a9d4 | ||
|
|
6b933f18dd | ||
|
|
31dbbb546c | ||
|
|
ac4f8a30e5 | ||
|
|
a060531ae3 | ||
|
|
fb77937acd | ||
|
|
9d9a81710f | ||
|
|
c3b4438d1f | ||
|
|
eeb3679b82 | ||
|
|
d2d9bfb4bd | ||
|
|
2dcbe4628b | ||
|
|
634247676a | ||
|
|
1a01b323e5 | ||
|
|
c71a33eba0 | ||
|
|
0387c7a8e1 | ||
|
|
1ae0404592 | ||
|
|
6796bef261 | ||
|
|
a5bd6dc7c5 | ||
|
|
4e7b9290ac | ||
|
|
9acbee976d | ||
|
|
e6f4f8c05e | ||
|
|
c89cb3d287 | ||
|
|
c5cb95fd8c | ||
|
|
fa46714b19 | ||
|
|
bfbcde3b47 | ||
|
|
c6dc66cb45 | ||
|
|
a9e6912a2f | ||
|
|
eb8885b915 | ||
|
|
029c808458 | ||
|
|
9269f69a38 | ||
|
|
63d938ae04 | ||
|
|
a8aa110f43 | ||
|
|
6f7af5aef4 | ||
|
|
6afbade8f7 | ||
|
|
5ec38498f1 | ||
|
|
e206d3a833 | ||
|
|
6529cf6498 | ||
|
|
21f5de8de8 | ||
|
|
837d5c7f68 | ||
|
|
f90a53c2b0 | ||
|
|
e184e5b7c5 | ||
|
|
1ca1381e05 | ||
|
|
811f807de6 | ||
|
|
95b76bc586 | ||
|
|
90fac39a26 | ||
|
|
44cf680f14 | ||
|
|
d0754e022f | ||
|
|
ed7245c852 | ||
|
|
2b44618858 | ||
|
|
a3634d689e | ||
|
|
96e8cbd3c1 | ||
|
|
658d41f0fd | ||
|
|
9dab8fd7dc | ||
|
|
2cb9d81a3c | ||
|
|
2b4662856e | ||
|
|
44e949eafe | ||
|
|
aa3acd12a6 | ||
|
|
1c00e62d3e | ||
|
|
0d630d9ea3 | ||
|
|
7de78cd088 | ||
|
|
0f98c72f1e | ||
|
|
459a79a1f1 | ||
|
|
aaea8d9717 | ||
|
|
d5b99732d1 | ||
|
|
f5cef8a997 | ||
|
|
44907aa700 | ||
|
|
54303d464b | ||
|
|
4e83a68bf1 | ||
|
|
00893a6cca | ||
|
|
008768cea1 | ||
|
|
43e096c6dc | ||
|
|
b10b48f5e9 | ||
|
|
1a76c72bf3 | ||
|
|
74a1f6301a | ||
|
|
dd22ec68fc | ||
|
|
6ecdfc25fd | ||
|
|
f439f09c2e | ||
|
|
ebe955020c | ||
|
|
60119a89c0 | ||
|
|
6a14353391 | ||
|
|
9090fe5fc9 | ||
|
|
93bc9a4293 | ||
|
|
80b2e22d9d | ||
|
|
5a6a098990 | ||
|
|
c64ef201ff | ||
|
|
817ae02295 | ||
|
|
910dab98f1 | ||
|
|
b9c59ffad4 | ||
|
|
79426ec959 | ||
|
|
2e0ba0e3d1 | ||
|
|
0c3ce58ffa | ||
|
|
c482820746 | ||
|
|
195bc1f290 | ||
|
|
d8108f998b | ||
|
|
40de60dd8b | ||
|
|
c9981472a8 | ||
|
|
83b3789282 | ||
|
|
0078e9e225 | ||
|
|
a62966227a | ||
|
|
5f0ccf3257 | ||
|
|
61d0a1d498 | ||
|
|
c626528a83 | ||
|
|
2e0e8e18ef | ||
|
|
54d98a6cad | ||
|
|
0fe503658b | ||
|
|
5941464402 | ||
|
|
3074ea62dc | ||
|
|
312bf91003 | ||
|
|
a42c323343 | ||
|
|
39d9fe2794 | ||
|
|
7993e2971c | ||
|
|
ba9efe43be | ||
|
|
cfa5e7d19c | ||
|
|
7acd2ad884 | ||
|
|
1f474c3097 | ||
|
|
c8b4f6e985 | ||
|
|
fc20bcca91 | ||
|
|
702b635826 | ||
|
|
990c5f67e4 | ||
|
|
8ef4ca2ce8 | ||
|
|
b105ce6698 | ||
|
|
6c93b836f5 | ||
|
|
f0e60ee577 | ||
|
|
2cfbb2373a | ||
|
|
d26d04d92b | ||
|
|
0d6fe32246 | ||
|
|
36de8073f2 | ||
|
|
5aaaa1e6a7 | ||
|
|
a4126a52ce | ||
|
|
076017128e | ||
|
|
7240147418 | ||
|
|
0923f2bb5c | ||
|
|
5ce0b9985a | ||
|
|
cd76375d8e | ||
|
|
df2ef01494 | ||
|
|
1d3d875f3d | ||
|
|
48446367f4 | ||
|
|
fb1f293a17 | ||
|
|
f85533d608 | ||
|
|
e32faf6053 | ||
|
|
a429ea4679 | ||
|
|
9112d2277e | ||
|
|
a9050045f3 | ||
|
|
ed3cad6e9c | ||
|
|
deee5aff00 | ||
|
|
8c36a4d4c6 | ||
|
|
157074db29 | ||
|
|
14ff04d2e3 | ||
|
|
d0e2d439aa | ||
|
|
159340a396 | ||
|
|
de6625bcaf | ||
|
|
0d7ed691e6 | ||
|
|
0721f723be | ||
|
|
2da7239ac6 | ||
|
|
7b4c07c837 | ||
|
|
169c56f105 | ||
|
|
d51cdfd7c4 | ||
|
|
3c02b139e8 | ||
|
|
9d660b9d4e | ||
|
|
eaf7c61f01 | ||
|
|
1234c05690 | ||
|
|
fd5b6769fa | ||
|
|
63db34070e | ||
|
|
b41cd3ff97 | ||
|
|
f2406ee0e4 | ||
|
|
4712c6a372 | ||
|
|
56dc1b2b6c | ||
|
|
cb13d00844 | ||
|
|
7a11e8eb19 | ||
|
|
482af25c90 | ||
|
|
0c17e21b85 | ||
|
|
0acb6ac548 | ||
|
|
7f339860ad | ||
|
|
67cf38a291 | ||
|
|
cad28b9fd5 | ||
|
|
80ceca6e28 | ||
|
|
bdcb8864fb | ||
|
|
ced444282f | ||
|
|
5c7c11e3f4 | ||
|
|
2b4628fb43 | ||
|
|
a0dbd75f35 | ||
|
|
a20877ea80 | ||
|
|
e151691711 | ||
|
|
f42db27eaa | ||
|
|
724eab69d8 | ||
|
|
a83dbccc6c | ||
|
|
178e987650 | ||
|
|
3cd126f08d | ||
|
|
b109123a43 | ||
|
|
c97e97d2cc | ||
|
|
160d098510 | ||
|
|
a72e1924ca | ||
|
|
fd7508f152 | ||
|
|
1de995f9d5 | ||
|
|
47fbe6423e | ||
|
|
461c2a38a5 | ||
|
|
d89036f9f3 | ||
|
|
998cb16bfa | ||
|
|
4c2a8c2892 | ||
|
|
8d3afa0bb6 | ||
|
|
3fd7bbc0a3 | ||
|
|
bf66500aac | ||
|
|
e28da0d7fd | ||
|
|
f10bc886c4 | ||
|
|
df578ac78b | ||
|
|
18e1557cf3 | ||
|
|
30e6131cd7 | ||
|
|
44310fda20 | ||
|
|
fb7431abb5 | ||
|
|
5b109ea3ce | ||
|
|
a671e9f925 | ||
|
|
8168804f05 | ||
|
|
ec576ad0a9 | ||
|
|
fa3abcfdec | ||
|
|
33864614e7 | ||
|
|
f4bf68ee59 | ||
|
|
2b3d6f976d | ||
|
|
641a3313ea | ||
|
|
08e6665ffc | ||
|
|
e6a7af4ab3 | ||
|
|
a0030a7909 | ||
|
|
13c7a7986e | ||
|
|
bd1ea872be | ||
|
|
dfcd595bc1 | ||
|
|
f77c97c66a | ||
|
|
ca53391bdb | ||
|
|
d01dd904da | ||
|
|
bb885bddd4 | ||
|
|
3d8f2c62ea | ||
|
|
7cdb5e86c6 | ||
|
|
255b2b464d | ||
|
|
0ef771ca15 | ||
|
|
a3207496b6 | ||
|
|
741724973c | ||
|
|
3375c91b56 | ||
|
|
67da9d7233 | ||
|
|
4b228b32f0 | ||
|
|
f66189de6b | ||
|
|
743805ecd5 | ||
|
|
6c9dcea08c | ||
|
|
fa1944090d | ||
|
|
04cf428619 | ||
|
|
86d6b00886 | ||
|
|
38429d98df | ||
|
|
0c9667fe58 | ||
|
|
3c6bb7be4c | ||
|
|
40fa732122 | ||
|
|
01e2e25bce | ||
|
|
29e916dcdd | ||
|
|
1bfa7610ae | ||
|
|
fb94c32bb4 | ||
|
|
94ad26d818 | ||
|
|
f323addc1c | ||
|
|
5559c91c0e | ||
|
|
6cc5eab94b | ||
|
|
c2a3450c8f | ||
|
|
01e1ec0794 | ||
|
|
ea381cde90 | ||
|
|
26074c67c6 | ||
|
|
e2b13fcda5 | ||
|
|
0130852d9a | ||
|
|
a027af9e84 | ||
|
|
ce81b3d4da | ||
|
|
96d8ff3cb7 | ||
|
|
b67c354fdb | ||
|
|
9a610197ea | ||
|
|
1109c3423c | ||
|
|
18fcb8d0ad | ||
|
|
a5845ed0d9 | ||
|
|
3392fa59fe | ||
|
|
95e816572a | ||
|
|
3acb3aab9f | ||
|
|
509860d890 | ||
|
|
61dcc467ee | ||
|
|
ae6601d9e3 | ||
|
|
33733af3c5 | ||
|
|
da7c0ab7d6 | ||
|
|
afd156b51f | ||
|
|
5d549b7c60 | ||
|
|
1b5671dc87 | ||
|
|
87e2893505 | ||
|
|
89443e342f | ||
|
|
de5ed803ed | ||
|
|
528f9a7ec4 | ||
|
|
a1f7656fe4 | ||
|
|
a5703a55eb | ||
|
|
1275b85465 | ||
|
|
52dc2738a1 | ||
|
|
7c0f7cbdc2 | ||
|
|
c14ef8bd13 | ||
|
|
ce62ae9f50 | ||
|
|
133347f884 | ||
|
|
ca4f56cb04 | ||
|
|
f512ae973d | ||
|
|
04cceb314e | ||
|
|
4138e10788 | ||
|
|
5d11b9aa97 | ||
|
|
6033c2b3ce | ||
|
|
dfb28dc155 | ||
|
|
756b29d9ed | ||
|
|
dc7a3af768 | ||
|
|
baf3a2d915 | ||
|
|
25832ab2ea | ||
|
|
c95c0401eb | ||
|
|
936580a924 | ||
|
|
00df147688 | ||
|
|
8273dcfdfc | ||
|
|
81e2dc3635 | ||
|
|
94611cd80b | ||
|
|
28af81142f | ||
|
|
b3fd3ec0ba | ||
|
|
44518d5b33 | ||
|
|
323e74f50f | ||
|
|
a972e755d1 | ||
|
|
49b6292f7f | ||
|
|
dd27dc1503 | ||
|
|
547ed1fd26 | ||
|
|
c387b0d069 | ||
|
|
ba2af4d84d | ||
|
|
20b7c6a823 | ||
|
|
b2edab0452 | ||
|
|
a72dc67268 | ||
|
|
11b9745268 | ||
|
|
1d1d49a3c9 | ||
|
|
31a4e7a22c | ||
|
|
dca13f0749 | ||
|
|
d377eee11c | ||
|
|
196052efed | ||
|
|
694c5104fa | ||
|
|
acbf765851 | ||
|
|
ef06dfb7b3 | ||
|
|
613432ef2c | ||
|
|
512dc87b3f | ||
|
|
480f034301 | ||
|
|
39275ce133 | ||
|
|
c73fa0b42d | ||
|
|
3d4ed43337 | ||
|
|
74067fd515 | ||
|
|
9538771eef | ||
|
|
7ecb968e23 | ||
|
|
c9365732d9 | ||
|
|
d41c13ac29 | ||
|
|
1b2fa2b2e8 | ||
|
|
b9a9113abe | ||
|
|
1dd9dbec6c | ||
|
|
84efe5447b | ||
|
|
61bab55d11 | ||
|
|
85d05153f7 | ||
|
|
f9bc316c98 | ||
|
|
c4adc8d9be | ||
|
|
169719c62d | ||
|
|
a509a491af | ||
|
|
aed8e26062 | ||
|
|
cec414126d | ||
|
|
6bf96ab808 | ||
|
|
d8d9f72985 | ||
|
|
cecb2e8f4c | ||
|
|
d9ae28d3ed | ||
|
|
761078625e | ||
|
|
be37a75928 | ||
|
|
884e9fb3c9 | ||
|
|
8821502a81 | ||
|
|
f66c012df6 | ||
|
|
baf996fc06 | ||
|
|
e56a1d3274 | ||
|
|
b04af33fb5 | ||
|
|
1f53d32a62 | ||
|
|
534aeb3d07 | ||
|
|
a3693d0a45 | ||
|
|
967a6bd4a4 | ||
|
|
c589c9b9ec | ||
|
|
d10ad4835b | ||
|
|
38a273b195 | ||
|
|
9618f46188 | ||
|
|
2562b384bc | ||
|
|
bc8d133b69 | ||
|
|
beb9967ad0 | ||
|
|
433baf0923 | ||
|
|
423da8b785 | ||
|
|
f4708d2b1a | ||
|
|
b00b7f7c31 | ||
|
|
ec4110cb2c | ||
|
|
b2f02c7fa6 | ||
|
|
aeb561c240 | ||
|
|
97a6abca0e | ||
|
|
5aa3a29288 | ||
|
|
bc49e7c48e | ||
|
|
9ba10446e9 | ||
|
|
a4c686876f | ||
|
|
f31ba7dea3 | ||
|
|
897946c1ce | ||
|
|
802266e3aa | ||
|
|
d9f89f7457 | ||
|
|
b871d84379 | ||
|
|
0375309060 | ||
|
|
a5ca653df8 | ||
|
|
ec194a15fb | ||
|
|
eaf5d71b40 | ||
|
|
7a9ee279ed | ||
|
|
827acdadea | ||
|
|
ef99b2057a | ||
|
|
c938714b70 | ||
|
|
21e3dd30fd | ||
|
|
b271ab4721 | ||
|
|
3abe382f44 | ||
|
|
88a6b702d2 | ||
|
|
497d1af8bf | ||
|
|
cc4b6acd14 | ||
|
|
1714e2331c | ||
|
|
4e419ec16d | ||
|
|
5e0f214a8f | ||
|
|
da1727e5e4 | ||
|
|
101be2eeb1 | ||
|
|
e69015204a | ||
|
|
1b203e3292 | ||
|
|
1ad8bd212c | ||
|
|
3711f30d01 | ||
|
|
85d59d25df | ||
|
|
6d7f55a435 | ||
|
|
c22ca18a82 | ||
|
|
ec48959600 | ||
|
|
67634c4a71 | ||
|
|
c31d38a562 | ||
|
|
6b0499b82e | ||
|
|
046364283f | ||
|
|
85880f9bd1 | ||
|
|
5fd436e5c5 | ||
|
|
01533cbf9f | ||
|
|
f5c8276fdc | ||
|
|
05f2b81025 | ||
|
|
9dfd6cecad | ||
|
|
2febf837e5 | ||
|
|
ac954bba11 | ||
|
|
2bda4fef5b | ||
|
|
5a815592dc | ||
|
|
3f4c6ce144 | ||
|
|
19e0c53d1e | ||
|
|
95963b2289 | ||
|
|
4cd21cad9c | ||
|
|
fcf1d7d502 | ||
|
|
e92d04771d | ||
|
|
ce43190ca6 | ||
|
|
284dbbad24 | ||
|
|
1e115a5eab | ||
|
|
f18a355469 | ||
|
|
8ba95bb82a | ||
|
|
ee5a2a320e | ||
|
|
738fd3da58 | ||
|
|
decc08934c | ||
|
|
5f5b7f92cf | ||
|
|
20fa280171 | ||
|
|
e5fa2ef750 | ||
|
|
bd31e25757 | ||
|
|
5b3113d96b | ||
|
|
84b4f7695b | ||
|
|
fc8ea3bcd0 | ||
|
|
9051a4df4d | ||
|
|
a7b42b6c97 | ||
|
|
f7675b1e46 | ||
|
|
db1117d892 | ||
|
|
4b14e19229 | ||
|
|
606021fb8a | ||
|
|
b15499c1dd | ||
|
|
950588cb65 | ||
|
|
7991c06543 | ||
|
|
b335c4ca05 | ||
|
|
cf3773dd28 | ||
|
|
2a3740e49f | ||
|
|
bcbd30ff6e | ||
|
|
571ab9602f | ||
|
|
cfab6a3bb6 | ||
|
|
7c9ab59aff | ||
|
|
7b1d1129a8 | ||
|
|
bf3e8fe3a9 | ||
|
|
baeac17d5b | ||
|
|
68ce6dea4b | ||
|
|
00df4b8920 | ||
|
|
36814514b7 | ||
|
|
381a9a28b0 | ||
|
|
5c364896d3 | ||
|
|
07ce1d44a9 | ||
|
|
1348ac86f7 | ||
|
|
b4c4855a9b | ||
|
|
7a1001a70b | ||
|
|
340a8130e9 | ||
|
|
9eb8de27d2 | ||
|
|
476a43a5bf | ||
|
|
9ab955d026 | ||
|
|
bcedb32cf0 | ||
|
|
6bc266f13c | ||
|
|
ed36feeb0a | ||
|
|
85400cd8f6 | ||
|
|
2866697b32 | ||
|
|
f1a99e1194 | ||
|
|
db47ddf3dc | ||
|
|
2cc4dbd2ba | ||
|
|
a38eef2971 | ||
|
|
e3e197a917 | ||
|
|
361d0befb6 | ||
|
|
ba35c662ea | ||
|
|
45ce763c71 | ||
|
|
73c85a0013 | ||
|
|
26361630c2 | ||
|
|
96c30c509b | ||
|
|
d9b9786486 | ||
|
|
bb9cea260d | ||
|
|
958c2f97ec | ||
|
|
27651f17bf | ||
|
|
71621f7bb5 | ||
|
|
d8add46215 | ||
|
|
e459f570d5 | ||
|
|
5b5057dee0 | ||
|
|
f21becb37d | ||
|
|
9d03eb1ad4 | ||
|
|
cb90c5e616 | ||
|
|
77059f2db0 | ||
|
|
fb72c36a48 | ||
|
|
b2b215a061 | ||
|
|
8d313e4cf8 | ||
|
|
6bb760375e | ||
|
|
025cd043d3 | ||
|
|
3f368d4a8e | ||
|
|
449e41e435 | ||
|
|
bf0062be52 | ||
|
|
3c025c8b52 | ||
|
|
6dc3d954c5 | ||
|
|
c9b7a11a89 | ||
|
|
33cb2d108e | ||
|
|
e053e74b58 | ||
|
|
4e35fc2fbe | ||
|
|
4768d8e459 | ||
|
|
3598cc1d85 | ||
|
|
e3a895b88c | ||
|
|
61eff3ddf0 | ||
|
|
e9268984ae | ||
|
|
f28b35bd28 | ||
|
|
a86618c2c2 | ||
|
|
1f1a4b8fb8 | ||
|
|
a1d0be34c2 | ||
|
|
ef0a04cc1c | ||
|
|
284262b7da | ||
|
|
58b0a17986 | ||
|
|
57abe00c62 | ||
|
|
d014407ba4 | ||
|
|
2e8bfa16f9 | ||
|
|
bf34713b0c | ||
|
|
c46c1a96cd | ||
|
|
2693b62de4 | ||
|
|
18387f6d98 | ||
|
|
08b7356184 | ||
|
|
e30cdfc176 | ||
|
|
cc0ed38e68 | ||
|
|
5ec0d20286 | ||
|
|
e0aa69f605 | ||
|
|
c859f39036 | ||
|
|
5251f62665 | ||
|
|
2b87e2b221 | ||
|
|
c185e9b487 | ||
|
|
b5d9a99f10 | ||
|
|
fec67fe0ea | ||
|
|
987997a986 | ||
|
|
27ef79ca27 | ||
|
|
bcc1932a37 | ||
|
|
2e163e9986 | ||
|
|
5473e57b10 | ||
|
|
b970bad058 | ||
|
|
bea1814cb9 | ||
|
|
45e58f29b4 | ||
|
|
f7a3df635a | ||
|
|
3f67984929 | ||
|
|
bf82171baa | ||
|
|
4c49a7f003 | ||
|
|
57b5d40851 | ||
|
|
ecde2d1627 | ||
|
|
b48e9a31f6 | ||
|
|
f64e5241ed | ||
|
|
4e9d01055a | ||
|
|
9d4ca25499 | ||
|
|
f1ddf9dc2b | ||
|
|
e99b790d58 | ||
|
|
5e4a99c1ad | ||
|
|
c89824bf25 | ||
|
|
1230d9cdd4 | ||
|
|
d3dd8dc686 | ||
|
|
382faa49cb | ||
|
|
5cf4b4663f | ||
|
|
749b4d3083 | ||
|
|
02835d057e | ||
|
|
f5aaaecc48 | ||
|
|
184ff84f92 | ||
|
|
ef56bc1f55 | ||
|
|
9846f7509e | ||
|
|
1cf7cefe83 | ||
|
|
b5f1dbc47b | ||
|
|
37b85491c3 | ||
|
|
30d792b35b | ||
|
|
ac29412b2f | ||
|
|
01c170afaf | ||
|
|
539e0811c9 | ||
|
|
940448ffae | ||
|
|
68a73f96c4 | ||
|
|
87a93745cb | ||
|
|
3b08abca10 | ||
|
|
5e68096a2e | ||
|
|
e3ef11ceae | ||
|
|
575fe8379f | ||
|
|
60feae7e5b | ||
|
|
2b45ecaea1 | ||
|
|
7a3a430137 | ||
|
|
11c0563fe5 | ||
|
|
00018b3e89 | ||
|
|
de787a069d | ||
|
|
5f33ea6013 | ||
|
|
ee74c4c17f | ||
|
|
b031f52ee2 | ||
|
|
77bb01b18c | ||
|
|
d2fdc28c85 | ||
|
|
d34e985a92 | ||
|
|
f0c2c834c3 | ||
|
|
ca0cce9401 | ||
|
|
2a0e211daf | ||
|
|
ea89983e45 | ||
|
|
e4ed1c8fd7 | ||
|
|
d46155bf32 | ||
|
|
9bdf0d8937 | ||
|
|
f56c1226b6 | ||
|
|
bc3e4742f0 | ||
|
|
891b16ac76 | ||
|
|
11a32c3537 | ||
|
|
8afc96e1ad | ||
|
|
55d2c9e49c | ||
|
|
9baabee3fd | ||
|
|
ad20cb6644 | ||
|
|
04d2d92524 | ||
|
|
6630a8c2a5 | ||
|
|
67ee86eaeb | ||
|
|
2fcfbc2e1a | ||
|
|
2bb1162adf | ||
|
|
f5e0b67305 | ||
|
|
0b25fb5771 | ||
|
|
27ff29329f | ||
|
|
b52cfbb602 | ||
|
|
c5a1c64a35 | ||
|
|
cbedd9bec5 | ||
|
|
7a90844970 | ||
|
|
45dcb72178 | ||
|
|
d23b5bb58b | ||
|
|
bf7de99182 | ||
|
|
8ddfab4b47 | ||
|
|
57c2dc2d65 | ||
|
|
62236a38f3 | ||
|
|
25ab7bba02 | ||
|
|
fe14f3ee0e | ||
|
|
425120de94 | ||
|
|
2520c8d173 | ||
|
|
7b4ee1c44b | ||
|
|
58a1dcd141 | ||
|
|
3b4f44f276 | ||
|
|
ebcc06686c | ||
|
|
45b3a7dbcd | ||
|
|
16f04f2255 | ||
|
|
a23fcbd095 | ||
|
|
7491c0f7c4 | ||
|
|
83da75a5e5 | ||
|
|
2474c32f60 | ||
|
|
6910f1f0b7 | ||
|
|
c426aeac6a | ||
|
|
0e2716ba31 | ||
|
|
011239d45c | ||
|
|
5bb3d1a9e1 | ||
|
|
43766c7ab9 | ||
|
|
e12eeed65d | ||
|
|
711ecb4025 | ||
|
|
88957699c5 | ||
|
|
fcd6c51d55 | ||
|
|
adda02dd0d | ||
|
|
ab1cff2a7d | ||
|
|
0716a743d8 | ||
|
|
d0e17fde77 | ||
|
|
d6c0aa8a80 | ||
|
|
815bf9b390 | ||
|
|
0aa6e0a8b2 | ||
|
|
8b1aff33fe | ||
|
|
fdc9464576 | ||
|
|
dc6c1a0fe1 | ||
|
|
78a73ac15f | ||
|
|
48891ed7c7 | ||
|
|
d3fd5ba9ac | ||
|
|
2b6f575802 | ||
|
|
f604460d56 | ||
|
|
7472893e8e | ||
|
|
eff074faae | ||
|
|
07c04b40b1 | ||
|
|
754adb545e | ||
|
|
5fc04277c1 | ||
|
|
78f5fd3f71 | ||
|
|
0277c6b9bd | ||
|
|
e34b4b8ae7 | ||
|
|
c0de18f3aa | ||
|
|
4b78918347 | ||
|
|
6606a883c5 | ||
|
|
30c1a64d31 | ||
|
|
91dbcc40aa | ||
|
|
b0f5119ec0 | ||
|
|
ed9aba18b8 | ||
|
|
d507325378 | ||
|
|
0a0546168b | ||
|
|
5abbbe80d1 | ||
|
|
4a6413f654 | ||
|
|
6c60244b26 | ||
|
|
a384f0e6e9 | ||
|
|
2a56410543 | ||
|
|
7ef22fc1e0 | ||
|
|
5051d698c0 | ||
|
|
1a87c08bc2 | ||
|
|
ed0c5908ce | ||
|
|
f571ced9c5 | ||
|
|
3110181a9f | ||
|
|
fcb7966f70 | ||
|
|
be2945a16f | ||
|
|
6b3326ad42 | ||
|
|
3e81a03087 | ||
|
|
3778430ead | ||
|
|
31bd251f37 | ||
|
|
f49f01ec85 | ||
|
|
1bd6721af9 | ||
|
|
4a069266d8 | ||
|
|
d10d7f3f02 | ||
|
|
05adbb1325 | ||
|
|
ca8719b42b | ||
|
|
ec80850e76 | ||
|
|
ab75a8b3e5 | ||
|
|
d00c8119fa | ||
|
|
2b7d188677 | ||
|
|
12c09693bd | ||
|
|
87793b3dc3 | ||
|
|
7ce8c0b966 | ||
|
|
a831944b14 | ||
|
|
17533d2c61 | ||
|
|
bee1d6beed | ||
|
|
a49ae076a5 | ||
|
|
4856503a33 | ||
|
|
0d4560f54e | ||
|
|
c5f7c2ace7 | ||
|
|
f8c03fa48c | ||
|
|
8b6b34d7c0 | ||
|
|
1fd7001e0c | ||
|
|
2631550c2f | ||
|
|
01734fadcf | ||
|
|
33cead0b03 | ||
|
|
08f86cbcf9 | ||
|
|
a83b96c74d | ||
|
|
7cf0ddc81b | ||
|
|
41640b5215 | ||
|
|
e1abedbfa4 | ||
|
|
a1482b9781 | ||
|
|
8a6d6ae771 | ||
|
|
a67e8314af | ||
|
|
f5e7497913 | ||
|
|
e4c7c601d7 | ||
|
|
02fdc78066 | ||
|
|
d1a4521396 | ||
|
|
646ddb4ddb | ||
|
|
8578078f7c | ||
|
|
ab204281ed | ||
|
|
31940d8f58 | ||
|
|
98874790fc | ||
|
|
5cd476687e | ||
|
|
73449e4407 | ||
|
|
eae06a4145 | ||
|
|
5a8d56c2b4 | ||
|
|
da6ccb6310 | ||
|
|
b0a8d04f97 | ||
|
|
99acfc9641 | ||
|
|
a731b606c5 | ||
|
|
c99b369b56 | ||
|
|
0f41e6053d | ||
|
|
b2ffddd84d | ||
|
|
1a9451fe61 | ||
|
|
0859eef869 | ||
|
|
1ad28cb9ac | ||
|
|
0f8aa5d7e7 | ||
|
|
5bf680a1cf | ||
|
|
e9d599ab26 | ||
|
|
dc83e66f72 | ||
|
|
5cf16dc9fd | ||
|
|
e076ac1a9c | ||
|
|
7a28932569 | ||
|
|
5567c5abf6 | ||
|
|
b2e2c5c173 | ||
|
|
e41731ce23 | ||
|
|
b1b6d6ace8 | ||
|
|
97b5a4c304 | ||
|
|
0722417e0b | ||
|
|
dc9e938510 | ||
|
|
c37b8f37e2 | ||
|
|
80116b6687 | ||
|
|
7d5225b2ba | ||
|
|
f2f318b11e | ||
|
|
6bfe1b0cfd | ||
|
|
f807cca8c7 | ||
|
|
feadf59fa0 | ||
|
|
10e64e04fe | ||
|
|
c2cbd502ea | ||
|
|
cf5fd8064b | ||
|
|
1a417b9d63 | ||
|
|
123cfe6a38 | ||
|
|
43c0681d35 | ||
|
|
2ec5784a44 | ||
|
|
d59f4229d6 | ||
|
|
69c8ac3942 | ||
|
|
acbc9370f5 | ||
|
|
f3563e8b4a | ||
|
|
5484ccc0f0 | ||
|
|
34fe0f1077 | ||
|
|
5039cbb529 | ||
|
|
93a9876de0 | ||
|
|
ae311718da | ||
|
|
441945d7e6 | ||
|
|
419b7fcb2c | ||
|
|
0692547440 | ||
|
|
6471928f91 | ||
|
|
1866295d5d | ||
|
|
4b538b419a | ||
|
|
72c19c08b8 | ||
|
|
57b5afa676 | ||
|
|
a5b8dfc1a3 | ||
|
|
9b87a3b755 | ||
|
|
191b8531be | ||
|
|
b1f6735e87 | ||
|
|
900968a91c | ||
|
|
1e25d93a05 | ||
|
|
bbdfb2b4f2 | ||
|
|
93ebcbac6c | ||
|
|
8c261215d7 | ||
|
|
8059c4f1d5 | ||
|
|
e9f5cf2259 | ||
|
|
86bec2943b | ||
|
|
f812646267 | ||
|
|
390582e3f2 | ||
|
|
5d5c6b06b9 | ||
|
|
10269dc779 | ||
|
|
b28c2ae735 | ||
|
|
8810d18020 | ||
|
|
9b8841174e | ||
|
|
a3a56b2ee9 | ||
|
|
41db56c093 | ||
|
|
c8e7cc856f | ||
|
|
18d3e0db42 | ||
|
|
865e5c85b8 | ||
|
|
e61175af6b | ||
|
|
4f1f72e1dc | ||
|
|
0da593a008 | ||
|
|
8e78140259 | ||
|
|
1376a05c6a | ||
|
|
7d8ca6fdc7 | ||
|
|
f5ad09619f | ||
|
|
685d83bd1e | ||
|
|
64a132808d | ||
|
|
26e1f4001b | ||
|
|
1178138ad2 | ||
|
|
9d3d075524 | ||
|
|
d157bc4769 | ||
|
|
ff72ede2f4 | ||
|
|
d2631a7586 | ||
|
|
d271acc67e | ||
|
|
503fb61ee1 | ||
|
|
9dde5cd0b0 | ||
|
|
9f96d171f7 | ||
|
|
a69fc24b40 | ||
|
|
82ab166b94 | ||
|
|
8c4b5c7f6b | ||
|
|
7f22f6d2a6 | ||
|
|
d66c688910 | ||
|
|
986373c30d | ||
|
|
2c45a20ca7 | ||
|
|
599f083fe2 | ||
|
|
c572223147 | ||
|
|
a35fbc9de4 | ||
|
|
4f4e7f8b61 | ||
|
|
5993408eff | ||
|
|
e9dfcfbb2f | ||
|
|
18e3dfb448 | ||
|
|
b1e1b0f6ac | ||
|
|
42718d3f7a | ||
|
|
63cfccab40 | ||
|
|
583b36667f | ||
|
|
c7f55b88a6 | ||
|
|
df7503cb3b | ||
|
|
75066477cf | ||
|
|
7be063f104 | ||
|
|
5521fccf73 | ||
|
|
b823535880 | ||
|
|
0971ba5cee | ||
|
|
ab584366f2 | ||
|
|
8bfb1fb348 | ||
|
|
acaa23fe88 | ||
|
|
6aea979ca5 | ||
|
|
34c7eb612a | ||
|
|
b3d472f88d | ||
|
|
6801fea2b1 | ||
|
|
effe010223 | ||
|
|
394f4bbd49 | ||
|
|
f48e5396e4 | ||
|
|
08c6a9a73b | ||
|
|
3ad05c2424 | ||
|
|
d3b07cf012 | ||
|
|
c6d2ba92d9 | ||
|
|
8569205297 | ||
|
|
8fd31eeb4b | ||
|
|
5fea19aa88 | ||
|
|
b338a4bba3 | ||
|
|
a67eb5128f | ||
|
|
7ff36c1bdb | ||
|
|
2cf683fc0f | ||
|
|
b605619baf | ||
|
|
57a6dc9225 | ||
|
|
dad80c023f | ||
|
|
d4bb7a14e3 | ||
|
|
f72c6d54a4 | ||
|
|
e679849568 | ||
|
|
5ea312c64e | ||
|
|
f7ad051eef | ||
|
|
8c5ef66865 | ||
|
|
f130d0c3bf | ||
|
|
1c2d5300f1 | ||
|
|
66804e5a47 | ||
|
|
01c343a13a | ||
|
|
932447aab6 | ||
|
|
7394813151 | ||
|
|
01824c518f | ||
|
|
e38ab21d27 | ||
|
|
e9370792f5 | ||
|
|
896e037662 | ||
|
|
644cc5e06a | ||
|
|
9f4d2d8924 | ||
|
|
7ee0e867c8 | ||
|
|
b9c5e5f077 | ||
|
|
8631d06ca5 | ||
|
|
e7d9ddd8e6 | ||
|
|
a264e8d0c6 | ||
|
|
eb3a2e0dbf | ||
|
|
b7ee6018ad | ||
|
|
df151027cc | ||
|
|
cf3e27431e | ||
|
|
585cf775bd | ||
|
|
b686cce21b | ||
|
|
512e9789d9 | ||
|
|
09c27a433b | ||
|
|
e28aa2ee67 | ||
|
|
73e87410ac | ||
|
|
baece5113b | ||
|
|
230db979ed | ||
|
|
0e31bbdbd4 | ||
|
|
8399322238 | ||
|
|
03b057be64 | ||
|
|
5bd621c540 | ||
|
|
228a451284 | ||
|
|
3c67755c6b | ||
|
|
4d6ffa78f6 | ||
|
|
a9be4a9860 | ||
|
|
6275ddbcba | ||
|
|
365784bbed | ||
|
|
c1623e412d | ||
|
|
13f6cca318 | ||
|
|
1f46214363 | ||
|
|
c39a807766 | ||
|
|
3082c02fbe | ||
|
|
96a134e69e | ||
|
|
14fcebdde3 | ||
|
|
b4211a57e6 | ||
|
|
4a421c705a | ||
|
|
695e3a912c | ||
|
|
af190697c2 | ||
|
|
814ffbd468 | ||
|
|
cb10f39db3 | ||
|
|
c8a496faaa | ||
|
|
7c858007b3 | ||
|
|
3a9e8f1a79 | ||
|
|
dfa839c4e0 | ||
|
|
876fee3bc5 | ||
|
|
22ffd2e0bd | ||
|
|
8cd59b9ab5 | ||
|
|
988714f92a | ||
|
|
789d39bfb2 | ||
|
|
1c333e68c2 | ||
|
|
e9ff2e4fca | ||
|
|
173622fea1 | ||
|
|
9881f2c39e | ||
|
|
0970c0bc52 | ||
|
|
cb2686dc3c | ||
|
|
e0d79a4079 | ||
|
|
2e54a182c9 | ||
|
|
83a405db8b | ||
|
|
cdddecb834 | ||
|
|
faf528a94e | ||
|
|
aff3c978cb | ||
|
|
c09c787507 | ||
|
|
9b82667fb5 | ||
|
|
6a8d341ecc | ||
|
|
743ce9f07c | ||
|
|
fff550ca37 | ||
|
|
3c5c76eec4 | ||
|
|
ea05b09491 | ||
|
|
d6b8993eb3 | ||
|
|
0d61926a7e | ||
|
|
f8acf9278c | ||
|
|
23f6a778a5 | ||
|
|
e6e950e58c | ||
|
|
7162a19982 | ||
|
|
5a77dd8a96 | ||
|
|
4e89546923 | ||
|
|
0429a689a4 | ||
|
|
b7239b611a | ||
|
|
057df4d35c | ||
|
|
886a07896b | ||
|
|
60857b55f1 | ||
|
|
160590149f | ||
|
|
98c6ab5aac | ||
|
|
7c18cd1009 | ||
|
|
27e53bc363 | ||
|
|
f0e5c6efe7 | ||
|
|
cbd86d3810 | ||
|
|
6f9a2dd57f | ||
|
|
7f45bb22f5 | ||
|
|
9137ed6278 | ||
|
|
3cda6109e9 | ||
|
|
0ce6f07064 | ||
|
|
6bbad0a399 | ||
|
|
8c371cf8af | ||
|
|
d74e484752 | ||
|
|
8f315d9311 | ||
|
|
4a63f14c75 | ||
|
|
e42f7426ee | ||
|
|
e96a1d6bde | ||
|
|
d08805dca6 | ||
|
|
1ca268e36a | ||
|
|
865afb2f9a | ||
|
|
44f448493c | ||
|
|
cee5f769b6 | ||
|
|
ea05d5635a | ||
|
|
984f8b2dd9 | ||
|
|
737f9b3d1e | ||
|
|
8b3158de99 | ||
|
|
cf054c401e | ||
|
|
3a34fa7a8f | ||
|
|
9438f91547 | ||
|
|
157b694727 | ||
|
|
3caf2fc62e | ||
|
|
e3b144a374 | ||
|
|
f7987b5c3e |
25
.gitattributes
vendored
Normal file
25
.gitattributes
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
* text=auto
|
||||
|
||||
# Use CRLF for certain Windows files.
|
||||
*.vcproj eol=crlf
|
||||
*.sln eol=crlf
|
||||
*.bat eol=crlf
|
||||
README-WINDOWS.txt eol=crlf
|
||||
nzbget-setup.nsi eol=crlf
|
||||
windows/package-info.json eol=crlf
|
||||
windows/resources/resource.h eol=crlf
|
||||
windows/resources/nzbget.rc eol=crlf
|
||||
|
||||
# Configure GitHub's language detector
|
||||
lib/* linguist-vendored linguist-language=C++
|
||||
webui/lib/* linguist-vendored
|
||||
Makefile.in linguist-vendored
|
||||
configure linguist-vendored
|
||||
config.sub linguist-vendored
|
||||
aclocal.m4 linguist-vendored
|
||||
config.guess linguist-vendored
|
||||
depcomp linguist-vendored
|
||||
install-sh linguist-vendored
|
||||
missing linguist-vendored
|
||||
configure.ac linguist-vendored=false
|
||||
Makefile.am linguist-vendored=false
|
||||
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
|
||||
# GNU Autotools
|
||||
.deps/
|
||||
config.h
|
||||
config.h.in~
|
||||
config.log
|
||||
config.status
|
||||
Makefile
|
||||
stamp-h1
|
||||
autom4te.cache/
|
||||
.dirstamp
|
||||
*.o-*
|
||||
|
||||
# Visual Studio User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
.vs/
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.ilk
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
*.sln
|
||||
.vscode/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# NZBGet specific
|
||||
nzbget
|
||||
code_revision.cpp
|
||||
*.temp
|
||||
*.pyc
|
||||
pytest.ini
|
||||
.cache
|
||||
17
.lgtm.yml
Normal file
17
.lgtm.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# Configuration file for integration with http://lgtm.com
|
||||
|
||||
path_classifiers:
|
||||
library:
|
||||
# exclude these directories from default alerts report:
|
||||
- lib
|
||||
- webui/lib
|
||||
|
||||
extraction:
|
||||
cpp:
|
||||
configure:
|
||||
command:
|
||||
# compile with tests to activate scanning of C++ sources for tests
|
||||
- ./configure --enable-tests
|
||||
|
||||
queries:
|
||||
- exclude: js/incomplete-sanitization # this one gives false positives only and nothing useful
|
||||
67
.travis.yml
Normal file
67
.travis.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-5
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-5
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-4.8
|
||||
- CXXFLAGS="-std=c++11 -O2 -s"
|
||||
- CONFIGUREOPTS="--disable-cpp-check"
|
||||
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.6
|
||||
packages:
|
||||
- clang-3.6
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=clang++-3.6
|
||||
|
||||
install:
|
||||
- sudo pip install -U pytest
|
||||
|
||||
script:
|
||||
- $COMPILER --version
|
||||
- CXX=$COMPILER ./configure $CONFIGUREOPTS --enable-tests && make
|
||||
- ./nzbget --tests
|
||||
- cd tests/functional && pytest -v
|
||||
4
AUTHORS
4
AUTHORS
@@ -1,4 +0,0 @@
|
||||
nzbget:
|
||||
Sven Henkel <sidddy@users.sourceforge.net> (versions 0.1.0 - ?)
|
||||
Bo Cordes Petersen <placebodk@users.sourceforge.net> (versions ? - 0.2.3)
|
||||
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 - 0.3.*)
|
||||
@@ -1,910 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern DownloadSpeedMeter* g_pDownloadSpeedMeter;
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
const char* ArticleDownloader::m_szJobStatus[] = { "WAITING", "RUNNING", "FINISHED", "FAILED", "DECODING", "JOINING", "NOT_FOUND", "FATAL_ERROR" };
|
||||
|
||||
ArticleDownloader::ArticleDownloader()
|
||||
{
|
||||
debug("Creating ArticleDownloader");
|
||||
|
||||
m_szResultFilename = NULL;
|
||||
m_szTempFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szOutputFilename = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_eStatus = adUndefined;
|
||||
m_bDuplicate = false;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
ArticleDownloader::~ArticleDownloader()
|
||||
{
|
||||
debug("Destroying ArticleDownloader");
|
||||
|
||||
if (m_szTempFilename)
|
||||
{
|
||||
free(m_szTempFilename);
|
||||
}
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetTempFilename(const char* v)
|
||||
{
|
||||
m_szTempFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetOutputFilename(const char* v)
|
||||
{
|
||||
m_szOutputFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetInfoName(const char * v)
|
||||
{
|
||||
m_szInfoName = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ArticleDownloader::Run()
|
||||
{
|
||||
debug("Entering ArticleDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
m_szResultFilename = m_pArticleInfo->GetResultFilename();
|
||||
|
||||
if (g_pOptions->GetContinuePartial())
|
||||
{
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(m_szResultFilename, &buffer);
|
||||
if (fileExists)
|
||||
{
|
||||
// file exists from previous program's start
|
||||
info("Article %s already downloaded, skipping", m_szInfoName);
|
||||
SetStatus(adFinished);
|
||||
FreeConnection(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info("Downloading %s", m_szInfoName);
|
||||
|
||||
int retry = g_pOptions->GetRetries();
|
||||
|
||||
EStatus Status = adFailed;
|
||||
int iMaxLevel = g_pServerPool->GetMaxLevel();
|
||||
int* LevelStatus = (int*)malloc((iMaxLevel + 1) * sizeof(int));
|
||||
for (int i = 0; i <= iMaxLevel; i++)
|
||||
{
|
||||
LevelStatus[i] = 0;
|
||||
}
|
||||
int level = 0;
|
||||
|
||||
while (!IsStopped() && (retry > 0))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = adFailed;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = g_pServerPool->GetConnection(level, true);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
debug("m_pConnection is NULL");
|
||||
error("Serious error: Connection is NULL");
|
||||
}
|
||||
|
||||
// test connection
|
||||
bool connected = m_pConnection && m_pConnection->Connect() >= 0;
|
||||
if (connected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading!!
|
||||
Status = Download();
|
||||
}
|
||||
|
||||
bool bAuthError = m_pConnection && m_pConnection->GetAuthError();
|
||||
|
||||
if (connected)
|
||||
{
|
||||
// freeing connection allows other threads to start.
|
||||
// we doing this only if the problem was with article or group.
|
||||
// if the problem occurs by Connect() we do not free the connection,
|
||||
// to prevent starting of thousands of threads (cause each of them
|
||||
// will also free it's connection after the same connect-error).
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
}
|
||||
|
||||
if ((Status == adFailed || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
|
||||
((retry > 1) || !connected || bAuthError) && !IsStopped())
|
||||
{
|
||||
info("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Status == adFinished) || (Status == adFatalError) ||
|
||||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LevelStatus[level] = Status;
|
||||
|
||||
bool bAllLevelNotFound = true;
|
||||
for (int lev = 0; lev <= iMaxLevel; lev++)
|
||||
{
|
||||
if (LevelStatus[lev] != adNotFound)
|
||||
{
|
||||
bAllLevelNotFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bAllLevelNotFound)
|
||||
{
|
||||
if (iMaxLevel > 0)
|
||||
{
|
||||
warn("Article %s @ all servers failed: Article not found", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// do not count connect-errors, only article- and group-errors
|
||||
if (connected && !bAuthError)
|
||||
{
|
||||
level++;
|
||||
if (level > iMaxLevel)
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
retry--;
|
||||
}
|
||||
}
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
|
||||
free(LevelStatus);
|
||||
|
||||
if (m_bDuplicate)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
info("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Download %s failed", m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting ArticleDownloader-loop");
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
// at first, change group
|
||||
bool grpchanged = false;
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
grpchanged = m_pConnection->JoinGroup(*it);
|
||||
if (grpchanged)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!grpchanged)
|
||||
{
|
||||
if (!m_pConnection->GetAuthError() && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Could not join group", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
}
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
// now, let's begin!
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "ARTICLE %s\r\n", m_pArticleInfo->GetMessageID());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = NULL;
|
||||
|
||||
for (int retry = 3; retry > 0; retry--)
|
||||
{
|
||||
answer = m_pConnection->Request(tmp);
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
if (!m_pConnection->GetAuthError() && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
}
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
warn("Article %s @ %s failed: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), answer);
|
||||
return (!strncmp(answer, "41", 2) || !strncmp(answer, "42", 2)) ? adNotFound : adFailed;
|
||||
}
|
||||
|
||||
// positive answer!
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
m_YDecoder.Clear();
|
||||
m_YDecoder.SetAutoSeek(g_pOptions->GetDirectWrite());
|
||||
m_YDecoder.SetCrcCheck(g_pOptions->GetCrcCheck());
|
||||
}
|
||||
|
||||
m_pOutFile = NULL;
|
||||
EStatus Status = adRunning;
|
||||
bool bBody = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
// Throttle the bandwidth
|
||||
while (!IsStopped() && (g_pOptions->GetDownloadRate() > 0.0f) &&
|
||||
(g_pDownloadSpeedMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
int iLen = 0;
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
|
||||
g_pDownloadSpeedMeter->AddSpeedReading(iLen);
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
//detect end of article
|
||||
if ((!strcmp(line, ".\r\n")) || (!strcmp(line, ".\n")))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//detect lines starting with "." (marked as "..")
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
// check id of returned article
|
||||
if (!bBody)
|
||||
{
|
||||
if ((!strcmp(line, "\r\n")) || (!strcmp(line, "\n")))
|
||||
{
|
||||
bBody = true;
|
||||
}
|
||||
else if (!strncmp(line, "Message-ID: ", 12))
|
||||
{
|
||||
char* p = line + 12;
|
||||
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
|
||||
{
|
||||
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
|
||||
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Write(line, iLen))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
|
||||
if (m_pOutFile)
|
||||
{
|
||||
fclose(m_pOutFile);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
remove(m_szTempFilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
remove(m_szTempFilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
FreeConnection(true);
|
||||
return Decode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Write(char* szLine, int iLen)
|
||||
{
|
||||
if (!m_pOutFile && !PrepareFile(szLine))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
return m_YDecoder.Write(szLine, m_pOutFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fwrite(szLine, 1, iLen, m_pOutFile) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
bool bOpen = false;
|
||||
|
||||
// prepare file for writing
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
if (!strncmp(szLine, "=ybegin part=", 13))
|
||||
{
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
char* pb = strstr(szLine, "name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (!m_szArticleFilename)
|
||||
{
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
}
|
||||
if (m_pFileInfo->IsDupe(m_szArticleFilename))
|
||||
{
|
||||
m_bDuplicate = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char* pb = strstr(szLine, "size=");
|
||||
if (pb)
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
pb += 5; //=strlen("size=")
|
||||
long iArticleFilesize = atol(pb);
|
||||
if (!SetFileSize(m_szOutputFilename, iArticleFilesize))
|
||||
{
|
||||
error("Could not create file %s!", m_szOutputFilename);
|
||||
return false;
|
||||
}
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
|
||||
if (bOpen)
|
||||
{
|
||||
const char* szFilename = g_pOptions->GetDirectWrite() ? m_szOutputFilename : m_szTempFilename;
|
||||
m_pOutFile = fopen(szFilename, g_pOptions->GetDirectWrite() ? "r+" : "w");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", g_pOptions->GetDirectWrite() ? "open" : "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, m_pArticleInfo->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Decode()
|
||||
{
|
||||
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
SetStatus(adDecoding);
|
||||
|
||||
char tmpdestfile[1024];
|
||||
char* szDecoderTempFilename = NULL;
|
||||
Decoder* pDecoder = NULL;
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
pDecoder = &m_YDecoder;
|
||||
szDecoderTempFilename = m_szTempFilename;
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcUulib)
|
||||
{
|
||||
pDecoder = new UULibDecoder();
|
||||
pDecoder->SetSrcFilename(m_szTempFilename);
|
||||
snprintf(tmpdestfile, 1024, "%s.dec", m_szResultFilename);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
szDecoderTempFilename = tmpdestfile;
|
||||
pDecoder->SetDestFilename(szDecoderTempFilename);
|
||||
}
|
||||
|
||||
bool bOK = pDecoder->Execute();
|
||||
|
||||
if (!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
if (bOK)
|
||||
{
|
||||
rename(szDecoderTempFilename, m_szResultFilename);
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcUulib)
|
||||
{
|
||||
remove(szDecoderTempFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_szArticleFilename && pDecoder->GetArticleFilename())
|
||||
{
|
||||
m_szArticleFilename = strdup(pDecoder->GetArticleFilename());
|
||||
}
|
||||
|
||||
remove(m_szTempFilename);
|
||||
bool bCrcError = pDecoder->GetCrcError();
|
||||
if (pDecoder != &m_YDecoder)
|
||||
{
|
||||
delete pDecoder;
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
info("Successfully downloaded %s", m_szInfoName);
|
||||
|
||||
if (g_pOptions->GetDirectWrite() && g_pOptions->GetContinuePartial())
|
||||
{
|
||||
// create empty flag-file to indicate that the artcile was downloaded
|
||||
FILE* flagfile = fopen(m_szResultFilename, "w");
|
||||
if (!flagfile)
|
||||
{
|
||||
error("Could not create file %s", m_szResultFilename);
|
||||
// this error can be ignored
|
||||
}
|
||||
fclose(flagfile);
|
||||
}
|
||||
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(m_szResultFilename);
|
||||
if (bCrcError)
|
||||
{
|
||||
warn("Decoding %s failed: CRC-Error", m_szInfoName);
|
||||
return adCrcError;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
// rawmode
|
||||
rename(m_szTempFilename, m_szResultFilename);
|
||||
info("Article %s successfully downloaded", m_szInfoName);
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
// should not occur
|
||||
error("Internal error: Decoding %s failed", m_szInfoName);
|
||||
return adFatalError;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::LogDebugInfo()
|
||||
{
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&m_tLastUpdateTime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&m_tLastUpdateTime, szTime);
|
||||
#endif
|
||||
|
||||
debug(" Download: status=%s, LastUpdateTime=%s, filename=%s", GetStatusText(), szTime, BaseFileName(GetTempFilename()));
|
||||
}
|
||||
|
||||
void ArticleDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop ArticleDownloader");
|
||||
Thread::Stop();
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
debug("ArticleDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Terminate()
|
||||
{
|
||||
NNTPConnection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
g_pServerPool->FreeConnection(pConnection, true);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void ArticleDownloader::FreeConnection(bool bKeepConnected)
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
if (!bKeepConnected || m_pConnection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
g_pServerPool->FreeConnection(m_pConnection, true);
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::CompleteFileParts()
|
||||
{
|
||||
debug("Completing file parts");
|
||||
debug("ArticleFilename: %s", m_pFileInfo->GetFilename());
|
||||
|
||||
SetStatus(adJoining);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
m_pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
char InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
info("Moving articles for %s", InfoFilename);
|
||||
}
|
||||
else if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
info("Checking articles for %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Joining articles for %s", InfoFilename);
|
||||
}
|
||||
|
||||
char ofn[1024];
|
||||
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
ofn[1024-1] = '\0';
|
||||
|
||||
// Ensure the DstDir is created
|
||||
mkdir(m_pFileInfo->GetDestDir(), S_DIRMODE);
|
||||
|
||||
// prevent overwriting existing files
|
||||
struct stat statbuf;
|
||||
int dupcount = 0;
|
||||
while (!stat(ofn, &statbuf))
|
||||
{
|
||||
dupcount++;
|
||||
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
|
||||
ofn[1024-1] = '\0';
|
||||
}
|
||||
|
||||
FILE* outfile = NULL;
|
||||
char tmpdestfile[1024];
|
||||
snprintf(tmpdestfile, 1024, "%s.tmp", ofn);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
|
||||
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
outfile = fopen(tmpdestfile, "w+");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s!", tmpdestfile);
|
||||
SetStatus(adFinished);
|
||||
return;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1 && (*m_pFileInfo->GetArticles())[0])
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, (*m_pFileInfo->GetArticles())[0]->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
mkdir(ofn, S_DIRMODE);
|
||||
}
|
||||
|
||||
bool complete = true;
|
||||
int iBrokenCount = 0;
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
char* buffer = NULL;
|
||||
|
||||
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
buffer = (char*)malloc(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetStatus() != ArticleInfo::aiFinished)
|
||||
{
|
||||
iBrokenCount++;
|
||||
complete = false;
|
||||
}
|
||||
else if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
FILE* infile;
|
||||
const char* fn = pa->GetResultFilename();
|
||||
|
||||
infile = fopen(fn, "r");
|
||||
if (infile)
|
||||
{
|
||||
int cnt = BUFFER_SIZE;
|
||||
|
||||
while (cnt == BUFFER_SIZE)
|
||||
{
|
||||
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
|
||||
fwrite(buffer, 1, cnt, outfile);
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete = false;
|
||||
iBrokenCount++;
|
||||
info("Could not find file %s. Status is broken", fn);
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
const char* fn = pa->GetResultFilename();
|
||||
char dstFileName[1024];
|
||||
snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
|
||||
dstFileName[1024-1] = '\0';
|
||||
rename(fn, dstFileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (outfile)
|
||||
{
|
||||
fclose(outfile);
|
||||
rename(tmpdestfile, ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
rename(m_szOutputFilename, ofn);
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
|
||||
if (complete)
|
||||
{
|
||||
info("Successfully downloaded %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
|
||||
|
||||
if (g_pOptions->GetRenameBroken())
|
||||
{
|
||||
char brokenfn[1024];
|
||||
snprintf(brokenfn, 1024, "%s_broken", ofn);
|
||||
brokenfn[1024-1] = '\0';
|
||||
bool OK = rename(ofn, brokenfn) == 0;
|
||||
if (OK)
|
||||
{
|
||||
info("Renaming broken file from %s to %s", ofn, brokenfn);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Not renaming broken file %s", ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
fprintf(file, "%s (%i/%i)\n", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size());
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
warn("%s is incomplete!", InfoFilename);
|
||||
}
|
||||
|
||||
SetStatus(adFinished);
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ARTICLEDOWNLOADER_H
|
||||
#define ARTICLEDOWNLOADER_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Thread.h"
|
||||
#include "NNTPConnection.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
class ArticleDownloader : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adDecodeError,
|
||||
adCrcError,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
adNotFound,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
private:
|
||||
FileInfo* m_pFileInfo;
|
||||
ArticleInfo* m_pArticleInfo;
|
||||
NNTPConnection* m_pConnection;
|
||||
EStatus m_eStatus;
|
||||
Mutex m_mutexConnection;
|
||||
const char* m_szResultFilename;
|
||||
char* m_szTempFilename;
|
||||
char* m_szArticleFilename;
|
||||
char* m_szInfoName;
|
||||
char* m_szOutputFilename;
|
||||
time_t m_tLastUpdateTime;
|
||||
static const char* m_szJobStatus[];
|
||||
YDecoder m_YDecoder;
|
||||
FILE* m_pOutFile;
|
||||
bool m_bDuplicate;
|
||||
|
||||
EStatus Download();
|
||||
bool Write(char* szLine, int iLen);
|
||||
bool PrepareFile(char* szLine);
|
||||
EStatus Decode();
|
||||
void FreeConnection(bool bKeepConnected);
|
||||
|
||||
public:
|
||||
ArticleDownloader();
|
||||
~ArticleDownloader();
|
||||
void SetFileInfo(FileInfo* pFileInfo) { m_pFileInfo = pFileInfo; }
|
||||
FileInfo* GetFileInfo() { return m_pFileInfo; }
|
||||
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
|
||||
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
const char* GetStatusText() { return m_szJobStatus[m_eStatus]; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
bool Terminate();
|
||||
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
|
||||
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
|
||||
const char* GetTempFilename() { return m_szTempFilename; }
|
||||
void SetTempFilename(const char* v);
|
||||
void SetOutputFilename(const char* v);
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void SetInfoName(const char* v);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void CompleteFileParts();
|
||||
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
virtual void AddSpeedReading(int iBytes) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
41
COPYING
41
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
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.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ 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 Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
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.)
|
||||
|
||||
|
||||
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
|
||||
@@ -168,7 +168,7 @@ 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.
|
||||
|
||||
|
||||
4. 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
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. 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
|
||||
@@ -255,7 +255,7 @@ 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.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ 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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
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
|
||||
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
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 GNU Library General
|
||||
Public License instead of this License.
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ColoredFrontend.h"
|
||||
|
||||
ColoredFrontend::ColoredFrontend()
|
||||
{
|
||||
m_bSummary = true;
|
||||
m_bNeedGoBack = false;
|
||||
#ifdef WIN32
|
||||
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforePrint()
|
||||
{
|
||||
if (m_bNeedGoBack)
|
||||
{
|
||||
// go back one line
|
||||
#ifdef WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
||||
GetConsoleScreenBufferInfo(m_hConsole, &BufInfo);
|
||||
BufInfo.dwCursorPosition.Y--;
|
||||
SetConsoleCursorPosition(m_hConsole, BufInfo.dwCursorPosition);
|
||||
#else
|
||||
printf("\r\033[1A");
|
||||
#endif
|
||||
m_bNeedGoBack = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintStatus()
|
||||
{
|
||||
char tmp[1024];
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
||||
|
||||
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long int)(fCurrentDownloadSpeed * 1024));
|
||||
int h = remain_sec / 3600;
|
||||
int m = (remain_sec % 3600) / 60;
|
||||
int s = remain_sec % 60;
|
||||
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
}
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
{
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
szDownloadLimit[0] = 0;
|
||||
}
|
||||
|
||||
char szParStatus[128];
|
||||
if (m_iParJobCount > 0)
|
||||
{
|
||||
sprintf(szParStatus, ", %i par", m_iParJobCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
szParStatus[0] = 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char* szControlSeq = "";
|
||||
#else
|
||||
printf("\033[s");
|
||||
char* szControlSeq = "\033[K";
|
||||
#endif
|
||||
|
||||
snprintf(tmp, 1024, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
|
||||
timeString, szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
#ifdef WIN32
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
SetConsoleTextAttribute(m_hConsole, 8);
|
||||
printf("[DEBUG]");
|
||||
break;
|
||||
case Message::mkError:
|
||||
SetConsoleTextAttribute(m_hConsole, 4);
|
||||
printf("[ERROR]");
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
SetConsoleTextAttribute(m_hConsole, 5);
|
||||
printf("[WARNING]");
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
SetConsoleTextAttribute(m_hConsole, 2);
|
||||
printf("[INFO]");
|
||||
break;
|
||||
}
|
||||
SetConsoleTextAttribute(m_hConsole, 7);
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
CharToOem(msg, msg);
|
||||
printf(" %s\n", msg);
|
||||
free(msg);
|
||||
#else
|
||||
const char* msg = pMessage->GetText();
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("\033[31m[ERROR]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("\033[35m[WARNING]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintSkip()
|
||||
{
|
||||
#ifdef WIN32
|
||||
printf(".....\n");
|
||||
#else
|
||||
printf(".....\033[K\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforeExit()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
542
Connection.cpp
542
Connection.cpp
@@ -1,542 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Intiializing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
|
||||
if (err != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
return;
|
||||
}
|
||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE( wsaData.wVersion ) != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::Final()
|
||||
{
|
||||
debug("Finalizing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Connection::Connection(NetAddress* pNetAddress)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = pNetAddress;
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
free(m_szReadBuf);
|
||||
}
|
||||
|
||||
int Connection::Connect()
|
||||
{
|
||||
debug("Connecting");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoConnect();
|
||||
|
||||
if (iRes >= 0)
|
||||
m_eStatus = csConnected;
|
||||
else
|
||||
Connection::DoDisconnect();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Disconnect()
|
||||
{
|
||||
debug("Disconnecting");
|
||||
|
||||
if (m_eStatus == csDisconnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoDisconnect();
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Bind()
|
||||
{
|
||||
debug("Binding");
|
||||
|
||||
if (m_eStatus == csListening)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iRes = DoBind();
|
||||
|
||||
if (iRes == 0)
|
||||
{
|
||||
m_eStatus = csListening;
|
||||
}
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::WriteLine(char* line)
|
||||
{
|
||||
//debug("Connection::write(char* line)");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = DoWriteLine(line);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Send(char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Sending data");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = send(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SOCKET Connection::Accept()
|
||||
{
|
||||
debug("Accepting connection");
|
||||
|
||||
if (m_eStatus != csListening)
|
||||
{
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET iRes = DoAccept();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Recv(char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
int iReceived = recv(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
if (iReceived < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
}
|
||||
|
||||
return iReceived;
|
||||
}
|
||||
|
||||
bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data (full buffer)");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
char* pBufPtr = (char*)pBuffer;
|
||||
int NeedBytes = iSize;
|
||||
// Read from the socket until nothing remains
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
int iReceived = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iReceived <= 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
return false;
|
||||
}
|
||||
pBufPtr += iReceived;
|
||||
NeedBytes -= iReceived;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoConnect()
|
||||
{
|
||||
debug("Do connecting");
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
int MSecVal = m_iTimeout * 1000;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
|
||||
#else
|
||||
struct timeval TimeVal;
|
||||
TimeVal.tv_sec = m_iTimeout;
|
||||
TimeVal.tv_usec = 0;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeVal, sizeof(TimeVal));
|
||||
#endif
|
||||
if (err != 0)
|
||||
{
|
||||
ReportError("setsockopt failed", NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
{
|
||||
unsigned int uaddr = inet_addr(szHost);
|
||||
if (uaddr == (unsigned int)-1)
|
||||
{
|
||||
struct hostent* hinfo;
|
||||
bool err = false;
|
||||
int h_errnop = 0;
|
||||
#ifdef WIN32
|
||||
hinfo = gethostbyname(szHost);
|
||||
err = hinfo == NULL;
|
||||
h_errnop = WSAGetLastError();
|
||||
#else
|
||||
struct hostent hinfobuf;
|
||||
static const int strbuflen = 1024;
|
||||
char* strbuf = (char*)malloc(strbuflen);
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_6
|
||||
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
|
||||
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
|
||||
#else
|
||||
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
#endif
|
||||
if (err)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", szHost, h_errnop);
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
|
||||
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
}
|
||||
return uaddr;
|
||||
}
|
||||
|
||||
int Connection::DoDisconnect()
|
||||
{
|
||||
debug("Do disconnecting");
|
||||
|
||||
if (m_iSocket > 0)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Connection::DoWriteLine(char* szText)
|
||||
{
|
||||
//debug("Connection::doWrite()");
|
||||
return send(m_iSocket, szText, strlen(szText), 0);
|
||||
}
|
||||
|
||||
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
//debug( "Connection::DoReadLine()" );
|
||||
char* pBufPtr = pBuffer;
|
||||
iSize--; // for trailing '0'
|
||||
int iBytesRead = 0;
|
||||
int iBufAvail = m_iBufAvail; // local variable is faster
|
||||
char* szBufPtr = m_szBufPtr; // local variable is faster
|
||||
while (iSize)
|
||||
{
|
||||
if (!iBufAvail)
|
||||
{
|
||||
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
|
||||
if (iBufAvail < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
break;
|
||||
}
|
||||
else if (iBufAvail == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
szBufPtr = m_szReadBuf;
|
||||
m_szReadBuf[iBufAvail] = '\0';
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
|
||||
if (p)
|
||||
{
|
||||
len = p - szBufPtr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = iBufAvail;
|
||||
}
|
||||
|
||||
if (len > iSize)
|
||||
{
|
||||
len = iSize;
|
||||
}
|
||||
|
||||
memcpy(pBufPtr, szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
szBufPtr += len;
|
||||
iBufAvail -= len;
|
||||
iBytesRead += len;
|
||||
iSize -= len;
|
||||
|
||||
if (p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
*pBufPtr = '\0';
|
||||
|
||||
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
|
||||
m_szBufPtr = szBufPtr; // copy back to member
|
||||
|
||||
if (pBytesRead)
|
||||
{
|
||||
*pBytesRead = iBytesRead;
|
||||
}
|
||||
|
||||
if (pBufPtr == pBuffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
int Connection::DoBind()
|
||||
{
|
||||
debug("Do binding");
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
|
||||
if (bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress)) < 0)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(m_iSocket, 10) < 0)
|
||||
{
|
||||
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SOCKET Connection::DoAccept()
|
||||
{
|
||||
struct sockaddr_in ClientAddress;
|
||||
socklen_t SockLen;
|
||||
|
||||
SockLen = sizeof(ClientAddress);
|
||||
|
||||
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
|
||||
|
||||
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
|
||||
{
|
||||
ReportError("Could not accept connection", NULL, 0);
|
||||
}
|
||||
|
||||
return iSocket;
|
||||
}
|
||||
|
||||
void Connection::Cancel()
|
||||
{
|
||||
debug("Cancelling connection");
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
m_eStatus = csCancelled;
|
||||
int r = shutdown(m_iSocket, SHUT_RDWR);
|
||||
if (r == -1)
|
||||
{
|
||||
ReportError("Could not shutdown connection", NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode)
|
||||
{
|
||||
if (ErrCode == 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ErrCode = WSAGetLastError();
|
||||
#else
|
||||
ErrCode = errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
char szErrPrefix[1024];
|
||||
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
|
||||
szErrPrefix[1024-1] = '\0';
|
||||
#ifdef WIN32
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
#else
|
||||
const char* szErrMsg = hstrerror(ErrCode);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
85
Connection.h
85
Connection.h
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled
|
||||
};
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
SOCKET m_iSocket;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(char* text);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int Bind();
|
||||
int Send(char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
int WriteLine(char* text);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
};
|
||||
|
||||
#endif
|
||||
373
Decoder.cpp
373
Decoder.cpp
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
|
||||
#ifdef ENABLE_UULIB
|
||||
#ifndef PROTOTYPES
|
||||
#define PROTOTYPES
|
||||
#endif
|
||||
#include <uudeview.h>
|
||||
#endif
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
Mutex UULibDecoder::m_mutexDecoder;
|
||||
unsigned int YDecoder::crc_tab[256];
|
||||
|
||||
Decoder::Decoder()
|
||||
{
|
||||
debug("Creating Decoder");
|
||||
|
||||
m_szSrcFilename = NULL;
|
||||
m_szDestFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_bCrcError = false;
|
||||
}
|
||||
|
||||
Decoder::~ Decoder()
|
||||
{
|
||||
debug("Destroying Decoder");
|
||||
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UULibDecoder
|
||||
*/
|
||||
|
||||
bool UULibDecoder::Execute()
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
#ifndef ENABLE_UULIB
|
||||
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
|
||||
#else
|
||||
|
||||
m_mutexDecoder.Lock();
|
||||
|
||||
UUInitialize();
|
||||
|
||||
UUSetOption(UUOPT_DESPERATE, 1, NULL);
|
||||
// UUSetOption(UUOPT_DUMBNESS,1,NULL);
|
||||
// UUSetOption( UUOPT_SAVEPATH, 1, szDestDir );
|
||||
|
||||
UULoadFile((char*) m_szSrcFilename, NULL, 0);
|
||||
|
||||
// choose right attachment
|
||||
|
||||
uulist* attachment = NULL;
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
uulist* att_tmp = UUGetFileListItem(i);
|
||||
|
||||
if (!att_tmp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((att_tmp) && (att_tmp->haveparts))
|
||||
{
|
||||
if (!attachment)
|
||||
{
|
||||
attachment = att_tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
//f**k, multiple attachments!? Can't handle this.
|
||||
attachment = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// okay, we got only one attachment, perfect!
|
||||
if ((attachment->haveparts) && (attachment->haveparts[0])) // && (!attachment->haveparts[1])) FUCK UULIB
|
||||
{
|
||||
int r = UUDecodeFile(attachment, (char*)m_szDestFilename);
|
||||
|
||||
if (r == UURET_OK)
|
||||
{
|
||||
// we did it!
|
||||
res = true;
|
||||
m_szArticleFilename = strdup(attachment->filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of parts!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of attachments!\n");
|
||||
}
|
||||
|
||||
UUCleanUp();
|
||||
|
||||
m_mutexDecoder.Unlock();
|
||||
|
||||
#endif // ENABLE_UULIB
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* YDecoder
|
||||
* Very primitive (but fast) implementation of yEnc-Decoder
|
||||
*/
|
||||
|
||||
void YDecoder::Init()
|
||||
{
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
void YDecoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
|
||||
YDecoder::YDecoder()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void YDecoder::Clear()
|
||||
{
|
||||
m_bBody = false;
|
||||
m_bEnd = false;
|
||||
m_lExpectedCRC = 0;
|
||||
m_lCalculatedCRC = 0xFFFFFFFF;
|
||||
m_iBegin = 0;
|
||||
m_iEnd = 0;
|
||||
m_bAutoSeek = false;
|
||||
m_bNeedSetPos = false;
|
||||
m_bCrcCheck = false;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
*
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
* Released under GPL (thanks)
|
||||
*
|
||||
* chksum_crc32gentab() -- to a global crc_tab[256], this one will
|
||||
* calculate the crcTable for crc32-checksums.
|
||||
* it is generated to the polynom [..]
|
||||
*/
|
||||
void YDecoder::crc32gentab()
|
||||
{
|
||||
unsigned long crc, poly;
|
||||
int i, j;
|
||||
|
||||
poly = 0xEDB88320L;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
crc = i;
|
||||
for (j = 8; j > 0; j--)
|
||||
{
|
||||
if (crc & 1)
|
||||
{
|
||||
crc = (crc >> 1) ^ poly;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
crc_tab[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is modified version of chksum_crc() from
|
||||
* crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
*
|
||||
* chksum_crc() -- to a given block, this one calculates the
|
||||
* crc32-checksum until the length is
|
||||
* reached. the crc32-checksum will be
|
||||
* the result.
|
||||
*/
|
||||
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
{
|
||||
register unsigned long crc = startCrc;
|
||||
for (unsigned long i = 0; i < length; i++)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
unsigned int YDecoder::DecodeBuffer(char* buffer)
|
||||
{
|
||||
if (m_bBody)
|
||||
{
|
||||
if (!strncmp(buffer, "=yend size=", 11))
|
||||
{
|
||||
m_bEnd = true;
|
||||
char* pc = strstr(buffer, "pcrc32=");
|
||||
if (pc)
|
||||
{
|
||||
pc += 7; //=strlen("pcrc32=")
|
||||
m_lExpectedCRC = strtoul(pc, NULL, 16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (true)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
if (m_bCrcCheck)
|
||||
{
|
||||
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, optr - buffer);
|
||||
}
|
||||
return optr - buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strncmp(buffer, "=ypart begin=", 13))
|
||||
{
|
||||
m_bBody = true;
|
||||
char* pb = strstr(buffer, "begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen("begin=")
|
||||
m_iBegin = (int)atoi(pb);
|
||||
}
|
||||
pb = strstr(buffer, "end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 4; //=strlen("end=")
|
||||
m_iEnd = (int)atoi(pb);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(buffer, "=ybegin part=", 13))
|
||||
{
|
||||
char* pb = strstr(buffer, "name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool YDecoder::Write(char* buffer, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
if (m_bNeedSetPos)
|
||||
{
|
||||
if (m_iBegin == 0 || m_iEnd == 0 || !outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_bNeedSetPos = false;
|
||||
}
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool YDecoder::Execute()
|
||||
{
|
||||
m_lCalculatedCRC ^= 0xFFFFFFFF;
|
||||
|
||||
debug("Expected pcrc32=%x", m_lExpectedCRC);
|
||||
debug("Calculated pcrc32=%x", m_lCalculatedCRC);
|
||||
m_bCrcError = m_bCrcCheck && (m_lExpectedCRC != m_lCalculatedCRC);
|
||||
|
||||
return m_bBody && m_bEnd && !m_bCrcError;
|
||||
}
|
||||
89
Decoder.h
89
Decoder.h
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Decoder
|
||||
{
|
||||
protected:
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
bool m_bCrcError;
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
virtual ~Decoder();
|
||||
virtual bool Execute() = 0;
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
bool GetCrcError() { return m_bCrcError; }
|
||||
};
|
||||
|
||||
class UULibDecoder: public Decoder
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexDecoder;
|
||||
|
||||
public:
|
||||
virtual bool Execute();
|
||||
};
|
||||
|
||||
class YDecoder: public Decoder
|
||||
{
|
||||
protected:
|
||||
static unsigned int crc_tab[256];
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
unsigned long m_lExpectedCRC;
|
||||
unsigned long m_lCalculatedCRC;
|
||||
unsigned long m_iBegin;
|
||||
unsigned long m_iEnd;
|
||||
bool m_bAutoSeek;
|
||||
bool m_bNeedSetPos;
|
||||
bool m_bCrcCheck;
|
||||
|
||||
unsigned int DecodeBuffer(char* buffer);
|
||||
static void crc32gentab();
|
||||
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
|
||||
|
||||
public:
|
||||
YDecoder();
|
||||
virtual bool Execute();
|
||||
void Clear();
|
||||
bool Write(char* buffer, FILE* outfile);
|
||||
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
|
||||
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
};
|
||||
|
||||
#endif
|
||||
396
DiskState.cpp
396
DiskState.cpp
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
/* Save Download Queue to Disk.
|
||||
* The Disk State consists of file "queue", which contains the order of files
|
||||
* and of one diskstate-file for each file in download queue.
|
||||
* This function saves only file "queue".
|
||||
*/
|
||||
bool DiskState::Save(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Saving queue to disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* outfile = fopen(fileName, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", fileName);
|
||||
perror(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "nzbget diskstate file version 1\n");
|
||||
|
||||
int cnt = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetDeleted())
|
||||
{
|
||||
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (cnt == 0)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::SaveFile(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return SaveFileInfo(pFileInfo, fileName);
|
||||
}
|
||||
|
||||
bool DiskState::Load(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Loading queue from disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) != EOF)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::LoadArticles(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return LoadFileInfo(pFileInfo, fileName, false, true);
|
||||
}
|
||||
|
||||
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
{
|
||||
debug("Saving FileInfo to disk");
|
||||
|
||||
FILE* outfile = fopen(szFilename, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
|
||||
|
||||
fprintf(outfile, "%lu,%lu\n", (unsigned long)(pFileInfo->GetSize() >> 32), (unsigned long)(pFileInfo->GetSize()));
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetGroups()->size());
|
||||
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
fprintf(outfile, "%s\n", *it);
|
||||
}
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetArticles()->size());
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = *it;
|
||||
fprintf(outfile, "%i,%i\n", pArticleInfo->GetPartNumber(), pArticleInfo->GetSize());
|
||||
fprintf(outfile, "%s\n", pArticleInfo->GetMessageID());
|
||||
}
|
||||
|
||||
fclose(outfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
|
||||
{
|
||||
debug("Loading FileInfo from disk");
|
||||
|
||||
FILE* infile = fopen(szFilename, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetFilename(buf);
|
||||
|
||||
int iFilenameConfirmed;
|
||||
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
||||
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
if (bFileSummary) pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
|
||||
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
||||
|
||||
int size;
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
}
|
||||
|
||||
if (bArticles)
|
||||
{
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int PartNumber, PartSize;
|
||||
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
|
||||
ArticleInfo* pArticleInfo = new ArticleInfo();
|
||||
pArticleInfo->SetPartNumber(PartNumber);
|
||||
pArticleInfo->SetSize(PartSize);
|
||||
pArticleInfo->SetMessageID(buf);
|
||||
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::Discard()
|
||||
{
|
||||
debug("Discarding queue");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) == 2)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not discard diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
if (res)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::Exists()
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(fileName, &buffer);
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
// delete diskstate-file
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return !pDownloadQueue || Save(pDownloadQueue);
|
||||
}
|
||||
|
||||
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
// build array of IDs of files in queue for faster access
|
||||
int* ids = (int*)malloc(sizeof(int) * (pDownloadQueue->size() + 1));
|
||||
int* ptr = ids;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
*ptr++ = pFileInfo->GetID();
|
||||
}
|
||||
*ptr = 0;
|
||||
|
||||
// read directory
|
||||
DirBrowser dir(g_pOptions->GetTempDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int id, part;
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec") ||
|
||||
((sscanf(filename, "%i.out", &id) == 1) &&
|
||||
!(g_pOptions->GetContinuePartial() && g_pOptions->GetDirectWrite()));
|
||||
if (!del)
|
||||
{
|
||||
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
|
||||
(sscanf(filename, "%i.out", &id) == 1))
|
||||
{
|
||||
del = true;
|
||||
ptr = ids;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == id)
|
||||
{
|
||||
del = false;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (del)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetTempDir(), filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
remove(szFullFilename);
|
||||
}
|
||||
}
|
||||
|
||||
free(ids);
|
||||
}
|
||||
48
DiskState.h
48
DiskState.h
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DISKSTATE_H
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
|
||||
|
||||
public:
|
||||
bool Exists();
|
||||
bool Save(DownloadQueue* pDownloadQueue);
|
||||
bool Load(DownloadQueue* pDownloadQueue);
|
||||
bool SaveFile(FileInfo* pFileInfo);
|
||||
bool LoadArticles(FileInfo* pFileInfo);
|
||||
bool Discard();
|
||||
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
258
DownloadInfo.cpp
258
DownloadInfo.cpp
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
|
||||
ArticleInfo::ArticleInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
m_szMessageID = NULL;
|
||||
m_iSize = 0;
|
||||
m_eStatus = aiUndefined;
|
||||
m_szResultFilename = NULL;
|
||||
}
|
||||
|
||||
ArticleInfo::~ ArticleInfo()
|
||||
{
|
||||
//debug("Destroying ArticleInfo");
|
||||
|
||||
if (m_szMessageID)
|
||||
{
|
||||
free(m_szMessageID);
|
||||
}
|
||||
if (m_szResultFilename)
|
||||
{
|
||||
free(m_szResultFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleInfo::SetMessageID(const char * szMessageID)
|
||||
{
|
||||
m_szMessageID = strdup(szMessageID);
|
||||
}
|
||||
|
||||
void ArticleInfo::SetResultFilename(const char * v)
|
||||
{
|
||||
m_szResultFilename = strdup(v);
|
||||
}
|
||||
|
||||
|
||||
FileInfo::FileInfo()
|
||||
{
|
||||
debug("Creating FileInfo");
|
||||
|
||||
m_Articles.clear();
|
||||
m_Groups.clear();
|
||||
m_szSubject = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_bFilenameConfirmed = false;
|
||||
m_szDestDir = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_bOutputInitialized = false;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
FileInfo::~ FileInfo()
|
||||
{
|
||||
debug("Destroying FileInfo");
|
||||
|
||||
if (m_szSubject)
|
||||
{
|
||||
free(m_szSubject);
|
||||
}
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
|
||||
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_Groups.clear();
|
||||
|
||||
ClearArticles();
|
||||
}
|
||||
|
||||
void FileInfo::ClearArticles()
|
||||
{
|
||||
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Articles.clear();
|
||||
}
|
||||
|
||||
void FileInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetSubject(const char* szSubject)
|
||||
{
|
||||
m_szSubject = strdup(szSubject);
|
||||
}
|
||||
|
||||
void FileInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void FileInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(m_szNZBFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void FileInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// OK, using stripped name
|
||||
}
|
||||
else
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is empty, use basename without cleaing up "msgid_"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is STILL empty, use "noname"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
strncpy(postname, "noname", 1024);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
void FileInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::MakeValidFilename()
|
||||
{
|
||||
::MakeValidFilename(m_szFilename, '_');
|
||||
}
|
||||
|
||||
void FileInfo::LockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Lock();
|
||||
}
|
||||
|
||||
void FileInfo::UnlockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Unlock();
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe(const char* szFilename)
|
||||
{
|
||||
struct stat buffer;
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
bool exists = !stat(fileName, &buffer);
|
||||
if (exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
if (exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
133
DownloadInfo.h
133
DownloadInfo.h
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DOWNLOADINFO_H
|
||||
#define DOWNLOADINFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iPartNumber;
|
||||
char* m_szMessageID;
|
||||
int m_iSize;
|
||||
EStatus m_eStatus;
|
||||
char* m_szResultFilename;
|
||||
|
||||
public:
|
||||
ArticleInfo();
|
||||
~ArticleInfo();
|
||||
void SetPartNumber(int s) { m_iPartNumber = s; }
|
||||
int GetPartNumber() { return m_iPartNumber; }
|
||||
const char* GetMessageID() { return m_szMessageID; }
|
||||
void SetMessageID(const char* szMessageID);
|
||||
void SetSize(int s) { m_iSize = s; }
|
||||
int GetSize() { return m_iSize; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
const char* GetResultFilename() { return m_szResultFilename; }
|
||||
void SetResultFilename(const char* v);
|
||||
};
|
||||
|
||||
class FileInfo
|
||||
{
|
||||
public:
|
||||
typedef std::vector<ArticleInfo*> Articles;
|
||||
typedef std::vector<char*> Groups;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
Articles m_Articles;
|
||||
Groups m_Groups;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szSubject;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
Articles* GetArticles() { return &m_Articles; }
|
||||
Groups* GetGroups() { return &m_Groups; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetSubject() { return m_szSubject; }
|
||||
void SetSubject(const char* szSubject);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void MakeValidFilename();
|
||||
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
|
||||
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
|
||||
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
|
||||
bool GetPaused() { return m_bPaused; }
|
||||
void SetPaused(bool Paused) { m_bPaused = Paused; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void ClearArticles();
|
||||
void LockOutputFile();
|
||||
void UnlockOutputFile();
|
||||
bool GetOutputInitialized() { return m_bOutputInitialized; }
|
||||
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
|
||||
bool IsDupe(const char* szFilename);
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
#endif
|
||||
428
Frontend.cpp
428
Frontend.cpp
@@ -1,428 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_iNeededLogFirstID = 0;
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
m_fCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPause = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iParJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
|
||||
m_iParJobCount = pParQueue->size();
|
||||
g_pPrePostProcessor->UnlockParQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frontend::FreeData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
for (Log::Messages::iterator it = m_RemoteMessages.begin(); it != m_RemoteMessages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteMessages.clear();
|
||||
|
||||
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteQueue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages * Frontend::LockMessages()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteMessages;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pLog->LockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockMessages()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pLog->UnlockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue * Frontend::LockQueue()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteQueue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->LockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockQueue()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::IsRemoteMode()
|
||||
{
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPause(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(fRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetDownloadRate(fRate);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerDumpDebug()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestDumpDebug();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
}
|
||||
|
||||
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
|
||||
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
|
||||
m_RemoteMessages.push_back(pMessage);
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(m_bFileList);
|
||||
ListRequest.m_bServerState = htonl(m_bSummary);
|
||||
|
||||
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ntohl(ListResponse.m_bServerPaused);
|
||||
m_lRemainingSize = JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0;
|
||||
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0;
|
||||
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
|
||||
m_iParJobCount = ntohl(ListResponse.m_iParJobCount);
|
||||
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
|
||||
m_iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
|
||||
pFileInfo->SetSize(JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
|
||||
pFileInfo->SetRemainingSize(JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
|
||||
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
|
||||
pFileInfo->SetNZBFilename(szNZBFilename);
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
|
||||
pFileInfo->SetDestDir(szDestDir);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
}
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(float fRate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(fRate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestDumpDebug()
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
|
||||
}
|
||||
85
Frontend.h
85
Frontend.h
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FRONTEND_H
|
||||
#define FRONTEND_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueEditor.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
private:
|
||||
Log::Messages m_RemoteMessages;
|
||||
DownloadQueue m_RemoteQueue;
|
||||
|
||||
bool RequestMessages();
|
||||
bool RequestFileList();
|
||||
|
||||
protected:
|
||||
bool m_bSummary;
|
||||
bool m_bFileList;
|
||||
unsigned int m_iNeededLogEntries;
|
||||
unsigned int m_iNeededLogFirstID;
|
||||
int m_iUpdateInterval;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
float m_fDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
int m_iParJobCount;
|
||||
int m_iUpTimeSec;
|
||||
int m_iDnTimeSec;
|
||||
long long m_iAllBytes;
|
||||
bool m_bStandBy;
|
||||
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
Log::Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause);
|
||||
bool RequestPauseUnpause(bool bPause);
|
||||
void ServerSetDownloadRate(float fRate);
|
||||
bool RequestSetDownloadRate(float fRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
167
INSTALL
167
INSTALL
@@ -1,167 +0,0 @@
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, a file
|
||||
`config.cache' that saves the results of its tests to speed up
|
||||
reconfiguring, and a file `config.log' containing compiler output
|
||||
(useful mainly for debugging `configure').
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If at some point `config.cache'
|
||||
contains results you don't want to keep, you may remove or edit it.
|
||||
|
||||
The file `configure.in' is used to create `configure' by a program
|
||||
called `autoconf'. You only need `configure.in' if you want to change
|
||||
it or regenerate `configure' using a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes a while. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
4. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. You can give `configure'
|
||||
initial values for variables by setting them in the environment. Using
|
||||
a Bourne-compatible shell, you can do that on the command line like
|
||||
this:
|
||||
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
|
||||
|
||||
Or on systems that have the `env' program, you can do it like this:
|
||||
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not supports the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a time
|
||||
in the source code directory. After you have installed the package for
|
||||
one architecture, use `make distclean' before reconfiguring for another
|
||||
architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' can not figure out
|
||||
automatically, but needs to determine by the type of host the package
|
||||
will run on. Usually `configure' can figure that out, but if it prints
|
||||
a message saying it can not guess the host type, give it the
|
||||
`--host=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name with three fields:
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the host type.
|
||||
|
||||
If you are building compiler tools for cross-compiling, you can also
|
||||
use the `--target=TYPE' option to select the type of system they will
|
||||
produce code for and the `--build=TYPE' option to select the type of
|
||||
system on which you are compiling the package.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Operation Controls
|
||||
==================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Use and save the results of the tests in FILE instead of
|
||||
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
|
||||
debugging `configure'.
|
||||
|
||||
`--help'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made.
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--version'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options.
|
||||
|
||||
334
Log.cpp
334
Log.cpp
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Log::Log()
|
||||
{
|
||||
m_Messages.clear();
|
||||
m_iIDGen = 0;
|
||||
m_szLogFilename = NULL;
|
||||
#ifdef DEBUG
|
||||
struct stat buffer;
|
||||
m_bExtraDebug = !stat("extradebug", &buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
free(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Filelog(const char* msg, ...)
|
||||
{
|
||||
if (
|
||||
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
#ifdef DEBUG
|
||||
|| (m_szLogFilename && m_bExtraDebug)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!m_szLogFilename)
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&rawtime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&rawtime, szTime);
|
||||
#endif
|
||||
szTime[50-1] = '\0';
|
||||
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
||||
|
||||
FILE* file = fopen(m_szLogFilename, "a+");
|
||||
if (file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
unsigned long iThreadId = GetCurrentThreadId();
|
||||
#else
|
||||
unsigned long iThreadId = (unsigned long)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
|
||||
#else
|
||||
fprintf(file, "%s\t%s\n", szTime, tmp2);
|
||||
#endif
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef debug
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
|
||||
#else
|
||||
void debug(const char* msg, ...)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char tmp1[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp1, 1024, msg, ap);
|
||||
tmp1[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
char tmp2[1024];
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
if (szFuncname)
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, BaseFileName(szFilename), iLineNr);
|
||||
}
|
||||
#else
|
||||
snprintf(tmp2, 1024, "%s", tmp1);
|
||||
#endif
|
||||
tmp2[1024-1] = '\0';
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
if (!g_pOptions)
|
||||
{
|
||||
if (g_pLog->m_bExtraDebug)
|
||||
{
|
||||
printf("%s\n", tmp2);
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkDebug, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void error(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("ERROR\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkError, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void warn(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("WARNING\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkWarning, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void info(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("INFO\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkInfo, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void abort(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
printf("\n%s", tmp2);
|
||||
|
||||
g_pLog->Filelog(tmp2);
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//************************************************************
|
||||
// Message
|
||||
|
||||
Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText)
|
||||
{
|
||||
m_iID = iID;
|
||||
m_eKind = eKind;
|
||||
m_tTime = tTime;
|
||||
if (szText)
|
||||
{
|
||||
m_szText = strdup(szText);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szText = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Message::~ Message()
|
||||
{
|
||||
if (m_szText)
|
||||
{
|
||||
free(m_szText);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages* Log::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void Log::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::ResetLog()
|
||||
{
|
||||
remove(g_pOptions->GetLogFile());
|
||||
}
|
||||
118
Log.h
118
Log.h
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
void error(const char* msg, ...);
|
||||
void warn(const char* msg, ...);
|
||||
void info(const char* msg, ...);
|
||||
void abort(const char* msg, ...);
|
||||
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
void debug(const char* msg, ...);
|
||||
#endif
|
||||
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
mkInfo,
|
||||
mkWarning,
|
||||
mkError,
|
||||
mkDebug
|
||||
};
|
||||
|
||||
private:
|
||||
unsigned int m_iID;
|
||||
EKind m_eKind;
|
||||
time_t m_tTime;
|
||||
char* m_szText;
|
||||
|
||||
public:
|
||||
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
|
||||
~Message();
|
||||
unsigned int GetID() { return m_iID; }
|
||||
EKind GetKind() { return m_eKind; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
const char* GetText() { return m_szText; }
|
||||
};
|
||||
|
||||
class Log
|
||||
{
|
||||
public:
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
char* m_szLogFilename;
|
||||
unsigned int m_iIDGen;
|
||||
#ifdef DEBUG
|
||||
bool m_bExtraDebug;
|
||||
#endif
|
||||
|
||||
void Filelog(const char* msg, ...);
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
friend void error(const char* msg, ...);
|
||||
friend void warn(const char* msg, ...);
|
||||
friend void info(const char* msg, ...);
|
||||
friend void abort(const char* msg, ...);
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
friend void debug(const char* msg, ...);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Log();
|
||||
~Log();
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
void ResetLog();
|
||||
};
|
||||
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
#ifdef DEBUG
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern Log* g_pLog;
|
||||
|
||||
#endif
|
||||
566
Makefile.am
566
Makefile.am
@@ -1,18 +1,554 @@
|
||||
bin_PROGRAMS = nzbget
|
||||
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
|
||||
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
|
||||
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
|
||||
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
|
||||
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
|
||||
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
|
||||
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
|
||||
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
|
||||
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
EXTRA_DIST = nzbget.conf.example \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
bin_PROGRAMS = nzbget
|
||||
|
||||
nzbget_SOURCES = \
|
||||
daemon/connect/Connection.cpp \
|
||||
daemon/connect/Connection.h \
|
||||
daemon/connect/TlsSocket.cpp \
|
||||
daemon/connect/TlsSocket.h \
|
||||
daemon/connect/WebDownloader.cpp \
|
||||
daemon/connect/WebDownloader.h \
|
||||
daemon/extension/FeedScript.cpp \
|
||||
daemon/extension/FeedScript.h \
|
||||
daemon/extension/CommandScript.cpp \
|
||||
daemon/extension/CommandScript.h \
|
||||
daemon/extension/NzbScript.cpp \
|
||||
daemon/extension/NzbScript.h \
|
||||
daemon/extension/PostScript.cpp \
|
||||
daemon/extension/PostScript.h \
|
||||
daemon/extension/QueueScript.cpp \
|
||||
daemon/extension/QueueScript.h \
|
||||
daemon/extension/ScanScript.cpp \
|
||||
daemon/extension/ScanScript.h \
|
||||
daemon/extension/SchedulerScript.cpp \
|
||||
daemon/extension/SchedulerScript.h \
|
||||
daemon/extension/ScriptConfig.cpp \
|
||||
daemon/extension/ScriptConfig.h \
|
||||
daemon/feed/FeedCoordinator.cpp \
|
||||
daemon/feed/FeedCoordinator.h \
|
||||
daemon/feed/FeedFile.cpp \
|
||||
daemon/feed/FeedFile.h \
|
||||
daemon/feed/FeedFilter.cpp \
|
||||
daemon/feed/FeedFilter.h \
|
||||
daemon/feed/FeedInfo.cpp \
|
||||
daemon/feed/FeedInfo.h \
|
||||
daemon/frontend/ColoredFrontend.cpp \
|
||||
daemon/frontend/ColoredFrontend.h \
|
||||
daemon/frontend/Frontend.cpp \
|
||||
daemon/frontend/Frontend.h \
|
||||
daemon/frontend/LoggableFrontend.cpp \
|
||||
daemon/frontend/LoggableFrontend.h \
|
||||
daemon/frontend/NCursesFrontend.cpp \
|
||||
daemon/frontend/NCursesFrontend.h \
|
||||
daemon/main/CommandLineParser.cpp \
|
||||
daemon/main/CommandLineParser.h \
|
||||
daemon/main/DiskService.cpp \
|
||||
daemon/main/DiskService.h \
|
||||
daemon/main/Maintenance.cpp \
|
||||
daemon/main/Maintenance.h \
|
||||
daemon/main/nzbget.cpp \
|
||||
daemon/main/nzbget.h \
|
||||
daemon/main/Options.cpp \
|
||||
daemon/main/Options.h \
|
||||
daemon/main/WorkState.cpp \
|
||||
daemon/main/WorkState.h \
|
||||
daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h \
|
||||
daemon/main/StackTrace.cpp \
|
||||
daemon/main/StackTrace.h \
|
||||
daemon/nntp/ArticleDownloader.cpp \
|
||||
daemon/nntp/ArticleDownloader.h \
|
||||
daemon/nntp/ArticleWriter.cpp \
|
||||
daemon/nntp/ArticleWriter.h \
|
||||
daemon/nntp/Decoder.cpp \
|
||||
daemon/nntp/Decoder.h \
|
||||
daemon/nntp/NewsServer.cpp \
|
||||
daemon/nntp/NewsServer.h \
|
||||
daemon/nntp/NntpConnection.cpp \
|
||||
daemon/nntp/NntpConnection.h \
|
||||
daemon/nntp/ServerPool.cpp \
|
||||
daemon/nntp/ServerPool.h \
|
||||
daemon/nntp/StatMeter.cpp \
|
||||
daemon/nntp/StatMeter.h \
|
||||
daemon/postprocess/Cleanup.cpp \
|
||||
daemon/postprocess/Cleanup.h \
|
||||
daemon/postprocess/DupeMatcher.cpp \
|
||||
daemon/postprocess/DupeMatcher.h \
|
||||
daemon/postprocess/ParChecker.cpp \
|
||||
daemon/postprocess/ParChecker.h \
|
||||
daemon/postprocess/ParParser.cpp \
|
||||
daemon/postprocess/ParParser.h \
|
||||
daemon/postprocess/ParRenamer.cpp \
|
||||
daemon/postprocess/ParRenamer.h \
|
||||
daemon/postprocess/PrePostProcessor.cpp \
|
||||
daemon/postprocess/PrePostProcessor.h \
|
||||
daemon/postprocess/RarRenamer.cpp \
|
||||
daemon/postprocess/RarRenamer.h \
|
||||
daemon/postprocess/RarReader.cpp \
|
||||
daemon/postprocess/RarReader.h \
|
||||
daemon/postprocess/Rename.cpp \
|
||||
daemon/postprocess/Rename.h \
|
||||
daemon/postprocess/Repair.cpp \
|
||||
daemon/postprocess/Repair.h \
|
||||
daemon/postprocess/Unpack.cpp \
|
||||
daemon/postprocess/Unpack.h \
|
||||
daemon/postprocess/DirectUnpack.cpp \
|
||||
daemon/postprocess/DirectUnpack.h \
|
||||
daemon/queue/DirectRenamer.cpp \
|
||||
daemon/queue/DirectRenamer.h \
|
||||
daemon/queue/DiskState.cpp \
|
||||
daemon/queue/DiskState.h \
|
||||
daemon/queue/DownloadInfo.cpp \
|
||||
daemon/queue/DownloadInfo.h \
|
||||
daemon/queue/DupeCoordinator.cpp \
|
||||
daemon/queue/DupeCoordinator.h \
|
||||
daemon/queue/HistoryCoordinator.cpp \
|
||||
daemon/queue/HistoryCoordinator.h \
|
||||
daemon/queue/NzbFile.cpp \
|
||||
daemon/queue/NzbFile.h \
|
||||
daemon/queue/QueueCoordinator.cpp \
|
||||
daemon/queue/QueueCoordinator.h \
|
||||
daemon/queue/QueueEditor.cpp \
|
||||
daemon/queue/QueueEditor.h \
|
||||
daemon/queue/Scanner.cpp \
|
||||
daemon/queue/Scanner.h \
|
||||
daemon/queue/UrlCoordinator.cpp \
|
||||
daemon/queue/UrlCoordinator.h \
|
||||
daemon/remote/BinRpc.cpp \
|
||||
daemon/remote/BinRpc.h \
|
||||
daemon/remote/MessageBase.h \
|
||||
daemon/remote/RemoteClient.cpp \
|
||||
daemon/remote/RemoteClient.h \
|
||||
daemon/remote/RemoteServer.cpp \
|
||||
daemon/remote/RemoteServer.h \
|
||||
daemon/remote/WebServer.cpp \
|
||||
daemon/remote/WebServer.h \
|
||||
daemon/remote/XmlRpc.cpp \
|
||||
daemon/remote/XmlRpc.h \
|
||||
daemon/util/Log.cpp \
|
||||
daemon/util/Log.h \
|
||||
daemon/util/NString.cpp \
|
||||
daemon/util/NString.h \
|
||||
daemon/util/Container.h \
|
||||
daemon/util/Observer.cpp \
|
||||
daemon/util/Observer.h \
|
||||
daemon/util/Script.cpp \
|
||||
daemon/util/Script.h \
|
||||
daemon/util/Thread.cpp \
|
||||
daemon/util/Thread.h \
|
||||
daemon/util/Service.cpp \
|
||||
daemon/util/Service.h \
|
||||
daemon/util/FileSystem.cpp \
|
||||
daemon/util/FileSystem.h \
|
||||
daemon/util/Util.cpp \
|
||||
daemon/util/Util.h \
|
||||
daemon/nserv/NServMain.h \
|
||||
daemon/nserv/NServMain.cpp \
|
||||
daemon/nserv/NServFrontend.h \
|
||||
daemon/nserv/NServFrontend.cpp \
|
||||
daemon/nserv/NntpServer.h \
|
||||
daemon/nserv/NntpServer.cpp \
|
||||
daemon/nserv/NzbGenerator.h \
|
||||
daemon/nserv/NzbGenerator.cpp \
|
||||
daemon/nserv/YEncoder.h \
|
||||
daemon/nserv/YEncoder.cpp \
|
||||
code_revision.cpp
|
||||
|
||||
if WITH_PAR2
|
||||
nzbget_SOURCES += \
|
||||
lib/par2/commandline.cpp \
|
||||
lib/par2/commandline.h \
|
||||
lib/par2/crc.cpp \
|
||||
lib/par2/crc.h \
|
||||
lib/par2/creatorpacket.cpp \
|
||||
lib/par2/creatorpacket.h \
|
||||
lib/par2/criticalpacket.cpp \
|
||||
lib/par2/criticalpacket.h \
|
||||
lib/par2/datablock.cpp \
|
||||
lib/par2/datablock.h \
|
||||
lib/par2/descriptionpacket.cpp \
|
||||
lib/par2/descriptionpacket.h \
|
||||
lib/par2/diskfile.cpp \
|
||||
lib/par2/diskfile.h \
|
||||
lib/par2/filechecksummer.cpp \
|
||||
lib/par2/filechecksummer.h \
|
||||
lib/par2/galois.cpp \
|
||||
lib/par2/galois.h \
|
||||
lib/par2/letype.h \
|
||||
lib/par2/mainpacket.cpp \
|
||||
lib/par2/mainpacket.h \
|
||||
lib/par2/md5.cpp \
|
||||
lib/par2/md5.h \
|
||||
lib/par2/par2cmdline.h \
|
||||
lib/par2/par2fileformat.cpp \
|
||||
lib/par2/par2fileformat.h \
|
||||
lib/par2/par2repairer.cpp \
|
||||
lib/par2/par2repairer.h \
|
||||
lib/par2/par2repairersourcefile.cpp \
|
||||
lib/par2/par2repairersourcefile.h \
|
||||
lib/par2/parheaders.cpp \
|
||||
lib/par2/parheaders.h \
|
||||
lib/par2/recoverypacket.cpp \
|
||||
lib/par2/recoverypacket.h \
|
||||
lib/par2/reedsolomon.cpp \
|
||||
lib/par2/reedsolomon.h \
|
||||
lib/par2/verificationhashtable.cpp \
|
||||
lib/par2/verificationhashtable.h \
|
||||
lib/par2/verificationpacket.cpp \
|
||||
lib/par2/verificationpacket.h
|
||||
endif
|
||||
|
||||
# Simd decoder and Crc32
|
||||
nzbget_SOURCES += \
|
||||
lib/yencode/YEncode.h \
|
||||
lib/yencode/SimdInit.cpp \
|
||||
lib/yencode/SimdDecoder.cpp \
|
||||
lib/yencode/ScalarDecoder.cpp \
|
||||
lib/yencode/Sse2Decoder.cpp \
|
||||
lib/yencode/Ssse3Decoder.cpp \
|
||||
lib/yencode/PclmulCrc.cpp \
|
||||
lib/yencode/NeonDecoder.cpp \
|
||||
lib/yencode/AcleCrc.cpp \
|
||||
lib/yencode/SliceCrc.cpp
|
||||
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT) : CXXFLAGS+=$(SSE2_CXXFLAGS)
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT) : CXXFLAGS+=$(SSSE3_CXXFLAGS)
|
||||
lib/yencode/PclmulCrc.$(OBJEXT) : CXXFLAGS+=$(PCLMUL_CXXFLAGS)
|
||||
lib/yencode/NeonDecoder.$(OBJEXT) : CXXFLAGS+=$(NEON_CXXFLAGS)
|
||||
lib/yencode/AcleCrc.$(OBJEXT) : CXXFLAGS+=$(ACLECRC_CXXFLAGS)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(srcdir)/daemon/connect \
|
||||
-I$(srcdir)/daemon/extension \
|
||||
-I$(srcdir)/daemon/feed \
|
||||
-I$(srcdir)/daemon/frontend \
|
||||
-I$(srcdir)/daemon/main \
|
||||
-I$(srcdir)/daemon/nntp \
|
||||
-I$(srcdir)/daemon/postprocess \
|
||||
-I$(srcdir)/daemon/queue \
|
||||
-I$(srcdir)/daemon/remote \
|
||||
-I$(srcdir)/daemon/util \
|
||||
-I$(srcdir)/daemon/nserv \
|
||||
-I$(srcdir)/lib/par2 \
|
||||
-I$(srcdir)/lib/yencode
|
||||
|
||||
if WITH_TESTS
|
||||
nzbget_SOURCES += \
|
||||
lib/catch/catch.h \
|
||||
tests/suite/TestMain.cpp \
|
||||
tests/suite/TestMain.h \
|
||||
tests/suite/TestUtil.cpp \
|
||||
tests/suite/TestUtil.h \
|
||||
tests/main/CommandLineParserTest.cpp \
|
||||
tests/main/OptionsTest.cpp \
|
||||
tests/feed/FeedFilterTest.cpp \
|
||||
tests/postprocess/DupeMatcherTest.cpp \
|
||||
tests/postprocess/RarRenamerTest.cpp \
|
||||
tests/postprocess/RarReaderTest.cpp \
|
||||
tests/postprocess/DirectUnpackTest.cpp \
|
||||
tests/queue/NzbFileTest.cpp \
|
||||
tests/nntp/ServerPoolTest.cpp \
|
||||
tests/util/FileSystemTest.cpp \
|
||||
tests/util/NStringTest.cpp \
|
||||
tests/util/UtilTest.cpp
|
||||
|
||||
if WITH_PAR2
|
||||
nzbget_SOURCES += \
|
||||
tests/postprocess/ParCheckerTest.cpp \
|
||||
tests/postprocess/ParRenamerTest.cpp
|
||||
endif
|
||||
|
||||
AM_CPPFLAGS += \
|
||||
-I$(srcdir)/lib/catch \
|
||||
-I$(srcdir)/tests/suite
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(windows_FILES) \
|
||||
$(osx_FILES) \
|
||||
$(linux_FILES) \
|
||||
$(testdata_FILES) \
|
||||
$(par2doc_FILES)
|
||||
|
||||
windows_FILES = \
|
||||
daemon/windows/StdAfx.cpp \
|
||||
daemon/windows/WinService.cpp \
|
||||
daemon/windows/WinService.h \
|
||||
daemon/windows/WinConsole.cpp \
|
||||
daemon/windows/WinConsole.h \
|
||||
nzbget.vcxproj \
|
||||
windows/nzbget-command-shell.bat \
|
||||
windows/install-update.bat \
|
||||
windows/README-WINDOWS.txt \
|
||||
windows/package-info.json \
|
||||
windows/resources/mainicon.ico \
|
||||
windows/resources/nzbget.rc \
|
||||
windows/resources/resource.h \
|
||||
windows/resources/trayicon_idle.ico \
|
||||
windows/resources/trayicon_paused.ico \
|
||||
windows/resources/trayicon_working.ico \
|
||||
windows/resources/install.bmp \
|
||||
windows/resources/uninstall.bmp \
|
||||
windows/nzbget-setup.nsi
|
||||
|
||||
osx_FILES = \
|
||||
osx/App_Prefix.pch \
|
||||
osx/NZBGet-Info.plist \
|
||||
osx/DaemonController.h \
|
||||
osx/DaemonController.m \
|
||||
osx/MainApp.h \
|
||||
osx/MainApp.m \
|
||||
osx/MainApp.xib \
|
||||
osx/PFMoveApplication.h \
|
||||
osx/PFMoveApplication.m \
|
||||
osx/PreferencesDialog.h \
|
||||
osx/PreferencesDialog.m \
|
||||
osx/PreferencesDialog.xib \
|
||||
osx/RPC.h \
|
||||
osx/RPC.m \
|
||||
osx/WebClient.h \
|
||||
osx/WebClient.m \
|
||||
osx/WelcomeDialog.h \
|
||||
osx/WelcomeDialog.m \
|
||||
osx/WelcomeDialog.xib \
|
||||
osx/NZBGet.xcodeproj/project.pbxproj \
|
||||
osx/Resources/Images/mainicon.icns \
|
||||
osx/Resources/Images/statusicon.png \
|
||||
osx/Resources/Images/statusicon@2x.png \
|
||||
osx/Resources/licenses/license-bootstrap.txt \
|
||||
osx/Resources/licenses/license-jquery-GPL.txt \
|
||||
osx/Resources/licenses/license-jquery-MIT.txt \
|
||||
osx/Resources/Credits.rtf \
|
||||
osx/Resources/Localizable.strings \
|
||||
osx/Resources/Welcome.rtf
|
||||
|
||||
linux_FILES = \
|
||||
linux/installer.sh \
|
||||
linux/install-update.sh \
|
||||
linux/package-info.json \
|
||||
linux/build-info.txt \
|
||||
linux/build-nzbget \
|
||||
linux/build-unpack \
|
||||
linux/build-toolchain-android \
|
||||
linux/build-toolchain-freebsd
|
||||
|
||||
doc_FILES = \
|
||||
README \
|
||||
ChangeLog \
|
||||
COPYING
|
||||
|
||||
par2doc_FILES = \
|
||||
lib/par2/AUTHORS \
|
||||
lib/par2/README
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf
|
||||
|
||||
webui_FILES = \
|
||||
webui/index.html \
|
||||
webui/index.js \
|
||||
webui/downloads.js \
|
||||
webui/edit.js \
|
||||
webui/fasttable.js \
|
||||
webui/history.js \
|
||||
webui/messages.js \
|
||||
webui/status.js \
|
||||
webui/style.css \
|
||||
webui/upload.js \
|
||||
webui/util.js \
|
||||
webui/config.js \
|
||||
webui/feed.js \
|
||||
webui/lib/bootstrap.js \
|
||||
webui/lib/bootstrap.min.js \
|
||||
webui/lib/bootstrap.css \
|
||||
webui/lib/jquery.js \
|
||||
webui/lib/jquery.min.js \
|
||||
webui/lib/raphael.js \
|
||||
webui/lib/raphael.min.js \
|
||||
webui/lib/elycharts.js \
|
||||
webui/lib/elycharts.min.js \
|
||||
webui/img/icons.png \
|
||||
webui/img/icons-2x.png \
|
||||
webui/img/transmit.gif \
|
||||
webui/img/transmit-file.gif \
|
||||
webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png \
|
||||
webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif \
|
||||
webui/img/favicon-256x256-opaque.png \
|
||||
webui/img/favicon-256x256.png
|
||||
|
||||
scripts_FILES = \
|
||||
scripts/EMail.py \
|
||||
scripts/Logger.py
|
||||
|
||||
testdata_FILES = \
|
||||
tests/testdata/dupematcher1/testfile.part01.rar \
|
||||
tests/testdata/dupematcher1/testfile.part24.rar \
|
||||
tests/testdata/dupematcher2/testfile.part04.rar \
|
||||
tests/testdata/dupematcher2/testfile.part43.rar \
|
||||
tests/testdata/nzbfile/dotless.nzb \
|
||||
tests/testdata/nzbfile/dotless.txt \
|
||||
tests/testdata/nzbfile/plain.nzb \
|
||||
tests/testdata/nzbfile/plain.txt \
|
||||
tests/testdata/parchecker/crc.txt \
|
||||
tests/testdata/parchecker/testfile.dat \
|
||||
tests/testdata/parchecker/testfile.nfo \
|
||||
tests/testdata/parchecker/testfile.par2 \
|
||||
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
|
||||
tests/testdata/parchecker2/crc.txt \
|
||||
tests/testdata/parchecker2/testfile.7z.001 \
|
||||
tests/testdata/parchecker2/testfile.7z.002 \
|
||||
tests/testdata/parchecker2/testfile.7z.003 \
|
||||
tests/testdata/parchecker2/testfile.7z.par2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol0+1.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol1+2.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol3+3.PAR2 \
|
||||
tests/testdata/rarrenamer/testfile3.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.rar \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.r00 \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.r01 \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part03.rar
|
||||
|
||||
# Install
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
scriptsdir = $(datadir)/nzbget
|
||||
nobase_dist_scripts_SCRIPTS = $(scripts_FILES)
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
|
||||
# 1) copy the original file to original.temp (delete existing original.temp, if any);
|
||||
# 2) sed < original.temp > original
|
||||
# 3) delete original.temp
|
||||
# These steps ensure that the output file has the same permissions as the original file.
|
||||
|
||||
# Prepare example configuration file
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:typically installed to /usr/local/share/nzbget/scripts:installed to $(scriptsdir)/scripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
|
||||
# Install configuration files into /etc
|
||||
# (only if they do not exist there to prevent override by update)
|
||||
install-conf:
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
|
||||
|
||||
# Determining git revision:
|
||||
# 1) If directory ".git" exists we take revision from git log.
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".git" doesn't exists we keep and reuse file "code_revision.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".git" nor file "code_revision.cpp" are available
|
||||
# we create new file "code_revision.c" with empty revision number.
|
||||
code_revision.cpp: FORCE
|
||||
@ if test -d ./.git ; then \
|
||||
B=`git branch | sed -n -e 's/^\* \(.*\)/\1/p'`; \
|
||||
M=`git status --porcelain` ; \
|
||||
if test "$$M" != "" ; then \
|
||||
M="M" ; \
|
||||
fi ; \
|
||||
if test "$$B" = "master" ; then \
|
||||
V="$$M" ; \
|
||||
elif test "$$B" = "develop" ; then \
|
||||
V=`git rev-list HEAD | wc -l | xargs` ; \
|
||||
V="$${V}$$M" ; \
|
||||
else \
|
||||
V=`git rev-list HEAD | wc -l | xargs` ; \
|
||||
V="$${V}$$M ($$B)" ; \
|
||||
fi ; \
|
||||
H=`test -f ./code_revision.cpp && head -n 1 code_revision.cpp`; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "#include \"nzbget.h\"" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"$$V\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi \
|
||||
elif test -f ./code_revision.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "#include \"nzbget.h\"" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
# Ignore "code_revision.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
find $(distdir)/daemon -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/lib -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/tests -type f -print -exec chmod -x {} \;
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
default: all
|
||||
|
||||
all:
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
autoconf
|
||||
|
||||
2047
Makefile.in
vendored
2047
Makefile.in
vendored
File diff suppressed because it is too large
Load Diff
302
MessageBase.h
302
MessageBase.h
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
/**
|
||||
* NZBGet communication protocol uses only two basic data types: integer and char.
|
||||
* Integer values are passed using network byte order (Big-Endian).
|
||||
* Use function "htonl" and "ntohl" to convert integers to/from machine
|
||||
' (host) byte order.
|
||||
* All char-strings must end with NULL-char.
|
||||
*/
|
||||
|
||||
// The pack-directive prevents aligning of structs.
|
||||
// This makes them more portable and allows to use together servers and clients
|
||||
// compiled on different cpu architectures
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
// Possible values for field "m_iType" of struct "SNZBRequestBase":
|
||||
enum eRemoteRequest
|
||||
{
|
||||
eRemoteRequestDownload = 1,
|
||||
eRemoteRequestPauseUnpause,
|
||||
eRemoteRequestList,
|
||||
eRemoteRequestSetDownloadRate,
|
||||
eRemoteRequestDumpDebug,
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestVersion
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
// File-Actions affect one file, Group-Actions affect all files in group.
|
||||
// Group is a list of files, added to queue from one NZB-File.
|
||||
enum eRemoteEditAction
|
||||
{
|
||||
eRemoteEditActionFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionFileMoveTop, // move to top of queue
|
||||
eRemoteEditActionFileMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionFilePause, // pause
|
||||
eRemoteEditActionFileResume, // resume (unpause)
|
||||
eRemoteEditActionFileDelete, // delete
|
||||
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionGroupMoveTop, // move to top of queue
|
||||
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionGroupPause, // pause
|
||||
eRemoteEditActionGroupResume, // resume (unpause)
|
||||
eRemoteEditActionGroupDelete, // delete
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionGroupPauseExtraPars // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
};
|
||||
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
struct SNZBRequestBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
|
||||
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
|
||||
};
|
||||
|
||||
// The basic SNZBResposneBase struct, used in all responses
|
||||
struct SNZBResponseBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
};
|
||||
|
||||
// A download request
|
||||
struct SNZBDownloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A download response
|
||||
struct SNZBDownloadResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A list and status request
|
||||
struct SNZBListRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList; // 1 - return file list
|
||||
int32_t m_bServerState; // 1 - return server state
|
||||
};
|
||||
|
||||
// A list response
|
||||
struct SNZBListResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
|
||||
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
|
||||
int32_t m_bServerPaused; // 1 - server is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iParJobCount; // Number of ParJobs in Par-Checker queue (including current file)
|
||||
int32_t m_iUpTimeSec; // Server up time in seconds
|
||||
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
|
||||
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
|
||||
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
|
||||
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
|
||||
int32_t m_iNrTrailingEntries; // Number of List-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list response entry
|
||||
struct SNZBListResponseEntry
|
||||
{
|
||||
int32_t m_iID; // Entry-ID
|
||||
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bPaused; // 1 - file is paused
|
||||
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szSubject[m_iSubjectLen]; // variable sized
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iIDFrom; // Only one of these two parameters
|
||||
int32_t m_iLines; // can be set. The another one must be set to "0".
|
||||
};
|
||||
|
||||
// A log response
|
||||
struct SNZBLogResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
|
||||
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A log response entry
|
||||
struct SNZBLogResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Log-entry
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
|
||||
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTextLen]; // variable sized
|
||||
};
|
||||
|
||||
// A Pause/Unpause request
|
||||
struct SNZBPauseUnpauseRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
|
||||
};
|
||||
|
||||
// A Pause/Unpause response
|
||||
struct SNZBPauseUnpauseResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request setting the download rate
|
||||
struct SNZBSetDownloadRateRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
|
||||
};
|
||||
|
||||
// A setting download rate response
|
||||
struct SNZBSetDownloadRateResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// An edit queue request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
|
||||
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
|
||||
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure
|
||||
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
struct SNZBEditQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request dumping of debug info
|
||||
struct SNZBDumpDebugRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Dumping of debug response
|
||||
struct SNZBDumpDebugResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Shutdown server request
|
||||
struct SNZBShutdownRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Shutdown server response
|
||||
struct SNZBShutdownResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Server version request
|
||||
struct SNZBVersionRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Server version response
|
||||
struct SNZBVersionResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1370
NCursesFrontend.cpp
1370
NCursesFrontend.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NCURSESFRONTEND_H
|
||||
#define NCURSESFRONTEND_H
|
||||
|
||||
#ifndef DISABLE_CURSES
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NCursesFrontend : public Frontend
|
||||
{
|
||||
private:
|
||||
|
||||
enum EInputMode
|
||||
{
|
||||
eNormal,
|
||||
eEditQueue,
|
||||
eDownloadRate
|
||||
};
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szNZBFilename;
|
||||
|
||||
public:
|
||||
int m_iFileCount;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iParCount;
|
||||
|
||||
public:
|
||||
GroupInfo(int iID, const char* szNZBFilename);
|
||||
~GroupInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
long long GetPausedSize() { return m_lPausedSize; }
|
||||
};
|
||||
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
|
||||
bool m_bUseColor;
|
||||
int m_iDataUpdatePos;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
int m_iQueueWinHeight;
|
||||
int m_iQueueWinClientHeight;
|
||||
int m_iMessagesWinTop;
|
||||
int m_iMessagesWinHeight;
|
||||
int m_iMessagesWinClientHeight;
|
||||
int m_iSelectedQueueEntry;
|
||||
int m_iLastEditEntry;
|
||||
bool m_bLastPausePars;
|
||||
int m_iQueueScrollOffset;
|
||||
GroupQueue m_groupQueue;
|
||||
|
||||
// Inputting numbres
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
|
||||
#ifdef WIN32
|
||||
CHAR_INFO* m_pScreenBuffer;
|
||||
CHAR_INFO* m_pOldScreenBuffer;
|
||||
int m_iScreenBufferSize;
|
||||
std::vector<WORD> m_ColorAttr;
|
||||
#else
|
||||
void* m_pWindow; // WINDOW*
|
||||
#endif
|
||||
|
||||
EInputMode m_eInputMode;
|
||||
bool m_bShowNZBname;
|
||||
bool m_bShowTimestamp;
|
||||
bool m_bGroupFiles;
|
||||
float m_QueueWindowPercentage;
|
||||
|
||||
#ifdef WIN32
|
||||
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
|
||||
#endif
|
||||
void PlotLine(const char * szString, int iRow, int iPos, int iColorPair);
|
||||
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
|
||||
void PrintMessages();
|
||||
void PrintQueue();
|
||||
void PrintFileQueue();
|
||||
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
void PrintGroupQueue();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
|
||||
void PrepareGroupQueue();
|
||||
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
|
||||
void ClearGroupQueue();
|
||||
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
|
||||
void PrintKeyInputBar();
|
||||
void PrintStatus();
|
||||
void UpdateInput();
|
||||
void Update();
|
||||
void SetCurrentQueueEntry(int iEntry);
|
||||
void CalcWindowSizes();
|
||||
void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
void RefreshScreen();
|
||||
int ReadConsoleKey();
|
||||
int CalcQueueSize();
|
||||
void NeedUpdateData();
|
||||
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
|
||||
public:
|
||||
NCursesFrontend();
|
||||
virtual ~NCursesFrontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,280 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "NNTPConnection.h"
|
||||
#include "Connection.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
{
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
|
||||
m_bAuthError = false;
|
||||
}
|
||||
|
||||
NNTPConnection::~NNTPConnection()
|
||||
{
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
|
||||
char* NNTPConnection::Request(char* req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_bAuthError = false;
|
||||
|
||||
WriteLine(req);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
|
||||
//authentication required!
|
||||
if (!Authenticate())
|
||||
{
|
||||
m_bAuthError = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//try again
|
||||
WriteLine(req);
|
||||
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
return answer;
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool NNTPConnection::Authenticate()
|
||||
{
|
||||
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
|
||||
!((NewsServer*)m_pNetAddress)->GetPassword())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AuthInfoUser();
|
||||
}
|
||||
|
||||
bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
else if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
return AuthInfoUser(++iRecur);
|
||||
}
|
||||
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NNTPConnection::JoinGroup(char* grp)
|
||||
{
|
||||
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
|
||||
{
|
||||
// already in group
|
||||
return true;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = Request(tmp);
|
||||
if (m_bAuthError)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((answer) && (!strncmp(answer, "2", 1)))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
m_szActiveGroup = strdup(grp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
if (!answer)
|
||||
{
|
||||
warn("Error changing group on %s: Connection closed by remote host.",
|
||||
GetServer()->GetHost());
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Error changing group on %s to %s: Answer was \"%s\".",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetServer()->GetHost());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoDisconnect()
|
||||
{
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NNTPCONNECTION_H
|
||||
#define NNTPCONNECTION_H
|
||||
|
||||
#include "NewsServer.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
void Clear();
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
char* Request(char* req);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur = 0);
|
||||
bool AuthInfoPass(int iRecur = 0);
|
||||
bool JoinGroup(char* grp);
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
582
NZBFile.cpp
582
NZBFile.cpp
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile::~NZBFile()
|
||||
{
|
||||
debug("Destroying NZBFile");
|
||||
|
||||
// Cleanup
|
||||
if (m_szFileName)
|
||||
{
|
||||
free(m_szFileName);
|
||||
}
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
{
|
||||
debug(" NZBFile %s", m_szFileName);
|
||||
}
|
||||
|
||||
void NZBFile::DetachFileInfos()
|
||||
{
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
|
||||
{
|
||||
return Create(szFileName, szBuffer, iSize, true);
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
|
||||
{
|
||||
return Create(szFileName, NULL, 0, false);
|
||||
}
|
||||
|
||||
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
{
|
||||
// make Article-List big enough
|
||||
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
|
||||
pFileInfo->GetArticles()->push_back(NULL);
|
||||
|
||||
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
|
||||
}
|
||||
|
||||
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
|
||||
{
|
||||
// deleting empty articles
|
||||
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
|
||||
int i = 0;
|
||||
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
|
||||
{
|
||||
if (*it == NULL)
|
||||
{
|
||||
pArticles->erase(it);
|
||||
it = pArticles->begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
{
|
||||
ParseSubject(pFileInfo);
|
||||
BuildDestDirName(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
{
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
|
||||
typedef std::list<char*> TokenList;
|
||||
TokenList tokens;
|
||||
tokens.clear();
|
||||
|
||||
// tokenizing
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
char* start = p;
|
||||
bool quot = false;
|
||||
while (true)
|
||||
{
|
||||
char ch = *p;
|
||||
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
|
||||
if (sep)
|
||||
{
|
||||
// end of token
|
||||
int len = p - start;
|
||||
if (len > 0)
|
||||
{
|
||||
char* token = (char*)malloc(len + 1);
|
||||
strncpy(token, start, len);
|
||||
token[len] = '\0';
|
||||
tokens.push_back(token);
|
||||
}
|
||||
start = p;
|
||||
if (ch != '\"' || quot)
|
||||
{
|
||||
start++;
|
||||
}
|
||||
quot = *start == '\"';
|
||||
if (quot)
|
||||
{
|
||||
start++;
|
||||
char* q = strchr(start, '\"');
|
||||
if (q)
|
||||
{
|
||||
p = q - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quot = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!tokens.empty())
|
||||
{
|
||||
// finding the best candidate for being a filename
|
||||
char* besttoken = tokens.back();
|
||||
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
|
||||
{
|
||||
char* s = *it;
|
||||
char* p = strchr(s, '.');
|
||||
if (p && (p[1] != '\0'))
|
||||
{
|
||||
besttoken = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pFileInfo->SetFilename(besttoken);
|
||||
|
||||
// free mem
|
||||
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// subject is empty or contains only separators?
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
|
||||
pFileInfo->MakeValidFilename();
|
||||
}
|
||||
|
||||
void NZBFile::BuildDestDirName(FileInfo* pFileInfo)
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNiceNZBName, 1024);
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
pFileInfo->SetDestDir(szBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::CheckFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 0;
|
||||
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
|
||||
{
|
||||
iDupe++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than two files have the same parsed filename but different subjects,
|
||||
// this means, that the parsing was not correct.
|
||||
// in this case we take subjects as filenames to prevent
|
||||
// false "duplicate files"-alarm.
|
||||
// It's Ok for just two files to have the same filename, this is
|
||||
// an often case by posting-errors to repost bad files
|
||||
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
|
||||
{
|
||||
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
|
||||
pFileInfo2->MakeValidFilename();
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo2);
|
||||
g_pDiskState->SaveFile(pFileInfo2);
|
||||
pFileInfo2->ClearArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MSXML::IXMLDOMDocumentPtr doc;
|
||||
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Load the XML document file...
|
||||
doc->put_resolveExternals(VARIANT_FALSE);
|
||||
doc->put_validateOnParse(VARIANT_FALSE);
|
||||
doc->put_async(VARIANT_FALSE);
|
||||
VARIANT_BOOL success;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
success = doc->loadXML(szBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// filename needs to be properly encoded
|
||||
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
|
||||
EncodeURL(szFileName, szURL);
|
||||
debug("url=\"%s\"", szURL);
|
||||
_variant_t v(szURL);
|
||||
free(szURL);
|
||||
success = doc->load(v);
|
||||
}
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
const char* szErrMsg = r;
|
||||
error("Error parsing nzb-file: %s", szErrMsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
|
||||
{
|
||||
while (char ch = *szFilename++)
|
||||
{
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z') )
|
||||
{
|
||||
*szURL++ = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
*szURL++ = '%';
|
||||
int a = ch >> 4;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
}
|
||||
}
|
||||
*szURL = NULL;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
{
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
|
||||
for (int i = 0; i < fileList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
|
||||
if (!attribute) return false;
|
||||
_bstr_t subject(attribute->Gettext());
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
pFileInfo->SetSubject(subject);
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = groupList->Getitem(g);
|
||||
_bstr_t group = node->Gettext();
|
||||
pFileInfo->GetGroups()->push_back(strdup((const char*)group));
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr segmentList = node->selectNodes("segments/segment");
|
||||
for (int g = 0; g < segmentList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = segmentList->Getitem(g);
|
||||
_bstr_t id = node->Gettext();
|
||||
char szId[2048];
|
||||
snprintf(szId, 2048, "<%s>", (const char*)id);
|
||||
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("number");
|
||||
if (!attribute) return false;
|
||||
_bstr_t number(attribute->Gettext());
|
||||
|
||||
attribute = node->Getattributes()->getNamedItem("bytes");
|
||||
if (!attribute) return false;
|
||||
_bstr_t bytes(attribute->Gettext());
|
||||
|
||||
int partNumber = atoi(number);
|
||||
int lsize = atoi(bytes);
|
||||
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(szId);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
}
|
||||
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
xmlTextReaderPtr doc;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
doc = xmlReaderForFile(szFileName, NULL, 0);
|
||||
}
|
||||
if (!doc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
xmlFreeTextReader(doc);
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(void* nzb)
|
||||
{
|
||||
FileInfo* pFileInfo = NULL;
|
||||
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
|
||||
// walk through whole doc and search for segments-tags
|
||||
int ret = xmlTextReaderRead(node);
|
||||
while (ret == 1)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
xmlChar *name, *value;
|
||||
|
||||
name = xmlTextReaderName(node);
|
||||
if (name == NULL)
|
||||
{
|
||||
name = xmlStrdup(BAD_CAST "--");
|
||||
}
|
||||
value = xmlTextReaderValue(node);
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 1)
|
||||
{
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
if (!strcmp("subject",(char*)name))
|
||||
{
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment",(char*)name))
|
||||
{
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
if (!strcmp("bytes",(char*)name))
|
||||
{
|
||||
lsize = atol((char*)value);
|
||||
}
|
||||
if (!strcmp("number",(char*)name))
|
||||
{
|
||||
partNumber = atol((char*)value);
|
||||
}
|
||||
}
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
|
||||
/* Get the #text part */
|
||||
ret = xmlTextReaderRead(node);
|
||||
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
char tmp[2048];
|
||||
snprintf(tmp, 2048, "<%s>", (char*)value);
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(tmp);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
}
|
||||
}
|
||||
else if (!strcmp("group",(char*)name))
|
||||
{
|
||||
ret = xmlTextReaderRead(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->GetGroups()->push_back(strdup((char*)value));
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 15)
|
||||
{
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
68
NZBFile.h
68
NZBFile.h
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBFILE_H
|
||||
#define NZBFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NZBFile
|
||||
{
|
||||
public:
|
||||
typedef std::vector<FileInfo*> FileInfos;
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo);
|
||||
void BuildDestDirName(FileInfo* pFileInfo);
|
||||
void CheckFilenames();
|
||||
#ifdef WIN32
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool ParseNZB(void* nzb);
|
||||
#endif
|
||||
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
|
||||
public:
|
||||
virtual ~NZBFile();
|
||||
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
|
||||
static NZBFile* CreateFromFile(const char* szFileName);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NetAddress.h"
|
||||
|
||||
NetAddress::NetAddress(const char* szHost, int iPort)
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
if (szHost)
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
|
||||
NetAddress::~NetAddress()
|
||||
{
|
||||
if (m_szHost)
|
||||
free(m_szHost);
|
||||
m_szHost = NULL;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NewsServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
NewsServer::NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level) : NetAddress(host, port)
|
||||
{
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = level;
|
||||
m_iMaxConnections = maxConnections;
|
||||
|
||||
if (pass)
|
||||
{
|
||||
m_szPassword = strdup(pass);
|
||||
}
|
||||
if (user)
|
||||
{
|
||||
m_szUser = strdup(user);
|
||||
}
|
||||
}
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
}
|
||||
49
NewsServer.h
49
NewsServer.h
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
{
|
||||
private:
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
|
||||
public:
|
||||
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
|
||||
virtual ~NewsServer();
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
int GetLevel() { return m_iLevel; }
|
||||
};
|
||||
|
||||
#endif
|
||||
62
Observer.cpp
62
Observer.cpp
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "Log.h"
|
||||
|
||||
Subject::Subject()
|
||||
{
|
||||
m_Observers.clear();
|
||||
}
|
||||
|
||||
void Subject::Attach(Observer* Observer)
|
||||
{
|
||||
m_Observers.push_back(Observer);
|
||||
}
|
||||
|
||||
void Subject::Detach(Observer* Observer)
|
||||
{
|
||||
m_Observers.remove(Observer);
|
||||
}
|
||||
|
||||
void Subject::Notify(void* Aspect)
|
||||
{
|
||||
debug("Notifying observers");
|
||||
|
||||
for (std::list<Observer*>::iterator it = m_Observers.begin(); it != m_Observers.end(); it++)
|
||||
{
|
||||
Observer* Observer = *it;
|
||||
Observer->Update(this, Aspect);
|
||||
}
|
||||
}
|
||||
1210
Options.cpp
1210
Options.cpp
File diff suppressed because it is too large
Load Diff
249
Options.h
249
Options.h
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestVersion
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EDecoder
|
||||
{
|
||||
dcNone,
|
||||
dcUulib,
|
||||
dcYenc
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
{
|
||||
plNone,
|
||||
plOne,
|
||||
plAll
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
bool m_bConfigInitialized;
|
||||
|
||||
// Options
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
EMessageTarget m_eDebugTarget;
|
||||
EDecoder m_eDecoder;
|
||||
bool m_bCreateBrokenLog;
|
||||
bool m_bResetLog;
|
||||
int m_iConnectionTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendNZBDir;
|
||||
bool m_bContinuePartial;
|
||||
bool m_bRenameBroken;
|
||||
int m_iRetries;
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szServerIP;
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
ELoadPars m_eLoadPars;
|
||||
bool m_bParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
bool m_bStrictParName;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
bool m_bCursesNZBName;
|
||||
bool m_bCursesTime;
|
||||
bool m_bCursesGroup;
|
||||
bool m_bCrcCheck;
|
||||
bool m_bRetryOnCrcError;
|
||||
int m_iThreadLimit;
|
||||
bool m_bDirectWrite;
|
||||
int m_iWriteBufferSize;
|
||||
int m_iNzbDirInterval;
|
||||
int m_iNzbDirFileAge;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
bool m_bDaemonMode;
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
char* m_szArgFilename;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
float m_fDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
const char* GetOption(const char* optname);
|
||||
void DelOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
// Options
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
|
||||
bool GetResetLog() const { return m_bResetLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
|
||||
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
|
||||
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
EDecoder GetDecoder() { return m_eDecoder; };
|
||||
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
|
||||
bool GetContinuePartial() { return m_bContinuePartial; }
|
||||
bool GetRenameBroken() { return m_bRenameBroken; }
|
||||
int GetRetries() { return m_iRetries; }
|
||||
int GetRetryInterval() { return m_iRetryInterval; }
|
||||
bool GetSaveQueue() { return m_bSaveQueue; }
|
||||
bool GetDupeCheck() { return m_bDupeCheck; }
|
||||
char* GetServerIP() { return m_szServerIP; }
|
||||
char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
char* GetLockFile() { return m_szLockFile; }
|
||||
char* GetDaemonUserName() { return m_szDaemonUserName; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
bool GetCreateLog() { return m_bCreateLog; }
|
||||
char* GetLogFile() { return m_szLogFile; }
|
||||
ELoadPars GetLoadPars() { return m_eLoadPars; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
bool GetStrictParName() { return m_bStrictParName; }
|
||||
int GetUMask() { return m_iUMask; }
|
||||
int GetUpdateInterval() {return m_iUpdateInterval; }
|
||||
bool GetCursesNZBName() { return m_bCursesNZBName; }
|
||||
bool GetCursesTime() { return m_bCursesTime; }
|
||||
bool GetCursesGroup() { return m_bCursesGroup; }
|
||||
bool GetCrcCheck() { return m_bCrcCheck; }
|
||||
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
|
||||
int GetThreadLimit() { return m_iThreadLimit; }
|
||||
bool GetDirectWrite() { return m_bDirectWrite; }
|
||||
int GetWriteBufferSize() { return m_iWriteBufferSize; }
|
||||
int GetNzbDirInterval() { return m_iNzbDirInterval; }
|
||||
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
bool GetDaemonMode() { return m_bDaemonMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
int GetEditQueueAction() { return m_iEditQueueAction; }
|
||||
int GetEditQueueOffset() { return m_iEditQueueOffset; }
|
||||
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
|
||||
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
|
||||
// Current state
|
||||
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
|
||||
bool GetPause() const { return m_bPause; }
|
||||
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
|
||||
float GetDownloadRate() const { return m_fDownloadRate; }
|
||||
};
|
||||
|
||||
#endif
|
||||
530
ParChecker.cpp
530
ParChecker.cpp
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#else
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ParChecker.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"data files are damaged and there is enough recovery data available to repair them",
|
||||
"data files are damaged and there is insufficient recovery data available to be able to repair them",
|
||||
"there was something wrong with the command line arguments",
|
||||
"the PAR2 files did not contain sufficient information about the data files to be able to verify them",
|
||||
"repair completed but the data files still appear to be damaged",
|
||||
"an error occured when accessing files",
|
||||
"internal error occurred",
|
||||
"out of memory" };
|
||||
|
||||
class Repairer : public Par2Repairer
|
||||
{
|
||||
friend class ParChecker;
|
||||
};
|
||||
|
||||
ParChecker::ParChecker()
|
||||
{
|
||||
debug("Creating ParChecker");
|
||||
|
||||
m_eStatus = psUndefined;
|
||||
m_szParFilename = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szErrMsg = NULL;
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
ParChecker::~ParChecker()
|
||||
{
|
||||
debug("Destroying ParChecker");
|
||||
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
|
||||
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
void ParChecker::SetParFilename(const char * szParFilename)
|
||||
{
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetInfoName(const char * szInfoName)
|
||||
{
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void ParChecker::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ParChecker::Run()
|
||||
{
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
debug("par: %s", m_szParFilename);
|
||||
CommandLine commandLine;
|
||||
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
|
||||
if (!commandLine.Parse(5, (char**)argv))
|
||||
{
|
||||
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
|
||||
SetStatus(psFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
Result res;
|
||||
Repairer* repairer = new Repairer();
|
||||
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
|
||||
res = repairer->PreProcess(commandLine);
|
||||
debug("ParChecker: PreProcess-result=%i", res);
|
||||
|
||||
if (res != eSuccess || IsStopped())
|
||||
{
|
||||
error("Could not verify %s: ", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
return;
|
||||
}
|
||||
|
||||
char BufReason[1024];
|
||||
BufReason[0] = '\0';
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
m_szErrMsg = NULL;
|
||||
|
||||
m_bRepairNotNeeded = false;
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
|
||||
while (!IsStopped() && res == eRepairNotPossible)
|
||||
{
|
||||
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
|
||||
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
bool hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
int iBlockFound = 0;
|
||||
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!requested && !hasMorePars)
|
||||
{
|
||||
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
|
||||
BufReason[1024-1] = '\0';
|
||||
m_szErrMsg = strdup(BufReason);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
m_semNeedMoreFiles.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LoadMorePars(repairer);
|
||||
repairer->UpdateVerificationResults();
|
||||
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Repair not needed for %s", m_szInfoName);
|
||||
m_bRepairNotNeeded = true;
|
||||
}
|
||||
else if (res == eRepairPossible)
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
info("Repairing %s", m_szInfoName);
|
||||
m_bRepairing = true;
|
||||
res = repairer->Process(commandLine, true);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Successfully repaired %s", m_szInfoName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Repair possible for %s", m_szInfoName);
|
||||
res = eSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
SetStatus(psFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
|
||||
{
|
||||
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
|
||||
}
|
||||
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
|
||||
SetStatus(psFailed);
|
||||
}
|
||||
|
||||
delete repairer;
|
||||
}
|
||||
|
||||
bool ParChecker::ParseParFilename(const char * szParFilename, int* iBaseNameLen, int* iBlocks)
|
||||
{
|
||||
char szFilename[1024];
|
||||
strncpy(szFilename, szParFilename, 1024);
|
||||
szFilename[1024-1] = '\0';
|
||||
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
int iLen = strlen(szFilename);
|
||||
if (iLen < 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find last occurence of ".par2" and trim filename after it
|
||||
char* szEnd = szFilename;
|
||||
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
|
||||
*szEnd = '\0';
|
||||
iLen = strlen(szFilename);
|
||||
|
||||
if (strcasecmp(szFilename + iLen - 5, ".par2"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*(szFilename + iLen - 5) = '\0';
|
||||
|
||||
int blockcnt = 0;
|
||||
char* p = strrchr(szFilename, '.');
|
||||
if (p && !strncasecmp(p, ".vol", 4))
|
||||
{
|
||||
char* b = strchr(p, '+');
|
||||
if (!b)
|
||||
{
|
||||
b = strchr(p, '-');
|
||||
}
|
||||
if (b)
|
||||
{
|
||||
blockcnt = atoi(b+1);
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (iBaseNameLen)
|
||||
{
|
||||
*iBaseNameLen = strlen(szFilename);
|
||||
}
|
||||
if (iBlocks)
|
||||
{
|
||||
*iBlocks = blockcnt;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpause par2-files
|
||||
* returns true, if the files with required number of blocks were unpaused,
|
||||
* or false if there are no more files in queue for this collection or not enough blocks
|
||||
*/
|
||||
bool ParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
Blocks blocks;
|
||||
blocks.clear();
|
||||
int iBlockFound = 0;
|
||||
|
||||
FindPars(pDownloadQueue, &blocks, true, &iBlockFound);
|
||||
if (iBlockFound == 0 && !g_pOptions->GetStrictParName())
|
||||
{
|
||||
FindPars(pDownloadQueue, &blocks, false, &iBlockFound);
|
||||
}
|
||||
|
||||
if (iBlockFound >= iBlockNeeded)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(m_szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
|
||||
// starting from the file with max block count.
|
||||
// if par-collection was built exponentially and all par-files present,
|
||||
// this step selects par-files with exact number of blocks we need.
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBestBlockInfo = NULL;
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
BlockInfo* pBlockInfo = *it;
|
||||
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
|
||||
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
|
||||
{
|
||||
pBestBlockInfo = pBlockInfo;
|
||||
}
|
||||
}
|
||||
if (pBestBlockInfo)
|
||||
{
|
||||
if (pBestBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBestBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. then unpause other files
|
||||
// this step only needed if the par-collection was built not exponentially
|
||||
// or not all par-files present (or some of them were corrupted)
|
||||
// this step is not optimal, but we hope, that the first step will work good
|
||||
// in most cases and we will not need the second step often
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBlockInfo = blocks.front();
|
||||
if (pBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBlockInfo->m_iBlockCount;
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pBlockFound)
|
||||
{
|
||||
*pBlockFound = iBlockFound;
|
||||
}
|
||||
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
blocks.clear();
|
||||
|
||||
return iBlockNeeded <= 0;
|
||||
}
|
||||
|
||||
void ParChecker::FindPars(DownloadQueue * pDownloadQueue, Blocks * pBlocks, bool bStrictParName, int* pBlockFound)
|
||||
{
|
||||
*pBlockFound = 0;
|
||||
|
||||
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
|
||||
char* szBaseParFilename = BaseFileName(m_szParFilename);
|
||||
char szMainBaseFilename[1024];
|
||||
int iMainBaseLen = 0;
|
||||
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
|
||||
{
|
||||
// should not happen
|
||||
error("Internal error: could not parse filename %s", szBaseParFilename);
|
||||
return;
|
||||
}
|
||||
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
|
||||
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
|
||||
szMainBaseFilename[maxlen] = '\0';
|
||||
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int iBlocks = 0;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), m_szNZBFilename) &&
|
||||
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
|
||||
iBlocks > 0)
|
||||
{
|
||||
if (bStrictParName)
|
||||
{
|
||||
// the pFileInfo->GetFilename() may be not confirmed and may contain
|
||||
// additional texts if Subject could not be parsed correctly
|
||||
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
char szCandidateFileName[1024];
|
||||
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it is a par2-file with blocks and it was from the same NZB-request
|
||||
// and it belongs to the same file collection (same base name),
|
||||
// then OK, we can use it
|
||||
BlockInfo* pBlockInfo = new BlockInfo();
|
||||
pBlockInfo->m_pFileInfo = pFileInfo;
|
||||
pBlockInfo->m_iBlockCount = iBlocks;
|
||||
pBlocks->push_back(pBlockInfo);
|
||||
*pBlockFound += iBlocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::LoadMorePars(void* repairer)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
QueuedParFiles moreFiles;
|
||||
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
|
||||
m_QueuedParFiles.clear();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
bool loadedOK = ((Repairer*)repairer)->LoadPacketsFromFile(szParFilename);
|
||||
if (loadedOK)
|
||||
{
|
||||
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::AddParFile(const char * szParFilename)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_QueuedParFiles.push_back(strdup(szParFilename));
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::QueueChanged()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::signal_filename(std::string str)
|
||||
{
|
||||
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
94
ParChecker.h
94
ParChecker.h
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PARCHECKER_H
|
||||
#define PARCHECKER_H
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class ParChecker : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
psUndefined,
|
||||
psWorking,
|
||||
psFailed,
|
||||
psFinished
|
||||
};
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
bool m_bRepairing;
|
||||
|
||||
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
|
||||
void LoadMorePars(void* repairer);
|
||||
void signal_filename(std::string str);
|
||||
|
||||
public:
|
||||
ParChecker();
|
||||
virtual ~ParChecker();
|
||||
virtual void Run();
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
const char* GetErrMsg() { return m_szErrMsg; }
|
||||
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
|
||||
void AddParFile(const char* szParFilename);
|
||||
void QueueChanged();
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,601 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const int PARSTATUS_NOT_CHECKED = 0;
|
||||
static const int PARSTATUS_FAILED = 1;
|
||||
static const int PARSTATUS_REPAIRED = 2;
|
||||
static const int PARSTATUS_REPAIR_POSSIBLE = 3;
|
||||
|
||||
PrePostProcessor::ParJob::ParJob(const char * szNZBFilename, const char * szParFilename, const char * szInfoName)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
PrePostProcessor::ParJob::~ ParJob()
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
PrePostProcessor::PrePostProcessor()
|
||||
{
|
||||
debug("Creating PrePostProcessor");
|
||||
|
||||
m_bHasMoreJobs = false;
|
||||
|
||||
m_QueueCoordinatorObserver.owner = this;
|
||||
g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver);
|
||||
|
||||
m_ParQueue.clear();
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_ParCheckerObserver.owner = this;
|
||||
m_ParChecker.Attach(&m_ParCheckerObserver);
|
||||
#endif
|
||||
}
|
||||
|
||||
PrePostProcessor::~PrePostProcessor()
|
||||
{
|
||||
debug("Destroying PrePostProcessor");
|
||||
|
||||
for (ParQueue::iterator it = m_ParQueue.begin(); it != m_ParQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::Run()
|
||||
{
|
||||
debug("Entering PrePostProcessor-loop");
|
||||
|
||||
int iNZBDirInterval = 0;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
int iParQueueInterval = 0;
|
||||
#endif
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (g_pOptions->GetNzbDir() && g_pOptions->GetNzbDirInterval() > 0 &&
|
||||
iNZBDirInterval == g_pOptions->GetNzbDirInterval() * 1000)
|
||||
{
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds
|
||||
CheckIncomingNZBs();
|
||||
iNZBDirInterval = 0;
|
||||
}
|
||||
iNZBDirInterval += 200;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (iParQueueInterval == 1000 && g_pOptions->GetParCheck())
|
||||
{
|
||||
// check par-queue every 1 second
|
||||
CheckParQueue();
|
||||
iParQueueInterval = 0;
|
||||
}
|
||||
iParQueueInterval += 200;
|
||||
#endif
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
debug("Exiting PrePostProcessor-loop");
|
||||
}
|
||||
|
||||
void PrePostProcessor::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_mutexParChecker.Lock();
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
m_ParChecker.Stop();
|
||||
int iMSecWait = 5000;
|
||||
while (m_ParChecker.IsRunning() && iMSecWait > 0)
|
||||
{
|
||||
usleep(50 * 1000);
|
||||
iMSecWait -= 50;
|
||||
}
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
|
||||
m_ParChecker.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QueueCoordinator::Aspect* pAspect = (QueueCoordinator::Aspect*)Aspect;
|
||||
if (pAspect->eAction == QueueCoordinator::eaNZBFileAdded &&
|
||||
g_pOptions->GetLoadPars() != Options::plAll)
|
||||
{
|
||||
PausePars(pAspect->pDownloadQueue, pAspect->szNZBFilename);
|
||||
}
|
||||
else if ((pAspect->eAction == QueueCoordinator::eaFileCompleted ||
|
||||
pAspect->eAction == QueueCoordinator::eaFileDeleted))
|
||||
{
|
||||
if (
|
||||
#ifndef DISABLE_PARCHECK
|
||||
!AddPar(pAspect->pFileInfo, pAspect->eAction == QueueCoordinator::eaFileDeleted) &&
|
||||
#endif
|
||||
WasLastInCollection(pAspect->pDownloadQueue, pAspect->pFileInfo, true))
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pAspect->pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
if (pAspect->eAction == QueueCoordinator::eaFileCompleted)
|
||||
{
|
||||
info("Collection %s completely downloaded", szNZBNiceName);
|
||||
}
|
||||
else if (WasLastInCollection(pAspect->pDownloadQueue, pAspect->pFileInfo, false))
|
||||
{
|
||||
info("Collection %s deleted from queue", szNZBNiceName);
|
||||
}
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (g_pOptions->GetParCheck() &&
|
||||
pAspect->eAction == QueueCoordinator::eaFileCompleted)
|
||||
{
|
||||
CheckPars(pAspect->pDownloadQueue, pAspect->pFileInfo);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ExecPostScript(pAspect->pFileInfo->GetDestDir(), pAspect->pFileInfo->GetNZBFilename(), "", PARSTATUS_NOT_CHECKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename)
|
||||
{
|
||||
debug("PrePostProcessor: Pausing pars");
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
|
||||
(g_pOptions->GetLoadPars() == Options::plOne ||
|
||||
(g_pOptions->GetLoadPars() == Options::plNone && g_pOptions->GetParCheck()))
|
||||
? QueueEditor::eaGroupPauseExtraPars : QueueEditor::eaGroupPauseAllPars,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are files in directory for incoming nzb-files
|
||||
* and add them to download queue
|
||||
*/
|
||||
void PrePostProcessor::CheckIncomingNZBs()
|
||||
{
|
||||
DirBrowser dir(g_pOptions->GetNzbDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int len = strlen(filename);
|
||||
if (len > 4 && !strcasecmp(filename + len - 4, ".nzb"))
|
||||
{
|
||||
// file found, checking modification-time
|
||||
struct stat buffer;
|
||||
char fullfilename[1024];
|
||||
snprintf(fullfilename, 1024, "%s%c%s", g_pOptions->GetNzbDir(), (int)PATH_SEPARATOR, filename);
|
||||
fullfilename[1024-1] = '\0';
|
||||
if (!stat(fullfilename, &buffer) &&
|
||||
time(NULL) - buffer.st_mtime > g_pOptions->GetNzbDirFileAge() &&
|
||||
time(NULL) - buffer.st_ctime > g_pOptions->GetNzbDirFileAge())
|
||||
{
|
||||
// the file is at least g_pOptions->GetNzbDirFileAge() seconds old, we can process it
|
||||
info("Collection %s found", filename);
|
||||
char bakname[1024];
|
||||
if (g_pQueueCoordinator->AddFileToQueue(fullfilename))
|
||||
{
|
||||
info("Collection %s added to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.queued", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not add collection %s to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.error", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
char bakname2[1024];
|
||||
strcpy(bakname2, bakname);
|
||||
int i = 2;
|
||||
while (!stat(bakname2, &buffer))
|
||||
{
|
||||
snprintf(bakname2, 1024, "%s%i", bakname, i++);
|
||||
bakname2[1024-1] = '\0';
|
||||
}
|
||||
|
||||
rename(fullfilename, bakname2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the completed file was last (unpaused, if bIgnorePaused is "true") file in nzb-collection
|
||||
*/
|
||||
bool PrePostProcessor::WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo, bool bIgnorePaused)
|
||||
{
|
||||
debug("File %s completed or deleted", pFileInfo->GetFilename());
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 != pFileInfo && (!bIgnorePaused || !pFileInfo2->GetPaused()) &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PrePostProcessor::ParQueue* PrePostProcessor::LockParQueue()
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
return &m_ParQueue;
|
||||
}
|
||||
|
||||
void PrePostProcessor::UnlockParQueue()
|
||||
{
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
void PrePostProcessor::CheckPars(DownloadQueue * pDownloadQueue, FileInfo * pFileInfo)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
FileList fileList;
|
||||
if (FindMainPars(pFileInfo->GetDestDir(), &fileList))
|
||||
{
|
||||
for (FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
debug("Found par: %s", szParFilename);
|
||||
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, szParFilename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
int iBaseLen = 0;
|
||||
ParChecker::ParseParFilename(szParFilename, &iBaseLen, NULL);
|
||||
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
|
||||
strncpy(szInfoName, szParFilename, maxlen);
|
||||
szInfoName[maxlen] = '\0';
|
||||
|
||||
char szParInfoName[1024];
|
||||
snprintf(szParInfoName, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
szParInfoName[1024-1] = '\0';
|
||||
|
||||
info("Queueing %s%c%s for par-check", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
ParJob* pParJob = new ParJob(pFileInfo->GetNZBFilename(), szFullFilename, szParInfoName);
|
||||
m_ParQueue.push_back(pParJob);
|
||||
m_bHasMoreJobs = true;
|
||||
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::FindMainPars(const char * szPath, FileList * pFileList)
|
||||
{
|
||||
pFileList->clear();
|
||||
DirBrowser dir(szPath);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int iBaseLen = 0;
|
||||
if (ParChecker::ParseParFilename(filename, &iBaseLen, NULL))
|
||||
{
|
||||
// check if the base file already added to list
|
||||
bool exists = false;
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
const char* filename2 = *it;
|
||||
exists = SameParCollection(filename, filename2);
|
||||
if (exists)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
pFileList->push_back(strdup(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
return !pFileList->empty();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::AddPar(FileInfo * pFileInfo, bool bDeleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
bool bSameCollection = m_ParChecker.IsRunning() &&
|
||||
!strcmp(pFileInfo->GetNZBFilename(), m_ParChecker.GetNZBFilename()) &&
|
||||
SameParCollection(pFileInfo->GetFilename(), BaseFileName(m_ParChecker.GetParFilename()));
|
||||
if (bSameCollection)
|
||||
{
|
||||
if (!bDeleted)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
szFullFilename[1024-1] = '\0';
|
||||
m_ParChecker.AddParFile(szFullFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParChecker.QueueChanged();
|
||||
}
|
||||
}
|
||||
m_mutexParChecker.Unlock();
|
||||
return bSameCollection;
|
||||
}
|
||||
|
||||
bool PrePostProcessor::SameParCollection(const char* szFilename1, const char* szFilename2)
|
||||
{
|
||||
int iBaseLen1 = 0, iBaseLen2 = 0;
|
||||
return ParChecker::ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
|
||||
ParChecker::ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
|
||||
iBaseLen1 == iBaseLen2 &&
|
||||
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
|
||||
}
|
||||
|
||||
void PrePostProcessor::CheckParQueue()
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
if (!m_ParChecker.IsRunning() && !m_ParQueue.empty())
|
||||
{
|
||||
ParJob* pParJob = m_ParQueue.front();
|
||||
|
||||
info("Checking pars for %s", pParJob->GetInfoName());
|
||||
m_ParChecker.SetNZBFilename(pParJob->GetNZBFilename());
|
||||
m_ParChecker.SetParFilename(pParJob->GetParFilename());
|
||||
m_ParChecker.SetInfoName(pParJob->GetInfoName());
|
||||
m_ParChecker.Start();
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
void PrePostProcessor::ParCheckerUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFinished ||
|
||||
m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
char szPath[1024];
|
||||
strncpy(szPath, m_ParChecker.GetParFilename(), 1024);
|
||||
szPath[1024-1] = '\0';
|
||||
if (char* p = strrchr(szPath, PATH_SEPARATOR)) *p = '\0';
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szPath, (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
|
||||
bool bExists = false;
|
||||
if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
struct stat buffer;
|
||||
bExists = !stat(szBrokenLogName, &buffer);
|
||||
}
|
||||
if (!m_ParChecker.GetRepairNotNeeded() || bExists)
|
||||
{
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
fprintf(file, "Repair failed for %s: %s\n", m_ParChecker.GetInfoName(), m_ParChecker.GetErrMsg() ? m_ParChecker.GetErrMsg() : "");
|
||||
}
|
||||
else if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
fprintf(file, "Repair not needed for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
fprintf(file, "Successfully repaired %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "Repair possible for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
int iParStatus = 0;
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
iParStatus = PARSTATUS_FAILED;
|
||||
}
|
||||
else if (g_pOptions->GetParRepair() || m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
iParStatus = PARSTATUS_REPAIRED;
|
||||
}
|
||||
else
|
||||
{
|
||||
iParStatus = PARSTATUS_REPAIR_POSSIBLE;
|
||||
}
|
||||
|
||||
ExecPostScript(szPath, m_ParChecker.GetNZBFilename(), m_ParChecker.GetParFilename(), iParStatus);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
ParJob* pParJob = m_ParQueue.front();
|
||||
m_ParQueue.pop_front();
|
||||
delete pParJob;
|
||||
m_bHasMoreJobs = !m_ParQueue.empty();
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void PrePostProcessor::ExecPostScript(const char * szPath, const char * szNZBFilename, const char * szParFilename, int iParStatus)
|
||||
{
|
||||
const char* szScript = g_pOptions->GetPostProcess();
|
||||
if (!szScript || strlen(szScript) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing post-process for %s (%s)", szPath, BaseFileName(szNZBFilename));
|
||||
struct stat buffer;
|
||||
bool bExists = !stat(szScript, &buffer);
|
||||
if (!bExists)
|
||||
{
|
||||
error("Could not start post-process: could not find file %s", szScript);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bCollectionCompleted = true;
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (!pFileInfo2->GetPaused() &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
bCollectionCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (bCollectionCompleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
for (ParQueue::iterator it = m_ParQueue.begin(); it != m_ParQueue.end(); it++)
|
||||
{
|
||||
ParJob* pParJob = *it;
|
||||
if (!strcmp(pParJob->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
bCollectionCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
char szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", iParStatus);
|
||||
szParStatus[10-1] = '\0';
|
||||
|
||||
char szCollectionCompleted[10];
|
||||
snprintf(szCollectionCompleted, 10, "%i", (int)bCollectionCompleted);
|
||||
szCollectionCompleted[10-1] = '\0';
|
||||
|
||||
#ifdef WIN32
|
||||
char szCmdLine[2048];
|
||||
snprintf(szCmdLine, 2048, "%s \"%s\" \"%s\" \"%s\" %s %s", szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted);
|
||||
szCmdLine[2048-1] = '\0';
|
||||
UINT ErrCode = WinExec(szCmdLine, SW_HIDE);
|
||||
if (ErrCode < 32)
|
||||
{
|
||||
char szErrMsg[255];
|
||||
szErrMsg[255-1] = '\0';
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, ErrCode, 0, szErrMsg, 255, NULL);
|
||||
error("Could not start post-process: %s", szErrMsg);
|
||||
}
|
||||
#else
|
||||
if (fork())
|
||||
{
|
||||
// continue the first instance
|
||||
return;
|
||||
}
|
||||
|
||||
// here goes the second instance
|
||||
|
||||
int h;
|
||||
for (h = getdtablesize(); h >= 0;--h) close(h); /* close all descriptors */
|
||||
h = open("/dev/null", O_RDWR); dup(h); dup(h); /* handle standart I/O */
|
||||
|
||||
execlp(szScript, szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted, NULL);
|
||||
error("Could not start post-process: %s", strerror(errno));
|
||||
exit(-1);
|
||||
#endif
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PREPOSTPROCESSOR_H
|
||||
#define PREPOSTPROCESSOR_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
#endif
|
||||
|
||||
class PrePostProcessor : public Thread
|
||||
{
|
||||
public:
|
||||
class ParJob
|
||||
{
|
||||
private:
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
|
||||
public:
|
||||
ParJob(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
|
||||
~ParJob();
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
};
|
||||
|
||||
typedef std::deque<ParJob*> ParQueue;
|
||||
|
||||
private:
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
class QueueCoordinatorObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
class ParCheckerObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
|
||||
};
|
||||
#endif
|
||||
|
||||
private:
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, bool bIgnorePaused);
|
||||
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, int iParStatus);
|
||||
|
||||
|
||||
Mutex m_mutexParChecker;
|
||||
ParQueue m_ParQueue;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
ParChecker m_ParChecker;
|
||||
ParCheckerObserver m_ParCheckerObserver;
|
||||
|
||||
void ParCheckerUpdate(Subject* Caller, void* Aspect);
|
||||
void CheckParQueue();
|
||||
void CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
|
||||
bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
#endif
|
||||
|
||||
public:
|
||||
PrePostProcessor();
|
||||
virtual ~PrePostProcessor();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
ParQueue* LockParQueue();
|
||||
void UnlockParQueue();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,781 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
{
|
||||
debug("Creating QueueCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
m_DownloadQueue.clear();
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
m_iSpeedBytes[i] = 0;
|
||||
}
|
||||
m_iSpeedBytesIndex = 0;
|
||||
|
||||
m_iAllBytes = 0;
|
||||
m_tStartServer = 0;
|
||||
m_tStartDownload = 0;
|
||||
m_tPausedFrom = 0;
|
||||
m_bStandBy = true;
|
||||
|
||||
YDecoder::Init();
|
||||
}
|
||||
|
||||
QueueCoordinator::~QueueCoordinator()
|
||||
{
|
||||
debug("Destroying QueueCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting DownloadQueue");
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_DownloadQueue.clear();
|
||||
|
||||
debug("Deleting ArticleDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
YDecoder::Final();
|
||||
|
||||
debug("QueueCoordinator destroyed");
|
||||
}
|
||||
|
||||
void QueueCoordinator::Run()
|
||||
{
|
||||
debug("Entering QueueCoordinator-loop");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->Exists())
|
||||
{
|
||||
if (g_pOptions->GetReloadQueue())
|
||||
{
|
||||
g_pDiskState->Load(&m_DownloadQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pDiskState->Discard();
|
||||
}
|
||||
}
|
||||
|
||||
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
m_tStartServer = time(NULL);
|
||||
bool bWasStandBy = true;
|
||||
bool bArticeDownloadsRunning = false;
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!g_pOptions->GetPause())
|
||||
{
|
||||
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
|
||||
if (pConnection)
|
||||
{
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
|
||||
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
|
||||
bArticeDownloadsRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pServerPool->FreeConnection(pConnection, false);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool bStandBy = !bArticeDownloadsRunning;
|
||||
if (bStandBy ^ bWasStandBy)
|
||||
{
|
||||
EnterLeaveStandBy(bStandBy);
|
||||
bWasStandBy = bStandBy;
|
||||
}
|
||||
|
||||
// sleep longer in StandBy
|
||||
int iSleepInterval = bStandBy ? 100 : 5;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
AddSpeedReading(0);
|
||||
|
||||
iResetCounter+= iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
g_pServerPool->CloseUnusedConnections();
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("QueueCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("QueueCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting QueueCoordinator-loop");
|
||||
}
|
||||
|
||||
void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
|
||||
{
|
||||
debug("Adding NZBFile to queue");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
DownloadQueue tmpDownloadQueue;
|
||||
tmpDownloadQueue.clear();
|
||||
DownloadQueue DupeList;
|
||||
DupeList.clear();
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
bool dupe = false;
|
||||
if (IsDupe(pFileInfo))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
}
|
||||
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
(pFileInfo->GetSize() < pFileInfo2->GetSize()))
|
||||
{
|
||||
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dupe)
|
||||
{
|
||||
DupeList.push_back(pFileInfo);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpDownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
{
|
||||
if (bAddFirst)
|
||||
{
|
||||
m_DownloadQueue.push_front(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
g_pDiskState->DiscardFile(NULL, pFileInfo);
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
pNZBFile->DetachFileInfos();
|
||||
|
||||
Aspect aspect = { eaNZBFileAdded, NULL, &m_DownloadQueue, pNZBFile->GetFileName() };
|
||||
Notify(&aspect);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->Save(&m_DownloadQueue);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool QueueCoordinator::AddFileToQueue(const char* szFileName)
|
||||
{
|
||||
// Parse the buffer and make it into a NZBFile
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFileName);
|
||||
|
||||
// Did file parse correctly?
|
||||
if (!pNZBFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add NZBFile to Queue
|
||||
AddNZBFileToQueue(pNZBFile, false);
|
||||
|
||||
delete pNZBFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: see note to "AddSpeedReading"
|
||||
*/
|
||||
float QueueCoordinator::CalcCurrentDownloadSpeed()
|
||||
{
|
||||
int iTotal = 0;
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
iTotal += m_iSpeedBytes[i];
|
||||
}
|
||||
|
||||
float fSpeed = iTotal / 1024.0 / SPEEDMETER_SECONDS;
|
||||
|
||||
return fSpeed;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
|
||||
* but this would results in a big performance loss (the function
|
||||
* "AddSpeedReading" is called extremly often), so we better agree with calculation
|
||||
* errors possible because of simultaneuos access from several threads.
|
||||
* The used algorithm is able to recover after few seconds.
|
||||
* In any case the calculation errors can not result in fatal system
|
||||
* errors (segmentation faults).
|
||||
*/
|
||||
void QueueCoordinator::AddSpeedReading(int iBytes)
|
||||
{
|
||||
int iIndex = time(NULL);
|
||||
|
||||
if (iIndex - m_iSpeedBytesIndex > SPEEDMETER_SECONDS)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex - SPEEDMETER_SECONDS - 1;
|
||||
}
|
||||
|
||||
for (int i = m_iSpeedBytesIndex + 1; i < iIndex; i++)
|
||||
{
|
||||
m_iSpeedBytes[i % SPEEDMETER_SECONDS] = 0;
|
||||
}
|
||||
|
||||
if (iIndex > m_iSpeedBytesIndex)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex;
|
||||
m_iSpeedBytes[iIndex % SPEEDMETER_SECONDS] = iBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iSpeedBytes[m_iSpeedBytesIndex % SPEEDMETER_SECONDS] += iBytes;
|
||||
}
|
||||
|
||||
m_iAllBytes += iBytes;
|
||||
}
|
||||
|
||||
long long QueueCoordinator::CalcRemainingSize()
|
||||
{
|
||||
long long lRemainingSize = 0;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
return lRemainingSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: DownloadQueue must be locked prior to call of this function
|
||||
* Returns True if Entry was deleted from Queue or False if it was scheduled for Deletion.
|
||||
* NOTE: "False" does not mean unsuccess; the entry is (or will be) deleted in any case.
|
||||
*/
|
||||
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
pFileInfo->SetDeleted(true);
|
||||
bool hasDownloads = false;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (pArticleDownloader->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
hasDownloads = true;
|
||||
pArticleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
if (!hasDownloads)
|
||||
{
|
||||
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, false);
|
||||
}
|
||||
return hasDownloads;
|
||||
}
|
||||
|
||||
void QueueCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping ArticleDownloads");
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
debug("ArticleDownloads are notified");
|
||||
}
|
||||
|
||||
bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo)
|
||||
{
|
||||
//debug("QueueCoordinator::GetNextArticle()");
|
||||
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo);
|
||||
}
|
||||
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
|
||||
{
|
||||
pArticleInfo = *at;
|
||||
if (pArticleInfo->GetStatus() == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
|
||||
{
|
||||
debug("Starting new ArticleDownloader");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = new ArticleDownloader();
|
||||
pArticleDownloader->SetAutoDestroy(true);
|
||||
pArticleDownloader->Attach(this);
|
||||
pArticleDownloader->SetFileInfo(pFileInfo);
|
||||
pArticleDownloader->SetArticleInfo(pArticleInfo);
|
||||
pArticleDownloader->SetConnection(pConnection);
|
||||
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
|
||||
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pArticleDownloader);
|
||||
pArticleDownloader->Start();
|
||||
}
|
||||
|
||||
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
{
|
||||
char name[1024];
|
||||
|
||||
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
|
||||
name[1024-1] = '\0';
|
||||
pArticleInfo->SetResultFilename(name);
|
||||
|
||||
char tmpname[1024];
|
||||
snprintf(tmpname, 1024, "%s.tmp", name);
|
||||
tmpname[1024-1] = '\0';
|
||||
pArticleDownloader->SetTempFilename(tmpname);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetInfoName(name);
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetOutputFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue* QueueCoordinator::LockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
return &m_DownloadQueue;
|
||||
}
|
||||
|
||||
void QueueCoordinator::UnlockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::Update(Subject* Caller, void* Aspect)
|
||||
{
|
||||
debug("Notification from ArticleDownloader received");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = (ArticleDownloader*) Caller;
|
||||
if ((pArticleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
|
||||
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed))
|
||||
{
|
||||
ArticleCompleted(pArticleDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
{
|
||||
debug("Article downloaded");
|
||||
|
||||
FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFinished);
|
||||
}
|
||||
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
|
||||
}
|
||||
|
||||
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
|
||||
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
|
||||
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
|
||||
|
||||
if (!pFileInfo->GetFilenameConfirmed() &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
|
||||
pArticleDownloader->GetArticleFilename())
|
||||
{
|
||||
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
|
||||
pFileInfo->SetFilenameConfirmed(true);
|
||||
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
|
||||
fileCompleted = false;
|
||||
DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
bool deleteFileObj = false;
|
||||
|
||||
if (pFileInfo->GetDeleted())
|
||||
{
|
||||
int cnt = 0;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
if ((*it)->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt == 1)
|
||||
{
|
||||
// this was the last Download for a file deleted from queue
|
||||
deleteFileObj = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
m_mutexDownloadQueue.Lock();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pa = *it;
|
||||
if (pa == pArticleDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deleteFileObj)
|
||||
{
|
||||
// delete File from Queue
|
||||
pFileInfo->SetDeleted(true);
|
||||
|
||||
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, fileCompleted);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pa = *it;
|
||||
if (pa == pFileInfo)
|
||||
{
|
||||
m_DownloadQueue.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(&m_DownloadQueue, pFileInfo);
|
||||
}
|
||||
|
||||
if (!bCompleted)
|
||||
{
|
||||
// deleting temporary files
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetResultFilename())
|
||||
{
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char name[1024];
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
{
|
||||
debug("Checking if the file is already queued");
|
||||
|
||||
// checking on disk
|
||||
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// checking in queue
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pQueueEntry = *it;
|
||||
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
|
||||
pFileInfo != pQueueEntry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug("--------------------------------------------");
|
||||
debug("Dumping debug info to log");
|
||||
debug("--------------------------------------------");
|
||||
|
||||
debug(" QueueCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
pArticleDownloader->LogDebugInfo();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
debug("");
|
||||
|
||||
g_pServerPool->LogDebugInfo();
|
||||
}
|
||||
|
||||
void QueueCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
debug("Terminating hanging download %s", pArticleDownloader->GetInfoName());
|
||||
if (pArticleDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pArticleDownloader->GetInfoName());
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", BaseFileName(pArticleInfo->GetResultFilename()));
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
|
||||
delete pArticleDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
m_bStandBy = bEnter;
|
||||
if (bEnter)
|
||||
{
|
||||
m_tPausedFrom = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tStartDownload == 0)
|
||||
{
|
||||
m_tStartDownload = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tStartDownload += time(NULL) - m_tPausedFrom;
|
||||
}
|
||||
m_tPausedFrom = 0;
|
||||
}
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
if (m_tStartServer > 0)
|
||||
{
|
||||
*iUpTimeSec = time(NULL) - m_tStartServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
*iUpTimeSec = 0;
|
||||
}
|
||||
*bStandBy = m_bStandBy;
|
||||
if (m_bStandBy)
|
||||
{
|
||||
*iDnTimeSec = m_tPausedFrom - m_tStartDownload;
|
||||
}
|
||||
else
|
||||
{
|
||||
*iDnTimeSec = time(NULL) - m_tStartDownload;
|
||||
}
|
||||
*iAllBytes = m_iAllBytes;
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUECOORDINATOR_H
|
||||
#define QUEUECOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
{
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
DownloadQueue* pDownloadQueue;
|
||||
const char* szNZBFilename;
|
||||
};
|
||||
|
||||
private:
|
||||
DownloadQueue m_DownloadQueue;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
QueueEditor m_QueueEditor;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
// statistics
|
||||
static const int SPEEDMETER_SECONDS = 5;
|
||||
int m_iSpeedBytes[SPEEDMETER_SECONDS];
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
time_t m_tStartDownload;
|
||||
time_t m_tPausedFrom;
|
||||
void EnterLeaveStandBy(bool bEnter);
|
||||
bool m_bStandBy;
|
||||
Mutex m_mutexStat;
|
||||
|
||||
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
|
||||
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
|
||||
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
bool IsDupe(FileInfo* pFileInfo);
|
||||
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
|
||||
void ResetHangingDownloads();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// statistics
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
virtual void AddSpeedReading(int iBytes);
|
||||
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
|
||||
|
||||
// Editing the queue
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue() ;
|
||||
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
|
||||
bool AddFileToQueue(const char* szFileName);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
655
QueueEditor.cpp
655
QueueEditor.cpp
@@ -1,655 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
const int MAX_ID = 100000000;
|
||||
|
||||
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
m_pFileInfo = pFileInfo;
|
||||
m_iOffset = iOffset;
|
||||
}
|
||||
|
||||
QueueEditor::QueueEditor()
|
||||
{
|
||||
debug("Creating QueueEditor");
|
||||
}
|
||||
|
||||
QueueEditor::~QueueEditor()
|
||||
{
|
||||
debug("Destroying QueueEditor");
|
||||
}
|
||||
|
||||
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 == pFileInfo)
|
||||
{
|
||||
return iEntry;
|
||||
}
|
||||
iEntry ++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the pause flag of the specific entry in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
|
||||
if (iEntry > -1)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
|
||||
{
|
||||
FileInfo* fi = (*pDownloadQueue)[iEntry];
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->Save(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
|
||||
}
|
||||
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
|
||||
case eaFileResume:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, false);
|
||||
break;
|
||||
|
||||
case eaFileMoveOffset:
|
||||
case eaFileMoveTop:
|
||||
case eaFileMoveBottom:
|
||||
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
|
||||
break;
|
||||
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
|
||||
break;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
return cItemList.size() > 0;
|
||||
}
|
||||
|
||||
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
|
||||
EEditAction EEditAction, int iOffset)
|
||||
{
|
||||
if (EEditAction == eaFileMoveTop)
|
||||
{
|
||||
iOffset = -MAX_ID;
|
||||
}
|
||||
else if (EEditAction == eaFileMoveBottom)
|
||||
{
|
||||
iOffset = MAX_ID;
|
||||
}
|
||||
|
||||
pItemList->reserve(pIDList->size());
|
||||
if (bSmartOrder && iOffset != 0 &&
|
||||
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
|
||||
{
|
||||
//add IDs to list in order they currently have in download queue
|
||||
int iLastDestPos = -1;
|
||||
int iStart, iEnd, iStep;
|
||||
if (iOffset < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
iEnd = pDownloadQueue->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
|
||||
int iID = pFileInfo->GetID();
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
if (iID == *it)
|
||||
{
|
||||
int iWorkOffset = iOffset;
|
||||
int iDestPos = iIndex + iWorkOffset;
|
||||
if (iLastDestPos == -1)
|
||||
{
|
||||
if (iDestPos < 0)
|
||||
{
|
||||
iWorkOffset = -iIndex;
|
||||
}
|
||||
else if (iDestPos > int(pDownloadQueue->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex + 1;
|
||||
}
|
||||
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex - 1;
|
||||
}
|
||||
}
|
||||
iLastDestPos = iIndex + iWorkOffset;
|
||||
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check ID range
|
||||
int iMaxID = 0;
|
||||
int iMinID = MAX_ID;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int ID = pFileInfo->GetID();
|
||||
if (ID > iMaxID)
|
||||
{
|
||||
iMaxID = ID;
|
||||
}
|
||||
if (ID < iMinID)
|
||||
{
|
||||
iMinID = ID;
|
||||
}
|
||||
}
|
||||
|
||||
//add IDs to list in order they were transmitted in command
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
int iID = *it;
|
||||
if (iMinID <= iID && iID <= iMaxID)
|
||||
{
|
||||
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
pItemList->push_back(new EditItem(pFileInfo, iOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
|
||||
// collecting files belonging to group
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
cIDList.push_back(pFileInfo2->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
// calculating offset in terms of files
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pGroupInfo = *it;
|
||||
if (!strcmp(pGroupInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
int iFileOffset = 0;
|
||||
if (iOffset > 0)
|
||||
{
|
||||
if (iNum + iOffset >= cGroupList.size() - 1)
|
||||
{
|
||||
eAction = eaGroupMoveBottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum + 2; i < cGroupList.size() && iOffset > 0; i++, iOffset--)
|
||||
{
|
||||
iFileOffset += FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iNum + iOffset <= 0)
|
||||
{
|
||||
eAction = eaGroupMoveTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum; i > 0 && iOffset < 0; i--, iOffset++)
|
||||
{
|
||||
iFileOffset -= FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
iOffset = iFileOffset;
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
|
||||
{
|
||||
FileInfo* pGroupInfo1 = *itg;
|
||||
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupList->push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::ItemExists(FileList* pFileList, FileInfo* pFileInfo)
|
||||
{
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
if (*it == pFileInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset)
|
||||
{
|
||||
// Build list of all groups; List contains first file of each group
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
|
||||
// Find affected groups. It includes groups being moved and groups directly
|
||||
// above or under of these groups (those order is also changed)
|
||||
FileList cAffectedGroupList;
|
||||
cAffectedGroupList.clear();
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eaFileMoveOffset, iOffset);
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!strcmp(pItem->m_pFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, pFileInfo))
|
||||
{
|
||||
cAffectedGroupList.push_back(pFileInfo);
|
||||
}
|
||||
if (iOffset < 0)
|
||||
{
|
||||
for (int i = iNum - 1; i >= -iOffset-1; i--)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iOffset > 0)
|
||||
{
|
||||
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (iNum + 1 < cGroupList.size())
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[iNum + 1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
cGroupList.clear();
|
||||
|
||||
// Aligning groups
|
||||
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
|
||||
if (!strcmp(pFirstFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
|
||||
iLastNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
iLastNum = iNum;
|
||||
}
|
||||
pLastFileInfo = pFileInfo;
|
||||
}
|
||||
iNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
FileList GroupFileList;
|
||||
GroupFileList.clear();
|
||||
FileInfo* pFirstFileInfo = NULL;
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (!pFirstFileInfo ||
|
||||
!strcmp(pFirstFileInfo->GetNZBFilename(), pItem->m_pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
GroupFileList.push_back(pItem->m_pFileInfo);
|
||||
if (!pFirstFileInfo)
|
||||
{
|
||||
pFirstFileInfo = pItem->m_pFileInfo;
|
||||
}
|
||||
delete pItem;
|
||||
pItemList->erase(it);
|
||||
it = pItemList->begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (!GroupFileList.empty())
|
||||
{
|
||||
PausePars(&GroupFileList, bExtraParsOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files.
|
||||
* If the parameter "bExtraParsOnly" is set to "true", we use the following strategy:
|
||||
* At first we find all par-files, which do not have "vol" in their names, then we pause
|
||||
* all vols and do not affect all just-pars.
|
||||
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
|
||||
* and do not affect it, but pause all other pars.
|
||||
*/
|
||||
void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
|
||||
{
|
||||
debug("QueueEditor: Pausing pars");
|
||||
|
||||
FileList Pars, Vols;
|
||||
Pars.clear();
|
||||
Vols.clear();
|
||||
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
if (strstr(szLoFileName, ".par2"))
|
||||
{
|
||||
if (!bExtraParsOnly)
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(szLoFileName, ".vol"))
|
||||
{
|
||||
Vols.push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pars.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bExtraParsOnly)
|
||||
{
|
||||
if (!Pars.empty())
|
||||
{
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pausing all Vol-files except the smallest one
|
||||
FileInfo* pSmallest = NULL;
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pSmallest)
|
||||
{
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else if (pSmallest->GetSize() > pFileInfo->GetSize())
|
||||
{
|
||||
pSmallest->SetPaused(true);
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUEEDITOR_H
|
||||
#define QUEUEEDITOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eaFileMoveTop,
|
||||
eaFileMoveBottom,
|
||||
eaFilePause,
|
||||
eaFileResume,
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars
|
||||
};
|
||||
|
||||
private:
|
||||
class EditItem
|
||||
{
|
||||
public:
|
||||
int m_iOffset;
|
||||
FileInfo* m_pFileInfo;
|
||||
|
||||
EditItem(FileInfo* pFileInfo, int iOffset);
|
||||
};
|
||||
|
||||
typedef std::vector<EditItem*> ItemList;
|
||||
typedef std::vector<FileInfo*> FileList;
|
||||
|
||||
private:
|
||||
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
|
||||
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
#endif
|
||||
376
README
376
README
@@ -2,6 +2,10 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.net
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. About NZBGet
|
||||
@@ -29,6 +33,9 @@ In server/client mode NZBGet runs as server in background.
|
||||
Then you use client to send requests to server. The sample requests
|
||||
are: download nzb-file, list files in queue, etc.
|
||||
|
||||
There is also a built-in web-interface. The server has RPC-support
|
||||
and can be controlled from third party applications too.
|
||||
|
||||
Standalone-tool, server and client are all contained in only one
|
||||
executable file "nzbget". The mode in which the program works
|
||||
depends on command-line parameters passed to the program.
|
||||
@@ -37,29 +44,16 @@ depends on command-line parameters passed to the program.
|
||||
2. Supported OS
|
||||
=====================================
|
||||
|
||||
NZBGet is written in C++ and was initialy developed on Linux.
|
||||
It was ported to Windows later and tested for compatibility with
|
||||
several POSIX-OS'es.
|
||||
|
||||
The current version (0.3.1) should run at least on:
|
||||
- Linux Debian 4.0 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
|
||||
The previous version (0.3.0) was also tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Linux Debian 3.1 on SPARC (QEmu).
|
||||
NZBGet is written in C++ and works on Windows, OS X, Linux and
|
||||
most POSIX-conform OS'es.
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
control your NZBGet-server running on Linux.
|
||||
|
||||
The download-section of NZBGet web-site provides binary files
|
||||
for Windows. The binary packages for many routers and NAS devices are
|
||||
also available in OPTWARE repository (http://www.nslu2-linux.org),
|
||||
but for most POSIX-systems you need to compile the program yourself.
|
||||
for Windows, OS X and Linux. For most POSIX-systems you need to compile
|
||||
the program yourself.
|
||||
|
||||
If you have downloaded binaries you can just jump to section
|
||||
"Configuration".
|
||||
@@ -68,8 +62,8 @@ If you have downloaded binaries you can just jump to section
|
||||
3. Prerequisites on POSIX
|
||||
=====================================
|
||||
|
||||
NZBGet is developed on a linux-system, but it should run on other
|
||||
POSIX platforms (see the list of tested platforms above).
|
||||
NZBGet is developed on a linux-system, but it runs on other
|
||||
POSIX platforms.
|
||||
|
||||
NZBGet absolutely needs the following libraries:
|
||||
|
||||
@@ -82,178 +76,174 @@ And the following libraries are optional:
|
||||
- libcurses (usually part of commercial systems)
|
||||
or (better)
|
||||
- libncurses (http://invisible-island.net/ncurses)
|
||||
|
||||
- for par-check and -repair (enabled by default):
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
- for encrypted connections (TLS/SSL):
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
- for support of encoding-formats other than yEnc (disabled by default):
|
||||
- libuu (http://www.fpx.de/fp/Software/UUDeview)
|
||||
- for gzip support in web-server and web-client (enabled by default):
|
||||
- zlib (http://www.zlib.net)
|
||||
|
||||
All these libraries are included in modern Linux distributions and
|
||||
All these libraries are included in modern POSIX distributions and
|
||||
should be available as installable packages. Please note that you also
|
||||
need the developer packages for these libraries too, they package names
|
||||
have often suffix "dev" or "devel". On other systems you may need to
|
||||
download the libraries at the given URLs and compile them (see hints below).
|
||||
|
||||
|
||||
=====================================
|
||||
4. Installation on POSIX
|
||||
=====================================
|
||||
|
||||
Well, the usual stuff:
|
||||
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
|
||||
|
||||
- untar the nzbget-source via
|
||||
tar -zxf nzbget-VERSION.tar.gz
|
||||
|
||||
- change into nzbget-directory via
|
||||
cd nzbget-VERSION
|
||||
|
||||
- configure it via
|
||||
./configure
|
||||
|
||||
(maybe you have to tell configure, where to find some libraries.
|
||||
./configure --help is your friend! ;-)
|
||||
also see "Configure-options" later.)
|
||||
./configure --help is your friend!
|
||||
also see "Configure-options" later)
|
||||
|
||||
- in a case you don't have root access or want to install the program
|
||||
in your home directory use the configure parameter --prefix, e. g.:
|
||||
|
||||
./configure --prefix ~/usr
|
||||
|
||||
- compile it via
|
||||
make
|
||||
(you may get some warnings concerning 'mktemp', simply ignore them!)
|
||||
- become root via
|
||||
|
||||
- to install system wide become root via:
|
||||
su
|
||||
- install it via
|
||||
|
||||
- install it via:
|
||||
make install
|
||||
|
||||
- install configuration files into <prefix>/etc via:
|
||||
make install-conf
|
||||
|
||||
(you can skip this step if you intend to store configuration
|
||||
files in a non-standard location)
|
||||
|
||||
Configure-options
|
||||
-----------------
|
||||
You may run configure with additional arguments:
|
||||
|
||||
--enable-uulib - to make with uulib-library, in a case you want it
|
||||
(see later section "Optional package: uulib"). This option is not
|
||||
enabled by default.
|
||||
|
||||
--disable-curses - to make without curses-support. Use this option
|
||||
if you can not use curses/ncurses.
|
||||
|
||||
--disable-parcheck - to make without parcheck-support. Use this option
|
||||
if you can not use libpar2 or libsigc++.
|
||||
if you have troubles when compiling par2-module.
|
||||
|
||||
--with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
|
||||
should be used for encrypted server connections.
|
||||
|
||||
--disable-tls - to make without TLS/SSL support. Use this option if
|
||||
you can not neither OpenSSL nor GnuTLS.
|
||||
|
||||
--disable-gzip - to make without gzip support. Use this option
|
||||
if you can not use zlib.
|
||||
|
||||
--enable-debug - to build in debug-mode, if you want to see and log
|
||||
debug-messages.
|
||||
|
||||
Optional package: uulib
|
||||
-----------------------
|
||||
uulib is not required to compile and run nzbget, because nzbget includes
|
||||
internal decoder for yEnc-format. However, uulib supports many other formats,
|
||||
you may possibly want to have support for. In this case you can build the
|
||||
program with uulib-support enabled.
|
||||
|
||||
NOTE: enabling uulib does not disable internal decoder. The program built with
|
||||
uulib-support can use both decoders (internal and uulib), depending on option
|
||||
"decoder" in program's configuration file.
|
||||
|
||||
To build with uulib use option "--enable-uulib" while running configure:
|
||||
|
||||
./configure --enable-uulib
|
||||
|
||||
The uulib must be installed on your system. On most linux distributions
|
||||
the package uulib-dev is available. So you only need to install this package
|
||||
and run configure with parameter "--enable-uulib".
|
||||
If you do not have this package you can compile uulib yourself:
|
||||
|
||||
- download source code of uudeview from
|
||||
http://www.fpx.de/fp/Software/UUDeview;
|
||||
- build uudeview as usually:
|
||||
/.confugure
|
||||
make
|
||||
- start nzbget's configure-script with following parameters:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=<path to uudeview>/uulib \
|
||||
--with-uulib-libraries=<path to uudeview>/uulib
|
||||
for example:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=/home/user/uudeview-0.5.20/uulib \
|
||||
--with-uulib-libraries=/home/user/uudeview-0.5.20/uulib
|
||||
- now you can compile nzbget.
|
||||
|
||||
NOTE: after nzbget is compiled, the code of uulib-library is built into
|
||||
nzbget's executable. You do not need to have uulib on target system
|
||||
to run nzbget.
|
||||
|
||||
|
||||
Optional package: par-check
|
||||
---------------------------
|
||||
NZBGet can check and repair downloaded files for you. For this purpose
|
||||
it uses library par2 (libpar2), which needs sigc++ on its part.
|
||||
it uses library par2.
|
||||
|
||||
To build with par-check use option "--enable-parcheck" while running
|
||||
configure:
|
||||
|
||||
./configure --enable-parcheck
|
||||
For your convenience the source code of libpar2 is integrated into
|
||||
NZBGet’s source tree and is compiled automatically when you make NZBGet.
|
||||
|
||||
The libpar2 and libsigc++ (version 2 or later) must be installed on your
|
||||
system. On most linux distributions these libraries are available as packages.
|
||||
So you only need to install theme and run configure with parameter
|
||||
"--enable-parcheck".
|
||||
If you do not have these package you can compile them yourself. Please
|
||||
refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. Following configure-parameters may be usefull:
|
||||
In a case errors occur during this process the inclusion of par2-module
|
||||
can be disabled using configure option "--disable-parcheck":
|
||||
|
||||
--with-libpar2-includes
|
||||
--with-libpar2-libraries
|
||||
--with-libsigc-includes
|
||||
--with-libsigc-libraries
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
./configure --disable-parcheck
|
||||
|
||||
Optional package: curses
|
||||
-------------------------
|
||||
For curses-outputmode you need ncurses or curses on your system.
|
||||
If you do not have one of them you can download and compile ncurses yourself.
|
||||
Please refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. Following configure-parameters may be usefull:
|
||||
Following configure-parameters may be useful:
|
||||
|
||||
--with-libcurses-includes
|
||||
--with-libcurses-libraries
|
||||
--with-libcurses-includes=/path/to/curses/includes
|
||||
--with-libcurses-libraries=/path/to/curses/libraries
|
||||
|
||||
If you are not able to use curses or ncurses or do not want them you can
|
||||
make the program without support for curses using option "--disable-curses":
|
||||
|
||||
./configure --disable-curses
|
||||
|
||||
Optional package: TLS
|
||||
-------------------------
|
||||
To enable encrypted server connections (TLS/SSL) you need to build the program
|
||||
with TLS/SSL support. NZBGet can use two libraries: OpenSSL or GnuTLS.
|
||||
Configure-script checks which library is installed and use it. If both are
|
||||
available it gives the precedence to OpenSSL. You may override that with
|
||||
the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
|
||||
|
||||
./configure --with-tlslib= GnuTLS
|
||||
|
||||
Following configure-parameters may be useful:
|
||||
|
||||
--with-libtls-includess=/path/to/gnutls/includes
|
||||
--with-libtls-libraries=/path/to/gnutls/libraries
|
||||
|
||||
--with-openssl-includess=/path/to/openssl/includes
|
||||
--with-openssl-libraries=/path/to/openssl/libraries
|
||||
|
||||
If none of these libraries is available you can make the program without
|
||||
TLS/SSL support using option "--disable-tls":
|
||||
|
||||
./configure --disable-tls
|
||||
|
||||
=====================================
|
||||
5. Compiling on Windows
|
||||
=====================================
|
||||
|
||||
NZBGet is developed using MS Visual C++ 2005. The project file and solution
|
||||
are provided. If you use MS Visual C++ 2005 Express you need to download
|
||||
and install Platform SDK.
|
||||
NZBGet is developed using MS Visual Studio 2015 (Community Edition). The project
|
||||
file is provided.
|
||||
|
||||
To compile the program with par-check-support you also need the following
|
||||
libraries:
|
||||
To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS:
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
|
||||
Download these libaries, then use patch-files provided with NZBGet to create
|
||||
preconfigured project files and solutions for each library.
|
||||
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
|
||||
to use patch-files, if you do not familiar with this technique.
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib)
|
||||
and include- and libraries-paths are configured in MS Visual C++ 2005 you
|
||||
should be able to compile NZBGet.
|
||||
Also required are:
|
||||
- Regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
|
||||
- Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
|
||||
=====================================
|
||||
6. Configuration
|
||||
=====================================
|
||||
|
||||
NZBGet needs a configuration-file to work properly.
|
||||
NZBGet needs a configuration file.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one newsserver in
|
||||
configuration file. Have a look at the example in nzbget.conf.example,
|
||||
it has comments on how to use each option.
|
||||
An example configuration file is provided in "nzbget.conf", which
|
||||
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
|
||||
system configuration and configure options - typically "/usr/local",
|
||||
"/usr" or "/opt"). The installer adjusts the file according to your
|
||||
system paths. If you have performed the installation step
|
||||
"make install-conf" this file is already copied to "<prefix>/etc" and
|
||||
NZBGet finds it automatically. If you install the program manually
|
||||
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
|
||||
to one of the locations listed below.
|
||||
|
||||
Open the file in a text editor and modify it accodring to your needs.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one news server in
|
||||
configuration file. The file has comments on how to use each option.
|
||||
|
||||
The program looks for configuration file in following standard
|
||||
locations (in this order):
|
||||
|
||||
On POSIX systems:
|
||||
|
||||
<EXE-DIR>/nzbget.conf
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -277,6 +267,12 @@ options via command-line.
|
||||
NZBGet can be used in either standalone mode which downloads a single file
|
||||
or as a server which is able to queue up numerous download requests.
|
||||
|
||||
TIP for Windows users: NZBGet is controlled via various command line
|
||||
parameters. For easier using there is a simple shell script included
|
||||
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
|
||||
be running a command shell with PATH adjusted to find NZBGet executable.
|
||||
Then you can type all commands without full path to nzbget.exe.
|
||||
|
||||
Standalone mode:
|
||||
----------------
|
||||
|
||||
@@ -304,15 +300,20 @@ To stop server use:
|
||||
|
||||
nzbget -Q
|
||||
|
||||
Depending on which frontend has been selected in the nzbget.conf file
|
||||
(option "outputmode") the server should display a message that
|
||||
it is ready to receive download requests (this applies only to console
|
||||
mode, not to daemon mode).
|
||||
TIP for POSIX users: with included script "nzbgetd" you can use standard
|
||||
commands to control daemon:
|
||||
|
||||
nzbgetd start
|
||||
nzbgetd stop
|
||||
etc.
|
||||
|
||||
When NZBGet is started in console server mode it displays a message that
|
||||
it is ready to receive download requests. In daemon mode it doesn't print any
|
||||
messages to console since it runs in background.
|
||||
|
||||
When the server is running it is possible to queue up downloads. This can be
|
||||
done either in terminal with "nzbget -A <nzb-file>" or by uploading
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default, the
|
||||
directory must exist on server's start; otherwise it will not be monitored).
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default).
|
||||
|
||||
To check the status of server start client and connect it to server:
|
||||
|
||||
@@ -335,9 +336,18 @@ It prints something like:
|
||||
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.00 MB)
|
||||
[3] another-nzb\filename3.r01 (100.00 MB)
|
||||
[4] another-nzb\filename3.r02 (100.00 MB)
|
||||
|
||||
The numbers in square braces are ID's of files in queue. They can be used
|
||||
in edit-command. For example to move file with ID 2 to the top of queue:
|
||||
This is the list of individual files listed within nzb-file. To print
|
||||
the list of nzb-files (without content) add G-modifier to the list command:
|
||||
|
||||
[1] nzbname (4.56 GB)
|
||||
[2] another-nzb (4.20 GB)
|
||||
|
||||
The numbers in square braces are ID's of files or groups in queue.
|
||||
They can be used in edit-command. For example to move file with
|
||||
ID 2 to the top of queue:
|
||||
|
||||
nzbget -E T 2
|
||||
|
||||
@@ -350,8 +360,8 @@ or to delete files from queue:
|
||||
nzbget -E D 3 10-15 20-21 16
|
||||
|
||||
The edit-command has also a group-mode which affects all files from the
|
||||
same nzb-request. You need to pass one ID of any file in the group. For
|
||||
example to delete all files from the first nzb-request:
|
||||
same nzb-file. You need to pass an ID of the group. For example to delete
|
||||
the whole group 1:
|
||||
|
||||
nzbget -E G D 1
|
||||
|
||||
@@ -370,7 +380,7 @@ Running client & server on seperate machines:
|
||||
Since nzbget communicates via TCP/IP it's possible to have a server running on
|
||||
one computer and adding downloads via a client on another computer.
|
||||
|
||||
Do this by setting the "serverip" option in the nzbget.conf file to point to the
|
||||
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
|
||||
IP of the server (default is localhost which means client and server runs on
|
||||
same computer)
|
||||
|
||||
@@ -386,36 +396,112 @@ If you need to control server from WAN it is better to connect to server's
|
||||
terminal via SSH (POSIX) or remote desktop (Windows) and then run
|
||||
nzbget-client-commands in this terminal.
|
||||
|
||||
Post processing scripts
|
||||
-----------------------
|
||||
|
||||
After the download of nzb-file is completed nzbget can call post-processing
|
||||
scripts, defined in configuration file.
|
||||
|
||||
Example post-processing scripts are provided in directory "scripts".
|
||||
|
||||
To use the scripts copy them into your local directory and set options
|
||||
<ScriptDir>, <PostScript> and <ScriptOrder>.
|
||||
|
||||
For information on writing your own post-processing scripts please
|
||||
visit NZBGet web site.
|
||||
|
||||
Web-interface
|
||||
-------------
|
||||
|
||||
NZBGet has a built-in web-server providing the access to the program
|
||||
functions via web-interface.
|
||||
|
||||
To activate web-interface set the option "WebDir" to the path with
|
||||
web-interface files. If you install using "make install-conf" as
|
||||
described above the option is set automatically. If you install using
|
||||
binary files you should check if the option is set correctly.
|
||||
|
||||
To access web-interface from your web-browser use the server address
|
||||
and port defined in NZBGet configuration file in options "ControlIP" and
|
||||
"ControlPort". For example:
|
||||
|
||||
http://localhost:6789/
|
||||
|
||||
For login credentials type username and the password defined by
|
||||
options "ControlUsername" (default "nzbget") and "ControlPassword"
|
||||
(default "tegbzn6789").
|
||||
|
||||
In a case your browser forget credentials, to prevent typing them each
|
||||
time, there is a workaround - use URL in the form:
|
||||
|
||||
http://localhost:6789/username:password/
|
||||
|
||||
Please note, that in this case the password is saved in a bookmark or in
|
||||
browser history in plain text and is easy to find by persons having
|
||||
access to your computer.
|
||||
|
||||
=====================================
|
||||
8. Authors
|
||||
=====================================
|
||||
|
||||
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
|
||||
Up to version 0.2.3 it was been developed and maintained by Bo Cordes Petersen
|
||||
(placebodk@users.sourceforge.net). Beginning at version 0.3.0 the program is
|
||||
being developed by Andrei Prygounkov (hugbug@users.sourceforge.net).
|
||||
NZBGet is developed and maintained by Andrey Prygunkov
|
||||
(hugbug@users.sourceforge.net).
|
||||
|
||||
The original project was initially created by Sven Henkel
|
||||
(sidddy@users.sourceforge.net) in 2004 and later developed by
|
||||
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
|
||||
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
|
||||
Since then the program has been completely rewritten.
|
||||
|
||||
NZBGet distribution archive includes additional components
|
||||
written by other authors:
|
||||
|
||||
Par2:
|
||||
Peter Brian Clements <peterbclements@users.sourceforge.net>
|
||||
|
||||
Par2 library API:
|
||||
Francois Lesueur <flesueur@users.sourceforge.net>
|
||||
|
||||
Catch:
|
||||
Two Blue Cubes Ltd <https://github.com/philsquared/Catch>
|
||||
|
||||
jQuery:
|
||||
John Resig <http://jquery.com>
|
||||
The Dojo Foundation <http://sizzlejs.com>
|
||||
|
||||
Bootstrap:
|
||||
Twitter, Inc <http://twitter.github.com/bootstrap>
|
||||
|
||||
Raphaël:
|
||||
Dmitry Baranovskiy <http://raphaeljs.com>
|
||||
Sencha Labs <http://sencha.com>
|
||||
|
||||
Elycharts:
|
||||
Void Labs s.n.c. <http://void.it>
|
||||
|
||||
iconSweets:
|
||||
Yummygum <http://yummygum.com>
|
||||
|
||||
=====================================
|
||||
9. Copyright
|
||||
=====================================
|
||||
|
||||
NZBGet is distributed under GNU General Pubic License Version 2.
|
||||
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.
|
||||
|
||||
The complete content of license is provided in file COPYING.
|
||||
|
||||
Binary distribution for Windows contains code from the following libraries:
|
||||
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
libpar2 is distributed under GPL and libsigc++ under LGPL.
|
||||
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
|
||||
|
||||
=====================================
|
||||
10. Contact
|
||||
=====================================
|
||||
|
||||
If you encounter any problem, feel free to use tracker/forums on
|
||||
If you encounter any problem, feel free to use the forum
|
||||
|
||||
sourceforge.net/projects/nzbget
|
||||
nzbget.net/forum
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# NZBGet #
|
||||
[](http://www.gnu.org/licenses/)
|
||||
[](https://travis-ci.org/nzbget/nzbget)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:cpp)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:javascript)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/alerts)
|
||||
|
||||
[](https://github.com/nzbget/nzbget/releases)
|
||||
[](https://github.com/nzbget/nzbget/releases/latest)
|
||||
|
||||
NZBGet is a binary downloader, which downloads files from Usenet
|
||||
based on information given in nzb-files.
|
||||
|
||||
NZBGet is written in C++ and is known for its performance and efficiency.
|
||||
|
||||
NZBGet can run on almost any device - classic PC, NAS, media player, SAT-receiver, WLAN-router, etc.
|
||||
The download area provides precompiled binaries for Windows, macOS, Linux (compatible with
|
||||
many CPUs and platform variants), FreeBSD and Android. For other platforms
|
||||
the program can be compiled from sources.
|
||||
582
RemoteClient.cpp
582
RemoteClient.cpp
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
RemoteClient::RemoteClient()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_pNetAddress = NULL;
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
|
||||
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
|
||||
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
|
||||
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
|
||||
printf("sizeof(SNZBListResponseEntry)=%i\n", sizeof(SNZBListResponseEntry));
|
||||
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
|
||||
printf("sizeof(SNZBLogResponse)=%i\n", sizeof(SNZBLogResponse));
|
||||
printf("sizeof(SNZBLogResponseEntry)=%i\n", sizeof(SNZBLogResponseEntry));
|
||||
printf("sizeof(SNZBPauseUnpauseRequest)=%i\n", sizeof(SNZBPauseUnpauseRequest));
|
||||
printf("sizeof(SNZBSetDownloadRateRequest)=%i\n", sizeof(SNZBSetDownloadRateRequest));
|
||||
printf("sizeof(SNZBEditQueueRequest)=%i\n", sizeof(SNZBEditQueueRequest));
|
||||
printf("sizeof(SNZBDumpDebugRequest)=%i\n", sizeof(SNZBDumpDebugRequest));
|
||||
*/
|
||||
}
|
||||
|
||||
RemoteClient::~RemoteClient()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
if (m_pNetAddress)
|
||||
{
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::printf(char * msg,...)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
::vprintf(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::perror(char * msg)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
::perror(msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteClient::InitConnection()
|
||||
{
|
||||
// Create a connection to the server
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
|
||||
bool OK = m_pConnection->Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool RemoteClient::ReceiveBoolResponse()
|
||||
{
|
||||
printf("request sent\n");
|
||||
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
|
||||
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
|
||||
if (iResponseLen != sizeof(BoolResponse) ||
|
||||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
|
||||
char* buf = (char*)malloc(iTextLen);
|
||||
iResponseLen = m_pConnection->Recv(buf, iTextLen);
|
||||
if (iResponseLen != iTextLen)
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("server returned: %s\n", buf);
|
||||
free(buf);
|
||||
return ntohl(BoolResponse.m_bSuccess);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a message to the running nzbget process.
|
||||
*/
|
||||
bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
{
|
||||
// Read the file into the buffer
|
||||
char* szBuffer = NULL;
|
||||
int iLength = 0;
|
||||
if (!LoadFileIntoBuffer(szName, &szBuffer, &iLength))
|
||||
{
|
||||
printf("Could not load file %s\n", szName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = InitConnection();
|
||||
if (OK)
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
|
||||
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
strncpy(DownloadRequest.m_szFilename, szName, NZBREQUESTFILENAMESIZE - 1);
|
||||
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
|
||||
|
||||
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
OK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send(szBuffer, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (szBuffer)
|
||||
{
|
||||
free(szBuffer);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerList()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(true);
|
||||
ListRequest.m_bServerState = htonl(true);
|
||||
|
||||
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for download\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Queue List\n");
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
long long lFileSize = JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
|
||||
long long lRemainingSize = JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
|
||||
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (lRemainingSize < lFileSize)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - lRemainingSize * 100.0 / lFileSize), "%");
|
||||
}
|
||||
char szStatus[100];
|
||||
if (ntohl(pListAnswer->m_bPaused))
|
||||
{
|
||||
sprintf(szStatus, " (paused)");
|
||||
lPaused += lRemainingSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus[0] = '\0';
|
||||
lRemaining += lRemainingSize;
|
||||
}
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename, lFileSize / 1024.0 / 1024.0, szCompleted, szStatus);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Files: %i\n", ntohl(ListResponse.m_iNrTrailingEntries));
|
||||
if (lPaused > 0)
|
||||
{
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", lRemaining / 1024.0 / 1024.0, lPaused / 1024.0 / 1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Remaining size: %.2f MB\n", lRemaining / 1024.0 / 1024.0);
|
||||
}
|
||||
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
long long iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
float fAverageSpeed = ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0;
|
||||
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
|
||||
|
||||
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
|
||||
{
|
||||
printf("Speed limit: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadLimit) / 1024.0));
|
||||
}
|
||||
|
||||
int sec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
int h = sec / 3600;
|
||||
int m = (sec % 3600) / 60;
|
||||
int s = sec % 60;
|
||||
printf("Up time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
sec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
h = sec / 3600;
|
||||
m = (sec % 3600) / 60;
|
||||
s = sec % 60;
|
||||
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
printf("Downloaded: %.2f MB\n", iAllBytes / 1024.0 / 1024.0);
|
||||
|
||||
printf("Threads running: %i\n", ntohl(ListResponse.m_iThreadCount));
|
||||
|
||||
if (ntohl(ListResponse.m_iParJobCount) > 0)
|
||||
{
|
||||
printf("Par-jobs: %i\n", (int)ntohl(ListResponse.m_iParJobCount));
|
||||
}
|
||||
|
||||
if (ntohl(ListResponse.m_bServerStandBy))
|
||||
{
|
||||
printf("Server state: Stand-By\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerLog(int iLines)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(iLines);
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (LogResponse.m_iTrailingDataLength == 0)
|
||||
{
|
||||
printf("Log is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
switch (ntohl(pLogAnswer->m_iKind))
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", szText);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("[ERROR] %s\n", szText);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("[WARNING] %s\n", szText);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", szText);
|
||||
break;
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = htonl(bPause);
|
||||
|
||||
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBSetDownloadRateRequest SetDownloadRateRequest;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
|
||||
|
||||
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerDumpDebug()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBDumpDebugRequest DumpDebugInfo;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
|
||||
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder)
|
||||
{
|
||||
if (iIDCount <= 0 || pIDList == NULL)
|
||||
{
|
||||
printf("File(s) not specified\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
int iLength = sizeof(int32_t) * iIDCount;
|
||||
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = htonl(iAction);
|
||||
EditQueueRequest.m_iOffset = htonl((int)iOffset);
|
||||
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
|
||||
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iLength);
|
||||
|
||||
for (int i = 0; i < iIDCount; i++)
|
||||
{
|
||||
pIDs[i] = htonl(pIDList[i]);
|
||||
}
|
||||
|
||||
bool OK = false;
|
||||
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send((char*)pIDs, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerShutdown()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerVersion()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBVersionRequest VersionRequest;
|
||||
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTECLIENT_H
|
||||
#define REMOTECLIENT_H
|
||||
|
||||
#include "Options.h"
|
||||
#include "MessageBase.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
NetAddress* m_pNetAddress;
|
||||
bool m_bVerbose;
|
||||
|
||||
bool InitConnection();
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
bool ReceiveBoolResponse();
|
||||
void printf(char* msg, ...);
|
||||
void perror(char* msg);
|
||||
|
||||
public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
|
||||
bool RequestServerDownload(const char* szName, bool bAddFirst);
|
||||
bool RequestServerList();
|
||||
bool RequestServerPauseUnpause(bool bPause);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerVersion();
|
||||
};
|
||||
|
||||
#endif
|
||||
705
RemoteServer.cpp
705
RemoteServer.cpp
@@ -1,705 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequestNames[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List",
|
||||
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
|
||||
};
|
||||
|
||||
const unsigned int g_iMessageRequestSizes[] =
|
||||
{ 0,
|
||||
sizeof(SNZBDownloadRequest),
|
||||
sizeof(SNZBPauseUnpauseRequest),
|
||||
sizeof(SNZBListRequest),
|
||||
sizeof(SNZBSetDownloadRateRequest),
|
||||
sizeof(SNZBDumpDebugRequest),
|
||||
sizeof(SNZBEditQueueRequest),
|
||||
sizeof(SNZBLogRequest),
|
||||
sizeof(SNZBRequestBase)
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
|
||||
RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
bool bBind = true;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
}
|
||||
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = INVALID_SOCKET;
|
||||
if (bBind)
|
||||
{
|
||||
iSocket = m_pConnection->Accept();
|
||||
}
|
||||
if (!bBind || iSocket == INVALID_SOCKET)
|
||||
{
|
||||
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
usleep(500 * 1000);
|
||||
delete m_pConnection;
|
||||
m_pConnection = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
RequestProcessor* commandThread = new RequestProcessor();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->Start();
|
||||
}
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
debug("Exiting RemoteServer-loop");
|
||||
}
|
||||
|
||||
void RemoteServer::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
#ifdef WIN32
|
||||
m_pConnection->Disconnect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// RequestProcessor
|
||||
|
||||
void RequestProcessor::Run()
|
||||
{
|
||||
int iBytesReceived = 0;
|
||||
|
||||
// Read the first package which needs to be a request
|
||||
|
||||
iBytesReceived = recv(m_iSocket, (char*) & m_MessageBase, sizeof(m_MessageBase), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i, but password invalid", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Info - connection received
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* ip = inet_ntoa(PeerName.sin_addr);
|
||||
#else
|
||||
char ip[20];
|
||||
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
|
||||
#endif
|
||||
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], ip);
|
||||
}
|
||||
|
||||
Dispatch();
|
||||
|
||||
// Close the socket
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
void RequestProcessor::Dispatch()
|
||||
{
|
||||
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
|
||||
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestShutdown &&
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
|
||||
{
|
||||
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
|
||||
return;
|
||||
}
|
||||
|
||||
MessageCommand* command = NULL;
|
||||
|
||||
switch (ntohl(m_MessageBase.m_iType))
|
||||
{
|
||||
case eRemoteRequestDownload:
|
||||
{
|
||||
command = new DownloadCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestList:
|
||||
{
|
||||
command = new ListCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestLog:
|
||||
{
|
||||
command = new LogCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPauseUnpause:
|
||||
{
|
||||
command = new PauseUnpauseCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestEditQueue:
|
||||
{
|
||||
command = new EditQueueCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestSetDownloadRate:
|
||||
{
|
||||
command = new SetDownloadRateCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestDumpDebug:
|
||||
{
|
||||
command = new DumpDebugCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestShutdown:
|
||||
{
|
||||
command = new ShutdownCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestVersion:
|
||||
{
|
||||
command = new VersionCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
|
||||
break;
|
||||
}
|
||||
|
||||
if (command)
|
||||
{
|
||||
command->SetSocket(m_iSocket);
|
||||
command->SetMessageBase(&m_MessageBase);
|
||||
command->Execute();
|
||||
delete command;
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// Commands
|
||||
|
||||
void MessageCommand::SendBoolResponse(bool bSuccess, const char* szText)
|
||||
{
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
|
||||
BoolResponse.m_bSuccess = htonl(bSuccess);
|
||||
int iTextLen = strlen(szText) + 1;
|
||||
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
|
||||
send(m_iSocket, (char*)szText, iTextLen, 0);
|
||||
}
|
||||
|
||||
bool MessageCommand::ReceiveRequest(void* pBuffer, int iSize)
|
||||
{
|
||||
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
|
||||
iSize -= sizeof(SNZBRequestBase);
|
||||
if (iSize > 0)
|
||||
{
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
|
||||
if (iBytesReceived != iSize)
|
||||
{
|
||||
error("invalid request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PauseUnpauseCommand::Execute()
|
||||
{
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
|
||||
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
|
||||
}
|
||||
|
||||
void SetDownloadRateCommand::Execute()
|
||||
{
|
||||
SNZBSetDownloadRateRequest SetDownloadRequest;
|
||||
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0);
|
||||
SendBoolResponse(true, "Rate-Command completed successfully");
|
||||
}
|
||||
|
||||
void DumpDebugCommand::Execute()
|
||||
{
|
||||
SNZBDumpDebugRequest DumpDebugRequest;
|
||||
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendBoolResponse(true, "Debug-Command completed successfully");
|
||||
}
|
||||
|
||||
void ShutdownCommand::Execute()
|
||||
{
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, "Stopping server");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
void VersionCommand::Execute()
|
||||
{
|
||||
SNZBVersionRequest VersionRequest;
|
||||
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, VERSION);
|
||||
}
|
||||
|
||||
void DownloadCommand::Execute()
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(true, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(false, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void ListCommand::Execute()
|
||||
{
|
||||
SNZBListRequest ListRequest;
|
||||
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBListResponse ListResponse;
|
||||
memset(&ListResponse, 0, sizeof(ListResponse));
|
||||
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
|
||||
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (ntohl(ListRequest.m_bFileList))
|
||||
{
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
int NrEntries = pDownloadQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bufsize += strlen(pFileInfo->GetNZBFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetSubject()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetDestDir()) + 1;
|
||||
}
|
||||
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
unsigned int iSizeHi, iSizeLo;
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
|
||||
pListAnswer->m_iID = htonl(pFileInfo->GetID());
|
||||
SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
|
||||
SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
|
||||
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
|
||||
pListAnswer->m_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBFilename()) + 1);
|
||||
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
|
||||
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
|
||||
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetDestDir()) + 1);
|
||||
bufptr += sizeof(SNZBListResponseEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += ntohl(pListAnswer->m_iSubjectLen);
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetDestDir());
|
||||
bufptr += ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
ListResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
}
|
||||
|
||||
if (htonl(ListRequest.m_bServerState))
|
||||
{
|
||||
unsigned int iSizeHi, iSizeLo;
|
||||
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
|
||||
SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
|
||||
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
|
||||
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
|
||||
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
|
||||
ListResponse.m_iParJobCount = htonl(pParQueue->size());
|
||||
g_pPrePostProcessor->UnlockParQueue();
|
||||
|
||||
int iUpTimeSec, iDnTimeSec;
|
||||
long long iAllBytes;
|
||||
bool bStandBy;
|
||||
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
|
||||
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
|
||||
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
|
||||
ListResponse.m_bServerStandBy = htonl(bStandBy);
|
||||
SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
|
||||
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void LogCommand::Execute()
|
||||
{
|
||||
SNZBLogRequest LogRequest;
|
||||
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = ntohl(LogRequest.m_iLines);
|
||||
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
|
||||
int iStart = pMessages->size();
|
||||
if (iNrEntries > 0)
|
||||
{
|
||||
if (iNrEntries > (int)pMessages->size())
|
||||
{
|
||||
iNrEntries = pMessages->size();
|
||||
}
|
||||
iStart = pMessages->size() - iNrEntries;
|
||||
}
|
||||
if (iIDFrom > 0 && !pMessages->empty())
|
||||
{
|
||||
iStart = iIDFrom - pMessages->front()->GetID();
|
||||
if (iStart < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
}
|
||||
iNrEntries = pMessages->size() - iStart;
|
||||
if (iNrEntries < 0)
|
||||
{
|
||||
iNrEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate required buffer size
|
||||
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
bufsize += strlen(pMessage->GetText()) + 1;
|
||||
}
|
||||
|
||||
char* buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
|
||||
pLogAnswer->m_iID = htonl(pMessage->GetID());
|
||||
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
|
||||
pLogAnswer->m_tTime = htonl(pMessage->GetTime());
|
||||
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
|
||||
bufptr += sizeof(SNZBLogResponseEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogResponse LogResponse;
|
||||
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
|
||||
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
|
||||
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
|
||||
LogResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void EditQueueCommand::Execute()
|
||||
{
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
|
||||
int iAction = ntohl(EditQueueRequest.m_iAction);
|
||||
int iOffset = ntohl(EditQueueRequest.m_iOffset);
|
||||
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
|
||||
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
|
||||
|
||||
if (iNrEntries * sizeof(int32_t) != iBufLength)
|
||||
{
|
||||
error("Invalid struct size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iNrEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iBufLength);
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
char* pBufPtr = (char*)pIDs;
|
||||
int NeedBytes = iBufLength;
|
||||
int iResult = 0;
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
QueueEditor::IDList cIDList;
|
||||
cIDList.reserve(iNrEntries);
|
||||
for (int i = 0; i < iNrEntries; i++)
|
||||
{
|
||||
cIDList.push_back(ntohl(pIDs[i]));
|
||||
}
|
||||
|
||||
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
|
||||
|
||||
free(pIDs);
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
SendBoolResponse(true, "Edit-Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed");
|
||||
}
|
||||
}
|
||||
133
RemoteServer.h
133
RemoteServer.h
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTESERVER_H
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
RemoteServer();
|
||||
~RemoteServer();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
};
|
||||
|
||||
class RequestProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase m_MessageBase;
|
||||
|
||||
void Dispatch();
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
};
|
||||
|
||||
class MessageCommand
|
||||
{
|
||||
protected:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase* m_pMessageBase;
|
||||
|
||||
bool ReceiveRequest(void* pBuffer, int iSize);
|
||||
void SendBoolResponse(bool bSuccess, const char* szText);
|
||||
|
||||
public:
|
||||
virtual ~MessageCommand() {};
|
||||
virtual void Execute() = 0;
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; };
|
||||
};
|
||||
|
||||
class DownloadCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseUnpauseCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
251
ServerPool.cpp
251
ServerPool.cpp
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* The queue of mutexes that was used did not work for every
|
||||
* implementation of POSIX threads. Now a m_Semaphore is used.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_HOLD_SECODNS = 5;
|
||||
|
||||
ServerPool::PooledConnection::PooledConnection(NewsServer* server) : NNTPConnection(server)
|
||||
{
|
||||
m_bInUse = false;
|
||||
m_tFreeTime = 0;
|
||||
}
|
||||
|
||||
ServerPool::ServerPool()
|
||||
{
|
||||
debug("Creating ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_Connections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Semaphores.clear();
|
||||
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Servers.clear();
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Connections.clear();
|
||||
}
|
||||
|
||||
void ServerPool::AddServer(NewsServer* pNewsServer)
|
||||
{
|
||||
debug("Adding server to ServerPool");
|
||||
|
||||
m_Servers.push_back(pNewsServer);
|
||||
}
|
||||
|
||||
void ServerPool::InitConnections()
|
||||
{
|
||||
debug("Initializing connections in ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (m_iMaxLevel < pNewsServer->GetLevel())
|
||||
{
|
||||
m_iMaxLevel = pNewsServer->GetLevel();
|
||||
}
|
||||
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
|
||||
{
|
||||
PooledConnection* pConnection = new PooledConnection(pNewsServer);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_Connections.push_back(pConnection);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
int iMaxConnectionsForLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (iLevel == pNewsServer->GetLevel())
|
||||
{
|
||||
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
|
||||
m_Semaphores.push_back(sem);
|
||||
}
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
|
||||
{
|
||||
bool bWaitVal = false;
|
||||
if (bWait)
|
||||
{
|
||||
debug("Getting connection (wait)");
|
||||
bWaitVal = m_Semaphores[iLevel]->Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitVal = m_Semaphores[iLevel]->TryWait();
|
||||
}
|
||||
|
||||
if (!bWaitVal)
|
||||
{
|
||||
// signal received or wait timeout
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
PooledConnection* pConnection = NULL;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
|
||||
{
|
||||
if (bUsed)
|
||||
{
|
||||
debug("Freeing used connection");
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
((PooledConnection*)pConnection)->SetInUse(false);
|
||||
if (bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::CloseUnusedConnections()
|
||||
{
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
time_t curtime = ::time(NULL);
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection = *it;
|
||||
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
|
||||
{
|
||||
int tdiff = curtime - pConnection->GetFreeTime();
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
{
|
||||
debug(" ServerPool");
|
||||
debug(" ----------------");
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
debug(" Connections: %i", m_Connections.size());
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
|
||||
}
|
||||
/*
|
||||
debug(" Semaphores: %i", m_Semaphores.size());
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
sem_t* sem = m_Semaphores[iLevel];
|
||||
int iSemValue;
|
||||
sem_getvalue(sem, &iSemValue);
|
||||
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
|
||||
}
|
||||
*/
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
79
ServerPool.h
79
ServerPool.h
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SERVERPOOL_H
|
||||
#define SERVERPOOL_H
|
||||
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NewsServer.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class ServerPool
|
||||
{
|
||||
private:
|
||||
class PooledConnection : public NNTPConnection
|
||||
{
|
||||
private:
|
||||
bool m_bInUse;
|
||||
time_t m_tFreeTime;
|
||||
public:
|
||||
PooledConnection(NewsServer* server);
|
||||
bool GetInUse() { return m_bInUse; }
|
||||
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
|
||||
time_t GetFreeTime() { return m_tFreeTime; }
|
||||
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
typedef std::vector<PooledConnection*> Connections;
|
||||
|
||||
Servers m_Servers;
|
||||
Connections m_Connections;
|
||||
Semaphores m_Semaphores;
|
||||
int m_iMaxLevel;
|
||||
Mutex m_mutexConnections;
|
||||
int m_iTimeout;
|
||||
|
||||
public:
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
void AddServer(NewsServer *s);
|
||||
void InitConnections();
|
||||
int GetMaxLevel() { return m_iMaxLevel; }
|
||||
NNTPConnection* GetConnection(int iLevel, bool bWait);
|
||||
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
|
||||
void CloseUnusedConnections();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
302
Thread.cpp
302
Thread.cpp
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
int Thread::m_iThreadCount = 1; // take the main program thread into account
|
||||
Mutex Thread::m_mutexThread;
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
InitializeCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_init(&m_mutexObj, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~ Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
DeleteCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection(&m_mutexObj);
|
||||
#ifdef DEBUG
|
||||
// CriticalSections on Windows can be locked many times from the same thread,
|
||||
// but we do not want this and must treat such situations as errors and detect them.
|
||||
if (m_mutexObj.RecursionCount > 1)
|
||||
{
|
||||
error("Internal program error: inconsistent thread-lock detected");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
pthread_mutex_lock(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
LeaveCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Semaphore::Semaphore()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(int iValue)
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, iValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::~ Semaphore()
|
||||
{
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_semObj);
|
||||
#else
|
||||
sem_destroy(&m_semObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Semaphore::Post()
|
||||
{
|
||||
#ifdef WIN32
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
#else
|
||||
sem_post(&m_semObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::Wait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, INFINITE) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_wait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TryWait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_trywait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TimedWait(int iMSec)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, iMSec) == WAIT_OBJECT_0;
|
||||
#else
|
||||
struct timespec alarm;
|
||||
alarm.tv_sec = ::time(NULL) + iMSec / 1000;
|
||||
alarm.tv_nsec = (iMSec % 1000) * 1000;
|
||||
return sem_timedwait(&m_semObj, &alarm) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::IsLocked()
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool bCanLock = WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
if (bCanLock)
|
||||
{
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
}
|
||||
return !bCanLock;
|
||||
#else
|
||||
int iSemValue;
|
||||
sem_getvalue(&m_semObj, &iSemValue);
|
||||
return iSemValue <= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Thread::Init()
|
||||
{
|
||||
debug("Initializing global thread data");
|
||||
}
|
||||
|
||||
void Thread::Final()
|
||||
{
|
||||
debug("Finalizing global thread data");
|
||||
}
|
||||
|
||||
Thread::Thread()
|
||||
{
|
||||
debug("Creating Thread");
|
||||
|
||||
m_Thread = 0;
|
||||
m_bRunning = false;
|
||||
m_bStopped = false;
|
||||
m_bAutoDestroy = false;
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
debug("Destroying Thread");
|
||||
}
|
||||
|
||||
void Thread::Start()
|
||||
{
|
||||
debug("Starting Thread");
|
||||
|
||||
m_bRunning = true;
|
||||
|
||||
// NOTE: we must garantee, that in a time we setting m_bRunning
|
||||
// to value returned from pthread_create, the thread-object still exists.
|
||||
// This is not obviously!
|
||||
// pthread_create could wait long enough before returning result
|
||||
// back to allow the started thread to complete it job
|
||||
// and terminate.
|
||||
// We lock mutex m_mutexThread on calling pthread_create; the started thread
|
||||
// then also try to lock the mutex (see thread_handler) and therefore
|
||||
// must wait until we unlock it
|
||||
m_mutexThread.Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_Thread != NULL;
|
||||
#else
|
||||
pthread_attr_t m_Attr;
|
||||
pthread_attr_init(&m_Attr);
|
||||
pthread_attr_setdetachstate(&m_Attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setinheritsched(&m_Attr , PTHREAD_INHERIT_SCHED);
|
||||
m_bRunning = !pthread_create(&m_Thread, &m_Attr, Thread::thread_handler, (void *) this);
|
||||
pthread_attr_destroy(&m_Attr);
|
||||
#endif
|
||||
|
||||
m_mutexThread.Unlock();
|
||||
}
|
||||
|
||||
void Thread::Stop()
|
||||
{
|
||||
debug("Stopping Thread");
|
||||
|
||||
m_bStopped = true;
|
||||
}
|
||||
|
||||
bool Thread::Kill()
|
||||
{
|
||||
debug("Killing Thread");
|
||||
|
||||
m_mutexThread.Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
bool terminated = TerminateThread(m_Thread, 0) != 0;
|
||||
#else
|
||||
bool terminated = pthread_cancel(m_Thread) == 0;
|
||||
#endif
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
m_iThreadCount--;
|
||||
}
|
||||
m_mutexThread.Unlock();
|
||||
return terminated;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void __cdecl Thread::thread_handler(void* pObject)
|
||||
#else
|
||||
void* Thread::thread_handler(void* pObject)
|
||||
#endif
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_iThreadCount++;
|
||||
m_mutexThread.Unlock();
|
||||
|
||||
debug("Entering Thread-func");
|
||||
|
||||
Thread* pThread = (Thread*)pObject;
|
||||
|
||||
pThread->Run();
|
||||
|
||||
debug("Thread-func exited");
|
||||
|
||||
pThread->m_bRunning = false;
|
||||
|
||||
if (pThread->m_bAutoDestroy)
|
||||
{
|
||||
debug("Autodestroying Thread-object");
|
||||
delete pThread;
|
||||
}
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_iThreadCount--;
|
||||
m_mutexThread.Unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
int iThreadCount = m_iThreadCount;
|
||||
m_mutexThread.Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
|
||||
115
Thread.h
115
Thread.h
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
class Mutex
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
CRITICAL_SECTION m_mutexObj;
|
||||
#else
|
||||
pthread_mutex_t m_mutexObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
HANDLE m_semObj;
|
||||
#else
|
||||
sem_t m_semObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Semaphore();
|
||||
Semaphore(int iValue);
|
||||
~Semaphore();
|
||||
void Post();
|
||||
bool Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(int iMSec);
|
||||
bool IsLocked();
|
||||
};
|
||||
|
||||
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexThread;
|
||||
static int m_iThreadCount;
|
||||
#ifdef WIN32
|
||||
HANDLE m_Thread;
|
||||
#else
|
||||
pthread_t m_Thread;
|
||||
#endif
|
||||
bool m_bRunning;
|
||||
bool m_bStopped;
|
||||
bool m_bAutoDestroy;
|
||||
|
||||
#ifdef WIN32
|
||||
static void __cdecl thread_handler(void* pObject);
|
||||
#else
|
||||
static void *thread_handler(void* pObject);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
bool Kill();
|
||||
|
||||
bool IsStopped() { return m_bStopped; };
|
||||
bool IsRunning() const { return m_bRunning; }
|
||||
void SetRunning(bool bOnOff) { m_bRunning = bOnOff; }
|
||||
bool GetAutoDestroy() { return m_bAutoDestroy; }
|
||||
void SetAutoDestroy(bool bAutoDestroy) { m_bAutoDestroy = bAutoDestroy; }
|
||||
static int GetThreadCount();
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
|
||||
protected:
|
||||
virtual void Run() {}; // Virtual function - override in derivatives
|
||||
};
|
||||
|
||||
#endif
|
||||
416
Util.cpp
416
Util.cpp
@@ -1,416 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
|
||||
char* BaseFileName(const char* filename)
|
||||
{
|
||||
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
|
||||
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
|
||||
if (p1)
|
||||
{
|
||||
if ((p && p < p1) || !p)
|
||||
{
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (char*)filename;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// getopt for WIN32:
|
||||
// from http://www.codeproject.com/cpp/xgetopt.asp
|
||||
// Original Author: Hans Dietrich (hdietrich2@hotmail.com)
|
||||
// Released to public domain from author (thanks)
|
||||
// Slightly modified by Andrei Prygounkov
|
||||
|
||||
char *optarg; // global argument pointer
|
||||
int optind = 0; // global argv index
|
||||
|
||||
int getopt(int argc, char *argv[], char *optstring)
|
||||
{
|
||||
static char *next = NULL;
|
||||
if (optind == 0)
|
||||
next = NULL;
|
||||
|
||||
optarg = NULL;
|
||||
|
||||
if (next == NULL || *next == '\0')
|
||||
{
|
||||
if (optind == 0)
|
||||
optind++;
|
||||
|
||||
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
|
||||
{
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[optind], "--") == 0)
|
||||
{
|
||||
optind++;
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
next = argv[optind];
|
||||
next++; // skip past -
|
||||
optind++;
|
||||
}
|
||||
|
||||
char c = *next++;
|
||||
char *cp = strchr(optstring, c);
|
||||
|
||||
if (cp == NULL || c == ':')
|
||||
{
|
||||
fprintf(stderr, "Invalid option %c", c);
|
||||
return '?';
|
||||
}
|
||||
|
||||
cp++;
|
||||
if (*cp == ':')
|
||||
{
|
||||
if (*next != '\0')
|
||||
{
|
||||
optarg = next;
|
||||
next = NULL;
|
||||
}
|
||||
else if (optind < argc)
|
||||
{
|
||||
optarg = argv[optind];
|
||||
optind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Option %c needs an argument", c);
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
char szMask[MAX_PATH + 1];
|
||||
int len = strlen(szPath);
|
||||
if (szPath[len] == '\\' || szPath[len] == '/')
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s*.*", szPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
|
||||
}
|
||||
szMask[MAX_PATH] = '\0';
|
||||
m_hFile = _findfirst(szMask, &m_FindData);
|
||||
m_bFirst = true;
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_hFile != -1L)
|
||||
{
|
||||
_findclose(m_hFile);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
bool bOK = false;
|
||||
if (m_bFirst)
|
||||
{
|
||||
bOK = m_hFile != -1L;
|
||||
m_bFirst = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bOK = _findnext(m_hFile, &m_FindData) == 0;
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
return m_FindData.name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
m_pDir = opendir(szPath);
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
closedir(m_pDir);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
m_pFindData = readdir(m_pDir);
|
||||
if (m_pFindData)
|
||||
{
|
||||
return m_pFindData->d_name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void NormalizePathSeparators(char* szPath)
|
||||
{
|
||||
for (char* p = szPath; *p; p++)
|
||||
{
|
||||
if (*p == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
*p = PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ForceDirectories(const char* szPath)
|
||||
{
|
||||
char* szNormPath = strdup(szPath);
|
||||
NormalizePathSeparators(szNormPath);
|
||||
int iLen = strlen(szNormPath);
|
||||
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
|
||||
#ifdef WIN32
|
||||
&& iLen > 3
|
||||
#endif
|
||||
)
|
||||
{
|
||||
szNormPath[iLen-1] = '\0';
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
if (!bOK
|
||||
#ifdef WIN32
|
||||
&& strlen(szNormPath) > 2
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char* szParentPath = strdup(szNormPath);
|
||||
bOK = true;
|
||||
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
|
||||
if (p)
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (p - szParentPath == 2 && szParentPath[1] == ':' && strlen(szParentPath) > 2)
|
||||
{
|
||||
szParentPath[3] = '\0';
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
if (strlen(szParentPath) != strlen(szPath))
|
||||
{
|
||||
bOK = ForceDirectories(szParentPath);
|
||||
}
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
mkdir(szNormPath, S_DIRMODE);
|
||||
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
}
|
||||
free(szParentPath);
|
||||
}
|
||||
free(szNormPath);
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
{
|
||||
FILE* pFile = fopen(szFileName, "r");
|
||||
if (!pFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
*pBuffer = (char*) malloc(iSize + 1);
|
||||
if (!*pBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(*pBuffer, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
(*pBuffer)[iSize] = 0;
|
||||
|
||||
*pBufferLength = iSize + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetFileSize(const char* szFilename, int iSize)
|
||||
{
|
||||
bool bOK = false;
|
||||
#ifdef WIN32
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
bOK = _chsize_s(pFile->_file, iSize) == 0;
|
||||
fclose(pFile);
|
||||
}
|
||||
#else
|
||||
// create file
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
// there no reliable function to expand file on POSIX, so we must try different approaches,
|
||||
// starting with the fastest one and hoping it will work
|
||||
// 1) set file size using function "truncate" (it is fast, if works)
|
||||
truncate(szFilename, iSize);
|
||||
// check if it worked
|
||||
pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fseek(pFile, 0, SEEK_END);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
if (!bOK)
|
||||
{
|
||||
// 2) truncate did not work, expanding the file by writing in it (it is slow)
|
||||
fclose(pFile);
|
||||
truncate(szFilename, 0);
|
||||
pFile = fopen(szFilename, "a");
|
||||
char c = '0';
|
||||
fwrite(&c, 1, iSize, pFile);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
#endif
|
||||
return bOK;
|
||||
}
|
||||
|
||||
//replace bad chars in filename
|
||||
void MakeValidFilename(char* szFilename, char cReplaceChar)
|
||||
{
|
||||
char* p = szFilename;
|
||||
while (*p)
|
||||
{
|
||||
if (strchr("\\/:*?\"><'\n\r\t", *p))
|
||||
{
|
||||
*p = cReplaceChar;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
// remove trailing points. they are not allowed in directory names on windows,
|
||||
// but we remove them on posix also, in a case the directory is accessed from windows via samba.
|
||||
for (int iLen = strlen(szFilename); iLen > 0 && szFilename[iLen - 1] == '.'; iLen--)
|
||||
{
|
||||
szFilename[iLen - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
long long JoinInt64(unsigned int Hi, unsigned int Lo)
|
||||
{
|
||||
return (((long long)Hi) << 32) + Lo;
|
||||
}
|
||||
|
||||
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo)
|
||||
{
|
||||
*Hi = (unsigned int)(Int64 >> 32);
|
||||
*Lo = (unsigned int)Int64;
|
||||
}
|
||||
|
||||
float EqualTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t1->time == t2->time && t1->millitm == t2->millitm;
|
||||
#else
|
||||
return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EmptyTime(_timeval* t)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t->time == 0 && t->millitm == 0;
|
||||
#else
|
||||
return t->tv_sec == 0 && t->tv_usec == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float DiffTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return ((t1->time - t2->time) + (t1->millitm - t2->millitm) / 1000.0);
|
||||
#else
|
||||
return (float)((t1->tv_sec - t2->tv_sec) + (t1->tv_usec - t2->tv_usec) / 1000000.0);
|
||||
#endif
|
||||
}
|
||||
75
Util.h
75
Util.h
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
extern int optind, opterr;
|
||||
extern char *optarg;
|
||||
int getopt(int argc, char *argv[], char *optstring);
|
||||
#endif
|
||||
|
||||
class DirBrowser
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
struct _finddata_t m_FindData;
|
||||
intptr_t m_hFile;
|
||||
bool m_bFirst;
|
||||
#else
|
||||
DIR* m_pDir;
|
||||
struct dirent* m_pFindData;
|
||||
#endif
|
||||
|
||||
public:
|
||||
DirBrowser(const char* szPath);
|
||||
~DirBrowser();
|
||||
const char* Next();
|
||||
};
|
||||
|
||||
char* BaseFileName(const char* filename);
|
||||
void NormalizePathSeparators(char* szPath);
|
||||
bool ForceDirectories(const char* szPath);
|
||||
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
bool SetFileSize(const char* szFilename, int iSize);
|
||||
void MakeValidFilename(char* szFilename, char cReplaceChar);
|
||||
|
||||
long long JoinInt64(unsigned int Hi, unsigned int Lo);
|
||||
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo);
|
||||
|
||||
float EqualTime(_timeval* t1, _timeval* t2);
|
||||
bool EmptyTime(_timeval* t);
|
||||
float DiffTime(_timeval* t1, _timeval* t2);
|
||||
|
||||
#endif
|
||||
1138
aclocal.m4
vendored
1138
aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
108
config.h.in
108
config.h.in
@@ -6,16 +6,28 @@
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable smart par-verification and restoration */
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to not use libxml2, only for development purposes */
|
||||
#undef DISABLE_LIBXML2
|
||||
|
||||
/* Define to 1 to disable par-verification and repair */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
/* Define to 1 to include support for uulib */
|
||||
#undef ENABLE_UULIB
|
||||
/* Define to 1 to not use TLS/SSL */
|
||||
#undef DISABLE_TLS
|
||||
|
||||
/* Define to 1 to enable unit and integration tests */
|
||||
#undef ENABLE_TESTS
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
|
||||
/* Define to 1 to create stacktrace on segmentation faults */
|
||||
#undef HAVE_BACKTRACE
|
||||
|
||||
/* Define to 1 if ctime_r takes 2 arguments */
|
||||
#undef HAVE_CTIME_R_2
|
||||
|
||||
@@ -25,18 +37,54 @@
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* define if the compiler supports basic C++14 syntax */
|
||||
#undef HAVE_CXX14
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#undef HAVE_ENDIAN_H
|
||||
|
||||
/* Define to 1 if fdatasync is supported */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#undef HAVE_FSEEKO
|
||||
|
||||
/* Define to 1 if F_FULLFSYNC is supported */
|
||||
#undef HAVE_FULLFSYNC
|
||||
|
||||
/* Define to 1 if getaddrinfo is supported */
|
||||
#undef HAVE_GETADDRINFO
|
||||
|
||||
/* Define to 1 if gethostbyname_r is supported */
|
||||
#undef HAVE_GETHOSTBYNAME_R
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 3 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_3
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 5 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_5
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 6 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_6
|
||||
|
||||
/* Define to 1 if you have the `getopt' function. */
|
||||
#undef HAVE_GETOPT
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define to 1 if getopt_long is supported */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
|
||||
#undef HAVE_LIBGNUTLS
|
||||
|
||||
/* Define to 1 if lockf is supported */
|
||||
#undef HAVE_LOCKF
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -46,8 +94,20 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 to use pragma pack directive in MessageBase.h */
|
||||
#undef HAVE_PRAGMA_PACK
|
||||
/* Define to 1 to use Nettle library for decryption. */
|
||||
#undef HAVE_NETTLE
|
||||
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if pthread_cancel is supported */
|
||||
#undef HAVE_PTHREAD_CANCEL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
|
||||
#undef HAVE_SC_NPROCESSORS_ONLN
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
@@ -55,12 +115,18 @@
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `stricmp' function. */
|
||||
#undef HAVE_STRICMP
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/prctl.h> header file. */
|
||||
#undef HAVE_SYS_PRCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
@@ -73,6 +139,12 @@
|
||||
/* Define to 1 if variadic macros are supported */
|
||||
#undef HAVE_VARIADIC_MACROS
|
||||
|
||||
/* Define to 1 if OpenSSL supports function "X509_check_host". */
|
||||
#undef HAVE_X509_CHECK_HOST
|
||||
|
||||
/* Define to 1 to exclude debug-code */
|
||||
#undef NDEBUG
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
@@ -88,11 +160,37 @@
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 to install an empty signal handler for SIGCHLD */
|
||||
#undef SIGCHLD_HANDLER
|
||||
|
||||
/* Determine what socket length (socklen_t) data type is */
|
||||
#undef SOCKLEN_T
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
|
||||
#undef _LARGEFILE_SOURCE
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
798
configure.ac
798
configure.ac
@@ -1,73 +1,72 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2021 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.3.1, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.3.1)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_PREREQ(2.65)
|
||||
AC_INIT(nzbget, 21.2-testing, hugbug@users.sourceforge.net)
|
||||
AC_CONFIG_AUX_DIR(posix)
|
||||
AC_CANONICAL_TARGET
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AC_CONFIG_SRCDIR([daemon/main/nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
|
||||
dnl Architecture check
|
||||
AC_CANONICAL_HOST
|
||||
case "$host" in
|
||||
*86-*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CFLAGS1="${CFLAGS} -m486"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-freebsd*)
|
||||
LIBPREF1="/usr/local"
|
||||
;;
|
||||
*-solaris*)
|
||||
LIBPREF1="/usr"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
if test "$LIBPREF" = ""; then
|
||||
LIBPREF="$LIBPREF1"
|
||||
fi
|
||||
if test "$CFLAGS" = ""; then
|
||||
CFLAGS="$CFLAGS1"
|
||||
fi
|
||||
if test "$CPPFLAGS" = ""; then
|
||||
CPPFLAGS="$CPPFLAGS1"
|
||||
fi
|
||||
m4_include([posix/ax_cxx_compile_stdcxx.m4])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Check for programs.
|
||||
dnl
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PATH_PROG(FALSE, false, /usr/bin/false)
|
||||
AC_PATH_PROG(TRUE, true, /usr/bin/true)
|
||||
AC_PATH_PROG(RM, rm, $FALSE)
|
||||
AC_PATH_PROG(LN, ln, $FALSE)
|
||||
AC_PATH_PROG(TAR, tar, $FALSE)
|
||||
AC_PATH_PROG(AR, ar, $FALSE)
|
||||
AC_PATH_PROG(MAKE, make, $FALSE)
|
||||
AC_PATH_PROG(CXXCPP, cpp, $FALSE)
|
||||
AC_PATH_PROG(MV, mv, $FALSE)
|
||||
AC_PATH_PROG(MKDIR, mkdir, $FALSE)
|
||||
AC_PATH_PROG(CP, cp, $FALSE)
|
||||
AC_PROG_INSTALL
|
||||
|
||||
|
||||
dnl
|
||||
dnl Do all tests with c++ compiler.
|
||||
dnl
|
||||
AC_LANG(C++)
|
||||
|
||||
dnl
|
||||
dnl Determine compiler switches to support C++14 standard.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to test compiler features)
|
||||
AC_ARG_ENABLE(cpp-check,
|
||||
[AS_HELP_STRING([--disable-cpp-check], [disable check for C++14 compiler features])],
|
||||
[ ENABLECPPCHECK=$enableval ],
|
||||
[ ENABLECPPCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLECPPCHECK)
|
||||
if test "$ENABLECPPCHECK" = "yes"; then
|
||||
AX_CXX_COMPILE_STDCXX(14,,[optional])
|
||||
if test "$HAVE_CXX14" != "1"; then
|
||||
AC_MSG_ERROR("A compiler with support for C++14 language features is required. For details visit http://nzbget.net/cpp14")
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl Checks for header files.
|
||||
dnl
|
||||
dnl AC_CHECK_HEADERS(stdarg.h time.h stdlib.h stdio.h unistd.h errno.h string.h sys/stat.h sys/time.h)
|
||||
dnl AC_CHECK_HEADERS(libgen.h pwd.h getopt.h dirent.h fcntl.h pthread.h semaphore.h)
|
||||
dnl AC_CHECK_HEADERS(sys/socket.h sys/types.h netinet/in.h arpa/inet.h netdb.h)
|
||||
AC_CHECK_HEADERS(sys/prctl.h regex.h endian.h getopt.h)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Check for libs
|
||||
@@ -75,16 +74,41 @@ dnl
|
||||
AC_SEARCH_LIBS([pthread_create], [pthread])
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([inet_addr], [nsl])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
AC_SEARCH_LIBS([hstrerror], [resolv])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Android NDK restrictions
|
||||
dnl
|
||||
AC_CHECK_FUNC(lockf,
|
||||
[AC_CHECK_DECL(lockf,
|
||||
[AC_DEFINE([HAVE_LOCKF], 1, [Define to 1 if lockf is supported])],,
|
||||
[#include <unistd.h>])])
|
||||
AC_CHECK_FUNC(pthread_cancel,
|
||||
[AC_CHECK_DECL(pthread_cancel,
|
||||
[AC_DEFINE([HAVE_PTHREAD_CANCEL], 1, [Define to 1 if pthread_cancel is supported])],,
|
||||
[#include <pthread.h>])])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Getopt
|
||||
dnl
|
||||
AC_CHECK_FUNC(getopt_long,
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
|
||||
|
||||
|
||||
dnl
|
||||
dnl fsync
|
||||
dnl
|
||||
AC_CHECK_FUNC(fdatasync,
|
||||
[AC_DEFINE([HAVE_FDATASYNC], 1, [Define to 1 if fdatasync is supported])],)
|
||||
AC_CHECK_DECL(F_FULLFSYNC,
|
||||
[AC_DEFINE([HAVE_FULLFSYNC], 1, [Define to 1 if F_FULLFSYNC is supported])],,[#include <fcntl.h>])
|
||||
|
||||
dnl
|
||||
dnl use 64-Bits for file sizes
|
||||
dnl
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
|
||||
dnl
|
||||
@@ -95,6 +119,7 @@ AC_TRY_COMPILE(
|
||||
[#include <time.h>],
|
||||
[ time_t clock; char buf[26]; ctime_r(&clock, buf, 26); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_CTIME_R_3], 1, [Define to 1 if ctime_r takes 3 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
@@ -108,93 +133,141 @@ AC_TRY_COMPILE(
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function ctime_r not found.")
|
||||
AC_MSG_ERROR("function ctime_r not found")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r
|
||||
dnl check getaddrinfo
|
||||
dnl
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
AC_CHECK_FUNC(getaddrinfo,
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
|
||||
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r, if getaddrinfo is not available
|
||||
dnl
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function gethostbyname_r not found.")
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
|
||||
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl Determine what socket length (socklen_t) data type is
|
||||
dnl
|
||||
AC_MSG_CHECKING(for __FUNCTION__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_MSG_CHECKING(for __func__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
fi
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
AC_MSG_CHECKING([for type of socket length (socklen_t)])
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL)],[
|
||||
AC_MSG_RESULT(socklen_t)
|
||||
SOCKLEN_T=socklen_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
|
||||
AC_MSG_RESULT(size_t)
|
||||
SOCKLEN_T=size_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
|
||||
AC_MSG_RESULT(int)
|
||||
SOCKLEN_T=int],[
|
||||
AC_MSG_WARN(could not determine)
|
||||
SOCKLEN_T=int])])])
|
||||
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for pragma pack
|
||||
dnl check cpu cores via sysconf
|
||||
dnl
|
||||
AC_MSG_CHECKING(for pragma pack)
|
||||
AC_TRY_COMPILE([#pragma pack(1)
|
||||
#pragma pack()],,
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_PRAGMA_PACK],1,[Define to 1 to use pragma pack directive in MessageBase.h]),
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_MSG_CHECKING(for cpu cores via sysconf)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <unistd.h>],
|
||||
[ int a = _SC_NPROCESSORS_ONLN; ],
|
||||
FOUND="yes"
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_SC_NPROCESSORS_ONLN], 1, [Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h]),
|
||||
FOUND="no")
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/libxml2"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[ --with-libxml2-includes=DIR libxml2 include directory],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files were not found."))
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[ --with-libxml2-libraries=DIR libxml2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found in $LIBVAL."))
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use libxml2)
|
||||
AC_ARG_ENABLE(libxml2,
|
||||
[AS_HELP_STRING([--disable-libxml2], [do not use libxml2 (removes dependency from libxml2-library, only for development purposes)])],
|
||||
[USELIBXML2=$enableval],
|
||||
[USELIBXML2=yes] )
|
||||
AC_MSG_RESULT($USELIBXML2)
|
||||
if test "$USELIBXML2" = "yes"; then
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libxml2, libxml-2.0,
|
||||
[LIBS="${LIBS} $libxml2_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_LIBXML2],1,[Define to 1 to not use libxml2, only for development purposes])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
@@ -202,23 +275,28 @@ dnl Use curses. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use curses)
|
||||
AC_ARG_ENABLE(curses,
|
||||
[ --disable-curses do not use curses (removes dependency from curses-library and makes executable smaller)],
|
||||
[ USECURSES=$enableval ],
|
||||
[ USECURSES=yes] )
|
||||
[AS_HELP_STRING([--disable-curses], [do not use curses (removes dependency from curses-library)])],
|
||||
[USECURSES=$enableval],
|
||||
[USECURSES=yes] )
|
||||
AC_MSG_RESULT($USECURSES)
|
||||
if test "$USECURSES" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libcurses_includes,
|
||||
[ --with-libcurses-includes=DIR libcurses include directory],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libcurses_libraries,
|
||||
[ --with-libcurses-libraries=DIR libcurses library directory],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(ncurses, ncurses,
|
||||
[LIBS="${LIBS} $ncurses_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $ncurses_CFLAGS"],
|
||||
AC_MSG_ERROR("ncurses library not found"))
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(ncurses.h,
|
||||
FOUND=yes
|
||||
AC_DEFINE([HAVE_NCURSES_H],1,[Define to 1 if you have the <ncurses.h> header file.]),
|
||||
@@ -236,114 +314,284 @@ if test "$USECURSES" = "yes"; then
|
||||
FOUND=no)
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h).])
|
||||
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h)])
|
||||
fi
|
||||
AC_SEARCH_LIBS([refresh], [ncurses curses],,
|
||||
AC_ERROR([Couldn't find curses library]))
|
||||
AC_SEARCH_LIBS([nodelay], [ncurses curses tinfo],,
|
||||
AC_ERROR([Couldn't find curses library]))
|
||||
else
|
||||
AC_DEFINE([DISABLE_CURSES],1,[Define to 1 to not use curses])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use uulib. Deafult: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use uulib for decoding and joining)
|
||||
AC_ARG_ENABLE(uulib,
|
||||
[ --enable-uulib use uulib for decoding and joining],
|
||||
[ ENABLEUULIB=$enableval ],
|
||||
[ ENABLEUULIB=no] )
|
||||
AC_MSG_RESULT($ENABLEUULIB)
|
||||
if test "$ENABLEUULIB" = "yes"; then
|
||||
AC_DEFINE([ENABLE_UULIB],1,[Define to 1 to include support for uulib])
|
||||
|
||||
dnl
|
||||
dnl checks for uulib includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(uulib_includes,
|
||||
[ --with-uulib-includes=DIR uulib include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER($INCVAL/uudeview.h,,
|
||||
AC_MSG_ERROR("uulib header files were not found in $INCVAL."))
|
||||
|
||||
AC_ARG_WITH(uulib_libraries,
|
||||
[ --with-uulib-libraries=DIR uulib library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_SEARCH_LIBS([UUInitialize], [uu],,
|
||||
AC_MSG_ERROR("uulib library not found in $LIBVAL."))
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use lib2par for par-checking. Deafult: no
|
||||
dnl Use par-checking. Deafult: yes.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include code for par-checking)
|
||||
AC_ARG_ENABLE(parcheck,
|
||||
[ --enable-parcheck include code for par-checking],
|
||||
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support])],
|
||||
[ ENABLEPARCHECK=$enableval ],
|
||||
[ ENABLEPARCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
if test "$ENABLEPARCHECK" = "yes"; then
|
||||
|
||||
dnl PAR2 checks.
|
||||
dnl
|
||||
dnl checks for libsigc++ includes and libraries (required for libpar2).
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/sigc++-2.0"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libsigc_includes,
|
||||
[ --with-libsigc-includes=DIR libsigc++-2.0 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
AC_ARG_WITH(libsigc_libraries,
|
||||
[ --with-libsigc-libraries=DIR libsigc++-2.0 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL} -I${LIBVAL}/sigc++-2.0/include"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(sigc++/type_traits.h,,
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
dnl
|
||||
dnl checks for libpar2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libpar2_includes,
|
||||
[ --with-libpar2-includes=DIR libpar2 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(libpar2/libpar2.h,,
|
||||
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
AC_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
dnl Q: How to check for c++-class in library?
|
||||
LIBS="${LIBS} -lpar2"
|
||||
dnl AC_CHECK_LIB(par2, GenerateCRC32Table, , FOUND=no)
|
||||
dnl if test "$FOUND" = "no"; then
|
||||
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
|
||||
dnl fi
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_FUNC_FSEEKO
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS([stricmp])
|
||||
AC_CHECK_FUNCS([getopt])
|
||||
AM_CONDITIONAL(WITH_PAR2, true)
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable par-verification and repair])
|
||||
AM_CONDITIONAL(WITH_PAR2, false)
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use TLS/SSL. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use TLS/SSL)
|
||||
AC_ARG_ENABLE(tls,
|
||||
[AS_HELP_STRING([--disable-tls], [do not use TLS/SSL (removes dependency from TLS/SSL-libraries)])],
|
||||
[ USETLS=$enableval ],
|
||||
[ USETLS=yes] )
|
||||
AC_MSG_RESULT($USETLS)
|
||||
if test "$USETLS" = "yes"; then
|
||||
AC_ARG_WITH(tlslib,
|
||||
[AS_HELP_STRING([--with-tlslib=(OpenSSL, GnuTLS)], [TLS/SSL library to use])],
|
||||
[TLSLIB="$withval"])
|
||||
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
|
||||
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
|
||||
AC_ARG_WITH(openssl_includes,
|
||||
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(openssl_libraries,
|
||||
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([openssl], [openssl],
|
||||
[LIBS="${LIBS} $openssl_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(openssl/ssl.h,
|
||||
FOUND=yes
|
||||
TLSHEADERS=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
|
||||
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_SEARCH_LIBS([ASN1_OBJECT_free], [crypto],
|
||||
AC_SEARCH_LIBS([SSL_CTX_new], [ssl],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
|
||||
AC_MSG_ERROR([Couldn't find OpenSSL library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="OpenSSL"
|
||||
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support and decryption.])
|
||||
AC_SEARCH_LIBS([X509_check_host], [crypto],
|
||||
AC_DEFINE([HAVE_X509_CHECK_HOST],1,[Define to 1 if OpenSSL supports function "X509_check_host".]))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "GnuTLS" -o "$TLSLIB" = ""; then
|
||||
AC_ARG_WITH(libgnutls_includes,
|
||||
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libgnutls_libraries,
|
||||
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([gnutls], [gnutls],
|
||||
[LIBS="${LIBS} $gnutls_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $gnutls_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(gnutls/gnutls.h,
|
||||
FOUND=yes
|
||||
TLSHEADERS=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS headers (gnutls.h)])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "yes"; then
|
||||
dnl gcrypt is optional
|
||||
AC_MSG_CHECKING([whether gcrypt is needed])
|
||||
AC_TRY_COMPILE(
|
||||
[#include <gnutls/gnutls.h>]
|
||||
[#if GNUTLS_VERSION_NUMBER <= 0x020b00]
|
||||
[compile error]
|
||||
[#endif],
|
||||
[int a;],
|
||||
AC_MSG_RESULT([no])
|
||||
GCRYPT=no,
|
||||
AC_MSG_RESULT([yes])
|
||||
GCRYPT=yes)
|
||||
if test "$GCRYPT" = "yes"; then
|
||||
AC_CHECK_HEADER([gcrypt.h],
|
||||
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=yes)
|
||||
fi
|
||||
fi
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="GnuTLS"
|
||||
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "GnuTLS"; then
|
||||
AC_ARG_WITH(libnettle_includes,
|
||||
[AS_HELP_STRING([--with-libnettle-includes=DIR], [Nettle include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libnettle_libraries,
|
||||
[AS_HELP_STRING([--with-libnettle-libraries=DIR], [Nettle library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([nettle], [nettle],
|
||||
[LIBS="${LIBS} $nettle_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $nettle_CFLAGS"])
|
||||
fi
|
||||
AC_CHECK_HEADER(nettle/sha.h,
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find Nettle headers (sha.h)])
|
||||
fi
|
||||
AC_SEARCH_LIBS([nettle_pbkdf2_hmac_sha256], [nettle],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find Nettle library, required when using GnuTLS])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_NETTLE],1,[Define to 1 to use Nettle library for decryption.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = ""; then
|
||||
if test "$TLSHEADERS" = ""; then
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS headers (ssl.h or gnutls.h)])
|
||||
else
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS library])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
AC_DEFINE([DISABLE_TLS],1,[Define to 1 to not use TLS/SSL])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for zlib includes and libraries.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use gzip)
|
||||
AC_ARG_ENABLE(gzip,
|
||||
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
|
||||
[USEZLIB=$enableval],
|
||||
[USEZLIB=yes] )
|
||||
AC_MSG_RESULT($USEZLIB)
|
||||
if test "$USEZLIB" = "yes"; then
|
||||
AC_ARG_WITH(zlib_includes,
|
||||
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(zlib_libraries,
|
||||
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([zlib], [zlib],
|
||||
[LIBS="${LIBS} $zlib_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $zlib_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(zlib.h,,
|
||||
AC_MSG_ERROR("zlib header files not found"))
|
||||
AC_SEARCH_LIBS([deflateBound], [z], ,
|
||||
AC_MSG_ERROR("zlib library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Determine if CPU supports SIMD instructions
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use SIMD-optimized routines)
|
||||
USE_SIMD=no
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
SSE2_CXXFLAGS="-msse2"
|
||||
SSSE3_CXXFLAGS="-mssse3"
|
||||
PCLMUL_CXXFLAGS="-msse4.1 -mpclmul"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
arm*)
|
||||
NEON_CXXFLAGS="-mfpu=neon"
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
aarch64)
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT($USE_SIMD)
|
||||
AC_SUBST([SSE2_CXXFLAGS])
|
||||
AC_SUBST([SSSE3_CXXFLAGS])
|
||||
AC_SUBST([PCLMUL_CXXFLAGS])
|
||||
AC_SUBST([NEON_CXXFLAGS])
|
||||
AC_SUBST([ACLECRC_CXXFLAGS])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Some Linux systems require an empty signal handler for SIGCHLD
|
||||
dnl in order for exit codes to be correctly delivered to parent process.
|
||||
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
|
||||
dnl The default behavior is to install the handler.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
|
||||
AC_ARG_ENABLE(sigchld-handler,
|
||||
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
|
||||
[SIGCHLDHANDLER=$enableval],
|
||||
[SIGCHLDHANDLER=yes])
|
||||
AC_MSG_RESULT($SIGCHLDHANDLER)
|
||||
if test "$SIGCHLDHANDLER" = "yes"; then
|
||||
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
|
||||
fi
|
||||
|
||||
|
||||
@@ -352,43 +600,103 @@ dnl Debugging. Default: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include all debugging code)
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug enable debugging],
|
||||
[AS_HELP_STRING([--enable-debug], [enable debugging])],
|
||||
[ ENABLEDEBUG=$enableval ],
|
||||
[ ENABLEDEBUG=no] )
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($ENABLEDEBUG)
|
||||
|
||||
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
|
||||
dnl
|
||||
dnl Begin of debugging code
|
||||
dnl
|
||||
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl
|
||||
AC_MSG_CHECKING(for macro returning current function name)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT(__FUNCTION__)
|
||||
FUNCTION_MACRO_NAME=__FUNCTION__,
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT(__func__)
|
||||
FUNCTION_MACRO_NAME=__func__,
|
||||
AC_MSG_RESULT(none)))
|
||||
if test "$FUNCTION_MACRO_NAME" != ""; then
|
||||
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl variadic macros
|
||||
dnl
|
||||
AC_MSG_CHECKING(for variadic macros)
|
||||
AC_COMPILE_IFELSE([
|
||||
#define macro(...) macrofunc(__VA_ARGS__)
|
||||
int macrofunc(int a, int b) { return a + b; }
|
||||
int test() { return macro(1, 2); }
|
||||
],
|
||||
AC_TRY_COMPILE(
|
||||
[ #define macro(...) macrofunc(__VA_ARGS__) ]
|
||||
[ int macrofunc(int a, int b) { return a + b; } ],
|
||||
[ int a=macro(1, 2); ],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_VARIADIC_MACROS], 1, Define to 1 if variadic macros are supported),
|
||||
AC_MSG_RESULT([no]))
|
||||
|
||||
|
||||
dnl Substitute flags.
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST(CXXFLAGS)
|
||||
AC_SUBST(TAR)
|
||||
AC_SUBST(AR)
|
||||
AC_SUBST(ADDSRCS)
|
||||
dnl
|
||||
dnl Backtracing on segmentation faults
|
||||
dnl
|
||||
AC_MSG_CHECKING(for backtrace)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <execinfo.h>]
|
||||
[#include <stdio.h>]
|
||||
[#include <stdlib.h>],
|
||||
[ void *array[100]; size_t size; char **strings; ]
|
||||
[ size = backtrace(array, 100); ]
|
||||
[ strings = backtrace_symbols(array, size); ],
|
||||
FOUND=yes
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
|
||||
FOUND=no
|
||||
AC_MSG_RESULT([[no]]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl "rdynamic" linker flag
|
||||
dnl
|
||||
AC_MSG_CHECKING(for rdynamic linker flag)
|
||||
old_LDFLAGS="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS -rdynamic"
|
||||
AC_TRY_LINK([], [],
|
||||
AC_MSG_RESULT([[yes]]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
[LDFLAGS="$old_LDFLAGS"])
|
||||
|
||||
dnl
|
||||
dnl End of debugging code
|
||||
dnl
|
||||
else
|
||||
AC_DEFINE([NDEBUG],1,Define to 1 to exclude debug-code)
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Enable test suite. Deafult: no.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to enable unit and integration tests)
|
||||
AC_ARG_ENABLE(tests,
|
||||
[AS_HELP_STRING([--enable-tests], [enable unit and integration tests])],
|
||||
[ ENABLETESTS=$enableval ],
|
||||
[ ENABLETESTS=no] )
|
||||
AC_MSG_RESULT($ENABLETESTS)
|
||||
if test "$ENABLETESTS" = "yes"; then
|
||||
AC_DEFINE([ENABLE_TESTS],1,[Define to 1 to enable unit and integration tests])
|
||||
AM_CONDITIONAL(WITH_TESTS, true)
|
||||
else
|
||||
AM_CONDITIONAL(WITH_TESTS, false)
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
1357
daemon/connect/Connection.cpp
Normal file
1357
daemon/connect/Connection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
157
daemon/connect/Connection.h
Normal file
157
daemon/connect/Connection.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NString.h"
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
#include "TlsSocket.h"
|
||||
#endif
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled,
|
||||
csBroken
|
||||
};
|
||||
|
||||
enum EIPVersion
|
||||
{
|
||||
ipAuto,
|
||||
ipV4,
|
||||
ipV6
|
||||
};
|
||||
|
||||
Connection(const char* host, int port, bool tls);
|
||||
Connection(SOCKET socket, bool tls);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
virtual bool Connect();
|
||||
virtual bool Disconnect();
|
||||
bool Bind();
|
||||
bool Send(const char* buffer, int size);
|
||||
bool Recv(char* buffer, int size);
|
||||
int TryRecv(char* buffer, int size);
|
||||
char* ReadLine(char* buffer, int size, int* bytesRead);
|
||||
void ReadBuffer(char** buffer, int *bufLen);
|
||||
int WriteLine(const char* buffer);
|
||||
std::unique_ptr<Connection> Accept();
|
||||
void Cancel();
|
||||
const char* GetHost() { return m_host; }
|
||||
int GetPort() { return m_port; }
|
||||
bool GetTls() { return m_tls; }
|
||||
const char* GetCipher() { return m_cipher; }
|
||||
void SetCipher(const char* cipher) { m_cipher = cipher; }
|
||||
void SetTimeout(int timeout) { m_timeout = timeout; }
|
||||
void SetIPVersion(EIPVersion ipVersion) { m_ipVersion = ipVersion; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetSuppressErrors(bool suppressErrors);
|
||||
bool GetSuppressErrors() { return m_suppressErrors; }
|
||||
const char* GetRemoteAddr();
|
||||
bool GetGracefull() { return m_gracefull; }
|
||||
void SetGracefull(bool gracefull) { m_gracefull = gracefull; }
|
||||
void SetForceClose(bool forceClose) { m_forceClose = forceClose; }
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTls(bool isClient, const char* certFile, const char* keyFile);
|
||||
#endif
|
||||
int FetchTotalBytesRead();
|
||||
|
||||
protected:
|
||||
CString m_host;
|
||||
int m_port;
|
||||
bool m_tls;
|
||||
EIPVersion m_ipVersion = ipAuto;
|
||||
SOCKET m_socket = INVALID_SOCKET;
|
||||
CString m_cipher;
|
||||
CharBuffer m_readBuf;
|
||||
int m_bufAvail = 0;
|
||||
char* m_bufPtr = nullptr;
|
||||
EStatus m_status = csDisconnected;
|
||||
int m_timeout = 60;
|
||||
bool m_suppressErrors = true;
|
||||
BString<100> m_remoteAddr;
|
||||
int m_totalBytesRead = 0;
|
||||
bool m_gracefull = false;
|
||||
bool m_forceClose = false;
|
||||
|
||||
struct SockAddr
|
||||
{
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
bool operator==(const SockAddr& rhs) const
|
||||
{ return memcmp(this, &rhs, sizeof(SockAddr)) == 0; }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
class ConTlsSocket: public TlsSocket
|
||||
{
|
||||
public:
|
||||
ConTlsSocket(SOCKET socket, bool isClient, const char* host,
|
||||
const char* certFile, const char* keyFile, const char* cipher, Connection* owner) :
|
||||
TlsSocket(socket, isClient, host, certFile, keyFile, cipher), m_owner(owner) {}
|
||||
protected:
|
||||
virtual void PrintError(const char* errMsg) { m_owner->PrintError(errMsg); }
|
||||
private:
|
||||
Connection* m_owner;
|
||||
};
|
||||
|
||||
std::unique_ptr<ConTlsSocket> m_tlsSocket;
|
||||
bool m_tlsError = false;
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
static std::unique_ptr<Mutex> m_getHostByNameMutex;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ReportError(const char* msgPrefix, const char* msgArg, bool printErrCode, int errCode = 0,
|
||||
const char* errMsg = nullptr);
|
||||
virtual void PrintError(const char* errMsg);
|
||||
int GetLastNetworkError();
|
||||
bool DoConnect();
|
||||
bool DoDisconnect();
|
||||
bool InitSocketOpts(SOCKET socket);
|
||||
bool ConnectWithTimeout(void* address, int address_len);
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
in_addr_t ResolveHostAddr(const char* host);
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
int recv(SOCKET s, char* buf, int len, int flags);
|
||||
int send(SOCKET s, const char* buf, int len, int flags);
|
||||
void CloseTls();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
696
daemon/connect/TlsSocket.cpp
Normal file
696
daemon/connect/TlsSocket.cpp
Normal file
@@ -0,0 +1,696 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
#include "TlsSocket.h"
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
CString TlsSocket::m_certStore;
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
|
||||
/**
|
||||
* Mutexes for gcryptlib
|
||||
*/
|
||||
|
||||
std::vector<std::unique_ptr<Mutex>> g_GCryptLibMutexes;
|
||||
|
||||
static int gcry_mutex_init(void **priv)
|
||||
{
|
||||
g_GCryptLibMutexes.emplace_back(std::make_unique<Mutex>());
|
||||
*priv = g_GCryptLibMutexes.back().get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_destroy(void **lock)
|
||||
{
|
||||
Mutex* mutex = ((Mutex*)*lock);
|
||||
g_GCryptLibMutexes.erase(std::find_if(g_GCryptLibMutexes.begin(), g_GCryptLibMutexes.end(),
|
||||
[mutex](std::unique_ptr<Mutex>& itMutex)
|
||||
{
|
||||
return itMutex.get() == mutex;
|
||||
}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_lock(void **lock)
|
||||
{
|
||||
((Mutex*)*lock)->Lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_unlock(void **lock)
|
||||
{
|
||||
((Mutex*)*lock)->Unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gcry_thread_cbs gcry_threads_Mutex =
|
||||
{ GCRY_THREAD_OPTION_USER, nullptr,
|
||||
gcry_mutex_init, gcry_mutex_destroy,
|
||||
gcry_mutex_lock, gcry_mutex_unlock,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#ifndef CRYPTO_set_locking_callback
|
||||
#define NEED_CRYPTO_LOCKING
|
||||
#endif
|
||||
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
|
||||
/**
|
||||
* Mutexes for OpenSSL
|
||||
*/
|
||||
|
||||
std::vector<std::unique_ptr<Mutex>> g_OpenSSLMutexes;
|
||||
|
||||
static void openssl_locking(int mode, int n, const char* file, int line)
|
||||
{
|
||||
Mutex* mutex = g_OpenSSLMutexes[n].get();
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
mutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line)
|
||||
{
|
||||
return (CRYPTO_dynlock_value*)new Mutex();
|
||||
}
|
||||
|
||||
static void openssl_dynlock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
Mutex* mutex = (Mutex*)l;
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
Mutex* mutex = (Mutex*)l;
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
mutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NEED_CRYPTO_LOCKING */
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
|
||||
void TlsSocket::Init()
|
||||
{
|
||||
debug("Initializing TLS library");
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
int error_code;
|
||||
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex);
|
||||
if (error_code != 0)
|
||||
{
|
||||
error("Could not initialize libcrypt");
|
||||
return;
|
||||
}
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
|
||||
error_code = gnutls_global_init();
|
||||
if (error_code != 0)
|
||||
{
|
||||
error("Could not initialize libgnutls");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
for (int i = 0, num = CRYPTO_num_locks(); i < num; i++)
|
||||
{
|
||||
g_OpenSSLMutexes.emplace_back(std::make_unique<Mutex>());
|
||||
}
|
||||
|
||||
CRYPTO_set_locking_callback(openssl_locking);
|
||||
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
|
||||
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
|
||||
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock);
|
||||
#endif /* NEED_CRYPTO_LOCKING */
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::Final()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_global_deinit();
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
FIPS_mode_set(0);
|
||||
#endif
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
CRYPTO_set_locking_callback(nullptr);
|
||||
CRYPTO_set_id_callback(nullptr);
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && ! defined (LIBRESSL_VERSION_NUMBER)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
//ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
#ifndef OPENSSL_NO_COMP
|
||||
COMP_zlib_cleanup();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
TlsSocket::~TlsSocket()
|
||||
{
|
||||
Close();
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void TlsSocket::ReportError(const char* errMsg, bool suppressable)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
const char* errstr = gnutls_strerror(m_retCode);
|
||||
if (suppressable && m_suppressErrors)
|
||||
{
|
||||
debug("%s: %s", errMsg, errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(BString<1024>("%s: %s", errMsg, errstr));
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
int errcode = ERR_get_error();
|
||||
do
|
||||
{
|
||||
char errstr[1024];
|
||||
ERR_error_string_n(errcode, errstr, sizeof(errstr));
|
||||
errstr[1024-1] = '\0';
|
||||
|
||||
if (suppressable && m_suppressErrors)
|
||||
{
|
||||
debug("%s: %s", errMsg, errstr);
|
||||
}
|
||||
else if (errcode != 0)
|
||||
{
|
||||
PrintError(BString<1024>("%s: %s", errMsg, errstr));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(errMsg);
|
||||
}
|
||||
|
||||
errcode = ERR_get_error();
|
||||
} while (errcode);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::PrintError(const char* errMsg)
|
||||
{
|
||||
error("%s", errMsg);
|
||||
}
|
||||
|
||||
bool TlsSocket::Start()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_credentials_t cred;
|
||||
m_retCode = gnutls_certificate_allocate_credentials(&cred);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS context", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context = cred;
|
||||
|
||||
if (m_certFile && m_keyFile)
|
||||
{
|
||||
m_retCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_context,
|
||||
m_certFile, m_keyFile, GNUTLS_X509_FMT_PEM);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not load certificate or key file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_session_t sess;
|
||||
m_retCode = gnutls_init(&sess, m_isClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_session = sess;
|
||||
|
||||
m_initialized = true;
|
||||
|
||||
const char* priority = !m_cipher.Empty() ? m_cipher.Str() :
|
||||
(m_certFile && m_keyFile ? "NORMAL:!VERS-SSL3.0" : "NORMAL");
|
||||
|
||||
m_retCode = gnutls_priority_set_direct((gnutls_session_t)m_session, priority, nullptr);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not select cipher for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_host)
|
||||
{
|
||||
m_retCode = gnutls_server_name_set((gnutls_session_t)m_session, GNUTLS_NAME_DNS, m_host, m_host.Length());
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not set hostname for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_retCode = gnutls_credentials_set((gnutls_session_t)m_session, GNUTLS_CRD_CERTIFICATE,
|
||||
(gnutls_certificate_credentials_t*)m_context);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not initialize TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr((gnutls_session_t)m_session, (gnutls_transport_ptr_t)(size_t)m_socket);
|
||||
|
||||
m_retCode = gnutls_handshake((gnutls_session_t)m_session);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
return true;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_context = SSL_CTX_new(SSLv23_method());
|
||||
|
||||
if (!m_context)
|
||||
{
|
||||
ReportError("Could not create TLS context", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_certFile && m_keyFile)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_chain_file((SSL_CTX*)m_context, m_certFile) != 1)
|
||||
{
|
||||
ReportError("Could not load certificate file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_context, m_keyFile, SSL_FILETYPE_PEM) != 1)
|
||||
{
|
||||
ReportError("Could not load key file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (!SSL_CTX_set_options((SSL_CTX*)m_context, SSL_OP_NO_SSLv3))
|
||||
{
|
||||
ReportError("Could not select minimum protocol version for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// For ECC certificates
|
||||
EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh)
|
||||
{
|
||||
ReportError("Could not generate ecdh parameters for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (!SSL_CTX_set_tmp_ecdh((SSL_CTX*)m_context, ecdh))
|
||||
{
|
||||
ReportError("Could not set ecdh parameters for TLS", false);
|
||||
EC_KEY_free(ecdh);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty())
|
||||
{
|
||||
// Enable certificate validation
|
||||
if (SSL_CTX_load_verify_locations((SSL_CTX*)m_context, m_certStore, nullptr) != 1)
|
||||
{
|
||||
ReportError("Could not set certificate store location", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify((SSL_CTX*)m_context, SSL_VERIFY_PEER, nullptr);
|
||||
}
|
||||
|
||||
m_session = SSL_new((SSL_CTX*)m_context);
|
||||
if (!m_session)
|
||||
{
|
||||
ReportError("Could not create TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_cipher.Empty() && !SSL_set_cipher_list((SSL*)m_session, m_cipher))
|
||||
{
|
||||
ReportError("Could not select cipher for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && m_host && !SSL_set_tlsext_host_name((SSL*)m_session, m_host))
|
||||
{
|
||||
ReportError("Could not set host name for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SSL_set_fd((SSL*)m_session, (int)m_socket))
|
||||
{
|
||||
ReportError("Could not set the file descriptor for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int error_code = m_isClient ? SSL_connect((SSL*)m_session) : SSL_accept((SSL*)m_session);
|
||||
if (error_code < 1)
|
||||
{
|
||||
long verifyRes = SSL_get_verify_result((SSL*)m_session);
|
||||
if (verifyRes != X509_V_OK)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification",
|
||||
*m_host, X509_verify_cert_error_string(verifyRes)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
|
||||
}
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
return true;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
bool TlsSocket::ValidateCert()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030306
|
||||
if (FileSystem::DirectoryExists(m_certStore))
|
||||
{
|
||||
if (gnutls_certificate_set_x509_trust_dir((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
|
||||
{
|
||||
ReportError("Could not set certificate store location");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (gnutls_certificate_set_x509_trust_file((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
|
||||
{
|
||||
ReportError("Could not set certificate store location");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int status = 0;
|
||||
if (gnutls_certificate_verify_peers3((gnutls_session_t)m_session, m_host, &status) != 0 ||
|
||||
gnutls_certificate_type_get((gnutls_session_t)m_session) != GNUTLS_CRT_X509)
|
||||
{
|
||||
ReportError("Could not verify TLS certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
|
||||
{
|
||||
// Extracting hostname from the certificate
|
||||
unsigned int cert_list_size = 0;
|
||||
const gnutls_datum_t* cert_list = gnutls_certificate_get_peers((gnutls_session_t)m_session, &cert_list_size);
|
||||
if (cert_list_size > 0)
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
gnutls_x509_crt_init(&cert);
|
||||
gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
|
||||
char dn[256];
|
||||
size_t size = sizeof(dn);
|
||||
if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &size) == 0)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, dn));
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
return false;
|
||||
}
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_datum_t msgdata;
|
||||
if (gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, &msgdata, 0) == 0)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, msgdata.data));
|
||||
gnutls_free(&msgdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportError(BString<1024>("TLS certificate verification failed for %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
// verify a server certificate was presented during the negotiation
|
||||
X509* cert = SSL_get_peer_certificate((SSL*)m_session);
|
||||
if (!cert)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: no certificate provided by server."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_X509_CHECK_HOST
|
||||
// hostname verification
|
||||
if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
|
||||
{
|
||||
const unsigned char* certHost = nullptr;
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
|
||||
if (common_name_loc >= 0)
|
||||
{
|
||||
// Extract the CN field
|
||||
X509_NAME_ENTRY* common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
|
||||
if (common_name_entry != nullptr)
|
||||
{
|
||||
// Convert the CN field to a C string
|
||||
ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
|
||||
if (common_name_asn1 != nullptr)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
certHost = ASN1_STRING_get0_data(common_name_asn1);
|
||||
#else
|
||||
certHost = ASN1_STRING_data(common_name_asn1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, certHost));
|
||||
X509_free(cert);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
X509_free(cert);
|
||||
return true;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::Close()
|
||||
{
|
||||
if (m_session)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
if (m_connected)
|
||||
{
|
||||
gnutls_bye((gnutls_session_t)m_session, GNUTLS_SHUT_WR);
|
||||
}
|
||||
if (m_initialized)
|
||||
{
|
||||
gnutls_deinit((gnutls_session_t)m_session);
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (m_connected)
|
||||
{
|
||||
SSL_shutdown((SSL*)m_session);
|
||||
}
|
||||
SSL_free((SSL*)m_session);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_session = nullptr;
|
||||
}
|
||||
|
||||
if (m_context)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_context);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
SSL_CTX_free((SSL_CTX*)m_context);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int TlsSocket::Send(const char* buffer, int size)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
m_retCode = gnutls_record_send((gnutls_session_t)m_session, buffer, size);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_retCode = SSL_write((SSL*)m_session, buffer, size);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (m_retCode < 0)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (ERR_peek_error() == 0)
|
||||
{
|
||||
ReportError("Could not write to TLS-Socket: Connection closed by remote host");
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_OPENSSL */
|
||||
ReportError("Could not write to TLS-Socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return m_retCode;
|
||||
}
|
||||
|
||||
int TlsSocket::Recv(char* buffer, int size)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
m_retCode = gnutls_record_recv((gnutls_session_t)m_session, buffer, size);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_retCode = SSL_read((SSL*)m_session, buffer, size);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (m_retCode < 0)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (ERR_peek_error() == 0)
|
||||
{
|
||||
ReportError("Could not read from TLS-Socket: Connection closed by remote host");
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_OPENSSL */
|
||||
{
|
||||
ReportError("Could not read from TLS-Socket");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return m_retCode;
|
||||
}
|
||||
|
||||
#endif
|
||||
69
daemon/connect/TlsSocket.h
Normal file
69
daemon/connect/TlsSocket.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TLSSOCKET_H
|
||||
#define TLSSOCKET_H
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
#include "NString.h"
|
||||
|
||||
class TlsSocket
|
||||
{
|
||||
public:
|
||||
TlsSocket(SOCKET socket, bool isClient, const char* host,
|
||||
const char* certFile, const char* keyFile, const char* cipher) :
|
||||
m_socket(socket), m_isClient(isClient), m_host(host),
|
||||
m_certFile(certFile), m_keyFile(keyFile), m_cipher(cipher) {}
|
||||
virtual ~TlsSocket();
|
||||
static void Init();
|
||||
static void InitOptions(const char* certStore) { m_certStore = certStore; }
|
||||
static void Final();
|
||||
bool Start();
|
||||
void Close();
|
||||
int Send(const char* buffer, int size);
|
||||
int Recv(char* buffer, int size);
|
||||
void SetSuppressErrors(bool suppressErrors) { m_suppressErrors = suppressErrors; }
|
||||
|
||||
protected:
|
||||
virtual void PrintError(const char* errMsg);
|
||||
|
||||
private:
|
||||
SOCKET m_socket;
|
||||
bool m_isClient;
|
||||
CString m_host;
|
||||
CString m_certFile;
|
||||
CString m_keyFile;
|
||||
CString m_cipher;
|
||||
bool m_suppressErrors = false;
|
||||
bool m_initialized = false;
|
||||
bool m_connected = false;
|
||||
int m_retCode;
|
||||
static CString m_certStore;
|
||||
|
||||
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TlsSocket.h
|
||||
void* m_context = nullptr;
|
||||
void* m_session = nullptr;
|
||||
|
||||
void ReportError(const char* errMsg, bool suppressable = true);
|
||||
bool ValidateCert();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
666
daemon/connect/WebDownloader.cpp
Normal file
666
daemon/connect/WebDownloader.cpp
Normal file
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
WebDownloader::WebDownloader()
|
||||
{
|
||||
debug("Creating WebDownloader");
|
||||
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
void WebDownloader::SetUrl(const char* url)
|
||||
{
|
||||
m_url = WebUtil::UrlEncode(url);
|
||||
}
|
||||
|
||||
void WebDownloader::SetStatus(EStatus status)
|
||||
{
|
||||
m_status = status;
|
||||
Notify(nullptr);
|
||||
}
|
||||
|
||||
void WebDownloader::SetLastUpdateTimeNow()
|
||||
{
|
||||
m_lastUpdateTime = Util::CurrentTime();
|
||||
}
|
||||
|
||||
void WebDownloader::Run()
|
||||
{
|
||||
debug("Entering WebDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
|
||||
int remainedDownloadRetries = g_Options->GetUrlRetries() > 0 ? g_Options->GetUrlRetries() : 1;
|
||||
int remainedConnectRetries = remainedDownloadRetries > 10 ? remainedDownloadRetries : 10;
|
||||
if (!m_retry)
|
||||
{
|
||||
remainedDownloadRetries = 1;
|
||||
remainedConnectRetries = 1;
|
||||
}
|
||||
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && remainedDownloadRetries > 0 && remainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = DownloadWithRedirects(5);
|
||||
|
||||
if ((((Status == adFailed) && (remainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (remainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
|
||||
!(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
Util::Sleep(100);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || (!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status != adConnectError)
|
||||
{
|
||||
remainedDownloadRetries--;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainedConnectRetries--;
|
||||
}
|
||||
}
|
||||
|
||||
if (Status != adFinished && Status != adRetry)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", *m_infoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Download %s failed", *m_infoName);
|
||||
}
|
||||
}
|
||||
|
||||
if (Status == adFinished)
|
||||
{
|
||||
detail("Download %s completed", *m_infoName);
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting WebDownloader-loop");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::Download()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
URL url(m_url);
|
||||
|
||||
Status = CreateConnection(&url);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
m_connection->SetTimeout(g_Options->GetUrlTimeout());
|
||||
m_connection->SetSuppressErrors(false);
|
||||
|
||||
// connection
|
||||
bool connected = m_connection->Connect();
|
||||
if (!connected || IsStopped())
|
||||
{
|
||||
FreeConnection();
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
detail("Downloading %s", *m_infoName);
|
||||
|
||||
SendHeaders(&url);
|
||||
|
||||
Status = DownloadHeaders();
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
Status = DownloadBody();
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
// Download failed, delete broken output file
|
||||
FileSystem::DeleteFile(m_outputFilename);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadWithRedirects(int maxRedirects)
|
||||
{
|
||||
// do sync download, following redirects
|
||||
EStatus status = adRedirect;
|
||||
while (status == adRedirect && maxRedirects >= 0)
|
||||
{
|
||||
maxRedirects--;
|
||||
status = Download();
|
||||
}
|
||||
|
||||
if (status == adRedirect && maxRedirects < 0)
|
||||
{
|
||||
warn("Too many redirects for %s", *m_infoName);
|
||||
status = adFailed;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CreateConnection(URL *url)
|
||||
{
|
||||
if (!url->IsValid())
|
||||
{
|
||||
error("URL is not valid: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
int port = url->GetPort();
|
||||
if (port == 0 && !strcasecmp(url->GetProtocol(), "http"))
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
if (port == 0 && !strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
port = 443;
|
||||
}
|
||||
|
||||
if (strcasecmp(url->GetProtocol(), "http") && strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
error("Unsupported protocol in URL: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
#ifdef DISABLE_TLS
|
||||
if (!strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool tls = !strcasecmp(url->GetProtocol(), "https");
|
||||
|
||||
m_connection = std::make_unique<Connection>(url->GetHost(), port, tls);
|
||||
|
||||
return adRunning;
|
||||
}
|
||||
|
||||
void WebDownloader::SendHeaders(URL *url)
|
||||
{
|
||||
// retrieve file
|
||||
m_connection->WriteLine(BString<1024>("GET %s HTTP/1.0\r\n", url->GetResource()));
|
||||
m_connection->WriteLine(BString<1024>("User-Agent: nzbget/%s\r\n", Util::VersionRevision()));
|
||||
|
||||
if ((!strcasecmp(url->GetProtocol(), "http") && (url->GetPort() == 80 || url->GetPort() == 0)) ||
|
||||
(!strcasecmp(url->GetProtocol(), "https") && (url->GetPort() == 443 || url->GetPort() == 0)))
|
||||
{
|
||||
m_connection->WriteLine(BString<1024>("Host: %s\r\n", url->GetHost()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connection->WriteLine(BString<1024>("Host: %s:%i\r\n", url->GetHost(), url->GetPort()));
|
||||
}
|
||||
|
||||
m_connection->WriteLine("Accept: */*\r\n");
|
||||
#ifndef DISABLE_GZIP
|
||||
m_connection->WriteLine("Accept-Encoding: gzip\r\n");
|
||||
#endif
|
||||
m_connection->WriteLine("Connection: close\r\n");
|
||||
m_connection->WriteLine("\r\n");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadHeaders()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_confirmedLength = false;
|
||||
CharBuffer lineBuf(1024*10);
|
||||
m_contentLen = -1;
|
||||
bool firstLine = true;
|
||||
m_gzip = false;
|
||||
m_redirecting = false;
|
||||
m_redirected = false;
|
||||
|
||||
// Headers
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
int len = 0;
|
||||
char* line = m_connection->ReadLine(lineBuf, lineBuf.Size(), &len);
|
||||
|
||||
if (firstLine)
|
||||
{
|
||||
Status = CheckResponse(lineBuf);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
firstLine = false;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", *m_infoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
debug("Header: %s", line);
|
||||
|
||||
// detect body of response
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Util::TrimRight(line);
|
||||
ProcessHeader(line);
|
||||
|
||||
if (m_redirected)
|
||||
{
|
||||
Status = adRedirect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadBody()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_outFile.Close();
|
||||
bool end = false;
|
||||
CharBuffer lineBuf(1024*10);
|
||||
int writtenLen = 0;
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
m_gUnzipStream.reset();
|
||||
if (m_gzip)
|
||||
{
|
||||
m_gUnzipStream = std::make_unique<GUnzipStream>(1024*10);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Body
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
char* buffer;
|
||||
int len;
|
||||
m_connection->ReadBuffer(&buffer, &len);
|
||||
if (len == 0)
|
||||
{
|
||||
len = m_connection->TryRecv(lineBuf, lineBuf.Size());
|
||||
buffer = lineBuf;
|
||||
}
|
||||
|
||||
// Connection closed or timeout?
|
||||
if (len <= 0)
|
||||
{
|
||||
if (len == 0 && m_contentLen == -1 && writtenLen > 0)
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", *m_infoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
// write to output file
|
||||
if (!Write(buffer, len))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
writtenLen += len;
|
||||
|
||||
//detect end of file
|
||||
if (writtenLen == m_contentLen || (m_contentLen == -1 && m_gzip && m_confirmedLength))
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
m_gUnzipStream.reset();
|
||||
#endif
|
||||
|
||||
m_outFile.Close();
|
||||
|
||||
if (!end && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("URL %s failed: file incomplete", *m_infoName);
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (end)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CheckResponse(const char* response)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s: Connection closed by remote host", *m_infoName);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
const char* hTTPResponse = strchr(response, ' ');
|
||||
if (strncmp(response, "HTTP", 4) || !hTTPResponse)
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, response);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
hTTPResponse++;
|
||||
|
||||
if (!strncmp(hTTPResponse, "400", 3) || !strncmp(hTTPResponse, "499", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "404", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "301", 3) || !strncmp(hTTPResponse, "302", 3) ||
|
||||
!strncmp(hTTPResponse, "303", 3) || !strncmp(hTTPResponse, "307", 3) ||
|
||||
!strncmp(hTTPResponse, "308", 3))
|
||||
{
|
||||
m_redirecting = true;
|
||||
return adRunning;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "200", 3))
|
||||
{
|
||||
// OK
|
||||
return adRunning;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("URL %s failed: %s", *m_infoName, response);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ProcessHeader(const char* line)
|
||||
{
|
||||
if (!strncasecmp(line, "Content-Length: ", 16))
|
||||
{
|
||||
m_contentLen = atoi(line + 16);
|
||||
m_confirmedLength = true;
|
||||
}
|
||||
else if (!strncasecmp(line, "Content-Encoding: gzip", 22))
|
||||
{
|
||||
m_gzip = true;
|
||||
}
|
||||
else if (!strncasecmp(line, "Content-Disposition: ", 21))
|
||||
{
|
||||
ParseFilename(line);
|
||||
}
|
||||
else if (m_redirecting && !strncasecmp(line, "Location: ", 10))
|
||||
{
|
||||
ParseRedirect(line + 10);
|
||||
m_redirected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ParseFilename(const char* contentDisposition)
|
||||
{
|
||||
// Examples:
|
||||
// Content-Disposition: attachment; filename="fname.ext"
|
||||
// Content-Disposition: attachement;filename=fname.ext
|
||||
// Content-Disposition: attachement;filename=fname.ext;
|
||||
const char *p = strstr(contentDisposition, "filename");
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p = strchr(p, '=');
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
while (*p == ' ') p++;
|
||||
|
||||
BString<1024> fname = p;
|
||||
|
||||
char *pe = fname + strlen(fname) - 1;
|
||||
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
|
||||
*pe = '\0';
|
||||
pe--;
|
||||
}
|
||||
|
||||
WebUtil::HttpUnquote(fname);
|
||||
|
||||
m_originalFilename = FileSystem::BaseFileName(fname);
|
||||
|
||||
debug("OriginalFilename: %s", *m_originalFilename);
|
||||
}
|
||||
|
||||
void WebDownloader::ParseRedirect(const char* location)
|
||||
{
|
||||
const char* newLocation = location;
|
||||
BString<1024> urlBuf;
|
||||
URL newUrl(newLocation);
|
||||
if (!newUrl.IsValid())
|
||||
{
|
||||
// redirect within host
|
||||
|
||||
BString<1024> resource;
|
||||
URL oldUrl(m_url);
|
||||
|
||||
if (*location == '/')
|
||||
{
|
||||
// absolute path within host
|
||||
resource = location;
|
||||
}
|
||||
else
|
||||
{
|
||||
// relative path within host
|
||||
resource = oldUrl.GetResource();
|
||||
|
||||
char* p = strchr(resource, '?');
|
||||
if (p)
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
p = strrchr(resource, '/');
|
||||
if (p)
|
||||
{
|
||||
p[1] = '\0';
|
||||
}
|
||||
|
||||
resource.Append(location);
|
||||
}
|
||||
|
||||
if (oldUrl.GetPort() > 0)
|
||||
{
|
||||
urlBuf.Format("%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), *resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
urlBuf.Format("%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), *resource);
|
||||
}
|
||||
newLocation = urlBuf;
|
||||
}
|
||||
detail("URL %s redirected to %s", *m_url, newLocation);
|
||||
SetUrl(newLocation);
|
||||
}
|
||||
|
||||
bool WebDownloader::Write(void* buffer, int len)
|
||||
{
|
||||
if (!m_outFile.Active() && !PrepareFile())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
if (m_gzip)
|
||||
{
|
||||
m_gUnzipStream->Write(buffer, len);
|
||||
const void *outBuf;
|
||||
int outLen = 1;
|
||||
while (outLen > 0)
|
||||
{
|
||||
GUnzipStream::EStatus gZStatus = m_gUnzipStream->Read(&outBuf, &outLen);
|
||||
|
||||
if (gZStatus == GUnzipStream::zlError)
|
||||
{
|
||||
error("URL %s: GUnzip failed", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outLen > 0 && m_outFile.Write(outBuf, outLen) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gZStatus == GUnzipStream::zlFinished)
|
||||
{
|
||||
m_confirmedLength = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
return m_outFile.Write(buffer, len) > 0;
|
||||
}
|
||||
|
||||
bool WebDownloader::PrepareFile()
|
||||
{
|
||||
// prepare file for writing
|
||||
|
||||
const char* filename = m_outputFilename;
|
||||
if (!m_outFile.Open(filename, DiskFile::omWrite))
|
||||
{
|
||||
error("Could not %s file %s", "create", filename);
|
||||
return false;
|
||||
}
|
||||
if (g_Options->GetWriteBuffer() > 0)
|
||||
{
|
||||
m_outFile.SetWriteBuffer(g_Options->GetWriteBuffer() * 1024);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebDownloader::LogDebugInfo()
|
||||
{
|
||||
info(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_status,
|
||||
*Util::FormatTime(m_lastUpdateTime), FileSystem::BaseFileName(m_outputFilename));
|
||||
}
|
||||
|
||||
void WebDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop WebDownloader");
|
||||
Thread::Stop();
|
||||
Guard guard(m_connectionMutex);
|
||||
if (m_connection)
|
||||
{
|
||||
m_connection->SetSuppressErrors(true);
|
||||
m_connection->Cancel();
|
||||
}
|
||||
debug("WebDownloader stopped successfully");
|
||||
}
|
||||
|
||||
void WebDownloader::FreeConnection()
|
||||
{
|
||||
if (m_connection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
Guard guard(m_connectionMutex);
|
||||
if (m_connection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
m_connection.reset();
|
||||
}
|
||||
}
|
||||
104
daemon/connect/WebDownloader.h
Normal file
104
daemon/connect/WebDownloader.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBDOWNLOADER_H
|
||||
#define WEBDOWNLOADER_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Observer.h"
|
||||
#include "Thread.h"
|
||||
#include "Connection.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Util.h"
|
||||
|
||||
class WebDownloader : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adRetry,
|
||||
adNotFound,
|
||||
adRedirect,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
WebDownloader();
|
||||
EStatus GetStatus() { return m_status; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
EStatus Download();
|
||||
EStatus DownloadWithRedirects(int maxRedirects);
|
||||
void SetInfoName(const char* infoName) { m_infoName = infoName; }
|
||||
const char* GetInfoName() { return m_infoName; }
|
||||
void SetUrl(const char* url);
|
||||
const char* GetOutputFilename() { return m_outputFilename; }
|
||||
void SetOutputFilename(const char* outputFilename) { m_outputFilename = outputFilename; }
|
||||
time_t GetLastUpdateTime() { return m_lastUpdateTime; }
|
||||
void SetLastUpdateTimeNow();
|
||||
bool GetConfirmedLength() { return m_confirmedLength; }
|
||||
const char* GetOriginalFilename() { return m_originalFilename; }
|
||||
void SetForce(bool force) { m_force = force; }
|
||||
void SetRetry(bool retry) { m_retry = retry; }
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* line);
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
CString m_outputFilename;
|
||||
std::unique_ptr<Connection> m_connection;
|
||||
Mutex m_connectionMutex;
|
||||
EStatus m_status = adUndefined;
|
||||
time_t m_lastUpdateTime;
|
||||
CString m_infoName;
|
||||
DiskFile m_outFile;
|
||||
int m_contentLen;
|
||||
bool m_confirmedLength = false;
|
||||
CString m_originalFilename;
|
||||
bool m_force = false;
|
||||
bool m_redirecting;
|
||||
bool m_redirected;
|
||||
bool m_gzip;
|
||||
bool m_retry = true;
|
||||
#ifndef DISABLE_GZIP
|
||||
std::unique_ptr<GUnzipStream> m_gUnzipStream;
|
||||
#endif
|
||||
|
||||
void SetStatus(EStatus status);
|
||||
bool Write(void* buffer, int len);
|
||||
bool PrepareFile();
|
||||
void FreeConnection();
|
||||
EStatus CheckResponse(const char* response);
|
||||
EStatus CreateConnection(URL *url);
|
||||
void ParseFilename(const char* contentDisposition);
|
||||
void SendHeaders(URL *url);
|
||||
EStatus DownloadHeaders();
|
||||
EStatus DownloadBody();
|
||||
void ParseRedirect(const char* location);
|
||||
};
|
||||
|
||||
#endif
|
||||
131
daemon/extension/CommandScript.cpp
Normal file
131
daemon/extension/CommandScript.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "CommandScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const int COMMANDPROCESS_SUCCESS = 93;
|
||||
static const int COMMANDPROCESS_ERROR = 94;
|
||||
|
||||
bool CommandScriptController::StartScript(const char* scriptName, const char* command,
|
||||
std::unique_ptr<Options::OptEntries> modifiedOptions)
|
||||
{
|
||||
CommandScriptController* scriptController = new CommandScriptController();
|
||||
scriptController->m_script = scriptName;
|
||||
scriptController->m_command = command;
|
||||
scriptController->m_logId = g_CommandScriptLog->Reset();
|
||||
scriptController->m_modifiedOptions = std::move(modifiedOptions);
|
||||
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandScriptController::Run()
|
||||
{
|
||||
ExecuteScriptList(m_script);
|
||||
}
|
||||
|
||||
void CommandScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Executing script %s with command %s", script->GetName(), *m_command);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("script %s with command %s", script->GetName(), *m_command);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
infoName[0] = 'S'; // uppercase
|
||||
SetLogPrefix(nullptr);
|
||||
|
||||
switch (exitCode)
|
||||
{
|
||||
case COMMANDPROCESS_SUCCESS:
|
||||
PrintMessage(Message::mkInfo, "%s successful", *infoName);
|
||||
break;
|
||||
|
||||
case COMMANDPROCESS_ERROR:
|
||||
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
|
||||
PrintMessage(Message::mkError, "%s failed", *infoName);
|
||||
break;
|
||||
|
||||
default:
|
||||
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", *infoName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBCP_COMMAND", m_command);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
|
||||
const char* CommandScriptController::GetOptValue(const char* name, const char* value)
|
||||
{
|
||||
Options::OptEntry* entry = m_modifiedOptions->FindOption(name);
|
||||
return entry ? entry->GetValue() : value;
|
||||
}
|
||||
|
||||
void CommandScriptController::AddMessage(Message::EKind kind, const char * text)
|
||||
{
|
||||
NzbScriptController::AddMessage(kind, text);
|
||||
g_CommandScriptLog->AddMessage(m_logId, kind, text);
|
||||
}
|
||||
|
||||
|
||||
int CommandScriptLog::Reset()
|
||||
{
|
||||
Guard guard(m_logMutex);
|
||||
m_messages.clear();
|
||||
return ++m_idScriptGen;
|
||||
}
|
||||
|
||||
void CommandScriptLog::AddMessage(int scriptId, Message::EKind kind, const char * text)
|
||||
{
|
||||
Guard guard(m_logMutex);
|
||||
|
||||
// save only messages from the last started script
|
||||
if (scriptId == m_idScriptGen)
|
||||
{
|
||||
m_messages.emplace_back(++m_idMessageGen, kind, Util::CurrentTime(), text);
|
||||
}
|
||||
}
|
||||
63
daemon/extension/CommandScript.h
Normal file
63
daemon/extension/CommandScript.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef COMMANDSCRIPT_H
|
||||
#define COMMANDSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
#include "Log.h"
|
||||
|
||||
class CommandScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static bool StartScript(const char* scriptName, const char* command, std::unique_ptr<Options::OptEntries> modifiedOptions);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
virtual const char* GetOptValue(const char* name, const char* value);
|
||||
|
||||
private:
|
||||
CString m_script;
|
||||
CString m_command;
|
||||
int m_logId;
|
||||
std::unique_ptr<Options::OptEntries> m_modifiedOptions;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
class CommandScriptLog
|
||||
{
|
||||
public:
|
||||
GuardedMessageList GuardMessages() { return GuardedMessageList(&m_messages, &m_logMutex); }
|
||||
int Reset();
|
||||
void AddMessage(int scriptId, Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
MessageList m_messages;
|
||||
Mutex m_logMutex;
|
||||
int m_idMessageGen;
|
||||
int m_idScriptGen;
|
||||
};
|
||||
|
||||
extern CommandScriptLog* g_CommandScriptLog;
|
||||
|
||||
#endif
|
||||
80
daemon/extension/FeedScript.cpp
Normal file
80
daemon/extension/FeedScript.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
static const int FEED_SUCCESS = 93;
|
||||
|
||||
void FeedScriptController::ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success)
|
||||
{
|
||||
FeedScriptController scriptController;
|
||||
scriptController.m_feedFile = feedFile;
|
||||
scriptController.m_feedId = feedId;
|
||||
|
||||
scriptController.ExecuteScriptList(feedScript);
|
||||
|
||||
if (success)
|
||||
{
|
||||
*success = scriptController.m_success;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetFeedScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing feed-script %s for Feed%i", script->GetName(), m_feedId);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("feed-script %s for Feed%i", script->GetName(), m_feedId);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
if (exitCode != FEED_SUCCESS)
|
||||
{
|
||||
infoName[0] = 'F'; // uppercase
|
||||
PrintMessage(Message::mkError, "%s failed", *infoName);
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void FeedScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBFP_FILENAME", m_feedFile);
|
||||
SetIntEnvVar("NZBFP_FEEDID", m_feedId);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
42
daemon/extension/FeedScript.h
Normal file
42
daemon/extension/FeedScript.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDSCRIPT_H
|
||||
#define FEEDSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class FeedScriptController : public NzbScriptController
|
||||
{
|
||||
public:
|
||||
static void ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
|
||||
private:
|
||||
const char* m_feedFile;
|
||||
int m_feedId;
|
||||
bool m_success = true;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
#endif
|
||||
86
daemon/extension/NzbScript.cpp
Normal file
86
daemon/extension/NzbScript.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
/**
|
||||
* If szStripPrefix is not nullptr, only pp-parameters, whose names start with the prefix
|
||||
* are processed. The prefix is then stripped from the names.
|
||||
* If szStripPrefix is nullptr, all pp-parameters are processed; without stripping.
|
||||
*/
|
||||
void NzbScriptController::PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix)
|
||||
{
|
||||
int prefixLen = stripPrefix ? strlen(stripPrefix) : 0;
|
||||
|
||||
for (NzbParameter& parameter : parameters)
|
||||
{
|
||||
const char* value = parameter.GetValue();
|
||||
|
||||
if (stripPrefix && !strncmp(parameter.GetName(), stripPrefix, prefixLen) && (int)strlen(parameter.GetName()) > prefixLen)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", parameter.GetName() + prefixLen, value);
|
||||
}
|
||||
else if (!stripPrefix)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", parameter.GetName(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzbScriptController::PrepareEnvScript(NzbParameterList* parameters, const char* scriptName)
|
||||
{
|
||||
if (parameters)
|
||||
{
|
||||
PrepareEnvParameters(parameters, nullptr);
|
||||
}
|
||||
|
||||
BString<1024> paramPrefix("%s:", scriptName);
|
||||
|
||||
if (parameters)
|
||||
{
|
||||
PrepareEnvParameters(parameters, paramPrefix);
|
||||
}
|
||||
|
||||
PrepareEnvOptions(paramPrefix);
|
||||
}
|
||||
|
||||
void NzbScriptController::ExecuteScriptList(const char* scriptList)
|
||||
{
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (scriptList && *scriptList)
|
||||
{
|
||||
// split szScriptList into tokens
|
||||
Tokenizer tok(scriptList, ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
ExecuteScript(&script);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
daemon/extension/NzbScript.h
Normal file
37
daemon/extension/NzbScript.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBSCRIPT_H
|
||||
#define NZBSCRIPT_H
|
||||
|
||||
#include "Script.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class NzbScriptController : public ScriptController
|
||||
{
|
||||
protected:
|
||||
void PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix);
|
||||
void PrepareEnvScript(NzbParameterList* parameters, const char* scriptName);
|
||||
void ExecuteScriptList(const char* scriptList);
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
309
daemon/extension/PostScript.cpp
Normal file
309
daemon/extension/PostScript.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PostScript.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
|
||||
static const int POSTPROCESS_PARCHECK = 92;
|
||||
static const int POSTPROCESS_SUCCESS = 93;
|
||||
static const int POSTPROCESS_ERROR = 94;
|
||||
static const int POSTPROCESS_NONE = 95;
|
||||
|
||||
void PostScriptController::StartJob(PostInfo* postInfo)
|
||||
{
|
||||
PostScriptController* scriptController = new PostScriptController();
|
||||
scriptController->m_postInfo = postInfo;
|
||||
scriptController->SetAutoDestroy(false);
|
||||
scriptController->m_prefixLen = 0;
|
||||
|
||||
postInfo->SetPostThread(scriptController);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void PostScriptController::Run()
|
||||
{
|
||||
StringBuilder scriptCommaList;
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (NzbParameter& parameter : m_postInfo->GetNzbInfo()->GetParameters())
|
||||
{
|
||||
const char* varname = parameter.GetName();
|
||||
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname) - 1] == ':' &&
|
||||
(!strcasecmp(parameter.GetValue(), "yes") || !strcasecmp(parameter.GetValue(), "on") || !strcasecmp(parameter.GetValue(), "1")))
|
||||
{
|
||||
CString scriptName(varname);
|
||||
scriptName[strlen(scriptName) - 1] = '\0'; // remove trailing ':'
|
||||
scriptCommaList.Append(scriptName);
|
||||
scriptCommaList.Append(",");
|
||||
}
|
||||
}
|
||||
m_postInfo->GetNzbInfo()->GetScriptStatuses()->clear();
|
||||
}
|
||||
|
||||
ExecuteScriptList(scriptCommaList);
|
||||
|
||||
m_postInfo->SetStage(PostInfo::ptFinished);
|
||||
m_postInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void PostScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
// if any script has requested par-check, do not execute other scripts
|
||||
if (!script->GetPostScript() || m_postInfo->GetRequestParCheck())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
|
||||
|
||||
BString<1024> progressLabel("Executing post-process-script %s", script->GetName());
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->SetProgressLabel(progressLabel);
|
||||
}
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
|
||||
SetInfoName(infoName);
|
||||
|
||||
m_script = script;
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
infoName[0] = 'P'; // uppercase
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
ScriptStatus::EStatus status = AnalyseExitCode(exitCode, infoName);
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->GetScriptStatuses()->emplace_back(script->GetName(), status);
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBPP_NZBID", m_postInfo->GetNzbInfo()->GetId());
|
||||
SetEnvVar("NZBPP_NZBNAME", m_postInfo->GetNzbInfo()->GetName());
|
||||
SetEnvVar("NZBPP_DIRECTORY", m_postInfo->GetNzbInfo()->GetDestDir());
|
||||
SetEnvVar("NZBPP_NZBFILENAME", m_postInfo->GetNzbInfo()->GetFilename());
|
||||
SetEnvVar("NZBPP_QUEUEDFILE", m_postInfo->GetNzbInfo()->GetQueuedFilename());
|
||||
SetEnvVar("NZBPP_URL", m_postInfo->GetNzbInfo()->GetUrl());
|
||||
SetEnvVar("NZBPP_FINALDIR", m_postInfo->GetNzbInfo()->GetFinalDir());
|
||||
SetEnvVar("NZBPP_CATEGORY", m_postInfo->GetNzbInfo()->GetCategory());
|
||||
SetIntEnvVar("NZBPP_HEALTH", m_postInfo->GetNzbInfo()->CalcHealth());
|
||||
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_postInfo->GetNzbInfo()->CalcCriticalHealth(false));
|
||||
|
||||
SetEnvVar("NZBPP_DUPEKEY", m_postInfo->GetNzbInfo()->GetDupeKey());
|
||||
SetIntEnvVar("NZBPP_DUPESCORE", m_postInfo->GetNzbInfo()->GetDupeScore());
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBPP_DUPEMODE", dupeModeName[m_postInfo->GetNzbInfo()->GetDupeMode()]);
|
||||
|
||||
BString<1024> status = m_postInfo->GetNzbInfo()->MakeTextStatus(true);
|
||||
SetEnvVar("NZBPP_STATUS", status);
|
||||
|
||||
char* detail = strchr(status, '/');
|
||||
if (detail) *detail = '\0';
|
||||
SetEnvVar("NZBPP_TOTALSTATUS", status);
|
||||
|
||||
const char* scriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
|
||||
SetEnvVar("NZBPP_SCRIPTSTATUS", scriptStatusName[m_postInfo->GetNzbInfo()->GetScriptStatuses()->CalcTotalStatus()]);
|
||||
|
||||
// deprecated
|
||||
int parStatusCodes[] = { 0, 0, 1, 2, 3, 4 };
|
||||
NzbInfo::EParStatus parStatus = m_postInfo->GetNzbInfo()->GetParStatus();
|
||||
// for downloads marked as bad and for deleted downloads pass par status "Failure"
|
||||
// for compatibility with older scripts which don't check "NZBPP_TOTALSTATUS"
|
||||
if (m_postInfo->GetNzbInfo()->GetDeleteStatus() != NzbInfo::dsNone ||
|
||||
m_postInfo->GetNzbInfo()->GetMarkStatus() == NzbInfo::ksBad)
|
||||
{
|
||||
parStatus = NzbInfo::psFailure;
|
||||
}
|
||||
SetIntEnvVar("NZBPP_PARSTATUS", parStatusCodes[parStatus]);
|
||||
|
||||
// deprecated
|
||||
int unpackStatus[] = { 0, 0, 1, 2, 3, 4 };
|
||||
SetIntEnvVar("NZBPP_UNPACKSTATUS", unpackStatus[m_postInfo->GetNzbInfo()->GetUnpackStatus()]);
|
||||
|
||||
// deprecated
|
||||
SetIntEnvVar("NZBPP_HEALTHDELETED", (int)m_postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsHealth);
|
||||
|
||||
SetIntEnvVar("NZBPP_TOTALARTICLES", (int)m_postInfo->GetNzbInfo()->GetTotalArticles());
|
||||
SetIntEnvVar("NZBPP_SUCCESSARTICLES", (int)m_postInfo->GetNzbInfo()->GetSuccessArticles());
|
||||
SetIntEnvVar("NZBPP_FAILEDARTICLES", (int)m_postInfo->GetNzbInfo()->GetFailedArticles());
|
||||
|
||||
for (ServerStat& serverStat : m_postInfo->GetNzbInfo()->GetServerStats())
|
||||
{
|
||||
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_SUCCESSARTICLES", serverStat.GetServerId()),
|
||||
serverStat.GetSuccessArticles());
|
||||
|
||||
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_FAILEDARTICLES", serverStat.GetServerId()),
|
||||
serverStat.GetFailedArticles());
|
||||
}
|
||||
|
||||
PrepareEnvScript(m_postInfo->GetNzbInfo()->GetParameters(), scriptName);
|
||||
}
|
||||
|
||||
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode, const char* upInfoName)
|
||||
{
|
||||
// The ScriptStatus is accumulated for all scripts:
|
||||
// If any script has failed the status is "failure", etc.
|
||||
|
||||
switch (exitCode)
|
||||
{
|
||||
case POSTPROCESS_SUCCESS:
|
||||
PrintMessage(Message::mkInfo, "%s successful", upInfoName);
|
||||
return ScriptStatus::srSuccess;
|
||||
|
||||
case POSTPROCESS_ERROR:
|
||||
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
|
||||
PrintMessage(Message::mkError, "%s failed", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
|
||||
case POSTPROCESS_NONE:
|
||||
PrintMessage(Message::mkInfo, "%s skipped", upInfoName);
|
||||
return ScriptStatus::srNone;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
case POSTPROCESS_PARCHECK:
|
||||
if (m_postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped)
|
||||
{
|
||||
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "%s requested par-check/repair", upInfoName);
|
||||
m_postInfo->SetRequestParCheck(true);
|
||||
m_postInfo->SetForceRepair(true);
|
||||
return ScriptStatus::srSuccess;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "FINALDIR=", 9))
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->SetFinalDir(msgText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DIRECTORY=", 10))
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->SetDestDir(msgText + 6 + 10);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->GetParameters()->SetParameter(param, value + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
SetLogPrefix(nullptr);
|
||||
PrintMessage(Message::mkWarning, "Marking %s as bad", m_postInfo->GetNzbInfo()->GetName());
|
||||
SetLogPrefix(m_script->GetDisplayName());
|
||||
m_postInfo->GetNzbInfo()->SetMarkStatus(NzbInfo::ksBad);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->SetProgressLabel(text);
|
||||
}
|
||||
|
||||
if (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
|
||||
{
|
||||
time_t stageTime = m_postInfo->GetStageTime();
|
||||
time_t startTime = m_postInfo->GetStartTime();
|
||||
time_t waitTime = Util::CurrentTime();
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
{
|
||||
Util::Sleep(100);
|
||||
|
||||
// update time stamps
|
||||
|
||||
time_t delta = Util::CurrentTime() - waitTime;
|
||||
|
||||
if (stageTime > 0)
|
||||
{
|
||||
m_postInfo->SetStageTime(stageTime + delta);
|
||||
}
|
||||
|
||||
if (startTime > 0)
|
||||
{
|
||||
m_postInfo->SetStartTime(startTime + delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::Stop()
|
||||
{
|
||||
debug("Stopping post-process-script");
|
||||
Thread::Stop();
|
||||
Terminate();
|
||||
}
|
||||
47
daemon/extension/PostScript.h
Normal file
47
daemon/extension/PostScript.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef POSTSCRIPT_H
|
||||
#define POSTSCRIPT_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NzbScript.h"
|
||||
|
||||
class PostScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartJob(PostInfo* postInfo);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
PostInfo* m_postInfo;
|
||||
int m_prefixLen;
|
||||
ScriptConfig::Script* m_script;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
ScriptStatus::EStatus AnalyseExitCode(int exitCode, const char* upInfoName);
|
||||
};
|
||||
|
||||
#endif
|
||||
495
daemon/extension/QueueScript.cpp
Normal file
495
daemon/extension/QueueScript.cpp
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NString.h"
|
||||
#include "QueueScript.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const char* QUEUE_EVENT_NAMES[] = {
|
||||
"FILE_DOWNLOADED",
|
||||
"URL_COMPLETED",
|
||||
"NZB_MARKED",
|
||||
"NZB_ADDED",
|
||||
"NZB_NAMED",
|
||||
"NZB_DOWNLOADED",
|
||||
"NZB_DELETED" };
|
||||
|
||||
class QueueScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
CString m_nzbName;
|
||||
CString m_nzbFilename;
|
||||
CString m_url;
|
||||
CString m_category;
|
||||
CString m_destDir;
|
||||
CString m_queuedFilename;
|
||||
int m_id;
|
||||
int m_priority;
|
||||
CString m_dupeKey;
|
||||
EDupeMode m_dupeMode;
|
||||
int m_dupeScore;
|
||||
NzbParameterList m_parameters;
|
||||
int m_prefixLen;
|
||||
ScriptConfig::Script* m_script;
|
||||
QueueScriptCoordinator::EEvent m_event;
|
||||
bool m_markBad;
|
||||
NzbInfo::EDeleteStatus m_deleteStatus;
|
||||
NzbInfo::EUrlStatus m_urlStatus;
|
||||
NzbInfo::EMarkStatus m_markStatus;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
|
||||
void QueueScriptController::StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event)
|
||||
{
|
||||
QueueScriptController* scriptController = new QueueScriptController();
|
||||
|
||||
scriptController->m_nzbName = nzbInfo->GetName();
|
||||
scriptController->m_nzbFilename = nzbInfo->GetFilename();
|
||||
scriptController->m_url = nzbInfo->GetUrl();
|
||||
scriptController->m_category = nzbInfo->GetCategory();
|
||||
scriptController->m_destDir = nzbInfo->GetDestDir();
|
||||
scriptController->m_queuedFilename = nzbInfo->GetQueuedFilename();
|
||||
scriptController->m_id = nzbInfo->GetId();
|
||||
scriptController->m_priority = nzbInfo->GetPriority();
|
||||
scriptController->m_dupeKey = nzbInfo->GetDupeKey();
|
||||
scriptController->m_dupeMode = nzbInfo->GetDupeMode();
|
||||
scriptController->m_dupeScore = nzbInfo->GetDupeScore();
|
||||
scriptController->m_parameters.CopyFrom(nzbInfo->GetParameters());
|
||||
scriptController->m_script = script;
|
||||
scriptController->m_event = event;
|
||||
scriptController->m_prefixLen = 0;
|
||||
scriptController->m_markBad = false;
|
||||
scriptController->m_deleteStatus = nzbInfo->GetDeleteStatus();
|
||||
scriptController->m_urlStatus = nzbInfo->GetUrlStatus();
|
||||
scriptController->m_markStatus = nzbInfo->GetMarkStatus();
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void QueueScriptController::Run()
|
||||
{
|
||||
ExecuteScript(m_script);
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
|
||||
if (m_markBad)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsBad);
|
||||
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
g_QueueScriptCoordinator->CheckQueue();
|
||||
}
|
||||
|
||||
void QueueScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
PrintMessage(m_event == QueueScriptCoordinator::qeFileDownloaded ? Message::mkDetail : Message::mkInfo,
|
||||
"Executing queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void QueueScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNA_NZBNAME", m_nzbName);
|
||||
SetIntEnvVar("NZBNA_NZBID", m_id);
|
||||
SetEnvVar("NZBNA_FILENAME", m_nzbFilename);
|
||||
SetEnvVar("NZBNA_DIRECTORY", m_destDir);
|
||||
SetEnvVar("NZBNA_QUEUEDFILE", m_queuedFilename);
|
||||
SetEnvVar("NZBNA_URL", m_url);
|
||||
SetEnvVar("NZBNA_CATEGORY", m_category);
|
||||
SetIntEnvVar("NZBNA_PRIORITY", m_priority);
|
||||
SetIntEnvVar("NZBNA_LASTID", m_id); // deprecated
|
||||
|
||||
SetEnvVar("NZBNA_DUPEKEY", m_dupeKey);
|
||||
SetIntEnvVar("NZBNA_DUPESCORE", m_dupeScore);
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNA_DUPEMODE", dupeModeName[m_dupeMode]);
|
||||
|
||||
SetEnvVar("NZBNA_EVENT", QUEUE_EVENT_NAMES[m_event]);
|
||||
|
||||
const char* deleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD", "GOOD", "COPY", "SCAN" };
|
||||
SetEnvVar("NZBNA_DELETESTATUS", deleteStatusName[m_deleteStatus]);
|
||||
|
||||
const char* urlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
|
||||
SetEnvVar("NZBNA_URLSTATUS", urlStatusName[m_urlStatus]);
|
||||
|
||||
const char* markStatusName[] = { "NONE", "BAD", "GOOD", "SUCCESS" };
|
||||
SetEnvVar("NZBNA_MARKSTATUS", markStatusName[m_markStatus]);
|
||||
|
||||
PrepareEnvScript(&m_parameters, scriptName);
|
||||
}
|
||||
|
||||
void QueueScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->GetParameters()->SetParameter(param, value + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DIRECTORY=", 10) &&
|
||||
m_event == QueueScriptCoordinator::qeNzbDownloaded)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->SetFinalDir(msgText + 6 + 10);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
m_markBad = true;
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName);
|
||||
nzbInfo->SetMarkStatus(NzbInfo::ksBad);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NzbInfo* nzbInfo = nullptr;
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nzbInfo)
|
||||
{
|
||||
ScriptController::AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QueueScriptCoordinator::InitOptions()
|
||||
{
|
||||
m_hasQueueScripts = false;
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (script.GetQueueScript())
|
||||
{
|
||||
m_hasQueueScripts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::EnqueueScript(NzbInfo* nzbInfo, EEvent event)
|
||||
{
|
||||
if (!m_hasQueueScripts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
if (event == qeNzbDownloaded)
|
||||
{
|
||||
// delete all other queued scripts for this nzb
|
||||
m_queue.erase(std::remove_if(m_queue.begin(), m_queue.end(),
|
||||
[nzbInfo](std::unique_ptr<QueueItem>& queueItem)
|
||||
{
|
||||
return queueItem->GetNzbId() == nzbInfo->GetId();
|
||||
}),
|
||||
m_queue.end());
|
||||
}
|
||||
|
||||
// respect option "EventInterval"
|
||||
time_t curTime = Util::CurrentTime();
|
||||
if (event == qeFileDownloaded &&
|
||||
(g_Options->GetEventInterval() == -1 ||
|
||||
(g_Options->GetEventInterval() > 0 && curTime - nzbInfo->GetQueueScriptTime() > 0 &&
|
||||
(int)(curTime - nzbInfo->GetQueueScriptTime()) < g_Options->GetEventInterval())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (UsableScript(script, nzbInfo, event))
|
||||
{
|
||||
bool alreadyQueued = false;
|
||||
if (event == qeFileDownloaded)
|
||||
{
|
||||
// check if this script is already queued for this nzb
|
||||
for (QueueItem* queueItem : &m_queue)
|
||||
{
|
||||
if (queueItem->GetNzbId() == nzbInfo->GetId() && queueItem->GetScript() == &script)
|
||||
{
|
||||
alreadyQueued = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyQueued)
|
||||
{
|
||||
std::unique_ptr<QueueItem> queueItem = std::make_unique<QueueItem>(nzbInfo->GetId(), &script, event);
|
||||
if (m_curItem)
|
||||
{
|
||||
m_queue.push_back(std::move(queueItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curItem = std::move(queueItem);
|
||||
QueueScriptController::StartScript(nzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
|
||||
}
|
||||
}
|
||||
|
||||
nzbInfo->SetQueueScriptTime(Util::CurrentTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event)
|
||||
{
|
||||
if (!script.GetQueueScript())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(script.GetQueueEvents()) && !strstr(script.GetQueueEvents(), QUEUE_EVENT_NAMES[event]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check extension scripts assigned for that nzb
|
||||
for (NzbParameter& parameter : nzbInfo->GetParameters())
|
||||
{
|
||||
const char* varname = parameter.GetName();
|
||||
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname)-1] == ':' &&
|
||||
(!strcasecmp(parameter.GetValue(), "yes") ||
|
||||
!strcasecmp(parameter.GetValue(), "on") ||
|
||||
!strcasecmp(parameter.GetValue(), "1")))
|
||||
{
|
||||
BString<1024> scriptName = varname;
|
||||
scriptName[strlen(scriptName)-1] = '\0'; // remove trailing ':'
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for URL-events the extension scripts are not assigned yet;
|
||||
// instead we take the default extension scripts for the category (or global)
|
||||
if (event == qeUrlCompleted)
|
||||
{
|
||||
const char* postScript = g_Options->GetExtensions();
|
||||
if (!Util::EmptyStr(nzbInfo->GetCategory()))
|
||||
{
|
||||
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
|
||||
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
|
||||
{
|
||||
postScript = categoryObj->GetExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(postScript))
|
||||
{
|
||||
Tokenizer tok(postScript, ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NzbInfo* QueueScriptCoordinator::FindNzbInfo(DownloadQueue* downloadQueue, int nzbId)
|
||||
{
|
||||
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(nzbId);
|
||||
if (nzbInfo)
|
||||
{
|
||||
return nzbInfo;
|
||||
}
|
||||
|
||||
HistoryInfo* historyInfo = downloadQueue->GetHistory()->Find(nzbId);
|
||||
if (historyInfo)
|
||||
{
|
||||
return historyInfo->GetNzbInfo();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::CheckQueue()
|
||||
{
|
||||
if (m_stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
m_curItem.reset();
|
||||
NzbInfo* curNzbInfo = nullptr;
|
||||
Queue::iterator itCurItem;
|
||||
|
||||
for (Queue::iterator it = m_queue.begin(); it != m_queue.end(); )
|
||||
{
|
||||
std::unique_ptr<QueueItem>& queueItem = *it;
|
||||
|
||||
NzbInfo* nzbInfo = FindNzbInfo(downloadQueue, queueItem->GetNzbId());
|
||||
|
||||
// in a case this nzb must not be processed further - delete queue script from queue
|
||||
EEvent event = queueItem->GetEvent();
|
||||
bool ignoreEvent = !nzbInfo ||
|
||||
(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone && event != qeNzbDeleted && event != qeNzbMarked) ||
|
||||
(nzbInfo->GetMarkStatus() == NzbInfo::ksBad && event != qeNzbMarked);
|
||||
|
||||
if (ignoreEvent)
|
||||
{
|
||||
it = m_queue.erase(it);
|
||||
if (curNzbInfo)
|
||||
{
|
||||
// process from the beginning, while "erase" invalidated "itCurItem"
|
||||
curNzbInfo = nullptr;
|
||||
it = m_queue.begin();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_curItem || queueItem->GetEvent() > m_curItem->GetEvent())
|
||||
{
|
||||
itCurItem = it;
|
||||
curNzbInfo = nzbInfo;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
if (curNzbInfo)
|
||||
{
|
||||
m_curItem = std::move(*itCurItem);
|
||||
m_queue.erase(itCurItem);
|
||||
QueueScriptController::StartScript(curNzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueScriptCoordinator::HasJob(int nzbId, bool* active)
|
||||
{
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
bool working = m_curItem && m_curItem->GetNzbId() == nzbId;
|
||||
if (active)
|
||||
{
|
||||
*active = working;
|
||||
}
|
||||
if (!working)
|
||||
{
|
||||
for (QueueItem* queueItem : &m_queue)
|
||||
{
|
||||
working = queueItem->GetNzbId() == nzbId;
|
||||
if (working)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
int QueueScriptCoordinator::GetQueueSize()
|
||||
{
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
int queuedCount = m_queue.size();
|
||||
if (m_curItem)
|
||||
{
|
||||
queuedCount++;
|
||||
}
|
||||
|
||||
return queuedCount;
|
||||
}
|
||||
77
daemon/extension/QueueScript.h
Normal file
77
daemon/extension/QueueScript.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUESCRIPT_H
|
||||
#define QUEUESCRIPT_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class QueueScriptCoordinator
|
||||
{
|
||||
public:
|
||||
enum EEvent
|
||||
{
|
||||
qeFileDownloaded, // lowest priority
|
||||
qeUrlCompleted,
|
||||
qeNzbMarked,
|
||||
qeNzbAdded,
|
||||
qeNzbNamed,
|
||||
qeNzbDownloaded,
|
||||
qeNzbDeleted // highest priority
|
||||
};
|
||||
|
||||
void Stop() { m_stopped = true; }
|
||||
void InitOptions();
|
||||
void EnqueueScript(NzbInfo* nzbInfo, EEvent event);
|
||||
void CheckQueue();
|
||||
bool HasJob(int nzbId, bool* active);
|
||||
int GetQueueSize();
|
||||
static NzbInfo* FindNzbInfo(DownloadQueue* downloadQueue, int nzbId);
|
||||
|
||||
private:
|
||||
class QueueItem
|
||||
{
|
||||
public:
|
||||
QueueItem(int nzbId, ScriptConfig::Script* script, EEvent event) :
|
||||
m_nzbId(nzbId), m_script(script), m_event(event) {}
|
||||
int GetNzbId() { return m_nzbId; }
|
||||
ScriptConfig::Script* GetScript() { return m_script; }
|
||||
EEvent GetEvent() { return m_event; }
|
||||
private:
|
||||
int m_nzbId;
|
||||
ScriptConfig::Script* m_script;
|
||||
EEvent m_event;
|
||||
};
|
||||
|
||||
typedef std::deque<std::unique_ptr<QueueItem>> Queue;
|
||||
|
||||
Queue m_queue;
|
||||
Mutex m_queueMutex;
|
||||
std::unique_ptr<QueueItem> m_curItem;
|
||||
bool m_hasQueueScripts = false;
|
||||
bool m_stopped = false;
|
||||
|
||||
bool UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event);
|
||||
};
|
||||
|
||||
extern QueueScriptCoordinator* g_QueueScriptCoordinator;
|
||||
|
||||
#endif
|
||||
200
daemon/extension/ScanScript.cpp
Normal file
200
daemon/extension/ScanScript.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ScanScript.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
class ScanScriptCheck : public NzbScriptController
|
||||
{
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script) { has |= script->GetScanScript(); }
|
||||
bool has = false;
|
||||
friend class ScanScriptController;
|
||||
};
|
||||
|
||||
|
||||
bool ScanScriptController::HasScripts()
|
||||
{
|
||||
ScanScriptCheck check;
|
||||
check.ExecuteScriptList(g_Options->GetExtensions());
|
||||
return check.has;
|
||||
}
|
||||
|
||||
void ScanScriptController::ExecuteScripts(const char* nzbFilename,
|
||||
const char* url, const char* directory, CString* nzbName, CString* category,
|
||||
int* priority, NzbParameterList* parameters, bool* addTop, bool* addPaused,
|
||||
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode)
|
||||
{
|
||||
ScanScriptController scriptController;
|
||||
scriptController.m_nzbFilename = nzbFilename;
|
||||
scriptController.m_url = url;
|
||||
scriptController.m_directory = directory;
|
||||
scriptController.m_nzbName = nzbName;
|
||||
scriptController.m_category = category;
|
||||
scriptController.m_parameters = parameters;
|
||||
scriptController.m_priority = priority;
|
||||
scriptController.m_addTop = addTop;
|
||||
scriptController.m_addPaused = addPaused;
|
||||
scriptController.m_dupeKey = dupeKey;
|
||||
scriptController.m_dupeScore = dupeScore;
|
||||
scriptController.m_dupeMode = dupeMode;
|
||||
scriptController.m_prefixLen = 0;
|
||||
|
||||
const char* extensions = g_Options->GetExtensions();
|
||||
|
||||
if (!Util::EmptyStr(*category))
|
||||
{
|
||||
Options::Category* categoryObj = g_Options->FindCategory(*category, false);
|
||||
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
|
||||
{
|
||||
extensions = categoryObj->GetExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
scriptController.ExecuteScriptList(extensions);
|
||||
}
|
||||
|
||||
void ScanScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetScanScript() || !FileSystem::FileExists(m_nzbFilename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void ScanScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNP_FILENAME", m_nzbFilename);
|
||||
SetEnvVar("NZBNP_URL", m_url);
|
||||
SetEnvVar("NZBNP_NZBNAME", strlen(*m_nzbName) > 0 ? **m_nzbName : FileSystem::BaseFileName(m_nzbFilename));
|
||||
SetEnvVar("NZBNP_CATEGORY", *m_category);
|
||||
SetIntEnvVar("NZBNP_PRIORITY", *m_priority);
|
||||
SetIntEnvVar("NZBNP_TOP", *m_addTop ? 1 : 0);
|
||||
SetIntEnvVar("NZBNP_PAUSED", *m_addPaused ? 1 : 0);
|
||||
SetEnvVar("NZBNP_DUPEKEY", *m_dupeKey);
|
||||
SetIntEnvVar("NZBNP_DUPESCORE", *m_dupeScore);
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNP_DUPEMODE", dupeModeName[*m_dupeMode]);
|
||||
|
||||
// remove trailing slash
|
||||
BString<1024> dir = m_directory;
|
||||
int len = strlen(dir);
|
||||
if (dir[len-1] == PATH_SEPARATOR)
|
||||
{
|
||||
dir[len-1] = '\0';
|
||||
}
|
||||
SetEnvVar("NZBNP_DIRECTORY", dir);
|
||||
|
||||
PrepareEnvScript(m_parameters, scriptName);
|
||||
}
|
||||
|
||||
void ScanScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "NZBNAME=", 8))
|
||||
{
|
||||
*m_nzbName = msgText + 6 + 8;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "CATEGORY=", 9))
|
||||
{
|
||||
*m_category = msgText + 6 + 9;
|
||||
g_Scanner->InitPPParameters(*m_category, m_parameters, true);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
m_parameters->SetParameter(param, value + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "PRIORITY=", 9))
|
||||
{
|
||||
*m_priority = atoi(msgText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "TOP=", 4))
|
||||
{
|
||||
*m_addTop = atoi(msgText + 6 + 4) != 0;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "PAUSED=", 7))
|
||||
{
|
||||
*m_addPaused = atoi(msgText + 6 + 7) != 0;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPEKEY=", 8))
|
||||
{
|
||||
*m_dupeKey = msgText + 6 + 8;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPESCORE=", 10))
|
||||
{
|
||||
*m_dupeScore = atoi(msgText + 6 + 10);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPEMODE=", 9))
|
||||
{
|
||||
const char* dupeMode = msgText + 6 + 9;
|
||||
if (strcasecmp(dupeMode, "score") && strcasecmp(dupeMode, "all") && strcasecmp(dupeMode, "force"))
|
||||
{
|
||||
error("Invalid value \"%s\" for command \"DUPEMODE\" received from %s", dupeMode, GetInfoName());
|
||||
return;
|
||||
}
|
||||
*m_dupeMode = !strcasecmp(dupeMode, "all") ? dmAll :
|
||||
!strcasecmp(dupeMode, "force") ? dmForce : dmScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
57
daemon/extension/ScanScript.h
Normal file
57
daemon/extension/ScanScript.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCANSCRIPT_H
|
||||
#define SCANSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class ScanScriptController : public NzbScriptController
|
||||
{
|
||||
public:
|
||||
static void ExecuteScripts(const char* nzbFilename, const char* url,
|
||||
const char* directory, CString* nzbName, CString* category, int* priority,
|
||||
NzbParameterList* parameters, bool* addTop, bool* addPaused,
|
||||
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode);
|
||||
static bool HasScripts();
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
const char* m_nzbFilename;
|
||||
const char* m_url;
|
||||
const char* m_directory;
|
||||
CString* m_nzbName;
|
||||
CString* m_category;
|
||||
int* m_priority;
|
||||
NzbParameterList* m_parameters;
|
||||
bool* m_addTop;
|
||||
bool* m_addPaused;
|
||||
CString* m_dupeKey;
|
||||
int* m_dupeScore;
|
||||
EDupeMode* m_dupeMode;
|
||||
int m_prefixLen;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
#endif
|
||||
114
daemon/extension/SchedulerScript.cpp
Normal file
114
daemon/extension/SchedulerScript.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "SchedulerScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
void SchedulerScriptController::StartScript(const char* param, bool externalProcess, int taskId)
|
||||
{
|
||||
std::vector<CString> argv;
|
||||
if (externalProcess && (argv = Util::SplitCommandLine(param)).empty())
|
||||
{
|
||||
error("Could not execute scheduled process-script, failed to parse command line: %s", param);
|
||||
return;
|
||||
}
|
||||
|
||||
SchedulerScriptController* scriptController = new SchedulerScriptController();
|
||||
|
||||
scriptController->m_externalProcess = externalProcess;
|
||||
scriptController->m_script = param;
|
||||
scriptController->m_taskId = taskId;
|
||||
|
||||
if (externalProcess)
|
||||
{
|
||||
scriptController->SetArgs(std::move(argv));
|
||||
}
|
||||
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void SchedulerScriptController::Run()
|
||||
{
|
||||
if (m_externalProcess)
|
||||
{
|
||||
ExecuteExternalProcess();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteScriptList(m_script);
|
||||
}
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetSchedulerScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BString<1024> taskName(" for Task%i", m_taskId);
|
||||
if (m_taskId == 0)
|
||||
{
|
||||
taskName = "";
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scheduler-script %s%s", script->GetName(), *taskName);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("scheduler-script %s%s", script->GetName(), *taskName);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBSP_TASKID", m_taskId);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteExternalProcess()
|
||||
{
|
||||
info("Executing scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
|
||||
|
||||
BString<1024> infoName("scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
|
||||
SetInfoName(infoName);
|
||||
|
||||
BString<1024> logPrefix = FileSystem::BaseFileName(GetScript());
|
||||
if (char* ext = strrchr(logPrefix, '.')) *ext = '\0'; // strip file extension
|
||||
SetLogPrefix(logPrefix);
|
||||
|
||||
Execute();
|
||||
}
|
||||
45
daemon/extension/SchedulerScript.h
Normal file
45
daemon/extension/SchedulerScript.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCHEDULERSCRIPT_H
|
||||
#define SCHEDULERSCRIPT_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "NzbScript.h"
|
||||
|
||||
class SchedulerScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(const char* param, bool externalProcess, int taskId);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
|
||||
private:
|
||||
CString m_script;
|
||||
bool m_externalProcess;
|
||||
int m_taskId;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
void ExecuteExternalProcess();
|
||||
};
|
||||
|
||||
#endif
|
||||
500
daemon/extension/ScriptConfig.cpp
Normal file
500
daemon/extension/ScriptConfig.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
static const char* BEGIN_SCRIPT_SIGNATURE = "### NZBGET ";
|
||||
static const char* POST_SCRIPT_SIGNATURE = "POST-PROCESSING";
|
||||
static const char* SCAN_SCRIPT_SIGNATURE = "SCAN";
|
||||
static const char* QUEUE_SCRIPT_SIGNATURE = "QUEUE";
|
||||
static const char* SCHEDULER_SCRIPT_SIGNATURE = "SCHEDULER";
|
||||
static const char* FEED_SCRIPT_SIGNATURE = "FEED";
|
||||
static const char* END_SCRIPT_SIGNATURE = " SCRIPT";
|
||||
static const char* QUEUE_EVENTS_SIGNATURE = "### QUEUE EVENTS:";
|
||||
static const char* TASK_TIME_SIGNATURE = "### TASK TIME:";
|
||||
static const char* DEFINITION_SIGNATURE = "###";
|
||||
|
||||
void ScriptConfig::InitOptions()
|
||||
{
|
||||
InitScripts();
|
||||
InitConfigTemplates();
|
||||
CreateTasks();
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
|
||||
{
|
||||
// read config file
|
||||
DiskFile infile;
|
||||
|
||||
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omRead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename());
|
||||
CString buf;
|
||||
buf.Reserve(fileLen);
|
||||
|
||||
while (infile.ReadLine(buf, fileLen + 1))
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
Util::TrimRight(buf);
|
||||
|
||||
// skip comments and empty lines
|
||||
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CString optname;
|
||||
CString optvalue;
|
||||
if (Options::SplitOptionString(buf, optname, optvalue))
|
||||
{
|
||||
optEntries->emplace_back(optname, optvalue);
|
||||
}
|
||||
}
|
||||
|
||||
infile.Close();
|
||||
|
||||
Options::ConvertOldOptions(optEntries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::SaveConfig(Options::OptEntries* optEntries)
|
||||
{
|
||||
// save to config file
|
||||
DiskFile infile;
|
||||
|
||||
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omReadWrite))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<CString> config;
|
||||
std::set<Options::OptEntry*> writtenOptions;
|
||||
|
||||
// read config file into memory array
|
||||
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename()) + 1;
|
||||
CString content;
|
||||
content.Reserve(fileLen);
|
||||
while (infile.ReadLine(content, fileLen + 1))
|
||||
{
|
||||
config.push_back(*content);
|
||||
}
|
||||
content.Clear();
|
||||
|
||||
// write config file back to disk, replace old values of existing options with new values
|
||||
infile.Seek(0);
|
||||
for (CString& buf : config)
|
||||
{
|
||||
const char* eq = strchr(buf, '=');
|
||||
if (eq && buf[0] != '#')
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
buf.TrimRight();
|
||||
|
||||
CString optname;
|
||||
CString optvalue;
|
||||
if (g_Options->SplitOptionString(buf, optname, optvalue))
|
||||
{
|
||||
Options::OptEntry* optEntry = optEntries->FindOption(optname);
|
||||
if (optEntry)
|
||||
{
|
||||
infile.Print("%s=%s\n", optEntry->GetName(), optEntry->GetValue());
|
||||
writtenOptions.insert(optEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
infile.Print("%s", *buf);
|
||||
}
|
||||
}
|
||||
|
||||
// write new options
|
||||
for (Options::OptEntry& optEntry : *optEntries)
|
||||
{
|
||||
std::set<Options::OptEntry*>::iterator fit = writtenOptions.find(&optEntry);
|
||||
if (fit == writtenOptions.end())
|
||||
{
|
||||
infile.Print("%s=%s\n", optEntry.GetName(), optEntry.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
// close and truncate the file
|
||||
int pos = (int)infile.Position();
|
||||
infile.Close();
|
||||
|
||||
FileSystem::TruncateFile(g_Options->GetConfigFilename(), pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
|
||||
{
|
||||
CharBuffer buffer;
|
||||
if (!FileSystem::LoadFileIntoBuffer(g_Options->GetConfigTemplate(), buffer, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
configTemplates->emplace_back(Script("", ""), buffer);
|
||||
|
||||
if (!g_Options->GetScriptDir())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Scripts scriptList;
|
||||
LoadScripts(&scriptList);
|
||||
|
||||
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
|
||||
|
||||
for (Script& script : scriptList)
|
||||
{
|
||||
DiskFile infile;
|
||||
if (!infile.Open(script.GetLocation(), DiskFile::omRead))
|
||||
{
|
||||
configTemplates->emplace_back(std::move(script), "");
|
||||
continue;
|
||||
}
|
||||
|
||||
StringBuilder templ;
|
||||
char buf[1024];
|
||||
bool inConfig = false;
|
||||
bool inHeader = false;
|
||||
|
||||
while (infile.ReadLine(buf, sizeof(buf) - 1))
|
||||
{
|
||||
if (!strncmp(buf, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) &&
|
||||
strstr(buf, END_SCRIPT_SIGNATURE) &&
|
||||
(strstr(buf, POST_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCAN_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, QUEUE_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCHEDULER_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, FEED_SCRIPT_SIGNATURE)))
|
||||
{
|
||||
if (inConfig)
|
||||
{
|
||||
break;
|
||||
}
|
||||
inConfig = true;
|
||||
inHeader = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
inHeader &= !strncmp(buf, DEFINITION_SIGNATURE, definitionSignatureLen);
|
||||
|
||||
if (inConfig && !inHeader)
|
||||
{
|
||||
templ.Append(buf);
|
||||
}
|
||||
}
|
||||
|
||||
infile.Close();
|
||||
|
||||
configTemplates->emplace_back(std::move(script), templ);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptConfig::InitConfigTemplates()
|
||||
{
|
||||
if (!LoadConfigTemplates(&m_configTemplates))
|
||||
{
|
||||
error("Could not read configuration templates");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::InitScripts()
|
||||
{
|
||||
LoadScripts(&m_scripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScripts(Scripts* scripts)
|
||||
{
|
||||
if (Util::EmptyStr(g_Options->GetScriptDir()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Scripts tmpScripts;
|
||||
|
||||
Tokenizer tokDir(g_Options->GetScriptDir(), ",;");
|
||||
while (const char* scriptDir = tokDir.Next())
|
||||
{
|
||||
LoadScriptDir(&tmpScripts, scriptDir, false);
|
||||
}
|
||||
|
||||
tmpScripts.sort(
|
||||
[](Script& script1, Script& script2)
|
||||
{
|
||||
return strcmp(script1.GetName(), script2.GetName()) < 0;
|
||||
});
|
||||
|
||||
// first add all scripts from ScriptOrder
|
||||
Tokenizer tokOrder(g_Options->GetScriptOrder(), ",;");
|
||||
while (const char* scriptName = tokOrder.Next())
|
||||
{
|
||||
Scripts::iterator pos = std::find_if(tmpScripts.begin(), tmpScripts.end(),
|
||||
[scriptName](Script& script)
|
||||
{
|
||||
return !strcmp(script.GetName(), scriptName);
|
||||
});
|
||||
|
||||
if (pos != tmpScripts.end())
|
||||
{
|
||||
scripts->splice(scripts->end(), tmpScripts, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// then add all other scripts from scripts directory
|
||||
scripts->splice(scripts->end(), std::move(tmpScripts));
|
||||
|
||||
BuildScriptDisplayNames(scripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir)
|
||||
{
|
||||
DirBrowser dir(directory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (filename[0] != '.' && filename[0] != '_')
|
||||
{
|
||||
BString<1024> fullFilename("%s%c%s", directory, PATH_SEPARATOR, filename);
|
||||
|
||||
if (!FileSystem::DirectoryExists(fullFilename))
|
||||
{
|
||||
BString<1024> scriptName = BuildScriptName(directory, filename, isSubDir);
|
||||
if (ScriptExists(scripts, scriptName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Script script(scriptName, fullFilename);
|
||||
if (LoadScriptFile(&script))
|
||||
{
|
||||
scripts->push_back(std::move(script));
|
||||
}
|
||||
}
|
||||
else if (!isSubDir)
|
||||
{
|
||||
LoadScriptDir(scripts, fullFilename, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadScriptFile(Script* script)
|
||||
{
|
||||
DiskFile infile;
|
||||
if (!infile.Open(script->GetLocation(), DiskFile::omRead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CharBuffer buffer(1024 * 10 + 1);
|
||||
|
||||
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
|
||||
const int taskTimeSignatureLen = strlen(TASK_TIME_SIGNATURE);
|
||||
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
|
||||
|
||||
// check if the file contains pp-script-signature
|
||||
// read first 10KB of the file and look for signature
|
||||
int readBytes = (int)infile.Read(buffer, buffer.Size() - 1);
|
||||
infile.Close();
|
||||
buffer[readBytes] = '\0';
|
||||
|
||||
bool postScript = false;
|
||||
bool scanScript = false;
|
||||
bool queueScript = false;
|
||||
bool schedulerScript = false;
|
||||
bool feedScript = false;
|
||||
char* queueEvents = nullptr;
|
||||
char* taskTime = nullptr;
|
||||
|
||||
bool inConfig = false;
|
||||
bool afterConfig = false;
|
||||
|
||||
// Declarations "QUEUE EVENT:" and "TASK TIME:" can be placed:
|
||||
// - in script definition body (between opening and closing script signatures);
|
||||
// - immediately before script definition (before opening script signature);
|
||||
// - immediately after script definition (after closing script signature).
|
||||
// The last two pissibilities are provided to increase compatibility of scripts with older
|
||||
// nzbget versions which do not expect the extra declarations in the script defintion body.
|
||||
|
||||
Tokenizer tok(buffer, "\n\r", true);
|
||||
while (char* line = tok.Next())
|
||||
{
|
||||
if (!strncmp(line, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen))
|
||||
{
|
||||
queueEvents = line + queueEventsSignatureLen;
|
||||
}
|
||||
else if (!strncmp(line, TASK_TIME_SIGNATURE, taskTimeSignatureLen))
|
||||
{
|
||||
taskTime = line + taskTimeSignatureLen;
|
||||
}
|
||||
|
||||
bool header = !strncmp(line, DEFINITION_SIGNATURE, definitionSignatureLen);
|
||||
if (!header && !inConfig)
|
||||
{
|
||||
queueEvents = nullptr;
|
||||
taskTime = nullptr;
|
||||
}
|
||||
|
||||
if (!header && afterConfig)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strncmp(line, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) && strstr(line, END_SCRIPT_SIGNATURE))
|
||||
{
|
||||
if (!inConfig)
|
||||
{
|
||||
inConfig = true;
|
||||
postScript = strstr(line, POST_SCRIPT_SIGNATURE);
|
||||
scanScript = strstr(line, SCAN_SCRIPT_SIGNATURE);
|
||||
queueScript = strstr(line, QUEUE_SCRIPT_SIGNATURE);
|
||||
schedulerScript = strstr(line, SCHEDULER_SCRIPT_SIGNATURE);
|
||||
feedScript = strstr(line, FEED_SCRIPT_SIGNATURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
afterConfig = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(postScript || scanScript || queueScript || schedulerScript || feedScript))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// trim decorations
|
||||
char* p;
|
||||
while (queueEvents && *queueEvents && *(p = queueEvents + strlen(queueEvents) - 1) == '#') *p = '\0';
|
||||
if (queueEvents) queueEvents = Util::Trim(queueEvents);
|
||||
while (taskTime && *taskTime && *(p = taskTime + strlen(taskTime) - 1) == '#') *p = '\0';
|
||||
if (taskTime) taskTime = Util::Trim(taskTime);
|
||||
|
||||
script->SetPostScript(postScript);
|
||||
script->SetScanScript(scanScript);
|
||||
script->SetQueueScript(queueScript);
|
||||
script->SetSchedulerScript(schedulerScript);
|
||||
script->SetFeedScript(feedScript);
|
||||
script->SetQueueEvents(queueEvents);
|
||||
script->SetTaskTime(taskTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BString<1024> ScriptConfig::BuildScriptName(const char* directory, const char* filename, bool isSubDir)
|
||||
{
|
||||
if (isSubDir)
|
||||
{
|
||||
BString<1024> directory2 = directory;
|
||||
int len = strlen(directory2);
|
||||
if (directory2[len-1] == PATH_SEPARATOR || directory2[len-1] == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
// trim last path-separator
|
||||
directory2[len-1] = '\0';
|
||||
}
|
||||
|
||||
return BString<1024>("%s%c%s", FileSystem::BaseFileName(directory2), PATH_SEPARATOR, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptConfig::ScriptExists(Scripts* scripts, const char* scriptName)
|
||||
{
|
||||
return std::find_if(scripts->begin(), scripts->end(),
|
||||
[scriptName](Script& script)
|
||||
{
|
||||
return !strcmp(script.GetName(), scriptName);
|
||||
}) != scripts->end();
|
||||
}
|
||||
|
||||
void ScriptConfig::BuildScriptDisplayNames(Scripts* scripts)
|
||||
{
|
||||
// trying to use short name without path and extension.
|
||||
// if there are other scripts with the same short name - using a longer name instead (with ot without extension)
|
||||
|
||||
for (Script& script : scripts)
|
||||
{
|
||||
BString<1024> shortName = script.GetName();
|
||||
if (char* ext = strrchr(shortName, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* displayName = FileSystem::BaseFileName(shortName);
|
||||
|
||||
for (Script& script2 : scripts)
|
||||
{
|
||||
BString<1024> shortName2 = script2.GetName();
|
||||
if (char* ext = strrchr(shortName2, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* displayName2 = FileSystem::BaseFileName(shortName2);
|
||||
|
||||
if (!strcmp(displayName, displayName2) && script.GetName() != script2.GetName())
|
||||
{
|
||||
if (!strcmp(shortName, shortName2))
|
||||
{
|
||||
displayName = script.GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
displayName = shortName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
script.SetDisplayName(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::CreateTasks()
|
||||
{
|
||||
for (Script& script : m_scripts)
|
||||
{
|
||||
if (script.GetSchedulerScript() && !Util::EmptyStr(script.GetTaskTime()))
|
||||
{
|
||||
Tokenizer tok(g_Options->GetExtensions(), ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
g_Options->CreateSchedulerTask(0, script.GetTaskTime(),
|
||||
nullptr, Options::scScript, script.GetName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
daemon/extension/ScriptConfig.h
Normal file
110
daemon/extension/ScriptConfig.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCRIPTCONFIG_H
|
||||
#define SCRIPTCONFIG_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Container.h"
|
||||
#include "Options.h"
|
||||
|
||||
class ScriptConfig
|
||||
{
|
||||
public:
|
||||
class Script
|
||||
{
|
||||
public:
|
||||
Script(const char* name, const char* location) :
|
||||
m_name(name), m_location(location), m_displayName(name) {};
|
||||
Script(Script&&) = default;
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetLocation() { return m_location; }
|
||||
void SetDisplayName(const char* displayName) { m_displayName = displayName; }
|
||||
const char* GetDisplayName() { return m_displayName; }
|
||||
bool GetPostScript() { return m_postScript; }
|
||||
void SetPostScript(bool postScript) { m_postScript = postScript; }
|
||||
bool GetScanScript() { return m_scanScript; }
|
||||
void SetScanScript(bool scanScript) { m_scanScript = scanScript; }
|
||||
bool GetQueueScript() { return m_queueScript; }
|
||||
void SetQueueScript(bool queueScript) { m_queueScript = queueScript; }
|
||||
bool GetSchedulerScript() { return m_schedulerScript; }
|
||||
void SetSchedulerScript(bool schedulerScript) { m_schedulerScript = schedulerScript; }
|
||||
bool GetFeedScript() { return m_feedScript; }
|
||||
void SetFeedScript(bool feedScript) { m_feedScript = feedScript; }
|
||||
void SetQueueEvents(const char* queueEvents) { m_queueEvents = queueEvents; }
|
||||
const char* GetQueueEvents() { return m_queueEvents; }
|
||||
void SetTaskTime(const char* taskTime) { m_taskTime = taskTime; }
|
||||
const char* GetTaskTime() { return m_taskTime; }
|
||||
|
||||
private:
|
||||
CString m_name;
|
||||
CString m_location;
|
||||
CString m_displayName;
|
||||
bool m_postScript = false;
|
||||
bool m_scanScript = false;
|
||||
bool m_queueScript = false;
|
||||
bool m_schedulerScript = false;
|
||||
bool m_feedScript = false;
|
||||
CString m_queueEvents;
|
||||
CString m_taskTime;
|
||||
};
|
||||
|
||||
typedef std::list<Script> Scripts;
|
||||
|
||||
class ConfigTemplate
|
||||
{
|
||||
public:
|
||||
ConfigTemplate(Script&& script, const char* templ) :
|
||||
m_script(std::move(script)), m_template(templ) {}
|
||||
Script* GetScript() { return &m_script; }
|
||||
const char* GetTemplate() { return m_template; }
|
||||
|
||||
private:
|
||||
Script m_script;
|
||||
CString m_template;
|
||||
};
|
||||
|
||||
typedef std::deque<ConfigTemplate> ConfigTemplates;
|
||||
|
||||
void InitOptions();
|
||||
Scripts* GetScripts() { return &m_scripts; }
|
||||
bool LoadConfig(Options::OptEntries* optEntries);
|
||||
bool SaveConfig(Options::OptEntries* optEntries);
|
||||
bool LoadConfigTemplates(ConfigTemplates* configTemplates);
|
||||
ConfigTemplates* GetConfigTemplates() { return &m_configTemplates; }
|
||||
|
||||
private:
|
||||
Scripts m_scripts;
|
||||
ConfigTemplates m_configTemplates;
|
||||
|
||||
void InitScripts();
|
||||
void InitConfigTemplates();
|
||||
void CreateTasks();
|
||||
void LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir);
|
||||
void BuildScriptDisplayNames(Scripts* scripts);
|
||||
void LoadScripts(Scripts* scripts);
|
||||
bool LoadScriptFile(Script* script);
|
||||
BString<1024>BuildScriptName(const char* directory, const char* filename, bool isSubDir);
|
||||
bool ScriptExists(Scripts* scripts, const char* scriptName);
|
||||
};
|
||||
|
||||
extern ScriptConfig* g_ScriptConfig;
|
||||
|
||||
#endif
|
||||
724
daemon/feed/FeedCoordinator.cpp
Normal file
724
daemon/feed/FeedCoordinator.cpp
Normal file
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedFilter.h"
|
||||
#include "FeedScript.h"
|
||||
#include "DiskState.h"
|
||||
#include "DupeCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
|
||||
std::unique_ptr<RegEx>& FeedCoordinator::FilterHelper::GetRegEx(int id)
|
||||
{
|
||||
m_regExes.resize(id);
|
||||
return m_regExes[id - 1];
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterHelper::CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen)
|
||||
{
|
||||
const char* dupeStatusName[] = { "", "QUEUED", "DOWNLOADING", "3", "SUCCESS", "5", "6", "7", "WARNING",
|
||||
"9", "10", "11", "12", "13", "14", "15", "FAILURE" };
|
||||
|
||||
DupeCoordinator::EDupeStatus dupeStatus = g_DupeCoordinator->GetDupeStatus(DownloadQueue::Guard(), title, dupeKey);
|
||||
|
||||
BString<1024> statuses;
|
||||
for (int i = 1; i <= (int)DupeCoordinator::dsFailure; i = i << 1)
|
||||
{
|
||||
if (dupeStatus & i)
|
||||
{
|
||||
if (!statuses.Empty())
|
||||
{
|
||||
statuses.Append(",");
|
||||
}
|
||||
statuses.Append(dupeStatusName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(statusBuf, statuses, bufLen);
|
||||
}
|
||||
|
||||
FeedCoordinator::FeedCoordinator()
|
||||
{
|
||||
debug("Creating FeedCoordinator");
|
||||
|
||||
m_downloadQueueObserver.m_owner = this;
|
||||
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
|
||||
|
||||
m_workStateObserver.m_owner = this;
|
||||
g_WorkState->Attach(&m_workStateObserver);
|
||||
}
|
||||
|
||||
FeedCoordinator::~FeedCoordinator()
|
||||
{
|
||||
debug("Destroying FeedCoordinator");
|
||||
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
delete feedDownloader;
|
||||
}
|
||||
m_activeDownloads.clear();
|
||||
}
|
||||
|
||||
void FeedCoordinator::Run()
|
||||
{
|
||||
debug("Entering FeedCoordinator-loop");
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
Util::Sleep(20);
|
||||
}
|
||||
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
g_DiskState->LoadFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
|
||||
time_t lastCleanup = 0;
|
||||
while (!IsStopped())
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
if (!g_WorkState->GetPauseDownload() || m_force || g_Options->GetUrlForce())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t current = Util::CurrentTime();
|
||||
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
|
||||
{
|
||||
m_force = false;
|
||||
// check feed list and update feeds
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (((feedInfo->GetInterval() > 0 &&
|
||||
(feedInfo->GetNextUpdate() == 0 ||
|
||||
current >= feedInfo->GetNextUpdate() ||
|
||||
current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
|
||||
feedInfo->GetFetch()) &&
|
||||
feedInfo->GetStatus() != FeedInfo::fsRunning)
|
||||
{
|
||||
StartFeedDownload(feedInfo, feedInfo->GetFetch());
|
||||
}
|
||||
else if (feedInfo->GetFetch())
|
||||
{
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckSaveFeeds();
|
||||
ResetHangingDownloads();
|
||||
|
||||
if (std::abs(Util::CurrentTime() - lastCleanup) >= 60)
|
||||
{
|
||||
// clean up feed history once a minute
|
||||
CleanupHistory();
|
||||
CleanupCache();
|
||||
CheckSaveFeeds();
|
||||
lastCleanup = Util::CurrentTime();
|
||||
}
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_force)
|
||||
{
|
||||
// don't sleep too long if there active feeds scheduled for redownload
|
||||
m_waitCond.WaitFor(m_downloadsMutex, 1000, [&]{ return IsStopped(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// no active jobs, we can sleep longer:
|
||||
// - if option "UrlForce" is active or if the feed list is empty we need to wake up
|
||||
// only when a new feed preview is requested. We could wait indefinitely for that
|
||||
// but we need to do some job every now and then and therefore we sleep only 60 seconds.
|
||||
// - if option "UrlForce" is disabled we need also to wake up when state "DownloadPaused"
|
||||
// is changed. We detect this via notification from 'WorkState'. However such
|
||||
// notifications are not 100% reliable due to possible race conditions. Therefore
|
||||
// we sleep for max. 5 seconds.
|
||||
int waitInterval = g_Options->GetUrlForce() || m_feeds.empty() ? 60000 : 5000;
|
||||
m_waitCond.WaitFor(m_downloadsMutex, waitInterval, [&]{ return m_force || IsStopped(); });
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("FeedCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
completed = m_activeDownloads.size() == 0;
|
||||
}
|
||||
CheckSaveFeeds();
|
||||
Util::Sleep(100);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("FeedCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting FeedCoordinator-loop");
|
||||
}
|
||||
|
||||
void FeedCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
debug("UrlDownloads are notified");
|
||||
|
||||
// Resume Run() to exit it
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::WorkStateUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
if (g_Options->GetUrlTimeout() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
time_t tm = Util::CurrentTime();
|
||||
|
||||
for (FeedDownloader* feedDownloader: m_activeDownloads)
|
||||
{
|
||||
if (tm - feedDownloader->GetLastUpdateTime() > g_Options->GetUrlTimeout() + 10 &&
|
||||
feedDownloader->GetStatus() == FeedDownloader::adRunning)
|
||||
{
|
||||
error("Cancelling hanging feed download %s", feedDownloader->GetInfoName());
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::LogDebugInfo()
|
||||
{
|
||||
info(" ---------- FeedCoordinator");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
info(" Active Downloads: %i", (int)m_activeDownloads.size());
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
feedDownloader->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::StartFeedDownload(FeedInfo* feedInfo, bool force)
|
||||
{
|
||||
debug("Starting new FeedDownloader for %s", feedInfo->GetName());
|
||||
|
||||
FeedDownloader* feedDownloader = new FeedDownloader();
|
||||
feedDownloader->SetAutoDestroy(true);
|
||||
feedDownloader->Attach(this);
|
||||
feedDownloader->SetFeedInfo(feedInfo);
|
||||
feedDownloader->SetUrl(feedInfo->GetUrl());
|
||||
feedDownloader->SetInfoName(feedInfo->GetName());
|
||||
feedDownloader->SetForce(force || g_Options->GetUrlForce());
|
||||
|
||||
BString<1024> outFilename;
|
||||
if (feedInfo->GetId() > 0)
|
||||
{
|
||||
outFilename.Format("%s%cfeed-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, feedInfo->GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
outFilename.Format("%s%cfeed-%i-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, (int)Util::CurrentTime(), rand());
|
||||
}
|
||||
feedDownloader->SetOutputFilename(outFilename);
|
||||
|
||||
feedInfo->SetStatus(FeedInfo::fsRunning);
|
||||
feedInfo->SetForce(force);
|
||||
feedInfo->SetFetch(false);
|
||||
|
||||
m_activeDownloads.push_back(feedDownloader);
|
||||
feedDownloader->Start();
|
||||
}
|
||||
|
||||
void FeedCoordinator::Update(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from FeedDownloader received");
|
||||
|
||||
FeedDownloader* feedDownloader = (FeedDownloader*) caller;
|
||||
if ((feedDownloader->GetStatus() == WebDownloader::adFinished) ||
|
||||
(feedDownloader->GetStatus() == WebDownloader::adFailed) ||
|
||||
(feedDownloader->GetStatus() == WebDownloader::adRetry))
|
||||
{
|
||||
FeedCompleted(feedDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
|
||||
{
|
||||
debug("Feed downloaded");
|
||||
|
||||
FeedInfo* feedInfo = feedDownloader->GetFeedInfo();
|
||||
bool statusOK = feedDownloader->GetStatus() == WebDownloader::adFinished;
|
||||
if (statusOK)
|
||||
{
|
||||
feedInfo->SetOutputFilename(feedDownloader->GetOutputFilename());
|
||||
}
|
||||
|
||||
// remove downloader from downloader list
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), feedDownloader));
|
||||
}
|
||||
|
||||
SchedulerNextUpdate(feedInfo, statusOK);
|
||||
|
||||
if (statusOK)
|
||||
{
|
||||
if (!feedInfo->GetPreview())
|
||||
{
|
||||
bool scriptSuccess = true;
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
|
||||
feedInfo->GetOutputFilename(), feedInfo->GetId(), &scriptSuccess);
|
||||
if (!scriptSuccess)
|
||||
{
|
||||
feedInfo->SetStatus(FeedInfo::fsFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo);
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
|
||||
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (feedFile)
|
||||
{
|
||||
std::unique_ptr<FeedItemList> feedItems = feedFile->DetachFeedItems();
|
||||
addedNzbs = ProcessFeed(feedInfo, feedItems.get());
|
||||
feedFile.reset();
|
||||
}
|
||||
feedInfo->SetLastUpdate(Util::CurrentTime());
|
||||
feedInfo->SetForce(false);
|
||||
m_save = true;
|
||||
}
|
||||
|
||||
for (std::unique_ptr<NzbInfo>& nzbInfo : addedNzbs)
|
||||
{
|
||||
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), false);
|
||||
}
|
||||
}
|
||||
feedInfo->SetStatus(FeedInfo::fsFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
feedInfo->SetStatus(FeedInfo::fsFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::SchedulerNextUpdate(FeedInfo* feedInfo, bool success)
|
||||
{
|
||||
time_t current = Util::CurrentTime();
|
||||
int interval;
|
||||
|
||||
if (success)
|
||||
{
|
||||
interval = feedInfo->GetInterval() * 60;
|
||||
feedInfo->SetLastInterval(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// On failure schedule next update sooner:
|
||||
// starting with 1 minute and increasing, but not greater than FeedX.Interval
|
||||
interval = feedInfo->GetLastInterval() * 2;
|
||||
interval = std::max(interval, 60);
|
||||
interval = std::min(interval, feedInfo->GetInterval() * 60);
|
||||
feedInfo->SetLastInterval(interval);
|
||||
}
|
||||
|
||||
detail("Scheduling update for feed %s in %i minute(s)", feedInfo->GetName(), interval / 60);
|
||||
feedInfo->SetNextUpdate(current + interval);
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
|
||||
{
|
||||
debug("Filtering feed %s", feedInfo->GetName());
|
||||
|
||||
FilterHelper filterHelper;
|
||||
std::unique_ptr<FeedFilter> feedFilter;
|
||||
|
||||
if (!Util::EmptyStr(feedInfo->GetFilter()))
|
||||
{
|
||||
feedFilter = std::make_unique<FeedFilter>(feedInfo->GetFilter());
|
||||
}
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems)
|
||||
{
|
||||
feedItemInfo.SetMatchStatus(FeedItemInfo::msAccepted);
|
||||
feedItemInfo.SetMatchRule(0);
|
||||
feedItemInfo.SetPauseNzb(feedInfo->GetPauseNzb());
|
||||
feedItemInfo.SetPriority(feedInfo->GetPriority());
|
||||
feedItemInfo.SetAddCategory(feedInfo->GetCategory());
|
||||
feedItemInfo.SetDupeScore(0);
|
||||
feedItemInfo.SetDupeMode(dmScore);
|
||||
feedItemInfo.SetFeedFilterHelper(&filterHelper);
|
||||
feedItemInfo.BuildDupeKey(nullptr, nullptr, nullptr, nullptr);
|
||||
if (feedFilter)
|
||||
{
|
||||
feedFilter->Match(feedItemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> FeedCoordinator::ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
|
||||
{
|
||||
debug("Process feed %s", feedInfo->GetName());
|
||||
|
||||
FilterFeed(feedInfo, feedItems);
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
|
||||
bool firstFetch = feedInfo->GetLastUpdate() == 0;
|
||||
int added = 0;
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems)
|
||||
{
|
||||
if (feedItemInfo.GetMatchStatus() == FeedItemInfo::msAccepted)
|
||||
{
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
|
||||
FeedHistoryInfo::EStatus status = FeedHistoryInfo::hsUnknown;
|
||||
if (firstFetch && feedInfo->GetBacklog())
|
||||
{
|
||||
status = FeedHistoryInfo::hsBacklog;
|
||||
}
|
||||
else if (!feedHistoryInfo)
|
||||
{
|
||||
addedNzbs.push_back(CreateNzbInfo(feedInfo, feedItemInfo));
|
||||
status = FeedHistoryInfo::hsFetched;
|
||||
added++;
|
||||
}
|
||||
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedHistoryInfo->SetLastSeen(Util::CurrentTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_feedHistory.emplace_back(feedItemInfo.GetUrl(), status, Util::CurrentTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (added)
|
||||
{
|
||||
info("%s has %i new item(s)", feedInfo->GetName(), added);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("%s has no new items", feedInfo->GetName());
|
||||
}
|
||||
|
||||
return addedNzbs;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzbInfo> FeedCoordinator::CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo)
|
||||
{
|
||||
debug("Download %s from %s", feedItemInfo.GetUrl(), feedInfo->GetName());
|
||||
|
||||
std::unique_ptr<NzbInfo> nzbInfo = std::make_unique<NzbInfo>();
|
||||
nzbInfo->SetKind(NzbInfo::nkUrl);
|
||||
nzbInfo->SetFeedId(feedInfo->GetId());
|
||||
nzbInfo->SetUrl(feedItemInfo.GetUrl());
|
||||
|
||||
// add .nzb-extension if not present
|
||||
BString<1024> nzbName = feedItemInfo.GetFilename();
|
||||
char* ext = strrchr(nzbName, '.');
|
||||
if (ext && !strcasecmp(ext, ".nzb"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
if (!nzbName.Empty())
|
||||
{
|
||||
BString<1024> nzbName2("%s.nzb", *nzbName);
|
||||
nzbInfo->SetFilename(FileSystem::MakeValidFilename(nzbName2));
|
||||
}
|
||||
|
||||
nzbInfo->SetCategory(feedItemInfo.GetAddCategory());
|
||||
nzbInfo->SetPriority(feedItemInfo.GetPriority());
|
||||
nzbInfo->SetAddUrlPaused(feedItemInfo.GetPauseNzb());
|
||||
nzbInfo->SetDupeKey(feedItemInfo.GetDupeKey());
|
||||
nzbInfo->SetDupeScore(feedItemInfo.GetDupeScore());
|
||||
nzbInfo->SetDupeMode(feedItemInfo.GetDupeMode());
|
||||
nzbInfo->SetSize(feedItemInfo.GetSize());
|
||||
nzbInfo->SetMinTime(feedItemInfo.GetTime());
|
||||
nzbInfo->SetMaxTime(feedItemInfo.GetTime());
|
||||
|
||||
return nzbInfo;
|
||||
}
|
||||
|
||||
std::shared_ptr<FeedItemList> FeedCoordinator::ViewFeed(int id)
|
||||
{
|
||||
if (id < 1 || id > (int)m_feeds.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedInfo>& feedInfo = m_feeds[id - 1];
|
||||
|
||||
return PreviewFeed(feedInfo->GetId(), feedInfo->GetName(), feedInfo->GetUrl(), feedInfo->GetFilter(),
|
||||
feedInfo->GetBacklog(), feedInfo->GetPauseNzb(), feedInfo->GetCategory(),
|
||||
feedInfo->GetPriority(), feedInfo->GetInterval(), feedInfo->GetExtensions(), 0, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
|
||||
const char* name, const char* url, const char* filter, bool backlog, bool pauseNzb,
|
||||
const char* category, int priority, int interval, const char* feedScript,
|
||||
int cacheTimeSec, const char* cacheId)
|
||||
{
|
||||
debug("Preview feed %s", name);
|
||||
|
||||
std::unique_ptr<FeedInfo> feedInfo = std::make_unique<FeedInfo>(id, name, url, backlog, interval,
|
||||
filter, pauseNzb, category, priority, feedScript);
|
||||
feedInfo->SetPreview(true);
|
||||
|
||||
std::shared_ptr<FeedItemList> feedItems;
|
||||
bool hasCache = false;
|
||||
if (cacheTimeSec > 0 && *cacheId != '\0')
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedCacheItem& feedCacheItem : m_feedCache)
|
||||
{
|
||||
if (!strcmp(feedCacheItem.GetCacheId(), cacheId))
|
||||
{
|
||||
feedCacheItem.SetLastUsage(Util::CurrentTime());
|
||||
feedItems = feedCacheItem.GetFeedItems();
|
||||
hasCache = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasCache)
|
||||
{
|
||||
bool firstFetch = true;
|
||||
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
for (FeedInfo* feedInfo2 : &m_feeds)
|
||||
{
|
||||
if (!strcmp(feedInfo2->GetUrl(), feedInfo->GetUrl()) &&
|
||||
!strcmp(feedInfo2->GetFilter(), feedInfo->GetFilter()) &&
|
||||
feedInfo2->GetLastUpdate() > 0)
|
||||
{
|
||||
firstFetch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StartFeedDownload(feedInfo.get(), true);
|
||||
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
// wait until the download in a separate thread completes
|
||||
while (feedInfo->GetStatus() == FeedInfo::fsRunning)
|
||||
{
|
||||
Util::Sleep(100);
|
||||
}
|
||||
|
||||
// now can process the feed
|
||||
|
||||
if (feedInfo->GetStatus() != FeedInfo::fsFinished)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
|
||||
feedInfo->GetOutputFilename(), feedInfo->GetId(), nullptr);
|
||||
|
||||
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo.get());
|
||||
if (!feedFile)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
feedItems = feedFile->DetachFeedItems();
|
||||
feedFile.reset();
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems.get())
|
||||
{
|
||||
feedItemInfo.SetStatus(firstFetch && feedInfo->GetBacklog() ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedItemInfo.SetStatus((FeedItemInfo::EStatus)feedHistoryInfo->GetStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterFeed(feedInfo.get(), feedItems.get());
|
||||
feedInfo.reset();
|
||||
|
||||
if (cacheTimeSec > 0 && *cacheId != '\0' && !hasCache)
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
m_feedCache.emplace_back(url, cacheTimeSec, cacheId, Util::CurrentTime(), feedItems);
|
||||
}
|
||||
|
||||
return feedItems;
|
||||
}
|
||||
|
||||
void FeedCoordinator::FetchFeed(int id)
|
||||
{
|
||||
debug("FetchFeeds");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (feedInfo->GetId() == id || id == 0)
|
||||
{
|
||||
feedInfo->SetFetch(true);
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
|
||||
{
|
||||
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename(), feedInfo->GetName());
|
||||
if (feedFile->Parse())
|
||||
{
|
||||
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Feed file %s kept for troubleshooting (will be deleted on next successful feed fetch)", feedInfo->GetOutputFilename());
|
||||
feedFile.reset();
|
||||
}
|
||||
return feedFile;
|
||||
}
|
||||
|
||||
void FeedCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from URL-Coordinator received");
|
||||
|
||||
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
|
||||
if (queueAspect->action == DownloadQueue::eaUrlCompleted)
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(queueAspect->nzbInfo->GetUrl());
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_feedHistory.emplace_back(queueAspect->nzbInfo->GetUrl(), FeedHistoryInfo::hsFetched, Util::CurrentTime());
|
||||
}
|
||||
m_save = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedCoordinator::HasActiveDownloads()
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
return !m_activeDownloads.empty();
|
||||
}
|
||||
|
||||
void FeedCoordinator::CheckSaveFeeds()
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_save)
|
||||
{
|
||||
debug("CheckSaveFeeds: save");
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->SaveFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
m_save = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupHistory()
|
||||
{
|
||||
debug("CleanupHistory");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t oldestUpdate = Util::CurrentTime();
|
||||
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (feedInfo->GetLastUpdate() < oldestUpdate)
|
||||
{
|
||||
oldestUpdate = feedInfo->GetLastUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
time_t borderDate = oldestUpdate - g_Options->GetFeedHistory() * 60*60*24;
|
||||
|
||||
m_feedHistory.erase(std::remove_if(m_feedHistory.begin(), m_feedHistory.end(),
|
||||
[borderDate, this](FeedHistoryInfo& feedHistoryInfo)
|
||||
{
|
||||
if (feedHistoryInfo.GetLastSeen() < borderDate)
|
||||
{
|
||||
detail("Deleting %s from feed history", feedHistoryInfo.GetUrl());
|
||||
m_save = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_feedHistory.end());
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupCache()
|
||||
{
|
||||
debug("CleanupCache");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t curTime = Util::CurrentTime();
|
||||
|
||||
m_feedCache.remove_if(
|
||||
[curTime](FeedCacheItem& feedCacheItem)
|
||||
{
|
||||
if (feedCacheItem.GetLastUsage() + feedCacheItem.GetCacheTimeSec() < curTime ||
|
||||
feedCacheItem.GetLastUsage() > curTime)
|
||||
{
|
||||
debug("Deleting %s from feed cache", feedCacheItem.GetUrl());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
150
daemon/feed/FeedCoordinator.h
Normal file
150
daemon/feed/FeedCoordinator.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDCOORDINATOR_H
|
||||
#define FEEDCOORDINATOR_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
class FeedDownloader;
|
||||
|
||||
class FeedCoordinator : public Thread, public Observer, public Subject, public Debuggable
|
||||
{
|
||||
public:
|
||||
FeedCoordinator();
|
||||
virtual ~FeedCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* caller, void* aspect);
|
||||
void AddFeed(std::unique_ptr<FeedInfo> feedInfo) { m_feeds.push_back(std::move(feedInfo)); }
|
||||
|
||||
/* may return empty pointer on error */
|
||||
std::shared_ptr<FeedItemList> PreviewFeed(int id, const char* name, const char* url,
|
||||
const char* filter, bool backlog, bool pauseNzb, const char* category, int priority,
|
||||
int interval, const char* feedScript, int cacheTimeSec, const char* cacheId);
|
||||
|
||||
/* may return empty pointer on error */
|
||||
std::shared_ptr<FeedItemList> ViewFeed(int id);
|
||||
|
||||
void FetchFeed(int id);
|
||||
bool HasActiveDownloads();
|
||||
Feeds* GetFeeds() { return &m_feeds; }
|
||||
|
||||
protected:
|
||||
virtual void LogDebugInfo();
|
||||
|
||||
private:
|
||||
class DownloadQueueObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class WorkStateObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class FeedCacheItem
|
||||
{
|
||||
public:
|
||||
FeedCacheItem(const char* url, int cacheTimeSec,const char* cacheId,
|
||||
time_t lastUsage, std::shared_ptr<FeedItemList> feedItems) :
|
||||
m_url(url), m_cacheTimeSec(cacheTimeSec), m_cacheId(cacheId),
|
||||
m_lastUsage(lastUsage), m_feedItems(feedItems) {}
|
||||
const char* GetUrl() { return m_url; }
|
||||
int GetCacheTimeSec() { return m_cacheTimeSec; }
|
||||
const char* GetCacheId() { return m_cacheId; }
|
||||
time_t GetLastUsage() { return m_lastUsage; }
|
||||
void SetLastUsage(time_t lastUsage) { m_lastUsage = lastUsage; }
|
||||
std::shared_ptr<FeedItemList> GetFeedItems() { return m_feedItems; }
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
int m_cacheTimeSec;
|
||||
CString m_cacheId;
|
||||
time_t m_lastUsage;
|
||||
std::shared_ptr<FeedItemList> m_feedItems;
|
||||
};
|
||||
|
||||
class FilterHelper : public FeedFilterHelper
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<RegEx>& GetRegEx(int id);
|
||||
virtual void CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen);
|
||||
private:
|
||||
std::vector<std::unique_ptr<RegEx>> m_regExes;
|
||||
};
|
||||
|
||||
typedef std::list<FeedCacheItem> FeedCache;
|
||||
typedef std::deque<FeedDownloader*> ActiveDownloads;
|
||||
|
||||
Feeds m_feeds;
|
||||
ActiveDownloads m_activeDownloads;
|
||||
FeedHistory m_feedHistory;
|
||||
Mutex m_downloadsMutex;
|
||||
DownloadQueueObserver m_downloadQueueObserver;
|
||||
WorkStateObserver m_workStateObserver;
|
||||
bool m_force = false;
|
||||
bool m_save = false;
|
||||
FeedCache m_feedCache;
|
||||
ConditionVar m_waitCond;
|
||||
bool m_wokenUp = false;
|
||||
|
||||
void StartFeedDownload(FeedInfo* feedInfo, bool force);
|
||||
void FeedCompleted(FeedDownloader* feedDownloader);
|
||||
void FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
|
||||
std::vector<std::unique_ptr<NzbInfo>> ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
|
||||
std::unique_ptr<NzbInfo> CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo);
|
||||
void ResetHangingDownloads();
|
||||
void DownloadQueueUpdate(Subject* caller, void* aspect);
|
||||
void CleanupHistory();
|
||||
void CleanupCache();
|
||||
void CheckSaveFeeds();
|
||||
std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
|
||||
void SchedulerNextUpdate(FeedInfo* feedInfo, bool success);
|
||||
void WorkStateUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
extern FeedCoordinator* g_FeedCoordinator;
|
||||
|
||||
class FeedDownloader : public WebDownloader
|
||||
{
|
||||
public:
|
||||
void SetFeedInfo(FeedInfo* feedInfo) { m_feedInfo = feedInfo; }
|
||||
FeedInfo* GetFeedInfo() { return m_feedInfo; }
|
||||
|
||||
private:
|
||||
FeedInfo* m_feedInfo;
|
||||
};
|
||||
|
||||
#endif
|
||||
607
daemon/feed/FeedFile.cpp
Normal file
607
daemon/feed/FeedFile.cpp
Normal file
@@ -0,0 +1,607 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
FeedFile::FeedFile(const char* fileName, const char* infoName) :
|
||||
m_fileName(fileName), m_infoName(infoName)
|
||||
{
|
||||
debug("Creating FeedFile");
|
||||
|
||||
m_feedItems = std::make_unique<FeedItemList>();
|
||||
|
||||
#ifndef WIN32
|
||||
m_feedItemInfo = nullptr;
|
||||
m_tagContent.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::LogDebugInfo()
|
||||
{
|
||||
info(" FeedFile %s", *m_fileName);
|
||||
}
|
||||
|
||||
void FeedFile::ParseSubject(FeedItemInfo& feedItemInfo)
|
||||
{
|
||||
// if title has quatation marks we use only part within quatation marks
|
||||
char* p = (char*)feedItemInfo.GetTitle();
|
||||
char* start = strchr(p, '\"');
|
||||
if (start)
|
||||
{
|
||||
start++;
|
||||
char* end = strchr(start + 1, '\"');
|
||||
if (end)
|
||||
{
|
||||
int len = (int)(end - start);
|
||||
char* point = strchr(start + 1, '.');
|
||||
if (point && point < end)
|
||||
{
|
||||
CString filename(start, len);
|
||||
|
||||
char* ext = strrchr(filename, '.');
|
||||
if (ext && !strcasecmp(ext, ".par2"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
|
||||
feedItemInfo.SetFilename(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feedItemInfo.SetFilename(feedItemInfo.GetTitle());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
bool FeedFile::Parse()
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MSXML::IXMLDOMDocumentPtr doc;
|
||||
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the XML document file...
|
||||
doc->put_resolveExternals(VARIANT_FALSE);
|
||||
doc->put_validateOnParse(VARIANT_FALSE);
|
||||
doc->put_async(VARIANT_FALSE);
|
||||
|
||||
_variant_t vFilename(*WString(m_fileName));
|
||||
|
||||
// 1. first trying to load via filename without URL-encoding (certain charaters doesn't work when encoded)
|
||||
VARIANT_BOOL success = doc->load(vFilename);
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
// 2. now trying filename encoded as URL
|
||||
char url[2048];
|
||||
EncodeUrl(m_fileName, url, 2048);
|
||||
debug("url=\"%s\"", url);
|
||||
_variant_t vUrl(url);
|
||||
|
||||
success = doc->load(vUrl);
|
||||
}
|
||||
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
const char* errMsg = r;
|
||||
error("Error parsing rss feed %s: %s", *m_infoName, errMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = ParseFeed(doc);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void FeedFile::EncodeUrl(const char* filename, char* url, int bufLen)
|
||||
{
|
||||
WString widefilename(filename);
|
||||
|
||||
char* end = url + bufLen;
|
||||
for (wchar_t* p = widefilename; *p && url < end - 3; p++)
|
||||
{
|
||||
wchar_t ch = *p;
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z') ||
|
||||
ch == '-' || ch == '.' || ch == '_' || ch == '~')
|
||||
{
|
||||
*url++ = (char)ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
*url++ = '%';
|
||||
uint32 a = (uint32)ch >> 4;
|
||||
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
|
||||
}
|
||||
}
|
||||
*url = '\0';
|
||||
}
|
||||
|
||||
bool FeedFile::ParseFeed(IUnknown* nzb)
|
||||
{
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr itemList = root->selectNodes("/rss/channel/item");
|
||||
for (int i = 0; i < itemList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
|
||||
|
||||
m_feedItems->emplace_back();
|
||||
FeedItemInfo& feedItemInfo = m_feedItems->back();
|
||||
|
||||
MSXML::IXMLDOMNodePtr tag;
|
||||
MSXML::IXMLDOMNodePtr attr;
|
||||
|
||||
// <title>Debian 6</title>
|
||||
tag = node->selectSingleNode("title");
|
||||
if (!tag)
|
||||
{
|
||||
// bad rss feed
|
||||
return false;
|
||||
}
|
||||
_bstr_t title(tag->Gettext());
|
||||
feedItemInfo.SetTitle(title);
|
||||
ParseSubject(feedItemInfo);
|
||||
|
||||
// <pubDate>Wed, 26 Jun 2013 00:02:54 -0600</pubDate>
|
||||
tag = node->selectSingleNode("pubDate");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t time(tag->Gettext());
|
||||
time_t unixtime = WebUtil::ParseRfc822DateTime(time);
|
||||
if (unixtime > 0)
|
||||
{
|
||||
feedItemInfo.SetTime(unixtime);
|
||||
}
|
||||
}
|
||||
|
||||
// <category>Movies > HD</category>
|
||||
tag = node->selectSingleNode("category");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t category(tag->Gettext());
|
||||
feedItemInfo.SetCategory(category);
|
||||
}
|
||||
|
||||
// <description>long text</description>
|
||||
tag = node->selectSingleNode("description");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t bdescription(tag->Gettext());
|
||||
// cleanup CDATA
|
||||
CString description = (const char*)bdescription;
|
||||
WebUtil::XmlStripTags(description);
|
||||
WebUtil::XmlDecode(description);
|
||||
WebUtil::XmlRemoveEntities(description);
|
||||
feedItemInfo.SetDescription(description);
|
||||
}
|
||||
|
||||
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
|
||||
tag = node->selectSingleNode("enclosure");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("url");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t url(attr->Gettext());
|
||||
feedItemInfo.SetUrl(url);
|
||||
}
|
||||
|
||||
attr = tag->Getattributes()->getNamedItem("length");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bsize(attr->Gettext());
|
||||
int64 size = atoll(bsize);
|
||||
feedItemInfo.SetSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!feedItemInfo.GetUrl())
|
||||
{
|
||||
// <link>https://nzb.org/fetch/334534ce/4364564564</link>
|
||||
tag = node->selectSingleNode("link");
|
||||
if (!tag)
|
||||
{
|
||||
// bad rss feed
|
||||
return false;
|
||||
}
|
||||
_bstr_t link(tag->Gettext());
|
||||
feedItemInfo.SetUrl(link);
|
||||
}
|
||||
|
||||
|
||||
// newznab special
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (feedItemInfo.GetSize() == 0)
|
||||
{
|
||||
tag = node->selectSingleNode("newznab:attr[@name='size'] | nZEDb:attr[@name='size']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bsize(attr->Gettext());
|
||||
int64 size = atoll(bsize);
|
||||
feedItemInfo.SetSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='imdb'] | nZEDb:attr[@name='imdb']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetImdbId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='rageid'] | nZEDb:attr[@name='rageid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetRageId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="tdvdbid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='tvdbid'] | nZEDb:attr[@name='tvdbid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetTvdbId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvmazeid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='tvmazeid'] | nZEDb:attr[@name='tvmazeid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetTvmazeId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='episode'] | nZEDb:attr[@name='episode']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
feedItemInfo.SetEpisode(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='season'] | nZEDb:attr[@name='season']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
feedItemInfo.SetSeason(val);
|
||||
}
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr | nZEDb:attr");
|
||||
for (int i = 0; i < itemList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
|
||||
MSXML::IXMLDOMNodePtr name = node->Getattributes()->getNamedItem("name");
|
||||
MSXML::IXMLDOMNodePtr value = node->Getattributes()->getNamedItem("value");
|
||||
if (name && value)
|
||||
{
|
||||
_bstr_t bname(name->Gettext());
|
||||
_bstr_t bval(value->Gettext());
|
||||
feedItemInfo.GetAttributes()->emplace_back(bname, bval);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool FeedFile::Parse()
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
error("Could not parse rss feed, program was compiled without libxml2 support");
|
||||
return false;
|
||||
#else
|
||||
xmlSAXHandler SAX_handler = {0};
|
||||
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
|
||||
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
|
||||
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
|
||||
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
|
||||
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
|
||||
|
||||
m_ignoreNextError = false;
|
||||
|
||||
int ret = xmlSAXUserParseFile(&SAX_handler, this, m_fileName);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse rss feed %s", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::Parse_StartElement(const char *name, const char **atts)
|
||||
{
|
||||
ResetTagContent();
|
||||
|
||||
if (!strcmp("item", name))
|
||||
{
|
||||
m_feedItems->emplace_back();
|
||||
m_feedItemInfo = &m_feedItems->back();
|
||||
}
|
||||
else if (!strcmp("enclosure", name) && m_feedItemInfo)
|
||||
{
|
||||
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
|
||||
for (; *atts; atts+=2)
|
||||
{
|
||||
if (!strcmp("url", atts[0]))
|
||||
{
|
||||
CString url = atts[1];
|
||||
WebUtil::XmlDecode(url);
|
||||
m_feedItemInfo->SetUrl(url);
|
||||
}
|
||||
else if (!strcmp("length", atts[0]))
|
||||
{
|
||||
int64 size = atoll(atts[1]);
|
||||
m_feedItemInfo->SetSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_feedItemInfo &&
|
||||
(!strcmp("newznab:attr", name) || !strcmp("nZEDb:attr", name)) &&
|
||||
atts[0] && atts[1] && atts[2] && atts[3] &&
|
||||
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
|
||||
{
|
||||
m_feedItemInfo->GetAttributes()->emplace_back(atts[1], atts[3]);
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (m_feedItemInfo->GetSize() == 0 &&
|
||||
!strcmp("size", atts[1]))
|
||||
{
|
||||
int64 size = atoll(atts[3]);
|
||||
m_feedItemInfo->SetSize(size);
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
else if (!strcmp("imdb", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetImdbId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
else if (!strcmp("rageid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetRageId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvdbid" value="33877"/>
|
||||
else if (!strcmp("tvdbid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetTvdbId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvmazeid" value="33877"/>
|
||||
else if (!strcmp("tvmazeid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetTvmazeId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
else if (!strcmp("episode", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetEpisode(atts[3]);
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
else if (!strcmp("season", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetSeason(atts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_EndElement(const char *name)
|
||||
{
|
||||
if (!strcmp("title", name) && m_feedItemInfo)
|
||||
{
|
||||
m_feedItemInfo->SetTitle(m_tagContent);
|
||||
ResetTagContent();
|
||||
ParseSubject(*m_feedItemInfo);
|
||||
}
|
||||
else if (!strcmp("link", name) && m_feedItemInfo &&
|
||||
(!m_feedItemInfo->GetUrl() || strlen(m_feedItemInfo->GetUrl()) == 0))
|
||||
{
|
||||
m_feedItemInfo->SetUrl(m_tagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("category", name) && m_feedItemInfo)
|
||||
{
|
||||
m_feedItemInfo->SetCategory(m_tagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("description", name) && m_feedItemInfo)
|
||||
{
|
||||
// cleanup CDATA
|
||||
CString description = *m_tagContent;
|
||||
WebUtil::XmlStripTags(description);
|
||||
WebUtil::XmlDecode(description);
|
||||
WebUtil::XmlRemoveEntities(description);
|
||||
m_feedItemInfo->SetDescription(description);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("pubDate", name) && m_feedItemInfo)
|
||||
{
|
||||
time_t unixtime = WebUtil::ParseRfc822DateTime(m_tagContent);
|
||||
if (unixtime > 0)
|
||||
{
|
||||
m_feedItemInfo->SetTime(unixtime);
|
||||
}
|
||||
ResetTagContent();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_Content(const char *buf, int len)
|
||||
{
|
||||
m_tagContent.Append(buf, len);
|
||||
}
|
||||
|
||||
void FeedFile::ResetTagContent()
|
||||
{
|
||||
m_tagContent.Clear();
|
||||
}
|
||||
|
||||
void FeedFile::SAX_StartElement(FeedFile* file, const char *name, const char **atts)
|
||||
{
|
||||
file->Parse_StartElement(name, atts);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_EndElement(FeedFile* file, const char *name)
|
||||
{
|
||||
file->Parse_EndElement(name);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_characters(FeedFile* file, const char * xmlstr, int len)
|
||||
{
|
||||
char* str = (char*)xmlstr;
|
||||
|
||||
// trim starting blanks
|
||||
int off = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
off++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int newlen = len - off;
|
||||
|
||||
// trim ending blanks
|
||||
for (int i = len - 1; i >= off; i--)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
newlen--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newlen > 0)
|
||||
{
|
||||
// interpret tag content
|
||||
file->Parse_Content(str + off, newlen);
|
||||
}
|
||||
}
|
||||
|
||||
void* FeedFile::SAX_getEntity(FeedFile* file, const char * name)
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
void* e = nullptr;
|
||||
#else
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
#endif
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
file->m_ignoreNextError = true;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void FeedFile::SAX_error(FeedFile* file, const char *msg, ...)
|
||||
{
|
||||
if (file->m_ignoreNextError)
|
||||
{
|
||||
file->m_ignoreNextError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, msg);
|
||||
char errMsg[1024];
|
||||
vsnprintf(errMsg, sizeof(errMsg), msg, argp);
|
||||
errMsg[1024-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// remove trailing CRLF
|
||||
for (char* pend = errMsg + strlen(errMsg) - 1; pend >= errMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
error("Error parsing rss feed %s: %s", *file->m_infoName, errMsg);
|
||||
}
|
||||
#endif
|
||||
62
daemon/feed/FeedFile.h
Normal file
62
daemon/feed/FeedFile.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDFILE_H
|
||||
#define FEEDFILE_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "FeedInfo.h"
|
||||
|
||||
class FeedFile
|
||||
{
|
||||
public:
|
||||
FeedFile(const char* fileName, const char* infoName);
|
||||
bool Parse();
|
||||
std::unique_ptr<FeedItemList> DetachFeedItems() { return std::move(m_feedItems); }
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
private:
|
||||
std::unique_ptr<FeedItemList> m_feedItems;
|
||||
CString m_fileName;
|
||||
CString m_infoName;
|
||||
|
||||
void ParseSubject(FeedItemInfo& feedItemInfo);
|
||||
#ifdef WIN32
|
||||
bool ParseFeed(IUnknown* nzb);
|
||||
static void EncodeUrl(const char* filename, char* url, int bufLen);
|
||||
#else
|
||||
FeedItemInfo* m_feedItemInfo;
|
||||
StringBuilder m_tagContent;
|
||||
bool m_ignoreNextError;
|
||||
|
||||
static void SAX_StartElement(FeedFile* file, const char *name, const char **atts);
|
||||
static void SAX_EndElement(FeedFile* file, const char *name);
|
||||
static void SAX_characters(FeedFile* file, const char * xmlstr, int len);
|
||||
static void* SAX_getEntity(FeedFile* file, const char * name);
|
||||
static void SAX_error(FeedFile* file, const char *msg, ...);
|
||||
void Parse_StartElement(const char *name, const char **atts);
|
||||
void Parse_EndElement(const char *name);
|
||||
void Parse_Content(const char *buf, int len);
|
||||
void ResetTagContent();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
1055
daemon/feed/FeedFilter.cpp
Normal file
1055
daemon/feed/FeedFilter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
188
daemon/feed/FeedFilter.h
Normal file
188
daemon/feed/FeedFilter.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDFILTER_H
|
||||
#define FEEDFILTER_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
class FeedFilter
|
||||
{
|
||||
public:
|
||||
FeedFilter(const char* filter);
|
||||
void Match(FeedItemInfo& feedItemInfo);
|
||||
|
||||
private:
|
||||
typedef std::vector<CString> RefValues;
|
||||
|
||||
enum ETermCommand
|
||||
{
|
||||
fcText,
|
||||
fcRegex,
|
||||
fcEqual,
|
||||
fcLess,
|
||||
fcLessEqual,
|
||||
fcGreater,
|
||||
fcGreaterEqual,
|
||||
fcOpeningBrace,
|
||||
fcClosingBrace,
|
||||
fcOrOperator
|
||||
};
|
||||
|
||||
class Term
|
||||
{
|
||||
public:
|
||||
Term() {}
|
||||
Term(Term&&) = delete; // catch performance issues
|
||||
void SetRefValues(RefValues* refValues) { m_refValues = refValues; }
|
||||
bool Compile(char* token);
|
||||
bool Match(FeedItemInfo& feedItemInfo);
|
||||
ETermCommand GetCommand() { return m_command; }
|
||||
|
||||
private:
|
||||
bool m_positive;
|
||||
CString m_field;
|
||||
ETermCommand m_command;
|
||||
CString m_param;
|
||||
int64 m_intParam = 0;
|
||||
double m_floatParam = 0.0;
|
||||
bool m_float = false;
|
||||
std::unique_ptr<RegEx> m_regEx;
|
||||
RefValues* m_refValues = nullptr;
|
||||
|
||||
bool GetFieldData(const char* field, FeedItemInfo* feedItemInfo,
|
||||
const char** StrValue, int64* IntValue);
|
||||
bool ParseParam(const char* field, const char* param);
|
||||
bool ParseSizeParam(const char* param);
|
||||
bool ParseAgeParam(const char* param);
|
||||
bool ParseNumericParam(const char* param);
|
||||
bool MatchValue(const char* strValue, int64 intValue);
|
||||
bool MatchText(const char* strValue);
|
||||
bool MatchRegex(const char* strValue);
|
||||
void FillWildMaskRefValues(const char* strValue, WildMask* mask, int refOffset);
|
||||
void FillRegExRefValues(const char* strValue, RegEx* regEx);
|
||||
};
|
||||
|
||||
typedef std::deque<Term> TermList;
|
||||
|
||||
enum ERuleCommand
|
||||
{
|
||||
frAccept,
|
||||
frReject,
|
||||
frRequire,
|
||||
frOptions,
|
||||
frComment
|
||||
};
|
||||
|
||||
class Rule
|
||||
{
|
||||
public:
|
||||
Rule() {}
|
||||
Rule(Rule&&) = delete; // catch performance issues
|
||||
void Compile(char* rule);
|
||||
bool IsValid() { return m_isValid; }
|
||||
ERuleCommand GetCommand() { return m_command; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
int GetPriority() { return m_priority; }
|
||||
int GetAddPriority() { return m_addPriority; }
|
||||
bool GetPause() { return m_pause; }
|
||||
const char* GetDupeKey() { return m_dupeKey; }
|
||||
const char* GetAddDupeKey() { return m_addDupeKey; }
|
||||
int GetDupeScore() { return m_dupeScore; }
|
||||
int GetAddDupeScore() { return m_addDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_dupeMode; }
|
||||
const char* GetRageId() { return m_rageId; }
|
||||
const char* GetTvdbId() { return m_tvdbId; }
|
||||
const char* GetTvmazeId() { return m_tvmazeId; }
|
||||
const char* GetSeries() { return m_series; }
|
||||
bool HasCategory() { return m_hasCategory; }
|
||||
bool HasPriority() { return m_hasPriority; }
|
||||
bool HasAddPriority() { return m_hasAddPriority; }
|
||||
bool HasPause() { return m_hasPause; }
|
||||
bool HasDupeScore() { return m_hasDupeScore; }
|
||||
bool HasAddDupeScore() { return m_hasAddDupeScore; }
|
||||
bool HasDupeKey() { return m_hasDupeKey; }
|
||||
bool HasAddDupeKey() { return m_hasAddDupeKey; }
|
||||
bool HasDupeMode() { return m_hasDupeMode; }
|
||||
bool HasRageId() { return m_hasRageId; }
|
||||
bool HasTvdbId() { return m_hasTvdbId; }
|
||||
bool HasTvmazeId() { return m_hasTvmazeId; }
|
||||
bool HasSeries() { return m_hasSeries; }
|
||||
bool Match(FeedItemInfo& feedItemInfo);
|
||||
void ExpandRefValues(FeedItemInfo& feedItemInfo, CString* destStr, const char* patStr);
|
||||
const char* GetRefValue(FeedItemInfo& feedItemInfo, const char* varName);
|
||||
|
||||
private:
|
||||
bool m_isValid = false;
|
||||
ERuleCommand m_command = frAccept;
|
||||
CString m_category;
|
||||
int m_priority = 0;
|
||||
int m_addPriority = 0;
|
||||
bool m_pause = false;
|
||||
int m_dupeScore;
|
||||
int m_addDupeScore = 0;
|
||||
CString m_dupeKey;
|
||||
CString m_addDupeKey;
|
||||
EDupeMode m_dupeMode = dmScore;
|
||||
CString m_series;
|
||||
CString m_rageId;
|
||||
CString m_tvdbId;
|
||||
CString m_tvmazeId;
|
||||
bool m_hasCategory = false;
|
||||
bool m_hasPriority = false;
|
||||
bool m_hasAddPriority = false;
|
||||
bool m_hasPause = false;
|
||||
bool m_hasDupeScore = false;
|
||||
bool m_hasAddDupeScore = false;
|
||||
bool m_hasDupeKey = false;
|
||||
bool m_hasAddDupeKey = false;
|
||||
bool m_hasDupeMode = false;
|
||||
bool m_hasPatCategory = false;
|
||||
bool m_hasPatDupeKey = false;
|
||||
bool m_hasPatAddDupeKey = false;
|
||||
bool m_hasSeries = false;
|
||||
bool m_hasRageId = false;
|
||||
bool m_hasTvdbId = false;
|
||||
bool m_hasTvmazeId = false;
|
||||
CString m_patCategory;
|
||||
CString m_patDupeKey;
|
||||
CString m_patAddDupeKey;
|
||||
TermList m_terms;
|
||||
RefValues m_refValues;
|
||||
|
||||
char* CompileCommand(char* rule);
|
||||
char* CompileOptions(char* rule);
|
||||
bool CompileTerm(char* term);
|
||||
bool MatchExpression(FeedItemInfo& feedItemInfo);
|
||||
};
|
||||
|
||||
typedef std::deque<Rule> RuleList;
|
||||
|
||||
RuleList m_rules;
|
||||
|
||||
void Compile(const char* filter);
|
||||
void CompileRule(char* rule);
|
||||
void ApplyOptions(Rule& rule, FeedItemInfo& feedItemInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
202
daemon/feed/FeedInfo.cpp
Normal file
202
daemon/feed/FeedInfo.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
FeedInfo::FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
|
||||
const char* filter, bool pauseNzb, const char* category, int priority, const char* extensions) :
|
||||
m_backlog(backlog), m_interval(interval), m_pauseNzb(pauseNzb), m_priority(priority)
|
||||
{
|
||||
m_id = id;
|
||||
m_name = name ? name : "";
|
||||
if (m_name.Length() == 0)
|
||||
{
|
||||
m_name.Format("Feed%i", m_id);
|
||||
}
|
||||
m_url = url ? url : "";
|
||||
m_filter = filter ? filter : "";
|
||||
m_filterHash = Util::HashBJ96(m_filter, strlen(m_filter), 0);
|
||||
m_category = category ? category : "";
|
||||
m_extensions = extensions ? extensions : "";
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* name)
|
||||
{
|
||||
for (Attr& attr : this)
|
||||
{
|
||||
if (!strcasecmp(attr.GetName(), name))
|
||||
{
|
||||
return &attr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void FeedItemInfo::SetSeason(const char* season)
|
||||
{
|
||||
m_season = season;
|
||||
m_seasonNum = season ? ParsePrefixedInt(season) : 0;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetEpisode(const char* episode)
|
||||
{
|
||||
m_episode = episode;
|
||||
m_episodeNum = episode ? ParsePrefixedInt(episode) : 0;
|
||||
}
|
||||
|
||||
int FeedItemInfo::ParsePrefixedInt(const char *value)
|
||||
{
|
||||
const char* val = value;
|
||||
if (!strchr("0123456789", *val))
|
||||
{
|
||||
val++;
|
||||
}
|
||||
return atoi(val);
|
||||
}
|
||||
|
||||
void FeedItemInfo::AppendDupeKey(const char* extraDupeKey)
|
||||
{
|
||||
if (!m_dupeKey.Empty() && !Util::EmptyStr(extraDupeKey))
|
||||
{
|
||||
m_dupeKey.AppendFmt("-%s", extraDupeKey);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedItemInfo::BuildDupeKey(const char* rageId, const char* tvdbId, const char* tvmazeId, const char* series)
|
||||
{
|
||||
int rageIdVal = !Util::EmptyStr(rageId) ? atoi(rageId) : m_rageId;
|
||||
int tvdbIdVal = !Util::EmptyStr(tvdbId) ? atoi(tvdbId) : m_tvdbId;
|
||||
int tvmazeIdVal = !Util::EmptyStr(tvmazeId) ? atoi(tvmazeId) : m_tvmazeId;
|
||||
|
||||
if (m_imdbId != 0)
|
||||
{
|
||||
m_dupeKey.Format("imdb=%i", m_imdbId);
|
||||
}
|
||||
else if (!Util::EmptyStr(series) && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("series=%s-%s-%s", series, *m_season, *m_episode);
|
||||
}
|
||||
else if (rageIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("rageid=%i-%s-%s", rageIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else if (tvdbIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("tvdbid=%i-%s-%s", tvdbIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else if (tvmazeIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("tvmazeid=%i-%s-%s", tvmazeIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dupeKey = "";
|
||||
}
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetSeasonNum()
|
||||
{
|
||||
if (!m_season && !m_seasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_seasonNum;
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetEpisodeNum()
|
||||
{
|
||||
if (!m_episode && !m_seasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_episodeNum;
|
||||
}
|
||||
|
||||
void FeedItemInfo::ParseSeasonEpisode()
|
||||
{
|
||||
m_seasonEpisodeParsed = true;
|
||||
|
||||
const char* pattern = "[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]";
|
||||
|
||||
std::unique_ptr<RegEx>& regEx = m_feedFilterHelper->GetRegEx(1);
|
||||
if (!regEx)
|
||||
{
|
||||
regEx = std::make_unique<RegEx>(pattern, 10);
|
||||
}
|
||||
|
||||
if (regEx->Match(m_title))
|
||||
{
|
||||
SetSeason(BString<100>("S%02d", atoi(m_title + regEx->GetMatchStart(1))));
|
||||
|
||||
BString<100> regValue;
|
||||
regValue.Set(m_title + regEx->GetMatchStart(2), regEx->GetMatchLen(2));
|
||||
|
||||
BString<100> episode("E%s", *regValue);
|
||||
Util::ReduceStr(episode, "-", "");
|
||||
for (char* p = episode; *p; p++) *p = toupper(*p); // convert string to uppercase e02 -> E02
|
||||
SetEpisode(episode);
|
||||
}
|
||||
}
|
||||
|
||||
const char* FeedItemInfo::GetDupeStatus()
|
||||
{
|
||||
if (!m_dupeStatus)
|
||||
{
|
||||
BString<1024> statuses;
|
||||
m_feedFilterHelper->CalcDupeStatus(m_title, m_dupeKey, statuses, statuses.Capacity());
|
||||
m_dupeStatus = statuses;
|
||||
}
|
||||
|
||||
return m_dupeStatus;
|
||||
}
|
||||
|
||||
|
||||
void FeedHistory::Remove(const char* url)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
FeedHistoryInfo& feedHistoryInfo = *it;
|
||||
if (!strcmp(feedHistoryInfo.GetUrl(), url))
|
||||
{
|
||||
erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedHistoryInfo* FeedHistory::Find(const char* url)
|
||||
{
|
||||
for (FeedHistoryInfo& feedHistoryInfo : this)
|
||||
{
|
||||
if (!strcmp(feedHistoryInfo.GetUrl(), url))
|
||||
{
|
||||
return &feedHistoryInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
263
daemon/feed/FeedInfo.h
Normal file
263
daemon/feed/FeedInfo.h
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDINFO_H
|
||||
#define FEEDINFO_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Util.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class FeedInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
fsUndefined,
|
||||
fsRunning,
|
||||
fsFinished,
|
||||
fsFailed
|
||||
};
|
||||
|
||||
FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
|
||||
const char* filter, bool pauseNzb, const char* category, int priority,
|
||||
const char* extensions);
|
||||
int GetId() { return m_id; }
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetUrl() { return m_url; }
|
||||
int GetInterval() { return m_interval; }
|
||||
const char* GetFilter() { return m_filter; }
|
||||
uint32 GetFilterHash() { return m_filterHash; }
|
||||
bool GetPauseNzb() { return m_pauseNzb; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
int GetPriority() { return m_priority; }
|
||||
const char* GetExtensions() { return m_extensions; }
|
||||
time_t GetLastUpdate() { return m_lastUpdate; }
|
||||
void SetLastUpdate(time_t lastUpdate) { m_lastUpdate = lastUpdate; }
|
||||
time_t GetNextUpdate() { return m_nextUpdate; }
|
||||
void SetNextUpdate(time_t nextUpdate) { m_nextUpdate = nextUpdate; }
|
||||
int GetLastInterval() { return m_lastInterval; }
|
||||
void SetLastInterval(int lastInterval) { m_lastInterval = lastInterval; }
|
||||
bool GetPreview() { return m_preview; }
|
||||
void SetPreview(bool preview) { m_preview = preview; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus Status) { m_status = Status; }
|
||||
const char* GetOutputFilename() { return m_outputFilename; }
|
||||
void SetOutputFilename(const char* outputFilename) { m_outputFilename = outputFilename; }
|
||||
bool GetFetch() { return m_fetch; }
|
||||
void SetFetch(bool fetch) { m_fetch = fetch; }
|
||||
bool GetForce() { return m_force; }
|
||||
void SetForce(bool force) { m_force = force; }
|
||||
bool GetBacklog() { return m_backlog; }
|
||||
void SetBacklog(bool backlog) { m_backlog = backlog; }
|
||||
|
||||
private:
|
||||
int m_id;
|
||||
CString m_name;
|
||||
CString m_url;
|
||||
bool m_backlog;
|
||||
int m_interval;
|
||||
CString m_filter;
|
||||
uint32 m_filterHash;
|
||||
bool m_pauseNzb;
|
||||
CString m_category;
|
||||
CString m_extensions;
|
||||
int m_priority;
|
||||
time_t m_lastUpdate = 0;
|
||||
time_t m_nextUpdate = 0;
|
||||
int m_lastInterval = 0;
|
||||
bool m_preview = false;
|
||||
EStatus m_status = fsUndefined;
|
||||
CString m_outputFilename;
|
||||
bool m_fetch = false;
|
||||
bool m_force = false;
|
||||
};
|
||||
|
||||
typedef std::deque<std::unique_ptr<FeedInfo>> Feeds;
|
||||
|
||||
class FeedFilterHelper
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<RegEx>& GetRegEx(int id) = 0;
|
||||
virtual void CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen) = 0;
|
||||
};
|
||||
|
||||
class FeedItemInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
isUnknown,
|
||||
isBacklog,
|
||||
isFetched,
|
||||
isNew
|
||||
};
|
||||
|
||||
enum EMatchStatus
|
||||
{
|
||||
msIgnored,
|
||||
msAccepted,
|
||||
msRejected
|
||||
};
|
||||
|
||||
class Attr
|
||||
{
|
||||
public:
|
||||
Attr(const char* name, const char* value) :
|
||||
m_name(name ? name : ""), m_value(value ? value : "") {}
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetValue() { return m_value; }
|
||||
private:
|
||||
CString m_name;
|
||||
CString m_value;
|
||||
};
|
||||
|
||||
typedef std::deque<Attr> AttributesBase;
|
||||
|
||||
class Attributes: public AttributesBase
|
||||
{
|
||||
public:
|
||||
Attr* Find(const char* name);
|
||||
};
|
||||
|
||||
FeedItemInfo() {}
|
||||
FeedItemInfo(FeedItemInfo&&) = delete; // catch performance issues
|
||||
void SetFeedFilterHelper(FeedFilterHelper* feedFilterHelper) { m_feedFilterHelper = feedFilterHelper; }
|
||||
const char* GetTitle() { return m_title; }
|
||||
void SetTitle(const char* title) { m_title = title; }
|
||||
const char* GetFilename() { return m_filename; }
|
||||
void SetFilename(const char* filename) { m_filename = filename; }
|
||||
const char* GetUrl() { return m_url; }
|
||||
void SetUrl(const char* url) { m_url = url; }
|
||||
int64 GetSize() { return m_size; }
|
||||
void SetSize(int64 size) { m_size = size; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
void SetCategory(const char* category) { m_category = category; }
|
||||
int GetImdbId() { return m_imdbId; }
|
||||
void SetImdbId(int imdbId) { m_imdbId = imdbId; }
|
||||
int GetRageId() { return m_rageId; }
|
||||
void SetRageId(int rageId) { m_rageId = rageId; }
|
||||
int GetTvdbId() { return m_tvdbId; }
|
||||
void SetTvdbId(int tvdbId) { m_tvdbId = tvdbId; }
|
||||
int GetTvmazeId() { return m_tvmazeId; }
|
||||
void SetTvmazeId(int tvmazeId) { m_tvmazeId = tvmazeId; }
|
||||
const char* GetDescription() { return m_description; }
|
||||
void SetDescription(const char* description) { m_description = description ? description: ""; }
|
||||
const char* GetSeason() { return m_season; }
|
||||
void SetSeason(const char* season);
|
||||
const char* GetEpisode() { return m_episode; }
|
||||
void SetEpisode(const char* episode);
|
||||
int GetSeasonNum();
|
||||
int GetEpisodeNum();
|
||||
const char* GetAddCategory() { return m_addCategory; }
|
||||
void SetAddCategory(const char* addCategory) { m_addCategory = addCategory ? addCategory : ""; }
|
||||
bool GetPauseNzb() { return m_pauseNzb; }
|
||||
void SetPauseNzb(bool pauseNzb) { m_pauseNzb = pauseNzb; }
|
||||
int GetPriority() { return m_priority; }
|
||||
void SetPriority(int priority) { m_priority = priority; }
|
||||
time_t GetTime() { return m_time; }
|
||||
void SetTime(time_t time) { m_time = time; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus status) { m_status = status; }
|
||||
EMatchStatus GetMatchStatus() { return m_matchStatus; }
|
||||
void SetMatchStatus(EMatchStatus matchStatus) { m_matchStatus = matchStatus; }
|
||||
int GetMatchRule() { return m_matchRule; }
|
||||
void SetMatchRule(int matchRule) { m_matchRule = matchRule; }
|
||||
const char* GetDupeKey() { return m_dupeKey; }
|
||||
void SetDupeKey(const char* dupeKey) { m_dupeKey = dupeKey ? dupeKey : ""; }
|
||||
void AppendDupeKey(const char* extraDupeKey);
|
||||
void BuildDupeKey(const char* rageId, const char* tvdbId, const char* tvmazeId, const char* series);
|
||||
int GetDupeScore() { return m_dupeScore; }
|
||||
void SetDupeScore(int dupeScore) { m_dupeScore = dupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_dupeMode; }
|
||||
void SetDupeMode(EDupeMode dupeMode) { m_dupeMode = dupeMode; }
|
||||
const char* GetDupeStatus();
|
||||
Attributes* GetAttributes() { return &m_attributes; }
|
||||
|
||||
private:
|
||||
CString m_title;
|
||||
CString m_filename;
|
||||
CString m_url;
|
||||
time_t m_time = 0;
|
||||
int64 m_size = 0;
|
||||
CString m_category = "";
|
||||
int m_imdbId = 0;
|
||||
int m_rageId = 0;
|
||||
int m_tvdbId = 0;
|
||||
int m_tvmazeId = 0;
|
||||
CString m_description = "";
|
||||
CString m_season;
|
||||
CString m_episode;
|
||||
int m_seasonNum = 0;
|
||||
int m_episodeNum = 0;
|
||||
bool m_seasonEpisodeParsed = false;
|
||||
CString m_addCategory = "";
|
||||
bool m_pauseNzb = false;
|
||||
int m_priority = 0;
|
||||
EStatus m_status = isUnknown;
|
||||
EMatchStatus m_matchStatus = msIgnored;
|
||||
int m_matchRule = 0;
|
||||
CString m_dupeKey;
|
||||
int m_dupeScore = 0;
|
||||
EDupeMode m_dupeMode = dmScore;
|
||||
CString m_dupeStatus;
|
||||
FeedFilterHelper* m_feedFilterHelper = nullptr;
|
||||
Attributes m_attributes;
|
||||
|
||||
int ParsePrefixedInt(const char *value);
|
||||
void ParseSeasonEpisode();
|
||||
};
|
||||
|
||||
typedef std::deque<FeedItemInfo> FeedItemList;
|
||||
|
||||
class FeedHistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
hsUnknown,
|
||||
hsBacklog,
|
||||
hsFetched
|
||||
};
|
||||
|
||||
FeedHistoryInfo(const char* url, EStatus status, time_t lastSeen) :
|
||||
m_url(url), m_status(status), m_lastSeen(lastSeen) {}
|
||||
const char* GetUrl() { return m_url; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus Status) { m_status = Status; }
|
||||
time_t GetLastSeen() { return m_lastSeen; }
|
||||
void SetLastSeen(time_t lastSeen) { m_lastSeen = lastSeen; }
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
EStatus m_status;
|
||||
time_t m_lastSeen;
|
||||
};
|
||||
|
||||
typedef std::deque<FeedHistoryInfo> FeedHistoryBase;
|
||||
|
||||
class FeedHistory : public FeedHistoryBase
|
||||
{
|
||||
public:
|
||||
void Remove(const char* url);
|
||||
FeedHistoryInfo* Find(const char* url);
|
||||
};
|
||||
|
||||
#endif
|
||||
161
daemon/frontend/ColoredFrontend.cpp
Normal file
161
daemon/frontend/ColoredFrontend.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ColoredFrontend.h"
|
||||
#include "Util.h"
|
||||
|
||||
ColoredFrontend::ColoredFrontend()
|
||||
{
|
||||
m_summary = true;
|
||||
#ifdef WIN32
|
||||
m_console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforePrint()
|
||||
{
|
||||
if (m_needGoBack)
|
||||
{
|
||||
// go back one line
|
||||
#ifdef WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
||||
GetConsoleScreenBufferInfo(m_console, &BufInfo);
|
||||
BufInfo.dwCursorPosition.Y--;
|
||||
SetConsoleCursorPosition(m_console, BufInfo.dwCursorPosition);
|
||||
#else
|
||||
printf("\r\033[1A");
|
||||
#endif
|
||||
m_needGoBack = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintStatus()
|
||||
{
|
||||
BString<100> timeString;
|
||||
int currentDownloadSpeed = m_standBy ? 0 : m_currentDownloadSpeed;
|
||||
|
||||
if (currentDownloadSpeed > 0 && !m_pauseDownload)
|
||||
{
|
||||
int64 remain_sec = (int64)(m_remainingSize / currentDownloadSpeed);
|
||||
int h = (int)(remain_sec / 3600);
|
||||
int m = (int)((remain_sec % 3600) / 60);
|
||||
int s = (int)(remain_sec % 60);
|
||||
timeString.Format(" (~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
}
|
||||
|
||||
BString<100> downloadLimit;
|
||||
if (m_downloadLimit > 0)
|
||||
{
|
||||
downloadLimit.Format(", Limit %i KB/s", m_downloadLimit / 1024);
|
||||
}
|
||||
|
||||
BString<100> postStatus;
|
||||
if (m_postJobCount > 0)
|
||||
{
|
||||
postStatus.Format(", %i post-job%s", m_postJobCount, m_postJobCount > 1 ? "s" : "");
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
const char* controlSeq = "";
|
||||
#else
|
||||
printf("\033[s");
|
||||
const char* controlSeq = "\033[K";
|
||||
#endif
|
||||
|
||||
BString<1024> status(" %d threads, %s, %s remaining%s%s%s%s%s\n",
|
||||
m_threadCount, *Util::FormatSpeed(currentDownloadSpeed),
|
||||
*Util::FormatSize(m_remainingSize), *timeString, *postStatus,
|
||||
m_pauseDownload ? (m_standBy ? ", Paused" : ", Pausing") : "",
|
||||
*downloadLimit, controlSeq);
|
||||
printf("%s", *status);
|
||||
m_needGoBack = true;
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message& message)
|
||||
{
|
||||
#ifdef WIN32
|
||||
switch (message.GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
SetConsoleTextAttribute(m_console, 8);
|
||||
printf("[DEBUG]");
|
||||
break;
|
||||
case Message::mkError:
|
||||
SetConsoleTextAttribute(m_console, 4);
|
||||
printf("[ERROR]");
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
SetConsoleTextAttribute(m_console, 5);
|
||||
printf("[WARNING]");
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
SetConsoleTextAttribute(m_console, 2);
|
||||
printf("[INFO]");
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
SetConsoleTextAttribute(m_console, 2);
|
||||
printf("[DETAIL]");
|
||||
break;
|
||||
}
|
||||
SetConsoleTextAttribute(m_console, 7);
|
||||
CString msg = message.GetText();
|
||||
CharToOem(msg, msg);
|
||||
printf(" %s\n", *msg);
|
||||
#else
|
||||
const char* msg = message.GetText();
|
||||
switch (message.GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("\033[31m[ERROR]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("\033[35m[WARNING]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("\033[32m[DETAIL]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintSkip()
|
||||
{
|
||||
#ifdef WIN32
|
||||
printf(".....\n");
|
||||
#else
|
||||
printf(".....\033[K\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforeExit()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -15,12 +15,7 @@
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
@@ -32,22 +27,22 @@
|
||||
|
||||
class ColoredFrontend : public LoggableFrontend
|
||||
{
|
||||
private:
|
||||
bool m_bNeedGoBack;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE m_hConsole;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void BeforePrint();
|
||||
virtual void PrintMessage(Message* pMessage);
|
||||
virtual void PrintStatus();
|
||||
virtual void PrintSkip();
|
||||
virtual void BeforeExit();
|
||||
|
||||
public:
|
||||
ColoredFrontend();
|
||||
|
||||
protected:
|
||||
virtual void BeforePrint();
|
||||
virtual void PrintMessage(Message& message);
|
||||
virtual void PrintStatus();
|
||||
virtual void PrintSkip();
|
||||
virtual void BeforeExit();
|
||||
|
||||
private:
|
||||
bool m_needGoBack = false;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE m_console;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
338
daemon/frontend/Frontend.cpp
Normal file
338
daemon/frontend/Frontend.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
#include "StatMeter.h"
|
||||
|
||||
Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_workStateObserver.m_owner = this;
|
||||
g_WorkState->Attach(&m_workStateObserver);
|
||||
|
||||
m_updateInterval = g_Options->GetUpdateInterval();
|
||||
}
|
||||
|
||||
void Frontend::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void Frontend::WorkStateUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_summary || m_fileList) && !RequestFileList()))
|
||||
{
|
||||
const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", controlIp, g_Options->GetControlPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_summary)
|
||||
{
|
||||
m_currentDownloadSpeed = g_StatMeter->CalcCurrentDownloadSpeed();
|
||||
m_pauseDownload = g_WorkState->GetPauseDownload();
|
||||
m_downloadLimit = g_WorkState->GetSpeedLimit();
|
||||
m_threadCount = Thread::GetThreadCount();
|
||||
g_StatMeter->CalcTotalStat(&m_upTimeSec, &m_dnTimeSec, &m_allBytes, &m_standBy);
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
m_postJobCount = 0;
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
m_postJobCount += nzbInfo->GetPostInfo() ? 1 : 0;
|
||||
}
|
||||
downloadQueue->CalcRemainingSize(&m_remainingSize, nullptr);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frontend::FreeData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
m_remoteMessages.clear();
|
||||
DownloadQueue::Guard()->GetQueue()->clear();
|
||||
}
|
||||
}
|
||||
|
||||
GuardedMessageList Frontend::GuardMessages()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return GuardedMessageList(&m_remoteMessages, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_Log->GuardMessages();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::IsRemoteMode()
|
||||
{
|
||||
return g_Options->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool pause)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(pause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_WorkState->SetResumeTime(0);
|
||||
g_WorkState->SetPauseDownload(pause);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(int rate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_WorkState->SetSpeedLimit(rate);
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(DownloadQueue::EEditAction action, int offset, int id)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(action, offset, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DownloadQueue::Guard()->EditEntry(id, action, CString::FormatStr("%i", offset));
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNzbRequestBase* messageBase, int request, int size)
|
||||
{
|
||||
messageBase->m_signature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
messageBase->m_type = htonl(request);
|
||||
messageBase->m_structSize = htonl(size);
|
||||
|
||||
strncpy(messageBase->m_username, g_Options->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
messageBase->m_username[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
|
||||
strncpy(messageBase->m_password, g_Options->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
messageBase->m_password[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
|
||||
Connection connection(controlIp, g_Options->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNzbLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_messageBase, rrLog, sizeof(LogRequest));
|
||||
LogRequest.m_lines = htonl(m_neededLogEntries);
|
||||
if (m_neededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_idFrom = htonl(m_neededLogFirstId > 0 ? m_neededLogFirstId : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRequest.m_idFrom = 0;
|
||||
}
|
||||
|
||||
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNzbLogResponse LogResponse;
|
||||
bool read = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (!read ||
|
||||
(int)ntohl(LogResponse.m_messageBase.m_signature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_messageBase.m_structSize) != sizeof(LogResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CharBuffer buf;
|
||||
if (ntohl(LogResponse.m_trailingDataLength) > 0)
|
||||
{
|
||||
buf.Reserve(ntohl(LogResponse.m_trailingDataLength));
|
||||
if (!connection.Recv(buf, buf.Size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (ntohl(LogResponse.m_trailingDataLength) > 0)
|
||||
{
|
||||
char* bufPtr = (char*)buf;
|
||||
for (uint32 i = 0; i < ntohl(LogResponse.m_nrTrailingEntries); i++)
|
||||
{
|
||||
SNzbLogResponseEntry* logAnswer = (SNzbLogResponseEntry*) bufPtr;
|
||||
|
||||
char* text = bufPtr + sizeof(SNzbLogResponseEntry);
|
||||
|
||||
m_remoteMessages.emplace_back(ntohl(logAnswer->m_id), (Message::EKind)ntohl(logAnswer->m_kind), ntohl(logAnswer->m_time), text);
|
||||
|
||||
bufPtr += sizeof(SNzbLogResponseEntry) + ntohl(logAnswer->m_textLen);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
const char* controlIp = !strcmp(g_Options->GetControlIp(), "0.0.0.0") ? "127.0.0.1" : g_Options->GetControlIp();
|
||||
Connection connection(controlIp, g_Options->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNzbListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_messageBase, rrList, sizeof(ListRequest));
|
||||
ListRequest.m_fileList = htonl(m_fileList);
|
||||
ListRequest.m_serverState = htonl(m_summary);
|
||||
|
||||
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNzbListResponse ListResponse;
|
||||
bool read = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (!read ||
|
||||
(int)ntohl(ListResponse.m_messageBase.m_signature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_messageBase.m_structSize) != sizeof(ListResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CharBuffer buf;
|
||||
if (ntohl(ListResponse.m_trailingDataLength) > 0)
|
||||
{
|
||||
buf.Reserve(ntohl(ListResponse.m_trailingDataLength));
|
||||
if (!connection.Recv(buf, buf.Size()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_summary)
|
||||
{
|
||||
m_pauseDownload = ntohl(ListResponse.m_downloadPaused);
|
||||
m_remainingSize = Util::JoinInt64(ntohl(ListResponse.m_remainingSizeHi), ntohl(ListResponse.m_remainingSizeLo));
|
||||
m_currentDownloadSpeed = ntohl(ListResponse.m_downloadRate);
|
||||
m_downloadLimit = ntohl(ListResponse.m_downloadLimit);
|
||||
m_threadCount = ntohl(ListResponse.m_threadCount);
|
||||
m_postJobCount = ntohl(ListResponse.m_postJobCount);
|
||||
m_upTimeSec = ntohl(ListResponse.m_upTimeSec);
|
||||
m_dnTimeSec = ntohl(ListResponse.m_downloadTimeSec);
|
||||
m_standBy = ntohl(ListResponse.m_downloadStandBy);
|
||||
m_allBytes = Util::JoinInt64(ntohl(ListResponse.m_downloadedBytesHi), ntohl(ListResponse.m_downloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_fileList && ntohl(ListResponse.m_trailingDataLength) > 0)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
|
||||
client.BuildFileList(&ListResponse, buf, DownloadQueue::Guard());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool pause)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(pause, rpDownload);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(int rate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(rate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(DownloadQueue::EEditAction action, int offset, int id)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
IdList ids = { id };
|
||||
return client.RequestServerEditQueue(action, offset, nullptr, &ids, nullptr, rmId);
|
||||
}
|
||||
|
||||
void Frontend::Wait(int milliseconds)
|
||||
{
|
||||
if (g_WorkState->GetPauseFrontend())
|
||||
{
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.WaitFor(m_waitMutex, 2000);
|
||||
}
|
||||
else
|
||||
{
|
||||
Util::Sleep(milliseconds);
|
||||
}
|
||||
}
|
||||
88
daemon/frontend/Frontend.h
Normal file
88
daemon/frontend/Frontend.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FRONTEND_H
|
||||
#define FRONTEND_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "Observer.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
public:
|
||||
Frontend();
|
||||
|
||||
protected:
|
||||
bool m_summary = false;
|
||||
bool m_fileList = false;
|
||||
uint32 m_neededLogEntries = 0;
|
||||
uint32 m_neededLogFirstId = 0;
|
||||
int m_updateInterval;
|
||||
|
||||
// summary
|
||||
int m_currentDownloadSpeed = 0;
|
||||
int64 m_remainingSize = 0;
|
||||
bool m_pauseDownload = false;
|
||||
int m_downloadLimit = 0;
|
||||
int m_threadCount = 0;
|
||||
int m_postJobCount = 0;
|
||||
int m_upTimeSec = 0;
|
||||
int m_dnTimeSec = 0;
|
||||
int64 m_allBytes = 0;
|
||||
bool m_standBy = false;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
|
||||
virtual void Stop();
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
GuardedMessageList GuardMessages();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNzbRequestBase* messageBase, int request, int size);
|
||||
void ServerPauseUnpause(bool pause);
|
||||
bool RequestPauseUnpause(bool pause);
|
||||
void ServerSetDownloadRate(int rate);
|
||||
bool RequestSetDownloadRate(int rate);
|
||||
bool ServerEditQueue(DownloadQueue::EEditAction action, int offset, int entry);
|
||||
bool RequestEditQueue(DownloadQueue::EEditAction action, int offset, int id);
|
||||
void Wait(int milliseconds);
|
||||
|
||||
private:
|
||||
class WorkStateObserver : public Observer
|
||||
{
|
||||
public:
|
||||
Frontend* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
MessageList m_remoteMessages;
|
||||
WorkStateObserver m_workStateObserver;
|
||||
|
||||
bool RequestMessages();
|
||||
bool RequestFileList();
|
||||
void WorkStateUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -15,43 +15,15 @@
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
#include "LoggableFrontend.h"
|
||||
#include "Log.h"
|
||||
|
||||
LoggableFrontend::LoggableFrontend()
|
||||
{
|
||||
debug("Creating LoggableFrontend");
|
||||
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
}
|
||||
|
||||
void LoggableFrontend::Run()
|
||||
{
|
||||
debug("Entering LoggableFrontend-loop");
|
||||
@@ -59,7 +31,7 @@ void LoggableFrontend::Run()
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
usleep(m_iUpdateInterval * 1000);
|
||||
Wait(m_updateInterval);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
@@ -79,23 +51,24 @@ void LoggableFrontend::Update()
|
||||
|
||||
BeforePrint();
|
||||
|
||||
Log::Messages* pMessages = LockMessages();
|
||||
if (!pMessages->empty())
|
||||
{
|
||||
Message* pFirstMessage = pMessages->front();
|
||||
int iStart = m_iNeededLogFirstID - pFirstMessage->GetID() + 1;
|
||||
if (iStart < 0)
|
||||
GuardedMessageList messages = GuardMessages();
|
||||
if (!messages->empty())
|
||||
{
|
||||
PrintSkip();
|
||||
iStart = 0;
|
||||
}
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
PrintMessage((*pMessages)[i]);
|
||||
m_iNeededLogFirstID = (*pMessages)[i]->GetID();
|
||||
Message& firstMessage = messages->front();
|
||||
int start = m_neededLogFirstId - firstMessage.GetId() + 1;
|
||||
if (start < 0)
|
||||
{
|
||||
PrintSkip();
|
||||
start = 0;
|
||||
}
|
||||
for (uint32 i = (uint32)start; i < messages->size(); i++)
|
||||
{
|
||||
PrintMessage(messages->at(i));
|
||||
m_neededLogFirstId = messages->at(i).GetId();
|
||||
}
|
||||
}
|
||||
}
|
||||
UnlockMessages();
|
||||
|
||||
PrintStatus();
|
||||
|
||||
@@ -104,15 +77,16 @@ void LoggableFrontend::Update()
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
void LoggableFrontend::PrintMessage(Message& message)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
CharToOem(msg, msg);
|
||||
CString cmsg = message.GetText();
|
||||
CharToOem(cmsg, cmsg);
|
||||
const char* msg = cmsg;
|
||||
#else
|
||||
const char* msg = pMessage->GetText();
|
||||
const char* msg = message.GetText();
|
||||
#endif
|
||||
switch (pMessage->GetKind())
|
||||
switch (message.GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", msg);
|
||||
@@ -126,10 +100,10 @@ void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("[DETAIL] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
#ifdef WIN32
|
||||
free(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintSkip()
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -15,12 +15,7 @@
|
||||
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
@@ -33,16 +28,13 @@
|
||||
class LoggableFrontend : public Frontend
|
||||
{
|
||||
protected:
|
||||
|
||||
virtual void Run();
|
||||
virtual void Update();
|
||||
virtual void BeforePrint() {};
|
||||
virtual void BeforeExit() {};
|
||||
virtual void PrintMessage(Message* pMessage);
|
||||
virtual void PrintStatus() {};
|
||||
virtual void PrintSkip();
|
||||
public:
|
||||
LoggableFrontend();
|
||||
virtual void Run();
|
||||
virtual void Update();
|
||||
virtual void BeforePrint() {};
|
||||
virtual void BeforeExit() {};
|
||||
virtual void PrintMessage(Message& message);
|
||||
virtual void PrintStatus() {};
|
||||
virtual void PrintSkip();
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user